//*****************************************************************************
//
// sd_card.c - Example program for reading files from an SD card.
//
// Copyright (c) 2011-2014 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
// 
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
// 
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// 
//NOTE: Adding new bootloader functions requires
//  1. the bl_link.cmd to be modified (add the specific functions).
//  2. the file with the function, it's properties changed to
//     2a. turn off optimization (Properties -> Build -> Arm Compiler -> Optimizations -> Optimization Level
//     2b. In order to map only a specific function to a section, it is necessary to assign the function to a section name.
//          In the compiler option-> Advance Options there is a switch (On/Off) name: "Place each function in a separate
//          subsection" The default is Off. So for the file sd_card.c change it to On.
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "ff.h"
#include "uartstdioBiolase.h"
#include "sd_card.h"
#include "diskio.h"
#include "Types.h"
#include "Constants.h"
#include  "Helpers.h"
#include "Registers.h"
#include "Compatibility.h"
#include "bl_config.h"
#include "bl_flash.h"
#include "hw_sci.h"
#include "sys_common.h"

// TI flash information
uint32_t flashAddress = 0x00020000;
uint32_t Freq_In_MHz = 200ul; // 160 or 200ul;
uint32_t programFlashDestination = 0x00020000;

//*****************************************************************************
//
// Defines the size of the buffers that hold the path, or temporary data from
// the SD card.  There are two buffers allocated of this size.  The buffer size
// must be large enough to hold the longest expected full path name, including
// the file name, and a trailing null character.
//
//*****************************************************************************
#define PATH_BUF_SIZE           80

//*****************************************************************************
//
// Defines the size of the buffer that holds the command line.
//
//*****************************************************************************
#define CMD_BUF_SIZE            64

//*****************************************************************************
//
// This buffer holds the full path to the current working directory.  Initially
// it is root ("/").
//
//*****************************************************************************
static char g_pcCwdBuf[PATH_BUF_SIZE] = "/";

//*****************************************************************************
//
// A temporary data buffer used when manipulating file paths, or reading data
// from the SD card.
//
//*****************************************************************************
static char g_pcTmpBuf[PATH_BUF_SIZE];
char *buf;

//*****************************************************************************
//
// The following are data structures used by FatFs.
//
//*****************************************************************************
static FATFS g_sFatFs;
static DIR g_sDirObject;
static FILINFO g_sFileInfo;
static FIL g_sFileObject;

extern int UARTgets(unsigned char *pcBuf, unsigned int ui32Len);
extern unsigned char UARTgetc(void);

extern Fapi_StatusType Fapi_issueAsyncCommandWithAddress(
Fapi_FlashStateCommandsType oCommand,
uint32_t* pu32StartAddress
);

extern Fapi_StatusType Fapi_doBlankCheck(
uint32_t* pu32StartAddress,
uint32_t u32Length,
Fapi_FlashStatusWordType* poFlashStatusWord
);

extern void UART_putString(sciBASE_t *sci, char *s);
extern Fapi_StatusType Fapi_setActiveFlashBank(Fapi_FlashBankType oNewFlashBank);
extern Fapi_StatusType Fapi_initializeFlashBanks(uint32_t u32HclkFrequency);
extern uint32_t Fapi_BlockErase( uint32_t Bank, uint32_t Flash_Start_Address, uint32_t Size_In_Bytes);
extern Fapi_StatusType Fapi_enableMainBankSectors(uint16_t u16SectorsEnables);
extern void UARTprintf(const char *pcString, ...);
extern FRESULT f_close (FIL* fp); /* Close an open file object */
extern uint32_t Fapi_BlockProgram( uint32_t Bank, uint32_t Flash_Start_Address, uint32_t Data_Start_Address, uint32_t Size_In_Bytes);


Fapi_StatusType bootloader_FlashApplicationRegion(void);
Fapi_StatusType bootloader_FlashEraseApplication(void);
Fapi_StatusType bootloader_FlashEraseSector(uint32_t ui32Address);
Fapi_StatusType bootloader_FlashProgramApplication(void);


//*****************************************************************************
//
// This function implements the "ls" command.  It opens the current directory
// and enumerates through the contents, and prints a line for each item it
// finds.  It shows details such as file attributes, time and date, and the
// file size, along with the name.  It shows a summary of file sizes at the end
// along with free space.
//
//*****************************************************************************
int sdCardLS(char *path)
{
    unsigned int ui32TotalSize;
    unsigned int ui32FileCount;
    unsigned int ui32DirCount;
    FRESULT iFResult;
    FATFS *psFatFs;
    char *pcFileName;
#if _USE_LFN
    char pucLfn[_MAX_LFN + 1];
    g_sFileInfo.lfname = pucLfn;
    g_sFileInfo.lfsize = sizeof(pucLfn);
#endif


    //
    // Open the current directory for access.
    //
    iFResult = f_opendir(&g_sDirObject, path);

    //
    // Check for error and return if there is a problem.
    //
    if(iFResult != FR_OK)
    {
        return((int)iFResult);
    }

    ui32TotalSize = 0;
    ui32FileCount = 0;
    ui32DirCount = 0;

    //
    // Give an extra blank line before the listing.
    //
    UARTprintf("\r\n");

    //
    // Enter loop to enumerate through all directory entries.
    //
    for(;;)
    {
        //
        // Read an entry from the directory.
        //
        iFResult = f_readdir(&g_sDirObject, &g_sFileInfo);

        //
        // Check for error and return if there is a problem.
        //
        if(iFResult != FR_OK)
        {
            return((int)iFResult);
        }

        //
        // If the file name is blank, then this is the end of the listing.
        //
        if(!g_sFileInfo.fname[0])
        {
            break;
        }

        //
        // If the attribue is directory, then increment the directory count.
        //
        if(g_sFileInfo.fattrib & AM_DIR)
        {
            ui32DirCount++;
        }

        //
        // Otherwise, it is a file.  Increment the file count, and add in the
        // file size to the total.
        //
        else
        {
            ui32FileCount++;
            ui32TotalSize += g_sFileInfo.fsize;
        }

#if _USE_LFN
        pcFileName = ((*g_sFileInfo.lfname)?g_sFileInfo.lfname:g_sFileInfo.fname);
#else
        pcFileName = g_sFileInfo.fname;
#endif
        //
        // Print the entry information on a single line with formatting to show
        // the attributes, date, time, size, and name.
        //
        UARTprintf("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9u  %s \r\n",
                   (g_sFileInfo.fattrib & AM_DIR) ? 'D' : '-',
                   (g_sFileInfo.fattrib & AM_RDO) ? 'R' : '-',
                   (g_sFileInfo.fattrib & AM_HID) ? 'H' : '-',
                   (g_sFileInfo.fattrib & AM_SYS) ? 'S' : '-',
                   (g_sFileInfo.fattrib & AM_ARC) ? 'A' : '-',
                   (g_sFileInfo.fdate >> 9) + 1980,
                   (g_sFileInfo.fdate >> 5) & 15,
                   g_sFileInfo.fdate & 31,
                   (g_sFileInfo.ftime >> 11),
                   (g_sFileInfo.ftime >> 5) & 63,
                   g_sFileInfo.fsize,
                   pcFileName);
    }

    UARTprintf("\r\n");
    //
    // Print summary lines showing the file, dir, and size totals.
    //
    UARTprintf("\r\n %4u File(s),%10u bytes total %4u Dir(s)",
                ui32FileCount, ui32TotalSize, ui32DirCount);


    //
    // Get the free space.
    //
    iFResult = f_getfree("/", (DWORD *)&ui32TotalSize, &psFatFs);

    //
    // Check for error and return if there is a problem.
    //
    if(iFResult != FR_OK)
    {
        return((int)iFResult);
    }

    //
    // Display the amount of free space that was calculated.
    //
    UARTprintf(", %10uK bytes free \r\n", (ui32TotalSize *
                                        psFatFs->free_clust / 2));

    //
    // Made it to here, return with no errors.
    //
    return(0);
}

/**
MethodName; lsUnitTest()
@brief lists directory contents of current working directory

@detail Prints current working directory contents to serial

@param None

@return None
 **/
void lsUnitTest()
{
	int result = 0;
	result = sdCardLS(g_pcCwdBuf);

	if(result != 0)
	{
		UARTprintf("ls Unit Test ... FAIL \r\n");
	}
	else
	{
		UARTprintf("ls Unit Test ... OK \r\n");
	}
}

/**
MethodName; writeUnitTest()
@brief Prints current working directory (user specified)

@detail Prints current working directory to serial

@param None

@return None
 **/
void pwdUnitTest()
{
	sdCardPWD();
	UARTprintf("Print Working Directory Unit Test ... OK \r\n");
}




//*****************************************************************************
//
// This function implements the "pwd" command.  It simply prints the current
// working directory.
//
//*****************************************************************************
int sdCardPWD()
{
    //
    // Print the CWD to the console.
    //
    UARTprintf("Current Directory: %s\n", g_pcCwdBuf);

    //
    // Return success.
    //
    return(0);
}


//*****************************************************************************
//
// This function implements the "cat Flash" command.  It reads the contents of a file
// and puts it in flash.  This should only be used on binary files.
//
//*****************************************************************************
int sdCardCatFlash(char *file)
{
    FRESULT iFResult;
    uint32_t ui32BytesRead;
    int programFlashBankNumber = 0;
    Fapi_StatusType FapiStatus    = Fapi_Error_Fail;

    //
    // First, check to make sure that the current path (CWD), plus the file
    // name, plus a separator and trailing null, will all fit in the temporary
    // buffer that will be used to hold the file name.  The file name must be
    // fully specified, with path, to FatFs.
    //
    if(strlen(g_pcCwdBuf) + strlen(file) + 1 + 1 > sizeof(g_pcTmpBuf))
    {
        UARTprintf("Error: Resulting path name is too long\r\n");
        return(-4);
    }

    //
    // Copy the current path to the temporary buffer so it can be manipulated.
    //
    strcpy(g_pcTmpBuf, g_pcCwdBuf);

    //
    // If not already at the root level, then append a separator.
    //
    if(strcmp("/", g_pcCwdBuf))
    {
        strcat(g_pcTmpBuf, "/");
    }

    //
    // Now finally, append the file name to result in a fully specified file.
    //
    strcat(g_pcTmpBuf, file);

    //
    // Open the file for reading.
    //
    iFResult = f_open(&g_sFileObject, g_pcTmpBuf, FA_READ);

    //
    // If there was some problem opening the file, then return an error.
    //
    if(iFResult != FR_OK)
    {
        return((int)iFResult);
    }
    else
    {
      //
      // Enter a loop to repeatedly read data from the file and display it, until
      // the end of the file is reached.
      //
      do
      {
        //
        // Read a block of data from the file.  Read as much as can fit in the
        // temporary buffer, including a space for the trailing null.
        //
        iFResult = f_read(&g_sFileObject, g_pcTmpBuf, sizeof(g_pcTmpBuf),(UINT *)&ui32BytesRead);

        // If there was an error reading, then print a newline and return the
        // error to the user.
        if(iFResult != FR_OK)
        {
          UARTprintf("\r\nERROR!!! Cannot Read File on SDCard \r\n");
          f_close(&g_sFileObject);
          return((int)iFResult);
        }

        buf = &g_pcTmpBuf[0];

        FapiStatus = Fapi_BlockProgram( programFlashBankNumber, programFlashDestination, (unsigned long) buf, ui32BytesRead);

        if (FapiStatus == Fapi_Status_Success)
        {
        	programFlashDestination = programFlashDestination + ui32BytesRead;
        	if ((programFlashDestination >= 0x00180000) && (programFlashBankNumber == 0))
        	{
        		programFlashBankNumber = 1;
        	}
        }
        else
        {
          UARTprintf("\r\nERROR!!! Cannot Program File from SDCard onto Flash \r\n");
          f_close(&g_sFileObject);
          return(-3);
        }
     }
     while ((ui32BytesRead > 0) || (programFlashDestination >= 0x002FFFFF));

     f_close(&g_sFileObject);

      if(programFlashDestination >= 0x002FFFFF)
      {
        return(-1);
      }
	}


    //
    // Return success.
    //
    return(0);
}


//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, unsigned int ui32Line)
{
}
#endif


//*****************************************************************************
//
// The program main function.  It performs initialization, then runs a command
// processing loop to read commands from the console.
//
//*****************************************************************************
int
sdCardInit(void)
{
    FRESULT iFResult;
    TCHAR volumePath = 0;

    //
    // Mount the file system, using logical disk 0.
    //
    iFResult = f_mount(&g_sFatFs, &volumePath, 1);
    if(iFResult != FR_OK)
    {
        return(1);
    }
    else
    {
        return(0);
    }
}

/*
*********************************************************************************************************
*   bootloader_FlashEraseApplicationRegion(void)
*
* Description : erase  flash application region
*
* Argument(s) :
*
* Return(s)   :Fapi_StatusType
*
* Caller(s)   : see Types.h in ..\BSP\FLASH\include
*
* Note(s)     : none.
*********************************************************************************************************
*/

Fapi_StatusType bootloader_FlashApplicationRegion(void)
{
	Fapi_StatusType FapiStatus    = Fapi_Error_Fail;

    // Erase Application Flash Region
	FapiStatus = bootloader_FlashEraseApplication();

	if ( FapiStatus == Fapi_Status_Success )
	{
	  //Program new Application File Into Flash
	  FapiStatus = bootloader_FlashProgramApplication();
	}

	return (FapiStatus);
}

/*
*********************************************************************************************************
*   bootloader_FlashProgramApplication(void)
*
* Description : program application on sdcard into flash regions
*
* Argument(s) :
*
* Return(s)   :Fapi_StatusType
*
* Caller(s)   : see Types.h in ..\BSP\FLASH\include
*
* Note(s)     : none.
*********************************************************************************************************
*/
Fapi_StatusType bootloader_FlashProgramApplication(void)
{
  char newFileName[9] = "newb.bin";
  char defaultFileName[9] = "facb.bin";
  char updateFileName[9] = "updb.bin";
  char *fname;
  FRESULT iFResult;
  FRESULT g_upgradeFile;
  static FIL g_upgradefileObject;
  Fapi_StatusType FapiStatus    = Fapi_Error_Fail;

  //check if upgrade file exists
  g_upgradeFile = f_open(&g_upgradefileObject,"newb.bin", FA_READ);
  //check if upgrade file exists
  if (g_upgradeFile != FR_OK)
  {
    //there was a problem and load factory binary
	fname = & defaultFileName[0];
  }
  else
  {
	fname = &newFileName[0];
	f_close(&g_upgradefileObject); /* Close file.*/
	f_rename("newb.bin","updb.bin");
	fname = &updateFileName[0];

  }


  iFResult = (FRESULT) sdCardCatFlash((char *)fname);

  switch (iFResult)
  {
      case -1:
        UART_putString(UART, "\n\rThe image size is larger than the allowed space memory!\n\r");
    	break;

      case -3:
          UART_putString(UART, "\n\rError: The flash programming failed!\n\r");
    	break;

      case -4:
   	    UART_putString(UART, "\r\n Error: Invalid Filename.\n\r");
     	break;

      default:
        UART_putString(UART, "\r\n The application image has been programmed successfully!");

    	break;
  }

  f_unlink("updb.bin");
  return (FapiStatus);
}

/*
*********************************************************************************************************
*   bootloader_FlashEraseApplication(void)
*
* Description : erase application flash regions
*
* Argument(s) :
*
* Return(s)   :Fapi_StatusType
*
* Caller(s)   : see Types.h in ..\BSP\FLASH\include
*
* Note(s)     : none.
*********************************************************************************************************
*/
Fapi_StatusType bootloader_FlashEraseApplication(void)
{
	Fapi_StatusType FapiStatus    = Fapi_Error_Fail;
	Fapi_FlashStatusWordType  oFlashStatusWord;

	//initialize Flash banks
	FapiStatus = Fapi_initializeFlashBanks(Freq_In_MHz); /* used for API Rev2.01 */

	if ( FapiStatus == Fapi_Status_Success )
	{

	  //set active bank
	  FapiStatus = Fapi_setActiveFlashBank(Fapi_FlashBank0);

	  // enable bank sectors
	  FapiStatus = Fapi_enableMainBankSectors(0xFFF0); // do not enable 1st 4 sectors storing bootloader

	  if ( FapiStatus == Fapi_Status_Success )
	  {

		/* Wait till command has finished */
	    while ( Fapi_checkFsmForReady() == Fapi_Status_FsmBusy );

        while (flashAddress <= 0x002E0000 )
	    {
          FapiStatus = bootloader_FlashEraseSector(flashAddress);
          if   (FapiStatus == Fapi_Status_Success)
          {
            /* Verify that needed space was erased properly */
            FapiStatus = Fapi_doBlankCheck( (uint32_t *)(flashAddress), 0x7FFF, &oFlashStatusWord);
            if (FapiStatus != Fapi_Status_Success)
            {
              UARTprintf("\r\nERROR: Fapi_doBlankCheck ...... \r\n");
        	  break;
            } //Fapi_doBlankCheck
            flashAddress = flashAddress + 0x00020000;
            if (flashAddress == 0x00180000)
            {
        	  //set active bank
        	  FapiStatus = Fapi_setActiveFlashBank(Fapi_FlashBank1);
        	  if ( FapiStatus != Fapi_Status_Success )
        	  {
                UARTprintf("\r\nERROR: Fapi_setActiveFlashBank Fapi_FlashBank 1...... \r\n");
                break;
        	  } //Fapi_setActiveFlashBank

        	  // enable bank sectors
        	  FapiStatus = Fapi_enableMainBankSectors(0xFFFF); /* used for API 2.01*/
        	  if ( FapiStatus != Fapi_Status_Success )
        	  {
        	    UARTprintf("\r\nERROR: Fapi_enableMainBankSectors Fapi_FlashBank 1...... \r\n");
        	    break;
        	  }

        	  /* Wait till command has finished */
        	  while ( Fapi_checkFsmForReady() == Fapi_Status_FsmBusy );

           } //if (flashAddress == 0x00180000)
          } //bootloader_FlashEraseSector
          else
    	  {
    		UARTprintf("\r\nERROR: bootloader_FlashEraseSector ...... \r\n");
    	  }
	    } //while (flashAddress <= 0x002E0000 )
	  } //Fapi_enableMainBankSectors
	  else
	  {
		UARTprintf("ERROR: Fapi_enableMainBankSectors FlashBank 0...... \r\n");
	  }
	} //Fapi_initializeFlashBanks
    else
    {
      UARTprintf("ERROR: Fapi_initializeFlashBanks ...... ");
      switch (FapiStatus)
      {
        case Fapi_Error_Fail:
          UARTprintf("Fapi_Error_Fail\r\n");
        break;

        case Fapi_Error_InvalidHclkValue:
          UARTprintf("Fapi_Error_InvalidHclkValue \r\n");
        break;

        case Fapi_Error_OtpChecksumMismatch:
          UARTprintf("Fapi_Error_OtpChecksumMismatch \r\n");
        break;

        default:
          UARTprintf("Other Error \r\n");
        break;
      }
    }
	return (FapiStatus);
}

/*
*********************************************************************************************************
*   bootloader_FlashEraseSector(uint32_t ui32Address)
*
* Description : erase  flash sector
*
* Argument(s) :
*
* Return(s)   :Fapi_StatusType
*
* Caller(s)   : see Types.h in ..\BSP\FLASH\include
*
* Note(s)     : none.
*********************************************************************************************************
*/
Fapi_StatusType bootloader_FlashEraseSector(uint32_t ui32Address)
{
	Fapi_StatusType FapiStatus    = Fapi_Error_Fail;

	//issue erase sector command
	FapiStatus = Fapi_issueAsyncCommandWithAddress(Fapi_EraseSector, (uint32_t*)(ui32Address));
	if ( FapiStatus == Fapi_Status_Success )
	{
	  /* Wait till sector erase command has finished */
	  while ( Fapi_checkFsmForReady() == Fapi_Status_FsmBusy );

	  /* Check for errors during sector erase */
	  if ( 0ul == Fapi_getFsmStatus() )
	  {
	  	FapiStatus = Fapi_Status_Success;
	  }
	}
	return (FapiStatus);
}

