// ###########################################################################
//
//  FILE:    flash_kernel_c28x_dual_ex1_can_get_function_cpu1.c
//
//  TITLE:   CAN commands and corresponding function calls draft
//
//  NOTE:    Draft CAN transport for the flash-kernel example. The command
//           parser and flash-loader logic are kept close to the original
//           version, but word-level transfers are buffered through CAN message
//           objects. This is a starting point for a real CAN host protocol.
//
// ###########################################################################
//
//  C2000Ware v26.01.00.00
//
//  Copyright (C) 2024 Texas Instruments Incorporated - http://www.ti.com
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//    Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//    Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the
//    distribution.
//
//    Neither the name of Texas Instruments Incorporated nor the names of
//    its contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
//  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//  $
// ###########################################################################

//
// Included Files
//
#include "cpu1bootrom.h"
#include "device.h"
#include "driverlib.h"
#include "f2838x_kernel_commands_cpu1.h"
#include "stdint.h"
#include "sysctl.h"
#include <assert.h>
#include <flash_kernel_c28x_dual_ex1_erase_cpu1.h>
// #include <flash_kernel_c28x_dual_ex1_verify_cpu1.h>

//
// Globals
//
typedef struct {
  uint16_t status;
  uint32_t address;
  uint16_t flashAPIError;
  uint32_t flashAPIFsmStatus;
} StatusCode;

StatusCode statusCode;

uint16_t checksum;

//
// getWordData is a pointer to the function that interfaces to the peripheral.
// The CAN draft keeps the loader API unchanged and just swaps the transport.
//
extern uint16fptr GetWordData;

//
// CAN transport state
//
#define CAN_KERNEL_RX_OBJ_ID 1U
#define CAN_KERNEL_TX_OBJ_ID 2U
#define CAN_KERNEL_MSG_ID_RX 0x1U
#define CAN_KERNEL_MSG_ID_TX 0x2U
#define CAN_KERNEL_WORDS_PER_FRAME 4U
#define BUFFER_SIZE 0x80U

static uint16_t canRxWords[CAN_KERNEL_WORDS_PER_FRAME];
static uint16_t canRxCount = 0U;
static uint16_t canRxIndex = 0U;
static uint16_t canTxWords[CAN_KERNEL_WORDS_PER_FRAME];
static uint16_t canTxCount = 0U;
static unsigned char erasedAlready[] = {0, 0, 0, 0, 0, 0, 0,
                                        0, 0, 0, 0, 0, 0, 0};

//
// Function Prototypes
//
uint32_t canGetFunction(uint32_t BootMode);
uint32_t canLoadApplication(uint32_t BootMode);
uint16_t canGetWordData(void);
uint16_t canGetOnlyWordData(void);
void canSendACK(void);
void canSendNAK(void);
uint16_t canGetPacket(uint16_t *length, uint16_t *data);
uint16_t canSendPacket(uint16_t command, uint16_t status, uint16_t length,
                       uint16_t *data1, uint16_t flashAPIerror,
                       uint16_t *data2);
void canSendWord(uint16_t word);
void canSendChecksum(void);
static void canFlushTxWords(void);
static uint16_t canReadFrameIntoCache(void);
static void canInit(uint32_t BootMode);
extern uint32_t getLongData(void);
extern void readReservedFn(void);
extern void copyData(void);
extern uint32_t findSector(uint32_t address);
extern uint16_t findSize(uint32_t address);
extern void setFlashAPIError(Fapi_StatusType status);
extern void exampleError(Fapi_StatusType status);

//
// This draft only implements the CPU1 flash-load path. If you need the CPU2
// boot-kernel path, duplicate the same CAN buffering pattern there too.
//

//
// canGetFunction - CAN equivalent of the command loop.
//
uint32_t canGetFunction(uint32_t BootMode) {
  uint32_t EntryAddr;

  GetWordData = canGetWordData;
  canInit(BootMode);

  EntryAddr = canLoadApplication(BootMode);
  return EntryAddr;
}

//
// canLoadApplication - CAN equivalent of the application load routine.
//
uint32_t canLoadApplication(uint32_t BootMode) {
  (void)BootMode;

  statusCode.status = NO_ERROR;
  statusCode.address = 0x12346578UL;
  statusCode.flashAPIError = NO_ERROR;
  statusCode.flashAPIFsmStatus = 0UL;

  uint32_t EntryAddr;

  GetWordData = canGetWordData;

  if (canGetWordData() != 0x08AAU) {
    statusCode.status = VERIFY_ERROR;
    statusCode.address = FLASH_ENTRY_POINT;
  }

  readReservedFn();
  EntryAddr = getLongData();
  copyData();

  return EntryAddr;
}

//
// CAN init and transport helpers
//
static void canInit(uint32_t BootMode) {
  (void)BootMode;

  EALLOW;

  SysCtl_selectCPUForPeripheral(SYSCTL_CPUSEL8_CAN, 1U, SYSCTL_CPUSEL_CPU1);
  SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_CANA);

  EDIS;

  GPIO_setPinConfig(DEVICE_GPIO_CFG_CANRXA);
  GPIO_setPinConfig(DEVICE_GPIO_CFG_CANTXA);

  CAN_initModule(CANA_BASE);
  CAN_setBitRate(CANA_BASE, DEVICE_SYSCLK_FREQ, 100000U, 20U);
  CAN_setupMessageObject(CANA_BASE, CAN_KERNEL_RX_OBJ_ID, CAN_KERNEL_MSG_ID_RX,
                         CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0U,
                         CAN_MSG_OBJ_RX_INT_ENABLE, 8U);
  CAN_setupMessageObject(CANA_BASE, CAN_KERNEL_TX_OBJ_ID, CAN_KERNEL_MSG_ID_TX,
                         CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0U,
                         CAN_MSG_OBJ_TX_INT_ENABLE, 8U);
  CAN_startModule(CANA_BASE);
}

static uint16_t canReadFrameIntoCache(void) {
  if (!CAN_readMessage(CANA_BASE, CAN_KERNEL_RX_OBJ_ID, canRxWords)) {
    return 0U;
  }

  canRxCount = CAN_KERNEL_WORDS_PER_FRAME;
  canRxIndex = 0U;
  return 1U;
}

uint16_t canGetWordData(void) {
  uint16_t wordData;

  if (canRxIndex >= canRxCount) {
    while (canReadFrameIntoCache() == 0U) {
    }
  }

  wordData = canRxWords[canRxIndex++];

  checksum += (wordData & 0xFFU);
  checksum += ((wordData >> 8) & 0xFFU);

  return wordData;
}

uint16_t canGetOnlyWordData(void) { return canGetWordData(); }

void canSendWord(uint16_t word) {
  canTxWords[canTxCount++] = word;

  checksum += (word & 0xFFU);
  checksum += ((word >> 8) & 0xFFU);

  if (canTxCount == CAN_KERNEL_WORDS_PER_FRAME) {
    canFlushTxWords();
  }
}

static void canFlushTxWords(void) {
  if (canTxCount == 0U) {
    return;
  }

  CAN_sendMessage(CANA_BASE, CAN_KERNEL_TX_OBJ_ID, (uint16_t)(canTxCount * 2U),
                  canTxWords);
  canTxCount = 0U;
}

void canSendChecksum(void) {
  canSendWord((uint16_t)(checksum & 0xFFFFU));
  canFlushTxWords();
}

uint16_t canSendPacket(uint16_t command, uint16_t status, uint16_t length,
                       uint16_t *data1, uint16_t flashAPIerror,
                       uint16_t *data2) {
  int i;

  canFlushTxWords();
  DEVICE_DELAY_US(1000U);

  checksum = 0U;
  canSendWord(0x1BE4U);
  canSendWord(length);

  canSendWord(command);
  canSendWord(status);

  for (i = 0; i < 2; i++) {
    canSendWord(*(data1 + i));
  }

  canSendWord(flashAPIerror);

  for (i = 0; i < 2; i++) {
    canSendWord(*(data2 + i));
  }

  canSendChecksum();
  canSendWord(0xE41BU);
  canFlushTxWords();

  return 0U;
}

void canSendACK(void) {
  canSendWord(ACK);
  canFlushTxWords();
}

void canSendNAK(void) {
  canSendWord(NAK);
  canFlushTxWords();
}

uint16_t canGetPacket(uint16_t *length, uint16_t *data) {
  uint16_t command;
  int i;

  if (canGetOnlyWordData() != 0x1BE4U) {
    canSendNAK();
    return 100U;
  }

  *length = canGetOnlyWordData();

  checksum = 0U;
  command = canGetOnlyWordData();

  for (i = 0; i < (*length) / 2U; i++) {
    *(data + i) = canGetOnlyWordData();
  }

  if (checksum != canGetOnlyWordData()) {
    canSendNAK();
    return 101U;
  }

  if (canGetOnlyWordData() != 0xE41BU) {
    canSendNAK();
    return 102U;
  }

  canSendACK();
  return command;
}

//
// copyData - Draft CAN version of the flash loader data copy routine.
//
void copyData(void) {
  struct HEADER {
    uint16_t BlockSize;
    uint32_t DestAddr;
  } BlockHeader;

  uint16_t Buffer[BUFFER_SIZE];
  uint16_t miniBuffer[8];
  uint16_t i = 0U;
  uint16_t j = 0U;
  uint16_t k = 0U;
  uint32_t sectorAddress;
  uint16_t sectorSize;
  uint16_t wordData;
  uint16_t wordsWritten = 0U;
  int fail = 0;

  assert((BUFFER_SIZE % 8U) == 0U);

  if (statusCode.status != NO_ERROR) {
    fail++;
  }

  for (i = 0U; i < 14U; i++) {
    erasedAlready[i] = 0U;
  }

#if checksum_enable
  canSendChecksum();
#endif

  BlockHeader.BlockSize = (*GetWordData)();

  EALLOW;
  while (BlockHeader.BlockSize != (uint16_t)0x0000U) {
    Fapi_StatusType oReturnCheck;
    Fapi_FlashStatusType oFlashStatus;
    Fapi_FlashStatusWordType oFlashStatusWord;

    BlockHeader.DestAddr = getLongData();

    for (i = 0U; i < BlockHeader.BlockSize; i += 0U) {
      if (BlockHeader.BlockSize < BUFFER_SIZE) {
        for (j = 0U; j < BlockHeader.BlockSize; j++) {
          wordData = (*GetWordData)();
          Buffer[j] = wordData;
          i++;
        }
        for (j = BlockHeader.BlockSize; j < BUFFER_SIZE; j++) {
          Buffer[j] = 0xFFFFU;
        }
      } else {
        if ((BlockHeader.BlockSize - i) < BUFFER_SIZE) {
          for (j = 0U; j < (BlockHeader.BlockSize - i); j++) {
            wordData = (*GetWordData)();
            Buffer[j] = wordData;
          }

          i += j;

          for (; j < BUFFER_SIZE; j++) {
            Buffer[j] = 0xFFFFU;
          }
        } else {
          for (j = 0U; j < BUFFER_SIZE; j++) {
            wordData = (*GetWordData)();
            Buffer[j] = wordData;
            i++;
          }
        }
      }

      for (k = 0U; k < (BUFFER_SIZE / 8U); k++) {
        miniBuffer[0] = Buffer[k * 8U + 0U];
        miniBuffer[1] = Buffer[k * 8U + 1U];
        miniBuffer[2] = Buffer[k * 8U + 2U];
        miniBuffer[3] = Buffer[k * 8U + 3U];
        miniBuffer[4] = Buffer[k * 8U + 4U];
        miniBuffer[5] = Buffer[k * 8U + 5U];
        miniBuffer[6] = Buffer[k * 8U + 6U];
        miniBuffer[7] = Buffer[k * 8U + 7U];

        if (wordsWritten < BlockHeader.BlockSize) {
          if (fail == 0) {
            sectorAddress = findSector(BlockHeader.DestAddr);
            if (sectorAddress != 0xDEADBEEFUL) {
              sectorSize = findSize(sectorAddress);
              oReturnCheck = Fapi_issueAsyncCommandWithAddress(
                  Fapi_EraseSector, (uint32_t *)sectorAddress);
              while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy) {
              }
              oReturnCheck = Fapi_doBlankCheck((uint32_t *)sectorAddress,
                                               sectorSize, &oFlashStatusWord);
              oFlashStatus = Fapi_getFsmStatus();
              if ((oReturnCheck != Fapi_Status_Success) ||
                  (oFlashStatus != 0U)) {
                if (fail == 0) {
                  statusCode.status = BLANK_ERROR;
                  statusCode.address = oFlashStatusWord.au32StatusWord[0];
                  setFlashAPIError(oReturnCheck);
                  statusCode.flashAPIFsmStatus = (uint32_t)oFlashStatus;
                }
                fail++;
              }
            }
          }

          if (fail == 0) {
            oReturnCheck = Fapi_issueProgrammingCommand(
                (uint32_t *)BlockHeader.DestAddr, miniBuffer,
                sizeof(miniBuffer), 0, 0, Fapi_AutoEccGeneration);
            while (Fapi_checkFsmForReady() == Fapi_Status_FsmBusy) {
            }
            oFlashStatus = Fapi_getFsmStatus();
            if ((oReturnCheck != Fapi_Status_Success) || (oFlashStatus != 0U)) {
              if (fail == 0) {
                statusCode.status = PROGRAM_ERROR;
                statusCode.address = BlockHeader.DestAddr;
                setFlashAPIError(oReturnCheck);
                statusCode.flashAPIFsmStatus = (uint32_t)oFlashStatus;
              }
              fail++;
            }
          }

          for (j = 0U; j < 8U; j += 2U) {
            if (fail == 0) {
              uint32_t toVerify = miniBuffer[j + 1U];
              toVerify = toVerify << 16U;
              toVerify |= miniBuffer[j];

              oReturnCheck =
                  Fapi_doVerify((uint32_t *)(BlockHeader.DestAddr + j), 1U,
                                (uint32_t *)(&toVerify), &oFlashStatusWord);
              if (oReturnCheck != Fapi_Status_Success) {
                if (fail == 0) {
                  statusCode.status = VERIFY_ERROR;
                  statusCode.address = oFlashStatusWord.au32StatusWord[0];
                  setFlashAPIError(oReturnCheck);
                  statusCode.flashAPIFsmStatus = 0U;
                }
                fail++;
              }
            }
          }
        }

        BlockHeader.DestAddr += 0x8U;
        wordsWritten += 0x8U;
      }

#if checksum_enable
      canSendChecksum();
#endif
    }

    BlockHeader.BlockSize = (*GetWordData)();
    wordsWritten = 0U;
  }
  EDIS;
}

uint32_t findSector(uint32_t address) {
  if ((address >= Bzero_Sector0_start) && (address < Bzero_Sector1_start) &&
      (erasedAlready[0] == 0U)) {
    erasedAlready[0] = 1U;
    return (uint32_t)Bzero_Sector0_start;
  } else if ((address >= Bzero_Sector1_start) &&
             (address < Bzero_Sector2_start) && (erasedAlready[1] == 0U)) {
    erasedAlready[1] = 1U;
    return (uint32_t)Bzero_Sector1_start;
  } else if ((address >= Bzero_Sector2_start) &&
             (address < Bzero_Sector3_start) && (erasedAlready[2] == 0U)) {
    erasedAlready[2] = 1U;
    return (uint32_t)Bzero_Sector2_start;
  } else if ((address >= Bzero_Sector3_start) &&
             (address < Bzero_Sector4_start) && (erasedAlready[3] == 0U)) {
    erasedAlready[3] = 1U;
    return (uint32_t)Bzero_Sector3_start;
  } else if ((address >= Bzero_Sector4_start) &&
             (address < Bzero_Sector5_start) && (erasedAlready[4] == 0U)) {
    erasedAlready[4] = 1U;
    return (uint32_t)Bzero_Sector4_start;
  } else if ((address >= Bzero_Sector5_start) &&
             (address < Bzero_Sector6_start) && (erasedAlready[5] == 0U)) {
    erasedAlready[5] = 1U;
    return (uint32_t)Bzero_Sector5_start;
  } else if ((address >= Bzero_Sector6_start) &&
             (address < Bzero_Sector7_start) && (erasedAlready[6] == 0U)) {
    erasedAlready[6] = 1U;
    return (uint32_t)Bzero_Sector6_start;
  } else if ((address >= Bzero_Sector7_start) &&
             (address < Bzero_Sector8_start) && (erasedAlready[7] == 0U)) {
    erasedAlready[7] = 1U;
    return (uint32_t)Bzero_Sector7_start;
  } else if ((address >= Bzero_Sector8_start) &&
             (address < Bzero_Sector9_start) && (erasedAlready[8] == 0U)) {
    erasedAlready[8] = 1U;
    return (uint32_t)Bzero_Sector8_start;
  } else if ((address >= Bzero_Sector9_start) &&
             (address < Bzero_Sector10_start) && (erasedAlready[9] == 0U)) {
    erasedAlready[9] = 1U;
    return (uint32_t)Bzero_Sector9_start;
  } else if ((address >= Bzero_Sector10_start) &&
             (address < Bzero_Sector11_start) && (erasedAlready[10] == 0U)) {
    erasedAlready[10] = 1U;
    return (uint32_t)Bzero_Sector10_start;
  } else if ((address >= Bzero_Sector11_start) &&
             (address < Bzero_Sector12_start) && (erasedAlready[11] == 0U)) {
    erasedAlready[11] = 1U;
    return (uint32_t)Bzero_Sector11_start;
  } else if ((address >= Bzero_Sector12_start) &&
             (address < Bzero_Sector13_start) && (erasedAlready[12] == 0U)) {
    erasedAlready[12] = 1U;
    return (uint32_t)Bzero_Sector12_start;
  } else if ((address >= Bzero_Sector13_start) &&
             (address <= FlashEndAddress) && (erasedAlready[13] == 0U)) {
    erasedAlready[13] = 1U;
    return (uint32_t)Bzero_Sector13_start;
  }

  return 0xDEADBEEFUL;
}

uint16_t findSize(uint32_t address) {
  if (address == Bzero_Sector0_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector1_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector2_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector3_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector4_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector5_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector6_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector7_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector8_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector9_start) {
    return Sector64KB_u32length;
  } else if (address == Bzero_Sector10_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector11_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector12_start) {
    return Sector16KB_u32length;
  } else if (address == Bzero_Sector13_start) {
    return Sector16KB_u32length;
  }

  return 0xBEEFU;
}

uint32_t getLongData(void) {
  uint32_t longData;

  longData = (uint32_t)(*GetWordData)();
  longData |= ((uint32_t)(*GetWordData)() << 16);

  return longData;
}

void readReservedFn(void) {
  uint16_t i;

  for (i = 0U; i < 8U; i++) {
    (void)(*GetWordData)();
  }
}

//
// End of file
//
