/*
 * nvm_bin_update.c
 *
 *  Created on: 19-Dec-2023
 *      Author: Invcuser52
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "exo_types.h"
#include "exo_osal_mem_cpy.h"
#include "exo_io_al_crc_common.h"
#include "nvm.h"


#ifndef LINUX_TEMP_PORT

#ifdef COREBOARD
/* Use FMC flash address 0x60000000 for NVM in coreboard, size 256kb
 * Bank 1 start address = 0x60000000, 128kb
 * Bank 2 start address = 0x60020000, 128kb
 *  */
#define PLACE_IN_EXT_FLASH_NVM __attribute__((aligned(128*1024))) __attribute__ ((section (".ext_fmcflash_nvm")))
#else
/* Use QSPI flash address 0x90000000 for NVM in discovery, size 256kb
 * Bank 1 start address = 0x90000000, 128kb
 * Bank 2 start address = 0x90020000, 128kb
 *  */
#define PLACE_IN_EXT_FLASH_NVM __attribute__((aligned(4*1024))) __attribute__ ((section (".ext_qspiflash_nvm")))
#endif


/*
 * storage for nvm update
 * It will have four location to store nvm data to adhere obc firmware update process
 * Location A - default nvm bin for running obc software
 * Location B - default nvm in for backup obc software
 * Location C - default nvm in for new obc software downloaded via ftm
 * Location D - default nvm in for factory obc software
 */

/* Allocated external flash for NVM module 128 kb*/
static uint8_t PLACE_IN_EXT_FLASH_NVM ext_mem_flash_nvm_locA[1024*128];
/* Allocated external flash for NVM module 128 kb*/
static uint8_t PLACE_IN_EXT_FLASH_NVM ext_mem_flash_nvm_locB[1024*128];
/* Allocated external flash for NVM module 128 kb*/
static uint8_t PLACE_IN_EXT_FLASH_NVM ext_mem_flash_nvm_locC[1024*128];
/* Allocated external flash for NVM module 128 kb*/
static uint8_t PLACE_IN_EXT_FLASH_NVM ext_mem_flash_nvm_locD[1024*128];


#endif


static void nvm_new_fw_image_prepration(nvm_mem_storage_id src_loc, nvm_mem_storage_id dest_loc);



/*
 * @breif : This API will return the storage address pointer.
 */
uint8_t* nvm_get_nvm_fwupd_memory_partition_address(nvm_mem_storage_id id)
{
	uint8_t* ret_addr=NULL;

#ifndef LINUX_TEMP_PORT

	switch(id)
	{
	case NVM_FW_UPDATE_LOC_A:
		ret_addr = ext_mem_flash_nvm_locA;
		break;
	case NVM_FW_UPDATE_LOC_B:
		ret_addr = ext_mem_flash_nvm_locB;
		break;
	case NVM_FW_UPDATE_LOC_C:
		ret_addr = ext_mem_flash_nvm_locC;
		break;
	case NVM_FW_UPDATE_LOC_D:
		ret_addr = ext_mem_flash_nvm_locD;
		break;
	case NVM_STORAGE_BANK1:
	case NVM_STORAGE_BANK2:
	case NVM_STORAGE_RAM:
		ret_addr = nvm_get_nvm_framework_bank_address(id);
		break;
	default:
		return NULL;
		break;
	}
#endif

	return ret_addr;
}

/*
 * @breif This API is used to get nvm firmware size from nvm header
 * which is available in nvm image.
 */
//uint32_t nvm_fw_get_size()
//{
//	return 0;
//}


/*
 * @brief This API is used to copy nvm firmware from one location to another
 */
static void nvm_new_fw_image_prepration(nvm_mem_storage_id src_loc, nvm_mem_storage_id dest_loc)
{
	uint8_t *src_addrs;
	uint8_t *dest_addrs;
	nvm_firmware_info_def nvm_info;
	src_addrs = nvm_get_nvm_fwupd_memory_partition_address(src_loc);
	dest_addrs = nvm_get_nvm_fwupd_memory_partition_address(dest_loc);
	nvm_get_nvm_bin_info(src_addrs, &nvm_info);
	os_memcpy_extmem(dest_addrs, src_addrs, nvm_info.size+8, NULL, 1,1);

}

/*
 * @brief This function is used to prepare nvm memory partition based on
 * nvm firmware update process such as trigger cycle, nvm fw update fail condition and
 * nvm firmware update pass condition
 *
 *
 */
void nvm_fw_update(nvm_fw_upd_states_def nvm_fw_state)
{
	switch(nvm_fw_state)
	{
	case NVM_FW_UPDATE_TRIGGER:
		/*
		 * copy downaloded nvm firmware to location A, so that new firmware will be
		 * loaded by nvm framework after obc software update
		 * Also it takes backup of previous nvm firmware from location nvm bank to location B
		 * where location B is backup area
		 */
		nvm_new_fw_image_prepration(NVM_FW_UPDATE_LOC_C, NVM_FW_UPDATE_LOC_A);
		nvm_commit();
		nvm_new_fw_image_prepration(NVM_STORAGE_BANK1, NVM_FW_UPDATE_LOC_B);
		break;

	case NVM_FW_UPDATE_PASS:
		/*
		 * if obc software update has failed, [previous nvm backup can be retrieved
		 * from location B
		 */
		nvm_commit();
		nvm_new_fw_image_prepration(NVM_STORAGE_BANK1, NVM_FW_UPDATE_LOC_B);
		break;

	case NVM_FW_UPDATE_FAIL:
		/*
		 * if obc software update has failed, [previous nvm backup can be retrieved
		 *  from location B
		 * also new firmware firmware downaloded on location B will be erased and
		 *  overwritten with previous nvm data
		 */

		nvm_new_fw_image_prepration(NVM_FW_UPDATE_LOC_B, NVM_FW_UPDATE_LOC_A);
		nvm_new_fw_image_prepration(NVM_FW_UPDATE_LOC_B, NVM_FW_UPDATE_LOC_C);
		break;

	default:
		break;
	}
}

/*
 * @brief This function is used to copy nvm firmware from ftm memory
 *  to nvm download location C
 */
void nvm_fw_download_in_c(uint8_t *ftm_mem_buf, uint32_t size, uint32_t checksum)
{
	uint8_t *flash_addrs;
	uint32_t cal_checksum;

	flash_addrs = nvm_get_nvm_fwupd_memory_partition_address(NVM_FW_UPDATE_LOC_C);
	os_memcpy_extmem(flash_addrs, ftm_mem_buf, size, NULL, 1,1);
	cal_checksum = exo_io_hal_crc_calculate(EXO_SOFTWARE_CRC_MODE, flash_addrs, size);
	if(cal_checksum != checksum)
	{
		cal_checksum = 0;
		// file copy from ftm to flash has failed
		// LOG > NVM_FW_COPY_TO_FLASH_FAIL
	}
	else
	{
		cal_checksum = 0;
		// LOG > NVM_FW_COPY_TO_FLASH_PASS
	}
}
