/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * =======================================================================
 *  This file contains the main() routine for the RAM part of the boot 
 *  loader.
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 *  4/03/03  CHG    Changed getMacAddress() to use define for BSP_EEPROM*
 *                  instead of BSP_NVRAM*.
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Npttypes.h>
#include "narmnvrm.h"
#include "boardParams.h"
#include "sflash.h"
#include "bsp_sys.h"
#include "bsp_bldr.h"
#include "bsp_api.h"
#include "simpleserial.h"
#include "spi_master_api.h"
#include "../bootloader/net/udp.h"
#include "../bootloader/net/tftpc.h"
#include "bootldr.h"
#include "init.h"
#include "blerror.h"
#include "gpio.h"
#include <armutils.h>
#include "gdb.h"

#define BL_TFTP_RECOVERY
#define BL_SERIAL_RECOVERY

#if (BSP_BOOTLOADER_RECOVERY_METHOD == BSP_BOOTLOADER_TFTP_RECOVERY)
    #undef BL_SERIAL_RECOVERY
#elif (BSP_BOOTLOADER_RECOVERY_METHOD == BSP_BOOTLOADER_SERIAL_RECOVERY)
    #undef BL_TFTP_RECOVERY
#elif (BSP_BOOTLOADER_RECOVERY_METHOD != BSP_BOOTLOADER_TRY_ALL)
    #undef BL_SERIAL_RECOVERY
#endif

#if BSP_GDB_SERVER_ENABLE
#include "narmapi.h"

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 *
 * Care must be taken when define these options.
 * Make sure the size of the bootloader is within a sector.
 *
 * BL_TFTP_RECOVERY_MENU - include TFTP recovery in the menu
 * BL_SERIAL_RECOVERY_MENU - include serial recovery in the menu
 * BL_SERIAL_DEBUG_MENU - include naGdbMonitor in the menu
 * BL_TFTP_DEBUG_MENU - include TFTP for downloading debug image
 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 */
/* #define BL_TFTP_RECOVERY_MENU */
/* #define BL_SERIAL_RECOVERY_MENU */
/* #define BL_SERIAL_DEBUG_MENU  */
#define BL_TFTP_DEBUG_MENU 

/* #define _DEBUG_PRINTF */


#ifdef BL_SERIAL_DEBUG_MENU
  #include "armgdb.h" 
#endif

#define DBG_IMAGE_FILE_LENGTH   50

#define ASCII_CR    0xd
#define ASCII_BS    0x8

enum {
    LOAD_FLASH_IMAGE_KEY  = '1',
    
  #ifdef BL_SERIAL_RECOVERY_MENU
    #if (BSP_BOOTLOADER_RECOVERY_METHOD == BSP_BOOTLOADER_TFTP_RECOVERY)
        #error "BSP_BOOTLOADER_SERIAL_RECOVERY or BSP_BOOTLOADER_TRY_ALL is not defined to support BL_SERIAL_RECOVERY_MENU\n"
    #endif
    LOAD_IMAGE_SERIAL_KEY,
  #endif
  
  #ifdef BL_TFTP_RECOVERY_MENU
    #if (BSP_BOOTLOADER_RECOVERY_METHOD == BSP_BOOTLOADER_SERIAL_RECOVERY)
        #error "BSP_BOOTLOADER_TFTP_RECOVERY or BSP_BOOTLOADER_TRY_ALL is not defined to support BL_TFTP_RECOVERY_MENU\n"
    #endif
    LOAD_IMAGE_TFTP_KEY,
  #endif
    
  #ifdef BL_TFTP_DEBUG_MENU
    LOAD_DEBUG_IMAGE_TFTP_KEY,
  #endif
    
  #ifdef BL_SERIAL_DEBUG_MENU
    LOAD_DEBUG_IMAGE_SERIAL_KEY,
  #endif
};

#endif /* BSP_GDB_SERVER_ENABLE */

/*
 * Size of RAM buffer to hold application image received from
 * TFTP server.
 */
#define DOWNLOAD_BUFFER_SIZE    (2*1024*1024)


#define NUMBER_BUFFERS      2
#define BP_BUFFER_SIZE      (sizeof(devBoardParamsType))

extern int blReadFromSFlash(WORD32 startOffset, char *dest, WORD32 size, WORD32 flashBanks);
extern int blWriteToSFlash(WORD32 startOffset, char *source, WORD32 size, WORD32 flashBanks);


static WORD32 blBuffer[NUMBER_BUFFERS][(BP_BUFFER_SIZE / sizeof(WORD32)) + 1];
static BOOLEAN blAllocated[NUMBER_BUFFERS];
static WORD32 dlBuffer[DOWNLOAD_BUFFER_SIZE / sizeof(WORD32)];

#ifdef _DEBUG_PRINTF

static char output[20] = "\0";

static unsigned char u8T_to_h( char i )
{
    static const uint8 i_to_h_chars[] = "0123456789abcdef";
    
    return i_to_h_chars[i & 0xf];
}

static void u32T_to_hbuf( unsigned long i, char * hbuf )
{
    unsigned long count;

    for( hbuf += 7, count = 0;
        count < 8;
        count++, hbuf-- ) {

        *hbuf = u8T_to_h( i & 0xf );
        i >>= 4;
    }
}

#endif

/*
 * This function allocates a memory buffer.
 *
 * This function implements a simple malloc function that will be used by
 * the NVRAM routines to allocate buffers.  The bootloader does not use the 
 * CLib malloc routine because it is gigantic.
 *
 * @category BootLoader 
 * @since 6.0 
 *
 * @param   bufferSize      Size of the buffer to allocate
 * 
 * @return  NULL            Out of memory
 * @return  pointer         Pointer to memory buffer
 */
void * blMalloc(size_t bufferSize)
{
    int idx;
    void * result=NULL;
    static int initialized = FALSE;
    
    if (!initialized)
    {
        initialized = TRUE;
        for (idx = 0; idx < NUMBER_BUFFERS; idx++)
        {
            blAllocated[idx] = FALSE;
        }
    }
    
    if (bufferSize <= BP_BUFFER_SIZE)
    {
        for (idx = 0; idx < NUMBER_BUFFERS; idx++)
        {
            if (!blAllocated[idx])
            {
                blAllocated[idx] = TRUE;
                result = blBuffer[idx];
                break;
            }
        }
    }
    
    return result;
}


/*
 * This function freea a buffer allocated with blMalloc.
 *
 * @category BootLoader 
 * @since 6.0 
 *
 * @param   buffer  Buffer to free
 */
void blFree(void * buffer)
{
    WORD32 bufferAddress = (WORD32) buffer;
    WORD32 blBufferAddress = (WORD32) blBuffer;
    WORD32 blBufferEndAddress = blBufferAddress + (BP_BUFFER_SIZE * NUMBER_BUFFERS);    
    int idx = ((int) (bufferAddress - blBufferAddress)) / BP_BUFFER_SIZE;
    
    if ((bufferAddress >= blBufferAddress) && (blBufferEndAddress > bufferAddress))
    {
        blAllocated [idx] = FALSE;
    }
}


/*
 *
 *  Function: void setupVectorTable(void)
 *
 *  Description:
 *
 *      This routine sets up the standard vector table.  When an exception occurs
 *      the ARM processor switches modes and changes the PC so that the processor
 *      executes the instruction at the exception vector.  For example, a prefetch
 *      abort will cause the processor to switch to abort mode and change the PC
 *      to 0xc.  Note that this is different from most other processors that load
 *      an address from the vector table.  The ARM *EXECUTES* the word in the
 *      exception table.  Normally the instruction is a jump to the exception
 *      handler.
 *
 *      In NET+OS, we implement the vector table by placing an instruction at each
 *      table entry that causes the processor to load the PC with the contents of the
 *      word at PC+0x20.  This implements a 32-bit jump.  So, the NET+OS vector
 *      table looks like this.
 *
 *      Note that if you wanted to be really tricky, you could move the second half
 *      of the vector table to create some space after address 0x1c.  This would
 *      allow you to implement a very fast FIQ handler that did not have to start
 *      with a jump.  We don't use FIQ in NET+OS, so we didn't bother with this.
 *
 *
 *           Address     Exception                   Contents
 *           ========    ========================    ===================================
 *           0x00        Reset                       LD PC,PC+0x20
 *           0x04        Undefined Instruction       LD PC,PC+0x20
 *           0x08        Software Interrupt          LD PC,PC+0x20
 *           0x0C        Prefetch Abort              LD PC,PC+0x20
 *           0x10        Data Abort                  LD PC,PC+0x20
 *           0x14        not used                    LD PC,PC+0x20
 *           0x18        IRQ                         LD PC,PC+0x20
 *           0x1c        FIQ                         LD PC,PC+0x20
 *
 *           0x20        Reset                       Address of Reset handler
 *           0x24        Undefined Instruction       Address of UNDEF handler
 *           0x28        Software Interrupt          Address of SWI handler
 *           0x2C        Prefetch Abort              Address of prefetch abort handler
 *           0x30        Data Abort                  Address of data abort handler
 *           0x34        not used                    Address of Reset Handler
 *           0x38        IRQ                         Address of IRQ handler
 *           0x3c        FIQ                         Address of FIQ handler
 *
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */

static void setupVectorTable(void)
{
   /*
   !! Don't change any thing in reset.s, tx_ill.s, and setupVectorTable() !!
   */
#define LOAD_PC_WITH_WORD_AT_PC_PLUS_20     0xe59ff018

      *(WORD32 *)(0x0)  = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;           
      *(WORD32 *)(0x4)  = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0x8)  = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0xc)  = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0x10) = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0x14) = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0x18) = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;
      *(WORD32 *)(0x1c) = LOAD_PC_WITH_WORD_AT_PC_PLUS_20;

      *(WORD32 *)(0x20) = (WORD32) Reset_Handler_ROM;
      *(WORD32 *)(0x24) = (WORD32) Undefined_Handler;
      *(WORD32 *)(0x28) = (WORD32) SWI_Handler;
      *(WORD32 *)(0x2c) = (WORD32) Prefetch_Handler;
      *(WORD32 *)(0x30) = (WORD32) Abort_Handler;
      *(WORD32 *)(0x34) = (WORD32) Reset_Handler_ROM;
      *(WORD32 *)(0x38) = (WORD32) IRQ_Handler;
      *(WORD32 *)(0x3c) = (WORD32) FIQ_Handler;
}



#if defined BL_TFTP_RECOVERY || defined BL_TFTP_DEBUG_MENU
/*
 * Returns a pointer to a byte array that contains the Ethernet MAC
 * address for this unit
 *
 * The byte array must be statically
 * allocated.
 *
 * @external 
 * @category BootLoader 
 * @since 5.1 
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @return "MAC Address"    Pointer to statically allocated BYTE array that contains
 *                          the Ethernet MAC address that should be used to 
 *                          communicate on the network.
 */
static BYTE * getMacAddress(void)
{
    static BYTE macAddress[6];
    static BOOLEAN firstTime = TRUE;

    if (firstTime)
    {
        NANVInit((char *) NABspEepromBase, NABspEepromSize);
        customizeGetMACAddress(macAddress);
        firstTime = FALSE;
    }

    return macAddress;
}

#endif /* BL_TFTP_RECOVERY || BL_TFTP_DEBUG_MENU */


/*
 * Determines whether an image is valid
 *
 * This function determines
 * whether the image contains a valid application and
 * whether it can be run on this unit.  The default implementation
 * determines these conditions by checking for a valid signature and checksum.
 *
 * @external 
 * @category BootLoader 
 * @since 5.1 
 *
 * @param imageInfo       Pointer to the image header.
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @return TRUE        Image is valid.
 * @return FALSE       Image is not valid.
 *
 */
static BOOLEAN isImageValid(blImageHeaderType * imageInfo, int imageIsInRAM)
{
    BOOLEAN result = FALSE;
    WORD32 size = imageInfo->headerSize + imageInfo->size + sizeof(WORD32);

    if (memcmp(imageInfo->signature, BL_SIGNATURE, sizeof(BL_SIGNATURE)) == 0)
    {
        if (imageIsInRAM)
        {
            result = (blCrc32(0, (BYTE *) imageInfo, size) == 0);
        }
        else
        {
            if (blReadFromSFlash(NAAppOffsetInSFlash, (char *)&dlBuffer[0], size, 0) != BL_SUCCESS)
                NABlReportError(SIMPLE_SPI_EEPROM_READ_FAIL);

            result = (blCrc32(0, (BYTE *) &dlBuffer[0], size) == 0);
        }
    }

    return result;
}






/*
 * This function determines whether to download a new application image.
 *
 * The default implementation does this by determining if the
 * application image in ROM has been corrupted.
 *
 * @external 
 * @category BootLoader 
 * @since 5.1 
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 *          You may want to add additional code that checks other things than just
 *          the application image.  For example, you may want to check for a jumper
 *          or a switch that would indicate that the current application image 
 *          should be replaced whether it is valid or not.
 *
 * @return TRUE        Need to download an application from the network.
 * @return FALSE       Do not download a new image, use the current image in flash.
 *
 */
static BOOLEAN shouldDownloadImage(void)
{
#if (BSP_BOOTLOADER_BOOT_FROM_NETWORK_ONLY == TRUE)
    return TRUE;
#else
    int result = TRUE;
    blImageHeaderType imageInfo;

    memset(&imageInfo, 0, sizeof(blImageHeaderType));
    if (blReadFromSFlash(NAAppOffsetInSFlash, (char *)&dlBuffer[0], sizeof (blImageHeaderType), 0) != BL_SUCCESS)
        NABlReportError(SIMPLE_SPI_EEPROM_READ_FAIL);

    fmemcpy(&imageInfo, &dlBuffer[0], sizeof (blImageHeaderType));
    
    result = (isImageValid(&imageInfo, 0/*image is in EEPROM*/) == FALSE);

    return result;
#endif
}


#if defined BL_TFTP_RECOVERY || defined BL_TFTP_DEBUG_MENU

/*
 * Returns the name of the application image to download
 *
 * The filename
 * is passed to the DHCP/BOOTP server.  The DHCP/BOOTP server 
 * returns the full pathname of the file to download.  The DHCP/BOOTP
 * server can use the filename as a hint about what file it should
 * download to the unit.  Some implementations, however, ignore
 * the filename.
 *
 *
 * The file name should be returned in a statically allocated buffer.
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @external 
 * @category BootLoader 
 * @since 5.1 
 *
 * @return "filename"   The file name of application image in a statically allocated buffer
 *
 */

static char * getDefaultFilename(void)
{
    static char *defaultFilename = "";

    return defaultFilename;
}

#endif /* BL_TFTP_RECOVERY || BL_TFTP_DEBUG_MENU */


#if defined BL_TFTP_RECOVERY || defined BL_TFTP_DEBUG_MENU

static int ethernet_initialize(void)
{
    int ecode;

    blEthernetInit(getMacAddress());
    ecode = blUdpInit(0, 0, 0);

    return ecode;

}

/**
 * Initializes the board with DHCP.
 *
 * This function gets, returns & initializes ip configuration from DHCP.
 * It returns:
 * <list type="ordered">
 *   <item>The IP address, subnet mask, and gateway address</item>
 *   <item>The name of the file to download</item>
 *   <item>The IP address of the server to download it from</item>
 * </list>
 * 
 * This function is used for TFTP recovery mode & TFTP download debug image.
 *
 * @external 
 * @category BSP:BootLoader 
 * @see @url http://www.ietf.org/rfc/rfc2131.txt DHCP:RFC 2131
 * @see @url http://www.ietf.org/rfc/rfc951.txt BOOTP:RFC 951
 * @see @url http://www.ietf.org/rfc/rfc783.txt TFTP:RFC 783
 * @since 5.1 
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @param info      DHCP info for ip configuration, TFTP server ip address, & image filename.
 *
 * @return BL_SUCCESS   It's successfully initlialized ip configuration.
 * @return "not BL_SUCCESS"   Not able to initialize the board.
 *
 * @note This is used if TFTP is used to download a debug image for gdb debugger.
 *
 * @link BSP_GDB_SERVER_ENABLE
**/
static int board_initialize_dhcp(blDhcpInfoType * info, BOOLEAN tftpEnabled)
{
    int ecode = BL_SUCCESS;

    if  (tftpEnabled == TRUE)
    {
        info->wantTftpInfo = tftpEnabled;
        strcpy(info->imageFilename, getDefaultFilename());
    }

    info->useDhcpOptions = TRUE;

    if (blDhcpGetInfo (info) != BL_SUCCESS)
    {
        info->useDhcpOptions = FALSE;
        ecode = blDhcpGetInfo (info); 
        if (ecode != BL_SUCCESS)
        {
            NaBootloaderDhcpError(ecode);
            goto _ret;
        }
    }

    #ifdef _DEBUG_PRINTF
        bsp_printf("\nDHCP Ip = ");
        u32T_to_hbuf( info->ipAddress, output );
  	    bsp_printf(output);
        bsp_printf("\nSubnet = ");
        u32T_to_hbuf( info->subnetMask, output );
  	    bsp_printf(output);
        bsp_printf("\nGateway = ");
        u32T_to_hbuf( info->gateway, output );
  	    bsp_printf(output);
  	    bsp_printf("\n");
    #endif

    ecode = blUdpInit(info->ipAddress, info->subnetMask, info->gateway);
    if (ecode != BL_SUCCESS)
    {
        NaBootloaderIpudpError(ecode);
    }
    else
    {
    }
_ret:
    return ecode;
        
}
#endif /* BL_TFTP_RECOVERY || BL_TFTP_DEBUG_MENU */

#if defined BL_TFTP_DEBUG_MENU
static int board_initialize_ip(blDhcpInfoType * info)
{
extern NaIamParams_t * customizeIamFindInterfaceConfig(char const * ifname, NaIamParamsInfo_t * config);

    int                             ecode = BL_SUCCESS;
    static devBoardParamsType       unit_params;
    static blDhcpInfoType           dhcp_ip;
    NaIamParams_t                   * interface;


    blEthernetInit(getMacAddress());

    ecode = ethernet_initialize();

    if (ecode != BL_SUCCESS)
    {
        NaBootloaderIpudpError(ecode);
        goto _ret;
    }

    customizeReadDevBoardParams(&unit_params);

    interface = customizeIamFindInterfaceConfig(BP_ETH_INTERFACE, &unit_params.iamParamsInfo);

    if (interface == NULL)
    {
        interface = customizeIamFindUnusedInterfaceConfig(&unit_params.iamParamsInfo, TRUE);
        customizeIamGetDefaultEthernetConfig(interface, 0);
    }

    if (/*(interface->staticCfg.isConfigValid) &&*/ (interface->staticParams.isEnabled))
    {

        info->ipAddress = interface->staticParams.IPV4_ADDR(ipAddress);
        info->subnetMask = interface->staticParams.subnetMask;
        info->gateway = interface->staticParams.IPV4_ADDR(gateway);

        ecode = blUdpInit(info->ipAddress, info->subnetMask, info->gateway);
        if (ecode != BL_SUCCESS)
        {
            bsp_printf("blUdpInit error\n");
            NaBootloaderIpudpError(ecode);
        }
    #ifdef _DEBUG_PRINTF
        bsp_printf("\nStatic Ip = ");
        u32T_to_hbuf( info->ipAddress, output );
  	    bsp_printf(output);
        bsp_printf("\nSubnet = ");
        u32T_to_hbuf( info->subnetMask, output );
  	    bsp_printf(output);
        bsp_printf("\nGateway = ");
        u32T_to_hbuf( info->gateway, output );
  	    bsp_printf(output);
  	    bsp_printf("\n");
    #endif
    }
    else
    {
        ecode = board_initialize_dhcp(&dhcp_ip, FALSE);

        info->ipAddress = dhcp_ip.ipAddress;
        info->subnetMask = dhcp_ip.subnetMask;
        info->gateway = dhcp_ip.gateway;
    }
                        
_ret:
    return ecode;    
}
#endif /* BL_TFTP_DEBUG_MENU */

#if defined BL_TFTP_RECOVERY || defined BL_TFTP_DEBUG_MENU
static int downloadImageUsingTftp(WORD32 * destination, WORD32 maxLength, WORD32 tftpIp, char * filename)
{
    int                     ecode = BL_SUCCESS;
    WORD32                  bytesReceived;
    static blDhcpInfoType   info;
    WORD32                  tftpServer = tftpIp;
    char                    * downloadImage = filename;
    if (tftpIp == 0)
    {
        memset(&info, 0, sizeof(blDhcpInfoType));
        ecode = ethernet_initialize();
        if (ecode != BL_SUCCESS)
        {
            bsp_printf(".");
            NaBootloaderTftpError(ecode);
            goto tftpDone; 
        }
        
        ecode = board_initialize_dhcp(&info, TRUE);
        if (ecode != BL_SUCCESS)
        {
            bsp_printf(".");
            NaBootloaderTftpError(ecode);
            goto tftpDone; 
        }
        
        tftpServer = info.serverIp;
        downloadImage = info.imageFilename;
        
    }
    ecode = blTftpGetFile((char *) destination, maxLength, tftpServer, downloadImage, &bytesReceived);
    if (ecode != BL_SUCCESS)
    {
        NaBootloaderTftpError(ecode);
        goto tftpDone; 
    }
    
    if (!isImageValid((blImageHeaderType *) destination, 1/*image is in RAM*/))
    {
        NaBootloaderImageError(BL_INVALID_IMAGE);
        goto tftpDone; 
    }

    tftpDone:
    return ecode;
}
#endif /* BL_TFTP_RECOVERY || BL_TFTP_DEBUG_MENU */

#ifdef BL_SERIAL_RECOVERY

static int downloadImageUsingSerial(WORD32 * destination, WORD32 maxLength)
{
    int ecode = BL_SUCCESS;

    ecode = NaBootloaderSerialRecv((unsigned char *)destination, maxLength);
    if (ecode != BL_SUCCESS)
    {
        NaBootloaderSerialError(ecode);
    }

    return ecode;
}

#endif /* BL_SERIAL_RECOVERY */

/*
 * Downloads a new application image from the network and stores it
 * in the buffer the caller provides.
 *
 * The default implementation uses TFTP recovery mode and/or serial 
 * recovery mode. 
 *
 * TFTP recovery mode uses the DHCP/BOOTP protocol to get an IP 
 * address and the name of the image file and the TFTP protocol to 
 * download the file.
 *
 * The default serial recovery mode uses XModem protocol to download
 * the file.
 *
 * TFTP mode uses DHCP/BOOTP is used to get:
 *
 *<list type="ordered">
 *   <item>The IP address, subnet mask, and gateway address</item>
 *   <item>The name of the file to download</item>
 *   <item>The IP address of the server to download it from</item>
 *</list>
 *
 * For many applications, the IP address, subnet mask, and
 * gateway address will already be stored in NVRAM, so it may
 * seem that these values should be read from NVRAM instead of getting them
 * from the DHCP/BOOTP server.  However, the bootloader still has to get
 * the image filename and server address.  So, unless these
 * values are also stored in NVARM, using the IP address, subnet
 * mask, and gateway address stored in NVRAM doesn't simplify 
 * anything.  Getting them from the server, however, provides 
 * more flexibility.  It's possible, for example, that
 * end users may take units with corrupted flash to a different
 * network where the unit's normal IP address isn't valid.  Getting the
 * IP networking parameters from DHCP/BOOTP lets the end user set
 * network parameters that will work on the recovery network. Calls
 * the error handler incase of failure. 
 *
 * @external 
 * @category BootLoader 
 * @see @url http://www.ietf.org/rfc/rfc2131.txt DHCP:RFC 2131
 * @see @url http://www.ietf.org/rfc/rfc951.txt BOOTP:RFC 951
 * @see @url http://www.ietf.org/rfc/rfc783.txt TFTP:RFC 783
 * @since 5.1 
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @param destination       pointer to RAM buffer to store image in
 *                          must be WORD32 aligned so that header
 *                          can be read as structure
 * @param maxLength         size of buffer in bytes
 * @param tftpIp            TFTP server IP address
 * @param filename          string contains the name of an image to be retrieved
 * @param method          string contains the name of an image to be retrieved
 *  <list type="ordered">
 *   <item>BSP_BOOTLOADER_TFTP_RECOVERY - retrieve an image from TFTP.</item>
 *   <item>BSP_BOOTLOADER_SERIAL_RECOVERY - retrieve image data from serial port.</item>
 *   <item>BSP_BOOTLOADER_TRY_ALL - retrieve an image from TFTP first. If fails, use
 *                          serial port to retrieve image data.</item>
 *  </list>
 *
 * @return BL_SUCCESS          image has been stored in buffer
 *
 */
static int downloadImage(WORD32 * destination, WORD32 maxLength, WORD32 tftpIp, char * filename, int method)
{
    int ecode = BL_SUCCESS;

    #if defined BL_TFTP_DEBUG_MENU || defined BL_TFTP_RECOVERY
    if (method == BSP_BOOTLOADER_TFTP_RECOVERY ||
        method == BSP_BOOTLOADER_TRY_ALL)
    {
        bsp_printf("Tftp...\n");

        ecode = downloadImageUsingTftp(destination, maxLength, tftpIp, filename);
        if (ecode == BL_SUCCESS)
        {
            goto imageDone;
        }
    }
    #endif


    #ifdef BL_SERIAL_RECOVERY
    if (method == BSP_BOOTLOADER_SERIAL_RECOVERY ||
        method == BSP_BOOTLOADER_TRY_ALL)
    {
        bsp_printf("Serial...\n");

        ecode = downloadImageUsingSerial(destination, maxLength);
        if (ecode == BL_SUCCESS)
        {
            goto imageDone;
        }
    }
    #endif

    imageDone:
    return ecode;
}

#ifdef BL_TFTP_DEBUG_MENU

static int key_ip_address(WORD32 *serverIp)
{
    int key_pressed;
    int i;
    char input[20];
    
    bsp_printf("\nEnter TFTP server ip address: \n");
            
    i = 0;
    do {
        
        key_pressed = bsp_fgetc(1000);
        if (key_pressed == ASCII_BS)
        {
            if (i > 0) {
                bsp_fputc( (char)key_pressed);
                bsp_fputc( (char)' ');
                bsp_fputc( (char)ASCII_BS);
                i--;
            }
        }
        else if (key_pressed > 0)
        {
            bsp_fputc( (char)key_pressed);
            input[i] = (char) key_pressed;
            i++;
        }
    } while (key_pressed != ASCII_CR && i < 20);

    input[i-1] = '\0';           
    
    *serverIp = NAInet_addr(input); 
    return (strlen(input));
}

static void key_filename(char *filename)
{    
    int key_pressed;
    int i;
            
    bsp_printf("\n\nEnter debug image filename: \n");
    
    i = 0;
    do {
        
        key_pressed = bsp_fgetc(1000);
        if (key_pressed == ASCII_BS)
        {
            if (i > 0) {
                bsp_fputc( (char)key_pressed);
                bsp_fputc( (char)' ');
                bsp_fputc( (char)ASCII_BS);
                i--;
            }
        }
        else if (key_pressed > 0)
        {
            bsp_fputc( (char)key_pressed);
            filename[i] = (char)key_pressed;
            i++;
         }
    } while (key_pressed != ASCII_CR && i < DBG_IMAGE_FILE_LENGTH);
    
    filename[i-1] = '\0';           
    
}    

static int get_menu_selection(void)
{
    int key_pressed;
    /*dialog will be here */
    bsp_printf("\n\n\n\n\nNET+Monitor Version 1.0\n");
    bsp_printf("Copyright (c) 2000-2007, Digi International\n");
    bsp_printf("PLATFORM: ");
    bsp_printf((char *)NABspPlatform);
    bsp_printf("\n---------------------------------------------------------------------------\n");
    bsp_printf("Press ");
    bsp_fputc(LOAD_FLASH_IMAGE_KEY);
    bsp_printf(" to boot from image in FLASH.\n");
    
#ifdef BL_SERIAL_RECOVERY_MENU
    bsp_printf("Press ");
    bsp_fputc(LOAD_IMAGE_SERIAL_KEY);
    bsp_printf(" to boot from image over serial.\n");
#endif
    
#ifdef BL_TFTP_RECOVERY_MENU
    bsp_printf("Press ");
    bsp_fputc(LOAD_IMAGE_TFTP_KEY);
    bsp_printf(" to boot from image over TFTP.\n");
#endif
    
#ifdef BL_TFTP_DEBUG_MENU
    bsp_printf("Press ");
    bsp_fputc(LOAD_DEBUG_IMAGE_TFTP_KEY);
    bsp_printf(" to load debug image over TFTP.\n");    
#endif
    
#ifdef BL_SERIAL_DEBUG_MENU
    bsp_printf("Press ");
    bsp_fputc(LOAD_DEBUG_IMAGE_SERIAL_KEY);
    bsp_printf(" to load debug image over serial using gdb.\n");    
#endif


    do {
        key_pressed = bsp_fgetc(5000);
    #if (NA_GDB_BOOTLOADER_START != NA_GDB_BOOTLOADER_NONE)
        if (key_pressed == -2) break;
    #endif        
        
    } while ( 
        #ifdef BL_TFTP_RECOVERY_MENU
            key_pressed != LOAD_IMAGE_TFTP_KEY && 
        #endif
        #ifdef BL_SERIAL_RECOVERY_MENU
            key_pressed != LOAD_IMAGE_SERIAL_KEY &&
        #endif
        #ifdef BL_SERIAL_DEBUG_MENU
            key_pressed != LOAD_DEBUG_IMAGE_SERIAL_KEY &&
        #endif
        #ifdef BL_TFTP_DEBUG_MENU
            key_pressed != LOAD_DEBUG_IMAGE_TFTP_KEY &&
        #endif
            key_pressed != LOAD_FLASH_IMAGE_KEY);
        
    if (key_pressed > 0)
    {
        bsp_fputc((char)key_pressed);
        bsp_printf("\n"); 
    }
    
    return key_pressed;
}

#endif /* NA_GDB_DEBUG_ENABLE */


/*ad*
 * This routine is called after the C runtime initialization has taken place
 * and made from the main() routine.
 *  
 * NASPIBlMain is the main function for the RAM part of the bootloader.
 *
 * The default implementation validates the application image currently
 * in flash.  If the image is okay, then it is decompressed to RAM and 
 * executed.  If the image is corrupt, then a new image is downloaded 
 * from the network.
 *
 * @note    This function must be customized to support your specific
 *          platform.  The default implementation supports NET+OS
 *          development boards.
 *
 * @external 
 * @category BootLoader 
 * @since 5.1 
 *
 * @return 0    "Success"
 * @return !=0  "Reserved for future use"
*ad*/
int NASPIBlMain(void)
{
    static blDhcpInfoType   dhcp_info;
    
    NaStatus                rcode;
    int                     ccode;
    WORD32                  imageSize = NAAppMaxSizeInFlash;
    int                     method = BSP_BOOTLOADER_TRY_ALL;
    BOOLEAN                 writeFlash = TRUE;
    
#ifdef BL_TFTP_DEBUG_MENU
    int key_pressed;
    BOOLEAN isSelected = TRUE;
#endif    

    memset(&dhcp_info, 0, sizeof(dhcp_info));

    setupSimpleSerial();
    NABoardParamsSetMalloc(blMalloc, blFree);   /* setup malloc for NVRAM routines*/
    
    setupVectorTable();
    init_irq_tables(); 
    
    rcode = NASpiRegisterDevice(&NADefaultSflashDevice);
    if (rcode != NASTATUS_SUCCESS)
    {
        NaBootloaderSpiError(BL_SPI_REGISTER_DEVICE_ERROR);
    }

    ccode = NASerialFlashInit(NADefaultSflashDevice.name);
    if (ccode != NASERIAL_FLASH_SUCCESS)
    {
        NaBootloaderSpiError(BL_SFLASH_INIT_ERROR);
    }

#ifdef BL_TFTP_DEBUG_MENU

    /* wait for selection */
    key_pressed = get_menu_selection();
    
    if (key_pressed <= 0)
    {   /* no key pressed */
        isSelected = FALSE;

    #if (NA_GDB_BOOTLOADER_START == NA_GDB_BOOTLOADER_RECOVER_TFTP)
        #ifdef BL_TFTP_RECOVERY_MENU
            key_pressed = LOAD_IMAGE_TFTP_KEY;
        #else
            #error "BL_TFTP_RECOVERY_MENU is not enabled to support NA_GDB_BOOTLOADER_RECOVER_TFTP"
        #endif
        
    #elif (NA_GDB_BOOTLOADER_START == NA_GDB_BOOTLOADER_RECOVER_SERIAL)
        #ifdef BL_SERIAL_RECOVERY_MENU
            key_pressed = LOAD_IMAGE_SERIAL_KEY;
        #else
            #error "BL_SERIAL_RECOVERY_MENU is not enabled to support NA_GDB_BOOTLOADER_RECOVER_SERIAL"
        #endif
        
        
    #elif (NA_GDB_BOOTLOADER_START == NA_GDB_BOOTLOADER_LOAD_DEBUG_IMAGE)
        key_pressed = LOAD_DEBUG_IMAGE_TFTP_KEY;
        dhcp_info.serverIp = NAInet_addr(NA_GDB_TFTP_SERVER); 
        strcpy(dhcp_info.imageFilename, NA_GDB_TFTP_IMAGE); 
        if (dhcp_info.serverIp == 0)
        {
            key_pressed = LOAD_FLASH_IMAGE_KEY;
        }
        
        #ifdef _DEBUG_PRINTF
        bsp_printf("TFTP Server: ");
        bsp_printf(NA_GDB_TFTP_SERVER);
        bsp_printf("\n");
        bsp_printf("image name: ");
        bsp_printf(NA_GDB_TFTP_IMAGE);
        bsp_printf("\n");
        #endif
        
    #else
        key_pressed = LOAD_FLASH_IMAGE_KEY;
    #endif
    }

_startKey:
    /* determine which key is pressed */
    method = BSP_BOOTLOADER_TRY_ALL;
    writeFlash = TRUE;
    
#ifdef BL_SERIAL_DEBUG_MENU
    /* Use gdb debugger to download debug image using
     * serial port. This accepts load command.
     */
    if (key_pressed == LOAD_DEBUG_IMAGE_SERIAL_KEY)
    {
        extern void naGdbSerialInit(void);
        
        static unsigned long    exception = 0xe6000010;    /* undefined exception for gdb */
        bsp_printf("\nDownloading debug image over serial...\n");
        *(WORD32 *)(0x24) = (WORD32) naGdbMonitor; 
        naGdbSerialInit();
        blExecuteImage(&exception);
    }
#endif    

#ifdef BL_SERIAL_RECOVERY_MENU
    if (key_pressed == LOAD_IMAGE_SERIAL_KEY)
    {
        bsp_printf("\nDownloading image over serial...\n");
        method = BSP_BOOTLOADER_SERIAL_RECOVERY;
        goto _recover;
    }
#endif        

#ifdef BL_TFTP_RECOVERY_MENU
    if (key_pressed == LOAD_IMAGE_TFTP_KEY)
    {
        bsp_printf("\nDownloading image over TFTP...\n");
        method = BSP_BOOTLOADER_TFTP_RECOVERY;
        goto _recover;
    }
#endif        
    
#ifdef BL_TFTP_DEBUG_MENU
    if (key_pressed == LOAD_DEBUG_IMAGE_TFTP_KEY)
    {
            bsp_printf("\nDownloading debug image over TFTP...\n");
            
            if (dhcp_info.serverIp == 0)
            {
                if (key_ip_address(&dhcp_info.serverIp) == 0)
                {
                    dhcp_info.serverIp = NAInet_addr(NA_GDB_TFTP_SERVER); 
                    bsp_printf(NA_GDB_TFTP_SERVER);
                    bsp_printf("\n");
                }
            }
#if 0
            if (dhcp_info.serverIp == 0)
            {
                dhcp_info.serverIp = NAInet_addr(NA_GDB_TFTP_SERVER); 
                bsp_printf(NA_GDB_TFTP_SERVER);
                bsp_printf("\n");
            }
#endif
            
            if (strlen(dhcp_info.imageFilename) == 0)
            {
                key_filename(dhcp_info.imageFilename);
            }
            if (strlen(dhcp_info.imageFilename) == 0)
            {
                strcpy(dhcp_info.imageFilename, NA_GDB_TFTP_IMAGE); 
                bsp_printf(dhcp_info.imageFilename);
            }            
            
            bsp_printf("\n");
            writeFlash = FALSE;
            method = BSP_BOOTLOADER_TFTP_RECOVERY;;
            
            if (board_initialize_ip(&dhcp_info) != BL_SUCCESS)
            {
                dhcp_info.ipAddress = 0;
            }
            goto _recover;
    }
    else
#endif /* BL_TFTP_RECOVERY_MENU */

#endif /* BL_TFTP_DEBUG_MENU */
    {
    if (shouldDownloadImage())
#if defined BL_SERIAL_RECOVERY_MENU || defined BL_TFTP_RECOVERY_MENU || defined BL_TFTP_DEBUG_MENU
_recover:
#endif
    {
        /*
         * If we take this branch, then we want to download a new image.
         */
        
        blImageHeaderType *imageInfo = (blImageHeaderType *) dlBuffer;

        do
        {
        #ifdef BL_TFTP_DEBUG_MENU
            if (key_pressed != LOAD_DEBUG_IMAGE_TFTP_KEY)
        #endif
                bsp_printf("Start recovery...\n");
                    
            ccode = downloadImage(dlBuffer, 
                  imageSize < DOWNLOAD_BUFFER_SIZE 
                  ? imageSize
                  : DOWNLOAD_BUFFER_SIZE,
                  dhcp_info.serverIp, 
                  dhcp_info.imageFilename, 
                  method);
            if (ccode != BL_SUCCESS)
            {
#ifdef BL_TFTP_DEBUG_MENU
                /* if this is default selection for debug image,
                 * try to load & run the image from the flash
                 * since error is encountered for debug image.
                 */
                if (isSelected == FALSE && key_pressed != LOAD_FLASH_IMAGE_KEY)
                {
                    bsp_printf("tftp fails...\n");
                    isSelected = TRUE;
                    key_pressed = LOAD_FLASH_IMAGE_KEY;
                    goto _startKey;
                }
#endif    
                NaBootloaderImageError(BL_DOWNLOAD_IMAGE_ERROR);
            }
        } while (ccode);

        /*
         * At this point, an application has been downloaded into RAM.  We can do one of 3
         * things with it.  We can write it into flash, we can copy it to somewhere in RAM
         * and execute it, or we can decompress it and then execute it.  The value of the
         * flags parameter determines what we are going to do.
         */
        if ((imageInfo->flags & BL_WRITE_TO_FLASH) && writeFlash == TRUE)
        {
            ccode = blWriteToSFlash(NAAppOffsetInSFlash, (char *) dlBuffer,  (imageInfo->headerSize + imageInfo->size + sizeof(WORD32)), NABspFlashBanks);
            if (ccode != BL_SUCCESS)
            {
                NaBootloaderSpiError(ccode);
            }
            customizeReset();
        }
        else
        {
        
            BYTE *appStart = ((BYTE *) dlBuffer) + imageInfo->headerSize;

        #ifdef _DEBUG_PRINTF
            bsp_printf("\nDecompressImage starting = ");
            u32T_to_hbuf( (unsigned long)imageInfo->ramAddress, output );
  	        bsp_printf(output);
            bsp_printf(" src ptr = ");
            u32T_to_hbuf( (unsigned long)appStart, output );
  	        bsp_printf(output);
            bsp_printf(" size = ");
            u32T_to_hbuf( (unsigned long)imageInfo->size, output );
  	        bsp_printf(output);
  	        bsp_printf("\n");
        #endif            
            if (imageInfo->flags & BL_IS_COMPRESSED)
            {
                blDecompressImage((WORD32 *) imageInfo->ramAddress, (WORD32 *) appStart, imageInfo->size, imageInfo->flags);
            }
            else
            {
                memcpy ((void *) imageInfo->ramAddress, appStart, imageInfo->size);
            }
            blExecuteImage((WORD32 *) imageInfo->ramAddress);
        }
    }
    else
    {
        /*
         * If we take this branch then the application image in flash is okay and we
         * are going to execute it.  There are three cases.  We can execute the image
         * directly from flash, we can copy the image from ROM to RAM and execute it
         * from RAM, or we can decompress the image from ROM to RAM and execute it from
         * RAM.  The last case will be the usual case since RAM is cheaper and faster
         * than flash.
         */
        blImageHeaderType imageInfo;
        WORD32 appStart = NAAppOffsetInSFlash;

        memset(&imageInfo, 0, sizeof(blImageHeaderType));
        ccode = blReadFromSFlash(NAAppOffsetInSFlash, (char *)&dlBuffer[0], sizeof (blImageHeaderType), 0);
        if (ccode != BL_SUCCESS)
        {
            NaBootloaderSpiError(ccode);
        }

        fmemcpy(&imageInfo, &dlBuffer[0], sizeof (blImageHeaderType));

            
        appStart += imageInfo.headerSize;

        if (imageInfo.flags & BL_EXECUTE_FROM_ROM)
        {
            /*code can't be running from serial flash, fatal error */           
            NaBootloaderImageError(BL_INVALID_IMAGE);
        }
        else if (imageInfo.flags & BL_IS_COMPRESSED)
        {
            ccode = blReadFromSFlash(appStart, (char *)&dlBuffer[0], imageInfo.size, 0);
            if (ccode != BL_SUCCESS)
            {
                NaBootloaderSpiError(ccode);
            }

            blDecompressImage((WORD32 *) imageInfo.ramAddress, &dlBuffer[0], imageInfo.size, imageInfo.flags);
            blExecuteImage((WORD32 *) imageInfo.ramAddress);
        }
        else
        {
            /*copy image in sflash to RAM*/
            ccode = blReadFromSFlash(appStart, (char *)imageInfo.ramAddress, imageInfo.size, 0);
            if (ccode != BL_SUCCESS)
            {
                NaBootloaderSpiError(ccode);
            }

            blExecuteImage((WORD32 *) imageInfo.ramAddress);
        }
    }
    }

    return 0;
}

 




