//----------------------------------------------------------------------------------
// <copyright file="BootEEPROM.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	Routines for reading and writing boot EEPROM variables
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#include <stdlib.h>
#include <string.h>
#include <util/crc16.h>
#include <avr/eeprom.h>
#include "BootEEPROM.h"

#define FALSE (0)
#define TRUE (!FALSE)

static int MatchesCopy(BOOT_EEPROM* pEEPROM, uint8_t idx);
static uint16_t UpdateCRC(void* pStart, uint16_t size, uint16_t initCRC);

// ------------------------------------------------------------------------------
//
// Reads the first encountered non-corrupted copy of the EEPROM into the buffer
// provided and returns TRUE if a valid copy was found (FALSE If no valid copy)
//
// ------------------------------------------------------------------------------

int ReadBootEEPROM(BOOT_EEPROM_VARS* pVars)
{
	uint8_t idx;
	int validCRC = FALSE;
	BOOT_EEPROM eepromCopy;


	for (idx = 0; !validCRC && (idx < 2); ++idx)
	{
#ifdef EXPAND_RAW_BOOT_FUNCTIONS
		ReadRawBootEEPROM(idx, &eepromCopy);
#else
		eeprom_busy_wait();
		eeprom_read_block(&eepromCopy, &BOOT_VARS(idx), sizeof(eepromCopy));
#endif

		validCRC = (ComputeCRC(&eepromCopy) == eepromCopy.crc);
	}

	if (validCRC)
	{
		memcpy(pVars, &eepromCopy.bootVars, sizeof(*pVars));
		return TRUE;
	}
	else
		return FALSE;
}

// ------------------------------------------------------------------------------
//
// Updates both copies of the EEPROM with the given variables and computes a new
// crc
//
// ------------------------------------------------------------------------------

void WriteBootEEPROM(BOOT_EEPROM_VARS* pVars)
{
	uint8_t idx;
	BOOT_EEPROM eepromCopy;

	memcpy(&eepromCopy.bootVars, pVars, sizeof(eepromCopy.bootVars));
	memset(eepromCopy.space, '\xFF', sizeof(eepromCopy.space));
	eepromCopy.crc = ComputeCRC(&eepromCopy);

	for (idx = 0; idx < 2; ++idx)
#ifdef EXPAND_RAW_BOOT_FUNCTIONS
		WriteRawBootEEPROM(idx, &eepromCopy);
#else
		if (!MatchesCopy(&eepromCopy, idx))
		{
			eeprom_busy_wait();
			eeprom_write_block(&eepromCopy, &BOOT_VARS(idx), sizeof(eepromCopy));
		}
#endif
}

// ------------------------------------------------------------------------------
// 
// Determines if the local copy of the EEPROM block (pEEPROM) matches the block
// at index "idx" in the EEPROM; returns TRUE if matching exactly, FALSE if not
//
// ------------------------------------------------------------------------------

static int MatchesCopy(BOOT_EEPROM* pEEPROM, uint8_t idx)
{
	BOOT_EEPROM eepromCopy;

#ifdef EXPAND_RAW_BOOT_FUNCTIONS
	ReadRawBootEEPROM(idx, &eepromCopy);
#else
	eeprom_busy_wait();
	eeprom_read_block(&eepromCopy, &BOOT_VARS(idx), sizeof(eepromCopy));
#endif

	return (memcmp(&eepromCopy, pEEPROM, sizeof(eepromCopy)) == 0);
}

// ------------------------------------------------------------------------------
//
// Computes the CRC for the given local copy of the EEPROM
//
// ------------------------------------------------------------------------------

uint16_t ComputeCRC(BOOT_EEPROM* pEEPROM)
{
	uint16_t crc = UpdateCRC(&pEEPROM->bootVars, sizeof(pEEPROM->bootVars), 0);

	crc = UpdateCRC(pEEPROM->space, sizeof(pEEPROM->space), crc);
	return crc;
}

// ------------------------------------------------------------------------------
// 
// Updates the CRC for a local block given a partially computed CRC
//
// ------------------------------------------------------------------------------

static uint16_t UpdateCRC(void* pStart, uint16_t size, uint16_t initCRC)
{
	uint8_t* ptr = (uint8_t*) pStart;
	uint16_t crc = initCRC;

	while(size--)
		crc = _crc16_update(crc, *ptr++);

	return crc;
}

// ------------------------------------------------------------------------------
// 
// Reads one of the raw boot EEPROM images
// 
// ------------------------------------------------------------------------------

#ifdef EXPAND_RAW_BOOT_FUNCTIONS

void ReadRawBootEEPROM(uint8_t idx, BOOT_EEPROM* pBuffer)
{
	eeprom_busy_wait();
	eeprom_read_block(pBuffer, &BOOT_VARS(idx), sizeof(*pBuffer));
}

#endif

// ------------------------------------------------------------------------------
// 
// Writes one of the raw boot EEPROM images
// 
// ------------------------------------------------------------------------------

#ifdef EXPAND_RAW_BOOT_FUNCTIONS

void WriteRawBootEEPROM(uint8_t idx, BOOT_EEPROM* pBuffer)
{
	if (!MatchesCopy(pBuffer, idx))
	{
		eeprom_busy_wait();
		eeprom_write_block(pBuffer, &BOOT_VARS(idx), sizeof(*pBuffer));
	}
}

#endif
