/*
 * otsu_validate_fw.c
 *
 *  Created on: 29-Dec-2023
 *      Author: Invcuser52
 */


#include "otsu_validate_fw.h"
#include "exo_osal.h"
#include "logger.h"
#include "nvm.h"
#include "otsu.h"
#include "otsu_ftm.h"
#include "btable_prepare.h"
#include "otsu_mem_def.h"
#include "otsu_shutdown.h"
#include "flash_drive.h"
#include "exo_tctm_ipc.h"


#define GS_WAITTIME_PERIODIC_10MIN_TIMER_VALUE_IN_SEC 180
#define GS_WAITTIME_DEFAULT_VALUE_IN_MIN              2880

extern firmware_upd_info_def fw_upd_info;
extern s_btable_api_list_def *btable_api;
extern e_otsu_err_flag otsu_fw_error_flag;
extern s_otsu_fw_update_cfg_tc_encode_def fw_reboot_config;


/**
 * @brief This is the callback function which will be invoked by memory copy module
 * once it completed the copy operation for firmware copy from internal B location to NOR flash C
 *
 * @param[in]  cpy_sts  : status of memory copy completion status
 *
 * @return none
 * @retval void : none
 */
static void fw_validate_fail_overwriteC_compld_cblk(uint8_t cpy_sts, memcpy_info_save_otsu *copy_info);




/**
 * @brief This is the callback function which will be invoked by memory copy module
 * once it completed the copy operation for firmware copy from NOR flash to internal B location
 *
 * @param[in]  cpy_sts  : status of memory copy completion status
 *
 * @return none
 * @retval void : none
 */
static void fw_validate_pass_overwriteB_compld_cblk(uint8_t cpy_sts, memcpy_info_save_otsu *copy_info);


#ifdef BTLR_ON



static os_timer_handle_ptr gs_sync_wait_timer_hndl;


static void send_ipc_gs_contact_waittime_expired(void);
static void gs_contact_periodic_timer_cblk(void* args);

/*
 * @brief : This will read timer value from NVM and initiate timer.
 *
 *  create periodic timer with periodicity for every 10minutes (600000ms)
 *
 */
void gs_contact_wait_timer_init(void)
{
	log_id(LOG_INFO_L1,LOG_OTSU,GS_SYNC_WAIT_TIMER_CREATED);
	os_timer_create(&gs_sync_wait_timer_hndl, "otsu_gs_contact_wait_timer_hndl", PERIODIC, gs_contact_periodic_timer_cblk,NULL);
	os_delay(100);
	os_timer_start(gs_sync_wait_timer_hndl,(os_tick_time_t)GS_WAITTIME_PERIODIC_10MIN_TIMER_VALUE_IN_SEC*1000);
	otsu_state_change(e_otsu_gscont_waittime_started);
}



/*
 * @brief
 *
 *  1. read gs contact waiting time from nvm
 *  2. calculate checksum for 3 bytes and compare checksum against 4th byte
 *  3. check read data is not equal to 0 and 0xffffffff
 *  4. if value is invalid , use default waiting time value 2880(minutes for two days)
 *  5. subtract 10 minutes from nvm timer value and check timer has elapsed
 *  6. if timer elapsed trigger gs contact failure fallback
 */
void check_gs_contact_time_is_expired(void)
{
	uint32_t gs_wait_timer_val;
	uint16_t gs_wait_time;

	uint8_t calc_checksum;

    nvm_parameter_read(NVM_DATA,NVM_OTSU_GSCONT_FAIL_TIMEOUT,(uint8_t*)&gs_wait_timer_val);

    /*
     * if read timer value is greater than 2 days time value , reload default value as 2880
     */
	if( (gs_wait_timer_val != 0) && ((gs_wait_timer_val & 0xFFFFFF) <= GS_WAITTIME_DEFAULT_VALUE_IN_MIN))
	{
		calc_checksum = gs_wait_timer_val & 0xFF;
		calc_checksum ^= (gs_wait_timer_val >> 8) & 0xFF;
		calc_checksum ^= (gs_wait_timer_val >> 16) & 0xFF;

		if(calc_checksum == ((gs_wait_timer_val >> 24) & 0xFF))
		{
			/* read timer value is valid */

		}
		else
		{
			gs_wait_timer_val = GS_WAITTIME_DEFAULT_VALUE_IN_MIN;
		}

	}
	else
	{
		gs_wait_timer_val = GS_WAITTIME_DEFAULT_VALUE_IN_MIN;
	}

	gs_wait_time = gs_wait_timer_val & 0xffff;
	if((GS_WAITTIME_PERIODIC_10MIN_TIMER_VALUE_IN_SEC/60) < gs_wait_time)
	{
		/* time not elapsed, subtract 10 minutes from remaining time then write in nvm*/
		gs_wait_time -= (GS_WAITTIME_PERIODIC_10MIN_TIMER_VALUE_IN_SEC/60);

		if(gs_wait_time == 0)
		{
			gs_wait_time = 1;
		}

		/* calculate checksum and store in nvm */
		gs_wait_timer_val = gs_wait_time;
		calc_checksum = gs_wait_timer_val & 0xFF;
		calc_checksum ^= (gs_wait_timer_val >> 8) & 0xFF;
		calc_checksum ^= (gs_wait_timer_val >> 16) & 0xFF;
		gs_wait_timer_val = gs_wait_timer_val | ((uint32_t)calc_checksum << 24);
	    nvm_parameter_write(NVM_DATA, NVM_OTSU_GSCONT_FAIL_TIMEOUT, (uint8_t*)&gs_wait_timer_val);
		otsu_state_change(e_otsu_gscont_waittime_running);
	}
	else
	{
		/* gs contact waiting has expired , send fallback message*/
		send_ipc_gs_contact_waittime_expired();
		otsu_state_change(e_otsu_gscont_waittime_over);

	}
}


/*
 * @brief this callback will check every 10 minutes that gs contact wait time is over
 *
 *
 */
static void gs_contact_periodic_timer_cblk(void* args)
{
    otsu_send_message_to_otsu_fsm(OTSU_GS_CONTACT_TIMER_CALBACK_SERVICE, 0);
}

static void send_ipc_gs_contact_waittime_expired(void)
{
    otsu_send_message_to_otsu_fsm(OTSU_GS_SYNC_TIMEOUT_SCH, 0);
}


#endif



void gs_contact_timer_stop(void)
{
#ifdef BTLR_ON
	os_timer_stop(gs_sync_wait_timer_hndl);
#endif
}



/*
 *  @brief This callback api will be called by sband module if otsu requested sband to update the gs sync status
 *
 *  uint8_t sts = 1 or 2
 *  1 = pass
 *  2 = fail
 *
 */
void otsu_send_sband_sync_status_cblk(uint8_t sts)
{
	if(fw_upd_info.new_fw_loaded == NW_FW_AVAILABLE_SET)
	{
		if(sts == new_fw_validate_pass || sts == new_fw_validate_fail)
		{
			gs_contact_timer_stop();
			otsu_state_change(e_otsu_new_fw_validated);
			fw_upd_info.e_new_fw_working_sts = (otsu_new_fw_validate_sts)sts;

		}
		else
		{
			fw_upd_info.e_new_fw_working_sts = new_fw_validate_none;
		}
	}
	DEBUG_CPRINT(("OTSU - SBAND SYNC STS updated FW:%d, S:%d\n",fw_upd_info.new_fw_loaded,sts));
	log_id_with_two_arg(LOG_INFO_L1,LOG_OTSU,OTSU__SBAND_SYNC_STS_UPDATED_FW_u8__cm__S_u8_,fw_upd_info.new_fw_loaded,sts);

}



/**
 * @brief This is the callback function which will be invoked by memory copy module
 * once it completed the copy operation for firmware copy from internal B location to NOR flash C
 */
static void fw_validate_fail_overwriteC_compld_cblk(uint8_t cpy_sts, memcpy_info_save_otsu *copy_info)
{
	e_ret_sts cal_checksum;
	DEBUG_CPRINT(("Overwrite C callback called\n"));

	if(cpy_sts == 0)
	{
		cal_checksum = flash_integrity_check(copy_info->des_addrs,copy_info->size);
		if(cal_checksum == R_OK1)
		{
			DEBUG_CPRINT(("Loc C overwrite from B CRC has passed\n"));

		}
		else
		{
			DEBUG_CPRINT(("Loc C overwrite from B CRC has failed\n"));
            log_id(LOG_ERROR,LOG_OTSU,LOC_C_TO_B_FAILED);
		}
	}
	else
	{
		otsu_fw_error_flag = mem_cpy_err;
		otsu_encode_tm(TM_OTSU_NOTIFICATION, otfs_flash_copy_fail,0xCD);
		DEBUG_CPRINT(("Overwrite C callback called - Fail\n"));
		log_id(LOG_ERROR,LOG_OTSU,OVERWRITE_C_CALLBACK_CALLED__FAIL);

	}
	otsu_state_change(e_otsu_shutdown_publish);

	DEBUG_CPRINT(("OTSU -> OBC Shutdown request create for Reboot\n"));
	log_id(LOG_WARNING,LOG_OTSU,OBC_SHUTDOWN_REQUEST_CREATE_FOR_REBOOT);
	otsu_state_change(e_otsu_shutdown_processsing);
	shtdwn_timer_trig();
	obc_shutdown_trigger();

}



/**
 * @brief This is the callback function which will be invoked by memory copy module
 * once it completed the copy operation for firmware copy from NOR flash to internal B location
 */
static void fw_validate_pass_overwriteB_compld_cblk(uint8_t cpy_sts, memcpy_info_save_otsu *copy_info)
{
	e_ret_sts cal_checksum;
	uint32_t fallback_level1_loc;

	DEBUG_CPRINT(("Overwrite B callback called\n"));

	if(cpy_sts == 0)
	{
		cal_checksum = flash_integrity_check(copy_info->des_addrs,copy_info->size);
		if(cal_checksum == R_OK1)
		{
			DEBUG_CPRINT(("Overwrite B CRC pass\n"));
			log_id(LOG_INFO_L1,LOG_OTSU,OVERWRITE_B_CALLBACK_CALLED);
			fallback_level1_loc = get_previous_fwstored_address();
		//	btable_fwrite_update_level1_fallback_info(boot_sel_src, boot_sel_dst, fallback_level1_loc);

			btable_swap();
			prepare_boottable_setup_for_module(fallback_level1_loc,BTL_WRITE, CLEAR_LOADING);
			btable_swap();

			otsu_state_change(e_otsu_new_fw_pass_overwrite_locB);

		}
		else
		{
			DEBUG_CPRINT(("Overwrite B CRC FAIL\n"));
            log_id(LOG_ERROR,LOG_OTSU,LOC_B_OVERWRITE_CRC_FAIL);
            otsu_state_change(e_otsu_new_fw_pass_overwrite_locB_fail);
			otsu_fw_error_flag = fw_nor_b_copy_err;
			otsu_encode_tm(TM_OTSU_NOTIFICATION, otfs_flash_copy_fail,0xCD);

		}
	}
	else
	{
		DEBUG_CPRINT(("Overwrite B callback called - Fail\n"));
		log_id(LOG_ERROR,LOG_OTSU,OVERWRITE_B_CALLBACK_CALLED__FAIL);
		otsu_state_change(e_otsu_new_fw_pass_overwrite_locB_norcopy_fail);
		otsu_fw_error_flag = mem_cpy_err;
		otsu_encode_tm(TM_OTSU_NOTIFICATION, otfs_flash_copy_fail,0xCD);
	}
}






void new_fw_validation_completed(void)
{
	uint32_t mem_address_src;
	uint32_t mem_address_dest;
	uint32_t bin_size;
	uint32_t bin_size_crc;
	uint8_t ret_sts;
	memcpy_info_save_otsu loc_memcpy_inf;

	if(fw_upd_info.e_new_fw_working_sts == new_fw_validate_fail)
	{
	/*
	 * new firmware validation fail
	 *  apply fallback level 1
	 */
		DEBUG_CPRINT(("New FW validation failed\n"));
        log_id(LOG_ERROR,LOG_OTSU,NEW_FW_VALIDATION_FAILED);

    	btable_get_active_boottable();
		bin_size = btable_get_module_bin_size_for_flash(0);
		bin_size_crc = btable_get_module_bin_size((uint8_t)0);
		mem_address_dest = otsu_get_fw_storage_address();
		mem_address_src = get_previous_fwstored_address();
		loc_memcpy_inf.src_addrs = mem_address_src;
		loc_memcpy_inf.des_addrs = mem_address_dest;
		loc_memcpy_inf.size = bin_size_crc;
		ret_sts = os_memcpy_extmem((uint8_t*)mem_address_dest, (uint8_t*)mem_address_src, bin_size, NULL, MEM_CPY_RUN_SAME_THREAD,1);
		otsu_state_change(e_otsu_new_fw_fail_overwrite_locC);
		DEBUG_CPRINT(("e_otsu_new_fw_fail_overwrite_locC in progress..\n"));
		fw_validate_fail_overwriteC_compld_cblk(ret_sts, &loc_memcpy_inf);
		fw_reboot_config.boot_operation_ctrl = BOOT_CONTROL_RESTORED_PREVIOUS_FW;
		nvm_parameter_write(NVM_DATA,NVM_OTSU_REBOOT_CONFIG,(uint8_t*)&fw_reboot_config);
		nvm_fw_update(NVM_FW_UPDATE_FAIL);
		otsu_encode_tm(TM_OTSU_NOTIFICATION, otfs_boot_fail,0xCD);
	}
	else if(fw_upd_info.e_new_fw_working_sts == new_fw_validate_pass)
	{
		/* new firmware validation pass  */
		/* overwrite previous version software with new firmware */

		/*
		 * TBD : perform copy operation here : From NOR flash to internal flash
		 */
		DEBUG_CPRINT(("New FW validation passed\n"));
        log_id(LOG_INFO_L1,LOG_OTSU,NEW_FW_VALIDATION_PASSED);

		mem_address_dest = get_previous_fwstored_address();
		mem_address_src = otsu_get_fw_storage_address();

		btable_get_active_boottable();
		btable_swap();
		bin_size = btable_get_module_bin_size_for_flash(0);
		bin_size_crc = btable_get_module_bin_size((uint8_t)0);
		btable_swap();
		loc_memcpy_inf.src_addrs = mem_address_src;
		loc_memcpy_inf.des_addrs = mem_address_dest;
		loc_memcpy_inf.size = bin_size_crc;
		/* Internal previous software storage flash memory to be erased */
		Flash_Erase_Data(mem_address_dest,bin_size/4);
	    //os_memcpy_extmem((uint8_t*)mem_address_src, (uint8_t*)mem_address_dest, bin_size, NULL, MEM_CPY_RUN_SAME_THREAD,1);
		ret_sts = os_memcpy_extmem((uint8_t*)mem_address_dest, (uint8_t*)mem_address_src, bin_size, NULL, MEM_CPY_RUN_SAME_THREAD,0);
		otsu_state_change(e_otsu_new_fw_pass_overwrite_locB);
		DEBUG_CPRINT(("e_otsu_new_fw_pass_overwrite_locB in progress..\n"));
		fw_validate_pass_overwriteB_compld_cblk(ret_sts, &loc_memcpy_inf);
		fw_reboot_config.boot_operation_ctrl = BOOT_CONTROL_FW_VALIDATION_PASS;
		nvm_parameter_write(NVM_DATA,NVM_OTSU_REBOOT_CONFIG,(uint8_t*)&fw_reboot_config);
		nvm_fw_update(NVM_FW_UPDATE_PASS);
		/* clear firmware validation request flag */
		fw_upd_info.new_fw_loaded = 0;
		otsu_encode_tm(TM_OTSU_NOTIFICATION, otfs_boot_pass,0xCD);
	}
	else
	{

	}

}








/*
 * this will check the current time and sband gs sync fail config time
 *
 */
void new_software_verification_fail(void)
{
	otsu_state_change(e_otsu_new_fw_fail);
    if(fw_upd_info.new_fw_loaded == NW_FW_AVAILABLE_SET)
	{
		DEBUG_CPRINT(("OTSU Sch Alarm pass for sband GS sync\n"));
		log_id(LOG_INFO_L1,LOG_OTSU,OTSU_SCH_ALARM_PASS_FOR_SBAND_GS_SYNC);
		otsu_send_sband_sync_status_cblk(new_fw_validate_fail);
		new_fw_validation_completed();
    }

}
