//------------------------------------------------------------------------------
// File:          partitioning.c
// Function:      Memory partitioning
// Supported chip(s):
//    - AT91SAM7XC128
//    - AT91SAM7XC256
// Supported toolchain(s):
//    - IAR Embedded Workbench
// Date created:  05 July 2006
// Created by:    JJo
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------

#include "partitioning.h"

#ifdef USE_MEMORY_PARTITIONING

//------------------------------------------------------------------------------
// Functions
//------------------------------------------------------------------------------

/**
 * Name:      partitioning_sign
 * Purpose:   Signs a firmware and store the signature at the specified address
 * Input(s):
 *  - Starting address of firmware
 *  - Length of firmware
 * Output:    Signature
 */
unsigned int partitioning_sign(void * vAddress, unsigned int length) {

  // Local variables
  unsigned char * pCurrent;
  unsigned int lowerByte, upperByte;
  unsigned int count;

  // Initialization
  pCurrent = (unsigned char *) vAddress;
  lowerByte = 1;
  upperByte = 0;

  // Calculate CRC
  while (length) {

    if (length > 5550) {

      count = 5550;
    }
    else {

      count = length;
    }
    length -= count;

    while (count != 0) {
  
      lowerByte += *pCurrent;
      upperByte += lowerByte;
      pCurrent++;
      count--;
    }

    lowerByte = (lowerByte & 0xffff) + (lowerByte >> 16) * 15;
    upperByte = (upperByte & 0xffff) + (upperByte >> 16) * 15;
  }

  if (lowerByte >= 65521) {

    lowerByte -= 65521;
  }

  upperByte = (upperByte & 0xffff) + (upperByte >> 16) * 15;
  if (upperByte >= 65521) {

    upperByte -= 65521;
  }

  // Output signature
  return (upperByte << 16) | lowerByte;
}

/**
 * Name:      partitioning_check
 * Purpose:   Compare region A and region B signatures
 * Input(s):
 *  - Region A signature
 *  - Region B signature
 * Output:    OK if both signatures are equals, ERROR otherwise.
 */
unsigned int partitioning_check(unsigned int signatureA, unsigned int signatureB) {

  // Compare
  if (signatureA == signatureB) {

    return OK;
  }
  else {

    return ERROR;
  }
}

/**
 * Name:      partitioning_writeInfo
 * Purpose:   Writes information about regions at the specified address
 * Input(s):
 *  - Destination address
 *  - Pointer to regions information
 */
void partitioning_writeInfo(void * vAddress, pRegionInfo_t pRegionInfo) {

  flash_write(vAddress, (char *) pRegionInfo);
}

/**
 * Name:      partitioning_readInfo
 * Purpose:   Reads the regions information and stores it
 * Input(s):
 *  - Address of regions info in flash
 *  - Pointer to structure to store info
 */
void partitioning_readInfo(void * vAddress, pRegionInfo_t pRegionInfo) {

  // Local variables
  pRegionInfo_t pAddress = (pRegionInfo_t) vAddress;

  // Copy fields
  pRegionInfo->signatureA = pAddress->signatureA;
  pRegionInfo->signatureB = pAddress->signatureB;
  pRegionInfo->lengthA = pAddress->lengthA;
  pRegionInfo->lengthB = pAddress->lengthB;
}

/**
 * Name:      partitioning_isRegionValid
 * Purpose:   Checks that a region is valid
 * Input(s):
 *  - Start address of region
 *  - Expected signature
 *  - Expected length
 * Output:    OK if region is valid, ERROR otherwise
 */
unsigned int partitioning_isRegionValid(void * vAddress,
                                        unsigned int signature,
                                        unsigned int length) {

  // Local variables
  unsigned int signature2;

  // Check length
  if ((length%MEMORY_PAGE_SIZE != 0) || (length > REGION_SIZE) || (length == 0)) {

    return ERROR;
  }
  
  // Recompute and check signature
  signature2 = partitioning_sign(vAddress, length);

  if (partitioning_check(signature, signature2) != OK) {

    return ERROR;
  }
  else {

    return OK;
  }
}

#endif // USE_MEMORY_PARTITIONING
