/**
 * vxr_interface.c
 *
 * Low-level commands for the VXR7200 Remote Control (RC) interface over I2C.
 * 
 * Copyright (c) 2022 Bigscreen, Inc.
 */

#include <asf.h>
#include <string.h>
#include <stdbool.h>
#include "main.h"
#include "i2c.h"
#include "rtt_util.h"
#include "vxr_interface.h"

bool vxr_interface_started = false;

static uint8_t rc_bus_write(uint32_t addr, uint32_t data) {
	uint8_t i2c_write_data[8];
	
	I2C_Transaction_Request i2ctxn = {
		.cb = NULL,
		.dev_addr = VXR_ADDR,
		.shift = I2C_Unshifted,
		.len = 8, // 32-bit address, 32-bit data
		.internal_addr = 0, // not used
		.data = i2c_write_data,
		.ttype = I2C_SIMPLE_WRITE
	};
	// Address and data are least-significant-byte first
	i2c_write_data[0] = (uint8_t)((addr & 0x000000FF) >> 0);
	i2c_write_data[1] = (uint8_t)((addr & 0x0000FF00) >> 8);
	i2c_write_data[2] = (uint8_t)((addr & 0x00FF0000) >> 16);
	i2c_write_data[3] = (uint8_t)((addr & 0xFF000000) >> 24);
	i2c_write_data[4] = (uint8_t)((data & 0x000000FF) >> 0);
	i2c_write_data[5] = (uint8_t)((data & 0x0000FF00) >> 8);
	i2c_write_data[6] = (uint8_t)((data & 0x00FF0000) >> 16);
	i2c_write_data[7] = (uint8_t)((data & 0xFF000000) >> 24);
	i2c_lock(DDIC_I2C());
	uint8_t retval = i2c_transact_blocking(DDIC_I2C(), &i2ctxn, DEFAULT_I2C_WAIT_TIME);
	i2c_unlock(DDIC_I2C());
	return retval;
}

static uint8_t rc_bus_read(uint32_t addr, uint32_t* data) {
	uint8_t retval;
	uint8_t i2c_read_data[4];
	I2C_Transaction_Request i2ctxn = {
		.cb = NULL,
		.dev_addr = VXR_ADDR,
		.shift = I2C_Unshifted,
		.len = 4, // 32-bit address or 32-bit data, two transactions to do the read
		.internal_addr = 0, // not used
		.data = i2c_read_data,
		.ttype = I2C_SIMPLE_WRITE // write address first, then switch to read
	};
	// Address and data are least-significant-byte first
	i2c_read_data[0] = (uint8_t)((addr & 0x000000FF) >> 0);
	i2c_read_data[1] = (uint8_t)((addr & 0x0000FF00) >> 8);
	i2c_read_data[2] = (uint8_t)((addr & 0x00FF0000) >> 16);
	i2c_read_data[3] = (uint8_t)((addr & 0xFF000000) >> 24);
	i2c_lock(DDIC_I2C());
	retval = i2c_transact_blocking(DDIC_I2C(), &i2ctxn, DEFAULT_I2C_WAIT_TIME);
	if(retval == pdPASS) {
		i2ctxn.ttype = I2C_SIMPLE_READ;
		retval = i2c_transact_blocking(DDIC_I2C(), &i2ctxn, DEFAULT_I2C_WAIT_TIME);
	}
	i2c_unlock(DDIC_I2C());
	if(retval == pdPASS) {
		*data = ((uint32_t)(i2c_read_data[0])) 
		+ (((uint32_t)(i2c_read_data[1])) << 8) 
		+ (((uint32_t)(i2c_read_data[2])) << 16) 
		+ (((uint32_t)(i2c_read_data[3])) << 24);
	}
	
	return retval;
}

// performs the command sequence to the VXR chip
static RC_Error_T rc_cmd_set(RC_Command_T cmd, uint32_t length, uint32_t offset, const uint8_t* data, uint32_t timeout) {
	uint8_t bus_error = pdPASS;
	uint8_t data_sent;
	uint8_t bytes_to_copy;
	uint32_t data_out_buf;
	
	// Set data, offset, length, and command first (any order is okay)
	
	// fill the data registers
	// length can be non-zero even without passing in data, for example in memory read commands
	// have to check if data pointer is null
	
	// Packed bytes. The data section is 32 bytes long, organized into 8x 4-byte words.
	// Data0 is B0 to B3, Data1 is B4-B7, and so on.
	if(data != NULL) {
		data_sent = 0;
		while(data_sent < length) {
			bytes_to_copy = (length - data_sent) >= 4 ? 4 : (length-data_sent); // 1 to 4 bytes at a time
			data_out_buf = 0;
			for(uint8_t i = 0; i < bytes_to_copy; i++) {
				data_out_buf += ((uint32_t)data[data_sent + i]) << (8*i);
			}
			if(bus_error == pdPASS) {
				bus_error = rc_bus_write(RC_DATA0_ADDR + (data_sent), data_out_buf);
			}
			data_sent += bytes_to_copy;
		}
	}
	
	// write command, length, and offset
	if(bus_error == pdPASS) {
		bus_error = rc_bus_write(RC_COMMAND_ADDR, RC_COMMAND_COMMAND(cmd) + RC_COMMAND_ACTIVE);
	}
	if(bus_error == pdPASS) {
		bus_error = rc_bus_write(RC_LENGTH_ADDR, length);
	}
	if(bus_error == pdPASS) {
		bus_error = rc_bus_write(RC_OFFSET_ADDR, offset);
	}
	
	// send the trigger
	if(bus_error == pdPASS) {
		bus_error = rc_bus_write(RC_TRIGGER_ADDR, RC_TRIGGER_VALUE);
	}

	uint32_t tempval = RC_COMMAND_ACTIVE;
	
	// timestamp of when we start
	uint32_t start_time = rtt_read_timer_value();
	
	// Wait on the command_active bit
	while((bus_error == pdPASS) && ((tempval & RC_COMMAND_ACTIVE) != 0)) {
		if(rtt_timeout_check(start_time, timeout)) {
			return RC_Timeout_Error;
		}
		bus_error = rc_bus_read(RC_COMMAND_ADDR, &tempval);
	}
	
	if(bus_error == pdFAIL) { return RC_Bus_Error; }
		
	// if everything went well, return the error code from the command register	
	return (uint8_t)((tempval & RC_COMMAND_RESULT_Msk) >> RC_COMMAND_RESULT_Pos);

}

RC_Error_T VXR_Reset(void) {
	// Trigger the reset pin on the VXR7200 to force it to reboot
	ioport_set_pin_level(PIN_DDIC_RST, false); // Active low, "false" asserts reset
	vTaskDelay(DDIC_RESET_DELAY_MS);
	ioport_set_pin_level(PIN_DDIC_RST, true); // release reset
	vTaskDelay(VXR_RC_INTERFACE_DELAY);
	return VXR_Start_Interface();
}

RC_Error_T VXR_Start_Interface(void) {
	// Send the RC_ENABLE command
	// requires a specific data sequence of 5 32-bit words:
	// "V", "X", "R", "0", "0"
	RC_Error_T retval;
	uint8_t enable_data[5] = {'V','X','R','0','0'};
	
	retval = rc_cmd_set(Enable_RC, 5, 0, enable_data, RC_DEFAULT_TIMEOUT);
	if(RC_Success == retval) {
		vxr_interface_started = true;
	}
	return retval;	
}

RC_Error_T VXR_Stop_Interface(void) {
	// send the RC_DISABLE command
	RC_Error_T retval;
	
	retval= rc_cmd_set(Disable_RC, 0, 0, NULL, RC_DEFAULT_TIMEOUT);
	if(retval == RC_Success) {
		vxr_interface_started = false;
	}
	return retval;
}

RC_Error_T VXR_Read_Register(uint32_t regaddr, uint32_t* result) {
	RC_Error_T retval;
	uint8_t buserror;
	retval = rc_cmd_set(Memory_Read, 4, regaddr, NULL, RC_DEFAULT_TIMEOUT);
	if(retval == RC_Success) {
		buserror = rc_bus_read(RC_DATA0_ADDR, result);
		if((buserror == pdPASS)) {
			return retval;
		} else {
			return RC_Bus_Error;
		}
	}
	return retval;
}
RC_Error_T VXR_Write_Register(uint32_t regaddr, uint32_t data) {
	uint8_t data_bytes[4];
	data_bytes[0] = data & 0x000000FF;
	data_bytes[1] = (data & 0x0000FF00) >> 8;
	data_bytes[2] = (data & 0x00FF0000) >> 16;
	data_bytes[3] = (data & 0xFF000000) >> 24;
	
	return rc_cmd_set(Memory_Write, 4, regaddr, data_bytes, RC_DEFAULT_TIMEOUT);
}

// Confirms there is a valid connection to the VXR7200 by
// checking that the RC interface is successfully started
RC_Error_T VXR_Check_Connection(void) {
	if(vxr_interface_started) {
		return RC_Success;
	} else {
		// attempt to start the interface
		return VXR_Start_Interface();
	}
}

RC_Error_T VXR_Set_EDID(const uint8_t* new_edid, uint16_t edid_size) {
	uint32_t tempreg;
	RC_Error_T retval = RC_Success;
	
	if((edid_size == 0) || (edid_size > 256)) { // max of 2 EDID blocks
		return RC_Invalid_Arg;
	}
	for(uint16_t i = 0; i < (edid_size/sizeof(uint32_t)); i++) {
		tempreg = (((uint32_t)new_edid[i*sizeof(uint32_t) + 0]) << 0)
		+ (((uint32_t)new_edid[i*sizeof(uint32_t) + 1]) << 8)
		+ (((uint32_t)new_edid[i*sizeof(uint32_t) + 2]) << 16)
		+ (((uint32_t)new_edid[i*sizeof(uint32_t) + 3]) << 24);
		if(retval == RC_Success) {
			retval = VXR_Write_Register(0x3000 + i*sizeof(uint32_t), tempreg);
		}
	}
	
	return retval;
}

RC_Error_T VXR_Get_PPS_TXn(uint8_t* rec_pps, uint8_t TX_Sel) {
	RC_Error_T retval = RC_Success;
	
	// Start address 
	// ** TX0_PPS_ADDRESS + PPS_OFFSET
	// End address
	// ** TX0_PPS_ADDRESS + PPS_OFFSET + PPS_LENGTH - 1
	
	uint32_t word_to_get;
	uint32_t current_read_pointer;
	uint32_t last_byte;
	if(TX_Sel == 0) {
		current_read_pointer = TX0_PPS_ADDRESS + PPS_OFFSET;
		last_byte = TX0_PPS_ADDRESS + PPS_OFFSET + PPS_LENGTH;
	} else if(TX_Sel == 1) {
		current_read_pointer = TX1_PPS_ADDRESS + PPS_OFFSET;
		last_byte = TX1_PPS_ADDRESS + PPS_OFFSET + PPS_LENGTH;
	} else {
		return RC_Invalid_Arg;
	}
	uint32_t word;
	uint8_t word_byte[4];
	uint8_t rec_pps_pos = 0;
	
	while(current_read_pointer < last_byte) {
		word_to_get = current_read_pointer - (current_read_pointer % sizeof(uint32_t)); // aligns to 4 byte boundary
		
		if(retval == RC_Success) {
			retval = VXR_Read_Register(word_to_get, &word);
			if(retval == RC_Success) {
				word_byte[0] = (uint8_t)(word & 0x000000FFu);
				word_byte[1] = (uint8_t)((word & 0x0000FF00u) >> 8);
				word_byte[2] = (uint8_t)((word & 0x00FF0000u) >> 16);
				word_byte[3] = (uint8_t)((word & 0xFF000000u) >> 24);
				
				uint8_t start_pos = (current_read_pointer - word_to_get); // position within word_bytes, ie 0 to 3
				uint8_t num_of_bytes_to_copy = Min( Min((last_byte - word_to_get), (4u - start_pos)), 4u);
				
				memcpy(&(rec_pps[rec_pps_pos]), &(word_byte[start_pos]), num_of_bytes_to_copy);
				
				current_read_pointer += num_of_bytes_to_copy;
				rec_pps_pos += num_of_bytes_to_copy;
			}
		}
		if(retval != RC_Success) {
			// break out of the loop on failure
			return retval;
		}
	}
	return retval;
}

bool VXR_Get_TXn_Video_Status(uint8_t TX_Sel) {
	uint32_t read_reg;
	uint32_t reg_value;

	if(TX_Sel == 0) {
		read_reg = TX0_VID_MODE_ADDRESS;
	} else if(TX_Sel == 1) {
		read_reg = TX1_VID_MODE_ADDRESS;
	} else {
		return false;
	}

	if(RC_Success == VXR_Read_Register(read_reg, &reg_value)) {
		if((reg_value & TXn_VID_MODE_ENABLED) != 0) {
			return true;
		}
	}
	return false;
}

RC_Error_T VXR_Get_Rx_Video_Format(Video_Format_T* fmt) {
	RC_Error_T retval;
	uint32_t tempval;
	float internal_clock = 0.0f;
	float framerate_working_data = 0.0f;
	// Query the received video format (height, width, framerate)
	
	// Htotal/Vtotal
	retval = VXR_Read_Register(RX_FRM0_MSA_TOTALS_ADDRESS, &tempval);
	if(RC_Success == retval) {
		fmt->Htotal = (uint16_t)((tempval & RX_FRMn_MSA_HTOTAL_Msk) >> RX_FRMn_MSA_HTOTAL_Pos);
		fmt->Vtotal = (uint16_t)((tempval & RX_FRMn_MSA_VTOTAL_Msk) >> RX_FRMn_MSA_VTOTAL_Pos);
		retval = VXR_Read_Register(RX_FRM0_MSA_ACTIVE_ADDRESS, &tempval);
	}
	if(RC_Success == retval) {
		fmt->Hactive = (uint16_t)((tempval & RX_FRMn_MSA_HACTIVE_Msk) >> RX_FRMn_MSA_HACTIVE_Pos);
		fmt->Vactive = (uint16_t)((tempval & RX_FRMn_MSA_VACTIVE_Msk) >> RX_FRMn_MSA_VACTIVE_Pos);
		retval = VXR_Read_Register(RX_FRM0_BPC_ADDRESS, &tempval);
	}
	if(RC_Success == retval) {
		fmt->ColorBPC = (uint8_t)((tempval & RX_FRMn_BPC_Msk) >> RX_FRMn_BPC_Pos);
		retval = VXR_Read_Register(RX_INT_CLK_REG, &tempval);
	}
	if(RC_Success == retval) {
		internal_clock = 10000.0f * (float)((tempval & RX_INT_CLK_Msk) >> RX_INT_CLK_Pos);
		retval = VXR_Read_Register(RX_PIXCLK_MULT_REG, &tempval);
	}
	if(RC_Success == retval) {
		framerate_working_data = internal_clock*100.0f*((float)((tempval & RX_PIXCLK_MULT_Msk) >> RX_PIXCLK_MULT_Pos));
		retval = VXR_Read_Register(RX_PIXCLK_DIV_REG, &tempval);
	}
	if(RC_Success == retval) {
		framerate_working_data = framerate_working_data / ((float)((tempval & RX_PIXCLK_DIV_Msk) >> RX_PIXCLK_DIV_Pos));
		fmt->Frame_100 = (uint16_t)(framerate_working_data*100.0f);
	}
	
	return retval;
}

RC_Error_T VXR_Get_Rx_Error_Counter(uint8_t DP_Lane_Num, uint16_t* errcount) {
	// Notes:
	//	Lane number can only be 0 to 3
	//  errcount will usually have bit 16 (0x8000) set true. this just indicates
	//	that the error count is valid. The lower 15 bits (0x7FFF) are the count
	//  of received errors.
	RC_Error_T retval;
	uint32_t regaddr;
	uint32_t regval;
	if(DP_Lane_Num > 3) {
		return RC_Invalid_Arg;
	}
	
	regaddr = RX_ML_PHY_ERR_COUNTERS_0 + (DP_Lane_Num*4);
	
	retval = VXR_Read_Register(regaddr, &regval);
	
	*errcount = (uint16_t) (regval & 0x0000FFFFu);
	
	return retval;
}

RC_Error_T VXR_Disable_DSC(void) {
	// Turns off DSC by changing the DisplayPort Configuration Data (DPCD)
	// register so it tells the GPU that DSC isn't available
	uint32_t reg_dsc;
	RC_Error_T errval;
	errval = VXR_Read_Register(0x1730, &reg_dsc);
	if(RC_Success == errval) {
		reg_dsc &= ~(0x06); // Turns off bits 1 and 2, which are DSC enable and FEC enable
		errval = VXR_Write_Register(0x1730, reg_dsc);
	}
	return errval;
}

/********* VXR7200 Flash Memory *********
 *
 * There are 8 Flash blocks, numbered 0 through 7
 * The total firmware size is 512kB (0x80000)
 *		Flash block mapping:
 *			0: 0x00000 through 0x0FFFF
 *			1: 0x10000 through 0x1FFFF
 *			2: 0x20000 through 0x2FFFF
 *			3: 0x30000 through 0x3FFFF
 *			4: 0x40000 through 0x4FFFF
 *			5: 0x50000 through 0x5FFFF
 *			6: 0x60000 through 0x6FFFF
 *			7: 0x70000 through 0x7FFFF
 *
 * The Flash is divided into two config/firmware sections and a "ESM" section for HDCP 2.2
 * Bank0 is 0x00000 through 0x1FFFF (blocks 0 and 1), Bank1 is 0x20000 through 0x3FFFF 
 * (blocks 2 and 3), and ESM is 0x40000 through 0x7FFFF (blocks 4, 5, 6, and 7).
 * 
 * Each bank has a Config section for the first 32k and a Firmware section for the next 96k
 *		Config, Firmware, and ESM mapping:
 *			Config, Bank0:	0x00000 through 0x07FFF
 *			FW, Bank0:		0x08000 through 0x1FFFF
 *			Config, Bank1:	0x20000 through 0x27FFF
 *			FW, Bank0:		0x28000 through 0x3FFFF
 *			ESM:			0x40000 through 0x7FFFF
 * 
 * The last 16 bytes of Config and Firmware are the tag, used by the ROM bootloader to determine
 * which bank to load. The Synaptics firmware generation tool creates the tag. So a valid tag
 * can simply be copied from the generated *.fullrom file to the tag location:
 *		Tag byte locations:
 *			Config, Bank0:	0x07FF0 through 0x07FFF
 *			FW, Bank0:		0x1FFF0 through 0x1FFFF
 *			Config, Bank1:	0x27FF0 through 0x27FFF
 *			FW, Bank0:		0x3FFF0 through 0x3FFFF
 * To invalidate the tag (forcing the bootloader to load the other bank), simply set the first
 * and last bytes of the tag to zero.
 */

/** VXR_Calc_Checksum
 * Calculates the checksum on firmware already in the Flash memory on
 * the VXR7200 chip. 
 * Checksum calculation is a simple 32-bit sum of all the memory starting
 * at starting address "start_addr" for length "len".
 */
RC_Error_T VXR_Calc_Checksum(uint32_t start_addr, uint32_t len, uint32_t* result)
{	
	RC_Error_T retval;
	uint8_t buserror;
	
	// checksum calculation appears to take about 1us per count plus a fixed 755us.
	// So for example, counting a length of 10000 should take about 10755us (10.7ms)
	// I'm just going to double the timeout to play it safe. Who knows if the VXR7200
	// might be doing something funky at the time we ask for a checksum.
	
	// Note that the timeout is in ticks of a 10kHz clock. So 10 = 1ms.
	uint32_t estimated_timeout = len / 50 + 15; 
	retval = rc_cmd_set(Get_FW_Chksum, len, start_addr, NULL, estimated_timeout);
	if(retval == RC_Success) {
		buserror = rc_bus_read(RC_DATA0_ADDR, result);
		if((buserror == pdPASS)) {
			return retval;
			} else {
			return RC_Bus_Error;
		}
	}
	return retval;
	
}

/** VXR_Program_Firmware
 * Programs "len" number of bytes in firmware starting at "start_addr".
 * Note that to program any '1' bit, the existing bit must already be '1'.
 * This is standard for Flash memory. Programming can only set '1' to '0', and
 * to set back to '1' an erase must be performed. Erase can only be done on a 
 * block, not individual bytes. Programming can be done on a single byte.
 */
RC_Error_T VXR_Program_Firmware(uint32_t start_addr, uint32_t len, const uint8_t* buf) {

	return rc_cmd_set(Write_Flash, len, start_addr, (uint8_t*)buf, RC_PROGRAM_TIMEOUT);
}

/** VXR_Erase_Firmware_Bank
 * Erases the bank (valid 0 through 7, see notes above) and sets
 * all bits back to '1'.
 */
RC_Error_T VXR_Erase_Firmware_Bank(uint8_t bank_num) {
	
	uint8_t erase_buf[2];
	
	if(bank_num > 7) {
		return RC_Invalid_Arg;
	}
	
	erase_buf[0] = bank_num;
	erase_buf[1] = 0x30; // block erase, 64kB
	
	return rc_cmd_set(Erase_Flash, 2, 0, erase_buf, RC_ERASE_TIMEOUT);
}

/** VXR_Erase_Firmware_Bank
 * Erases the sector (valid 0 through 127, see notes above) and sets
 * all bits back to '1'.
 */
RC_Error_T VXR_Erase_Firmware_Sector(uint8_t sector_num) {
	uint8_t erase_buf[2];
	
	if(sector_num > 127) {
		return RC_Invalid_Arg;
	}
	
	erase_buf[0] = sector_num;
	erase_buf[1] = 0x10; // sector erase, 4kB
	
	return rc_cmd_set(Erase_Flash, 2, 0, erase_buf, RC_ERASE_TIMEOUT);
}

/** VXR_Tag_Valid
 * Determines if the tag region (last 16 bytes of any Config or Firmware region)
 * is valid. This tag is used by the VXR7200 bootloader to load one of two 
 * config or firmware regions. The config and FW are independent, they can be
 * updated separately. So that means it's perfectly valid to be operating 
 * with Config0 and FW1, or Config1 and FW0, or both 0, or both 1. 
 * The tag is invalidated by setting the first and last bytes to zero. So
 * if they are both nonzero, we are considering it a valid tag.
 * One exception...erased memory is invalid. If it's all 0xFF for the
 * entire 16 bytes of the tag, it's invalid.
 *
 * Returns: true (tag is valid), or false (tag is invalid or 
 *			operation failed)
 */

bool VXR_Tag_Valid(Vxr_Flash_Tag_T region) {
	// We have a hacky workaround to reading flash bytes. The 
	// "Firmware Data Read Command" listed in the Remote Control
	// user manual for the VXR7200 doesn't appear to work on the
	// built-in flash memory. It might be for external SPI flash
	// (unclear for most of the flash operations if they are
	// internal or external). But we can read a single byte
	// at a time by running a checksum command with length of 1.
	
	uint32_t flash_addr;
	
	// Read the 16 bytes of the tag
	switch(region) {
		case Vxr_Tag_Config_0:
			flash_addr = VXR_CONFIG_0_TAG_START;
		break;
		
		case Vxr_Tag_Config_1:
			flash_addr = VXR_CONFIG_1_TAG_START;
		break;
		
		case Vxr_Tag_Firmware_0:
			flash_addr = VXR_FIRMWARE_0_TAG_START;
		break;
		
		case Vxr_Tag_Firmware_1:
			flash_addr = VXR_FIRMWARE_1_TAG_START;
		break;
		
		default:
			return false;
		break;
	}
	
	uint32_t temp_flash_data;
	bool is_erased = true;
	bool is_valid = true;
	for(uint8_t i = 0; i < 16; i++) {
		if(RC_Success != VXR_Calc_Checksum(flash_addr + i, 1, &temp_flash_data)) {
			return false;
		}

		// Check for erased memory
		if(is_erased) {
			if(((uint8_t)temp_flash_data) != 0xFF) is_erased = false;
		}
		
		// Check for first and last tags zero'ed
		// Only valid if both are nonzero. And not erased. That too.
		if((i == 0) || (i == 15)) {
			if(((uint8_t)temp_flash_data) == 0x00) is_valid = false;
		}
	}
	return (is_valid && (!is_erased));
}

/** VXR_Get_Firmware_Name
 * Reads the firmware name saved in the config region of VXR7200 Flash.
 * First determines the active config (if none, returns an error), then
 * reads the Flash data by using the hacky checksum workaround.
 * Since the name is only 15 bytes, this doesn't take very long.
 * The argument "fwname" should point to an array at least 15 characters long.
 * This function will not append any zero characters to the end of the string. 
 * If you wanna use C string functions, please do that first.
 */
RC_Error_T VXR_Get_Firmware_Name(uint8_t* fwname) {
	RC_Error_T retval = RC_Success;
	uint32_t flashaddr;
	uint32_t temp_flash_data;
	if(VXR_Tag_Valid(Vxr_Tag_Config_0)) {
		flashaddr = 0x005F0u;
	}else if(VXR_Tag_Valid(Vxr_Tag_Config_1)) {
		flashaddr = 0x205F0u;
	} else {
		return RC_Unknown_Error;
	}
	
	// Read 15 bytes from Flash by computing the checksum on a single byte, 15 times.
	for(uint8_t i = 0; i < 15; i++) {
		if(RC_Success == retval) {
			retval = VXR_Calc_Checksum(flashaddr + i, 1, &temp_flash_data);
			if(RC_Success == retval) {
				fwname[i] = (uint8_t)(temp_flash_data & 0xFFu);
			}
		}
	}
	
	return retval;
}