/*
 *  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
 *
 * Description.
 * =======================================================================
 *  This is the RAM based FTP Server
 *
 *
 *
 *  Edit Date/Ver   Edit Description
 *  =============   =======================================================
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Npttypes.h>
#include "fservapi.h"
#include "sysAccess.h"
#include "bsp_api.h"
#include "reg_def.h"
#include "narmled.h"
#include "cs.h"
#include "naflash.h"
#include "tx_api.h"
#include <Flash.h>
#include "gpiomux_def.h"
#include "yaffs_api.h"
#include "bootldr.h"
#include "fsintflocal.h"
#include "platformCodes.h"

/*****************************************************************************
 *  Declarations for Flash Image Downloads. 
 */
static char *sectorDatap = NULL;
static unsigned long *sectorSizes = NULL;
static char os_boot_file[64] = "image-xxx.bin";
static char u_boot_file[64]  = "u-boot-xxx.bin";
static NaFWDl_t fwInfo;
static unsigned short fwDlState = NA_FWDL_INIT;

static void *fwDlStack = NULL;
static TX_THREAD *fwDlTid = NULL;

static unsigned int currentLength = 0;
static unsigned int sectors = 0;
static unsigned int blocks = 0;
static unsigned int pageSize = 0;
static unsigned int currentSector = 0;
static unsigned int writeIndex = 0;
static unsigned int remnantCount  = 0;

static unsigned char uboot_image;
static unsigned char os_image;
static unsigned char flashFull = 0;

/* user can update these defines if they intend to use different name */ 
#define ROM_BOOT_FILE       "rom.bin"
//#define SPI_BOOT_FILE       "spi_rom.bin"
#define ROMZIP_BOOT_FILE    "romzip.bin"
#define ROM_UBOOT_FILE      "u-boot.bin"
#define BACKUP_IMAGE_FILE   "backup.bin"
#define APP_IMAGE_FILE      "image.bin"

#define NAND_DEV_NAME       "/root"

#define FTP_RESET_DELAY     2

/*ad*
 *
 * This API can be used to check whether the given filename is in the list
 * of firmware filenames. FTP server calls this function to decide where
 * to store the file from the client.  
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  fname  File name to be varified against allowed firmware image name
 *
 * @return  TRUE   If the provided file name is a firmware image
 * @return  FALSE  If the file name is not a firmware image
 *
 * @note Don't change the name or prototype of this API. It is used by FTP 
 *       server to identify the firmware image. User can change the firmware
 *       filename list by updating the macros defined in bsp/fsintf/fwdl.c 
 *       and rebuilding the bsp.  
 *
 * @see @link naFtpFSInit
 * @see @link naFtpDlInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWStoreImage
 * @see @link naFWDataClose
 * @see @link naFWControlClose
 *
*ad*/
unsigned char naFWIsFirmwareFile(char * fname)
{
    /* Is it valid pointer? */
    if ((fname == NULL) || (*fname == '\0'))
        return FALSE;

    if ((strcmp (fname, ROM_BOOT_FILE) == 0)     || 
        /*(strcmp (fname, SPI_BOOT_FILE) == 0) || */
        (strcmp (fname, ROMZIP_BOOT_FILE) == 0)  || 
        (strcmp (fname, ROM_UBOOT_FILE) == 0) || 
        (strcmp (fname, u_boot_file) == 0) ||
        (strcmp (fname, os_boot_file) == 0) ||
#if (NA_DEV_BOARD_TYPE != connectcore9p9360_a)
        (strcmp (fname, BACKUP_IMAGE_FILE) == 0) ||
#endif
        (strcmp (fname, APP_IMAGE_FILE) == 0))
        return TRUE;
    else
    {
        if (!(fwDlState & NA_FWDL_INVALID_FILENAME))
        {
            fwDlState |= NA_FWDL_INVALID_FILENAME;
            if (strcmp (fname, BACKUP_IMAGE_FILE) == 0)
            {
                printf("\nThe firmware file name is not supported in the current platform.\n");
                printf("The FTP transfer will complete, but the data will be discarded.\n\n");
            }
        }
        return FALSE;
    }
}

/*
 * Check if it is application image
 */
static unsigned char isApplicationImage(char *fname)
{
    if ((strcmp (fname, os_boot_file) == 0) ||
        (strcmp (fname, BACKUP_IMAGE_FILE) == 0) ||
        (strcmp (fname, APP_IMAGE_FILE) == 0))
    {
        if (strcmp (fname, BACKUP_IMAGE_FILE) == 0)        
            fwInfo.allocated_size = NABackupRecoveryMaxSizeInFlash;
        else
            fwInfo.allocated_size = NAAppMaxSizeInFlash;
        return TRUE;
    }
    else
        return FALSE;    
}

/*
 * Function releases all allocated memory which are used to store the 
 * downloaded application firmware.
 */
static int cleanupFwData(void)
{
    int result = NAFTPS_SUCCESS;
    NaFWDl_t *fwp = &fwInfo;
    NaFWDlNode_t *cur, *nxt;

    /* cleanup all memory */
    if (fwp->node != NULL)
    {   
        /* wait until flash update completes... */
        while (fwDlState & NA_FWDL_UPDATE_FLASH)
            tx_thread_sleep(1);
             
        cur = fwp->node;        
        while (cur != NULL)
        {
            nxt = cur->next;
            if (cur->data)            
                free(cur->data);
            free(cur);
            cur = nxt;            
        }
        
        fwp->node = NULL;
        fwp->last_node = NULL;
    }
    
    return result;
}

/*
 * This function writes the firmware to NOR flash sector by sector. The last 
 * portion of the memory will be written when data connection is closed.
 */
static int updateNORFlash(char * bufferp, unsigned int buflen, char * argp)
{
    unsigned int templen, k;
    int result;

    /*  our protocol is to verify they are trying to download rom.bin   */
    if ( isApplicationImage(argp) && (currentSector == 0))
    {
        if (strcmp (argp, BACKUP_IMAGE_FILE)) /* if not backup file */
            writeIndex = NAAppOffsetInFlash;
        else
        {
            if (NABackupRecoveryMaxSizeInFlash > 0)
            {
                writeIndex = NABackupRecoveryOffsetInFlash;
            }
            else
            {
                printf ("Attempting to store file backup.bin, but linker customization file did not allocate space for the backup recovery image\n");
                fwDlState |= NA_FWDL_FAILED;
                return NAFTPS_FILESYSTEMERR;
            }
        }
        for (k = currentSector; k < sectors; k++)
        {
            if (sectorSizes[k] <= writeIndex)
            {
                writeIndex -= sectorSizes[k];
                if (k == (sectors - 1))
                {
                    /* No more flash memory to write, so set flag */
                    flashFull = 1;
                }
            }
            else
            {
                currentSector = k;
                break;
            }
        }
        if (writeIndex != 0)
        {
            printf ("_NAAppOffsetInFlash or _NABackupRecoveryOffsetInFlash in the linker customizatiom file is set incorrectly.  \n");
            printf ("The application image must start on a sector boundary.\n");
            fwDlState |= NA_FWDL_FAILED;
            return NAFTPS_FILESYSTEMERR;
        }
    }

    if (flashFull)
    {
        /* If all flash memory has been written, clear flag and return -2 */
        printf("Image size exceeds flash memory size, and flash memory has been overwritten.\n");
        fwDlState |= NA_FWDL_FAILED;
        return NAFTPS_FILESYSTEMERR;
    }

    if ((currentLength + buflen) > fwInfo.romsize)
    {
        /* If data received exceeds available flash memory, clear flag and return -2 */
        printf("Image size exceeds flash memory size, but flash memory has not been overwritten.\n");
        fwDlState |= NA_FWDL_FAILED;
        return NAFTPS_FILESYSTEMERR;
    }

    /*  copy this file into our buffer  */
    if (currentLength + buflen > BUFFERSIZE)
    {
        templen = BUFFERSIZE - currentLength;
    } 
    else
    {
        templen = buflen;
    }

    memcpy ((char *)&sectorDatap[currentLength], bufferp, templen);

    currentLength += templen;

    /* Write the data to Flash in 128K chunks */
    /* Smaller data chunks decrease performance but require a smaller buffer */
    while (currentLength == BUFFERSIZE)
    {
        /* Write to a valid Flash sector */
        if (currentSector < sectors)
        {
            #ifdef DOTNETMF
            printf ("currentSector=%d, writeIndex=%d, BUFFERSIZE=%d, sectorDatap=0x%x\n", currentSector, writeIndex, BUFFERSIZE, sectorDatap);               
            #endif
            result = NAFlashWrite(currentSector, writeIndex, BUFFERSIZE, sectorDatap, ERASE_AS_NEEDED);
            if (result)
            {
                printf ("FSStoreImageToFlash NAFlashWrite() error %d.\n", result);
                fwDlState = NA_FWDL_FAILED;
                return result;
            }

            /*  save the extra data not written to flash -- for the next NAFlashWrite() */
            currentLength = 0;
            if (buflen > templen)
            {
                if ((buflen - templen) >= BUFFERSIZE)
                {
                    memcpy ((char *)&sectorDatap[currentLength], (char *)&bufferp[templen], BUFFERSIZE);
                    templen += BUFFERSIZE;
                    currentLength = BUFFERSIZE;
                }
                else
                {
                    memcpy ((char *)&sectorDatap[currentLength], (char *)&bufferp[templen], buflen - templen);
                    currentLength = buflen - templen;
                }
            }
            writeIndex += BUFFERSIZE;

            /* Compute the sector and sector offset to start writing next */
            for (k = currentSector; k < sectors; k++)
            {
                if (sectorSizes[k] <= writeIndex)
                {
                    writeIndex -= sectorSizes[k];
                    if (k == (sectors - 1))
                    {
                        /* No more flash memory to write, so set flag */
                        flashFull = 1;
                    }
                }
                else
                {
                    currentSector = k;
                    break;
                }
            }
        }
    }

    return NAFTPS_SUCCESS;
}

/*
 * ftp_nand_stor
 *
 * This routine is the nand flash handler used by the FTP server STOR command
 * to write a u-boot or kernel boot image into the boot sector (non file system)
 * partition.
 * This routine collects data in g_flashbuf and then
 * writes it to nand flash memory at the current sector in the boot sector.
 * If the file is u-boot.bin, we write in blocks 0-15 (<= 256 KB). 
 * If the file is image.bin, we write in blocks 16-4095 (<= 2.75 MB)
 *
 * INPUT
 *
 *    bufferp    Contains data read by the FTP server.
 *    buflen     Specifies length of bufferp data.
 *    argp       Specifies file name being read.
 *    handle     Handle to FTP server client connection object. 
 */ 
static int updateNANDFlash (char *bufferp, int buflen, char *argp)
{
    unsigned int dataLeft;
    unsigned char *buf;
    NA_UNUSED_PARAM (argp);

    if (uboot_image || os_image)
    {
        /*
         * global vars keep track of
         * how many bytes we've written to flash, (currentLength)
         * next page to write, (currentSector)
         * how many are in our orphan buffer, (remnantCount)
         * and how big the file can be. (romsize)
         */ 

        if ((currentLength + buflen) > fwInfo.romsize)
        {
            /* If data received exceeds available flash memory, clear
             * flag and return -2 */
            printf("Image size would exceed partition size. Not writing.\n");
            fwDlState |= NA_FWDL_FAILED;
            return NAFTPS_FILESYSTEMERR;
        }

        /* Write the data to nand in pageSize chunks */
        dataLeft = buflen;
        buf = (unsigned char *)bufferp;
        /* take care of any data left from last time */
        if (remnantCount)
        {
            /* make a full page in the remnant buffer */
            if (dataLeft >= pageSize - remnantCount)
            {
                memcpy(&sectorDatap[remnantCount], buf,
                                pageSize - remnantCount);
                yaffs_writeChunkAPI(NAND_DEV_NAME, currentSector,
                                (unsigned char *)sectorDatap);
                dataLeft -= (pageSize - remnantCount);
                buf += (pageSize - remnantCount);
                currentSector++;
                currentLength += pageSize;
                remnantCount = 0;   /* nothing hanging around now */
            }
            else    /* not a full chunk -- save, don't send */
            {
                memcpy(&sectorDatap[remnantCount], buf, dataLeft);
                remnantCount += dataLeft;
                dataLeft -= dataLeft;
            }
        }

        /* then write out the rest of the receive buffer */
        while (dataLeft >= pageSize)
        {
            yaffs_writeChunkAPI(NAND_DEV_NAME, currentSector, buf);
            dataLeft -= pageSize;
            buf += pageSize;
            currentSector++;
            currentLength += pageSize;
        }

        if (dataLeft > 0)
        {
            /* save data not written to flash for the next time around. */
            memset(sectorDatap, 0xff, pageSize);
            memcpy(sectorDatap, buf, dataLeft);
            remnantCount = dataLeft;	/* save in global */
        }
    }   /* os_image or uboot_image */
    
    return NAFTPS_SUCCESS;
}


/*
 * This function updates the final remaining bytes of the image to the NAND
 * flash.
 */
static int updateNANDLast (void)
{
    /* we already checked that the received data will fit in 
     * our partition. */
    /* take care of any data left from last time. We have to write
     * the partial page. */
    if (remnantCount)
    {
        yaffs_writeChunkAPI(NAND_DEV_NAME, currentSector,
                        (unsigned char *) sectorDatap);
        printf ("FTP: final partial page %d. %d bytes\n\r",
                        currentSector, remnantCount);
        currentSector++;
        currentLength += (remnantCount);
    }

    return NAFTPS_SUCCESS;
}

/*
 * In normal scenario this funtion is called after succesful CRC check on 
 * entire image. The other case when this function is called is if there is
 * not enough memory to hold entire image. At that time we skip the CRC check
 * and start writing the received partial image to flash.
 */
static void writeFwData(unsigned long argp)
{
    NaFWDlNode_t *cur = NULL; 
    char fileName[64];
    int ret = NAFTPS_SUCCESS, handle;   

    strcpy(fileName, (char *)argp);
    do
    {
        if (fwDlState & NA_FWDL_FAILED)
            goto fwdl_done;
        
        if (!(fwDlState & NA_FWDL_UPDATE_FLASH))
        {        
            tx_thread_sleep(1);
            continue;
        }

        cur = fwInfo.node;        
        while (cur != NULL)
        {    
            if (NABspIsFlashNand() == TRUE)
                ret = updateNANDFlash(cur->data, cur->len, fileName);
            else
                ret = updateNORFlash(cur->data, cur->len, fileName);
            
            if (ret != NAFTPS_SUCCESS)
            {
                printf("Failed to write flash [%d]\n", ret);
                fwDlState |= NA_FWDL_FAILED;
                goto fwdl_done;
            }
    
            cur = cur->next;
        }

        /* write remaining bytes to flash */
        if (fwDlState & NA_FWDL_DATA_CLOSED)
        {        
            if (NABspIsFlashNand() == TRUE)
            {
                ret = updateNANDLast();
            }
            else if (currentLength)
            {
                /* Write to a valid Flash sector */
                if (currentSector < sectors)
                {
                   #ifdef DOTNETMF
            	   printf ("currentSector=%d, writeIndex=%d, BUFFERSIZE=%d, sectorDatap=0x%x\n", currentSector, writeIndex, currentLength, sectorDatap);                
                   #endif
                   ret = NAFlashWrite(currentSector, writeIndex, currentLength, sectorDatap, ERASE_AS_NEEDED);
                }
            }        
            fwDlState |= NA_FWDL_COMPLETED;
        }

        fwDlState &= ~NA_FWDL_UPDATE_FLASH;        
    } while (!( fwDlState & NA_FWDL_COMPLETED));

    fwdl_done:
    /* Reset variables to allow downloading again */
    cleanupFwData();
    handle = fwInfo.handle;
    memset(&fwInfo, 0, sizeof fwInfo);
    fwInfo.handle = handle;    
    currentSector = 0;
    writeIndex = 0;
    currentLength = 0;
    flashFull = 0;

    if (sectorDatap != NULL)
    {
        free (sectorSizes);
        free (sectorDatap);
        sectorDatap = NULL;
    }                    
    
    if (ret == NAFTPS_SUCCESS)
    {
        if (!(fwDlState & NA_FWDL_FAILED))
        {
            #ifndef DOTNETMF
            printf ("Firmware updated, quit the session to restart.\n" ); 
            #endif
        }
    }
    else
    {
        printf("Failed to write flash [%d]\n", ret);    
        fwDlState |= NA_FWDL_FAILED; 
    }
    
    fwDlState &= ~(NA_FWDL_CRC_CHECK | NA_FWDL_DIRECT);
}

/*
 * This function stores the received image to a dynamic memory. Does CRC
 * calculation as data comes.
 */
static int storeFwData(char *buf, int len)
{
    NaFWDl_t *fwp = &fwInfo;
    NaFWDlNode_t *cur = fwp->last_node;

    if ((buf == NULL) || (len == 0))
        return NAFTPS_SUCCESS;
    
    /* first block */
    if (cur == NULL)
    {
        if ((cur = ( NaFWDlNode_t *) malloc (sizeof *cur)) == NULL)
        {
            return NAFTPS_CONTINUE;
        }

        if ((cur->data = (char *) malloc(FS_BLOCK_SIZE)) == NULL)
        {
            free(cur);
            return NAFTPS_CONTINUE;
        }
        
        cur->len    = 0;
        cur->next   = NULL;
        fwp->node = cur;
        fwp->last_node = cur;
    }

    /* update length and CRC */
    fwp->total_len += len;
    if (!fwp->uboot) 
        fwp->accumulator = blCrc32(fwp->accumulator, (BYTE *)buf, len);
    else
        fwp->accumulator = 0;    

	/* copy the received data to the dynamically allocated block */
    while (len > 0)
    {
        int space_left = FS_BLOCK_SIZE - cur->len;

        if (len < space_left)
        {
            memcpy(&cur->data[cur->len], buf, len);
            cur->len += len;
            break;
        }
        else
        {
            /* first make sure we have enough memory to allocate */
            if ((cur->next = ( NaFWDlNode_t *) malloc (sizeof *cur)) == NULL)
                return NAFTPS_CONTINUE;

            if ((cur->next->data = (char *) malloc(FS_BLOCK_SIZE)) == NULL)
            {
                free(cur->next);
                fwp->last_node->next = NULL;
                return NAFTPS_CONTINUE;
            }
        
            /* then update the data */
            memcpy(&cur->data[cur->len], buf, space_left);
            cur->len += space_left;
            len -= space_left;
            buf += space_left;
       
            cur = cur->next;
            cur->len    = 0;
            cur->next   = NULL;
            fwp->last_node = cur;
        }
    }
    
    /* check image signature and get expected image size */
    if (fwp->exp_len == 0)
    {
        if (fwp->total_len >= sizeof(blImageHeaderType))
        {
            blImageHeaderType *imageInfo = (blImageHeaderType *)fwp->node->data;

            if (memcmp(imageInfo->signature, BL_SIGNATURE, sizeof(BL_SIGNATURE)) == 0)
            {
                fwp->exp_len = imageInfo->headerSize + imageInfo->size + sizeof(WORD32);

                if (fwp->exp_len > fwp->allocated_size)
                {
                    printf("Application image size [%d] is more than allocated size [%d]!!\n", 
                            (int)fwp->exp_len, (int)fwp->allocated_size);
                    return NAFTPS_GENERROR;
                }								  

                if (imageInfo->version >= BL_HEADER_VERSION_74)
                {
                    /*
                     * If this image was created by NET+OS 7.4 or later, then check the
                     * platform code in it and verify that this platform will run
                     * the application.
                     */
                    unsigned i;
                    unsigned isPlatformAcceptable = FALSE;
                    WORD32 thisPlatform = NABspGetThisPlatformsId();
                    /*
                     * At first glance you may wonder why we don't look at the 
                     * actual amount of RAM on this platform.  The reason is that
                     * the bootloader RAM image is linked to load at the top of
                     * the RAM set by RAM_SIZE in the linker script, and this is
                     * the code that is executing these statements and which will
                     * have to unzip the application image.  If the application is
                     * too large, then it will overwrite the bootloader when it is
                     * unzipped.  So, we need to make sure the app image fits into
                     * the RAM size indicated by the linker script, not the real
                     * RAM size, which may be larger.
                     */
                    WORD32 minRamOnThisPlatform = BSP_PLATFORM_RAM_BYTES(thisPlatform);
                    
                    thisPlatform &= ~BSP_PLATFORM_RAM_MASK;
                    
                    for (i = 0; i < imageInfo->platformCount; i++)
                    {
                        WORD32 platformCode = imageInfo->platformCode[i];
                        WORD32 requiredRam = BSP_PLATFORM_RAM_BYTES(platformCode);
                        
                        platformCode &= ~BSP_PLATFORM_RAM_MASK;
                        
                        if ((platformCode == thisPlatform) && (requiredRam <= minRamOnThisPlatform))
                        {
                            isPlatformAcceptable = TRUE;
                            break;
                        }
                    }
                    if (!isPlatformAcceptable)
                    {
                        printf("Application image was not built for this hardware!!\n");
                        return NAFTPS_GENERROR;
                    }
                }
            }
            else
            {
                uboot_header_t *ubootInfo = (uboot_header_t *)fwp->node->data;

                if (ubootInfo->ih_sign == UBOOT_SIGNATURE)
                {
                    fwp->exp_len = ubootInfo->ih_size + sizeof (uboot_header_t);
                    fwp->uboot = 1;
                }    
                else
                {
                    printf("Invalid signature in the header!!\n");
                    return NAFTPS_GENERROR;
                }                
            }
        }
    }
    
    return NAFTPS_SUCCESS;
}

/*
 * This function allocates the resources required to write the image to flash.
 * Function also calculates the sector offset based on the image name. 
 */
static int initializeDownload(char *argp, int handle)
{
    int ret;
    
    if (fwDlState & (NA_FWDL_CRC_CHECK | NA_FWDL_DIRECT))
    {
        if ((fwDlState & NA_FWDL_DATA_CLOSED) && !(fwDlState & NA_FWDL_COMPLETED))
        {
            printf("Firmware update is in progress, wait for it to complete..\n");
            return NAFTPS_GENERROR;
        }
        else        
            return NAFTPS_SUCCESS;    
    }
    
    memset(&fwInfo, 0, sizeof fwInfo);
    fwInfo.handle = handle;

    /* needed for multiple file download */
    if (fwDlTid == NULL)
    {
        fwDlTid = (TX_THREAD *) malloc (sizeof (TX_THREAD));
        if (fwDlTid == NULL)
        {
            printf ("Unable to allocate thread block\n");
            return NAFTPS_GENERROR;
        }
        memset(fwDlTid, 0, sizeof (TX_THREAD));
    }
    else
    {
        tx_thread_delete(fwDlTid);
        tx_thread_sleep(1);
        memset(fwDlTid, 0, sizeof (TX_THREAD));
    }

    /* needed for multiple file download */
    if (fwDlStack == NULL)
    {
        fwDlStack = malloc (FWDL_STACK_SIZE);
        if (fwDlStack == NULL)
        {
            printf ("Unable to allocate thread stack\n");
            return NAFTPS_GENERROR;
        }
    }

    ret = tx_thread_create (fwDlTid,              /* control block for thread */
                            "FW Download Thread",   /* thread name */
                            writeFwData,            /* entry function */
                            (unsigned long)argp,    /* parameter */
                            fwDlStack,              /* start of stack*/
                            FWDL_STACK_SIZE,        /* size of stack*/
                            FWDL_THREAD_PRIORITY,   /* priority*/
                            FWDL_THREAD_PRIORITY,   /* preemption threshold */
                            1,                      /* time slice threshold*/
                            TX_AUTO_START);         /* start immediately*/

    if (ret != TX_SUCCESS)
    {
        printf ("Unable to create flash download thread[%d]\n", ret);
        return NAFTPS_GENERROR;
    }

    if (NABspIsFlashNand() == TRUE)
    {
        int blocksize;
        int pagesize;
        int firstBlock;
        int endBlock;
        int startBlock;
        int chunksperblock;
        unsigned int fsStart, fsSize;

        uboot_image = ((strcmp (argp, u_boot_file) == 0) || 
            (strcmp (argp, ROM_UBOOT_FILE) == 0));
        os_image = ((strcmp (argp, os_boot_file) == 0) ||
            (strcmp (argp, APP_IMAGE_FILE) == 0));

        printf("starting nand flash boot sector download\n\r");
        /*
         * This end block is for the whole "/root" partition.
         * Find the specific sections within the partition used
         * for the U-Boot or NET+OS image below. 
         */
        if (yaffs_ReadNandParms(NAND_DEV_NAME, &pagesize, &blocksize,
                    &chunksperblock, &firstBlock, &endBlock) != 0)
        {
            return NAFTPS_FILESYSTEMERR;
        }

        sectorDatap = malloc (pagesize);    /* for final partial page */
        if (sectorDatap == NULL)
        {
            printf ("FTP Svr could not allocate memory for Flash write.\n");
            return NAFTPS_GENERROR;
        }
        /* blocks means 'eraseable blocks" here, not pages */
        if (uboot_image)
        {
            if (yaffs_FindUBootPartition(&fsStart, &fsSize) != 0)
            {
                free(sectorDatap);
                sectorDatap = NULL;
                return NAFTPS_GENERROR;
            }
        
            blocks = fsSize / (pagesize * chunksperblock);
            startBlock = fsStart / (pagesize * chunksperblock);
        }
        else    /* OS image */
        {
            if (yaffs_FindNetOSPartition(&fsStart, &fsSize) != 0)
            {
                free(sectorDatap);
                sectorDatap = NULL;
                return NAFTPS_GENERROR;
            }

            blocks = fsSize / (pagesize * chunksperblock);
            startBlock = fsStart / (pagesize * chunksperblock);
        }

        fwInfo.romsize = blocks * blocksize;   /* this is how much we can write */
        /* The global var currentSector is running page (chunk) offset */
        currentSector = startBlock * chunksperblock;
        pageSize = pagesize;
        writeIndex = 0;
        currentLength = 0;
        remnantCount = 0;
        yaffs_ErasePartition(NAND_DEV_NAME, startBlock, blocks);
    }
    else
    {
        sectorDatap = malloc (BUFFERSIZE);
        if (sectorDatap == NULL)
        {
            printf ("FTP Server could not allocate memory for Flash write.\n");
            return NAFTPS_GENERROR;
        }

        /*
         * Initialize the FTP server internal data structures. The following
         * allocates control data structures for two concurrent users.
         */
#ifdef BSP_ARM9
        fwInfo.romsize = customizeGetCSSize ((WORD32 *)NA_SCM_CS1_BASE_ADDR, (WORD32 *)NA_SCM_CS1_BASE_ADDR);
#else
        fwInfo.romsize = customizeGetCSSize ((WORD32 *)NARM_CSAR0_ADDR, (WORD32 *)NARM_CSOR0_ADDR);
#endif

        /* determine the flash physical sectors and sector sizes */
        sectors = NAFlashSectors();
        sectorSizes = (unsigned long *)malloc(sizeof(unsigned long) * sectors);
        if (NAFlashSectorSizes(sectorSizes))
        {
            printf ("Unable to get Flash sector size\n");
            printf ("Check if flash is enabled\n");
            free (sectorSizes);
            free (sectorDatap);
            sectorDatap = NULL;
            return NAFTPS_FILESYSTEMERR;
        }
    }

    /* clear all other bits */
    fwDlState = isApplicationImage(argp) ? NA_FWDL_CRC_CHECK : NA_FWDL_DIRECT;
   
    return NAFTPS_SUCCESS;    
}

/*
 * Middle level function which monitors the length of the received data.  
 */
static int ftpStoreInRam(char * buf, int len)
{
    NaFWDl_t *fwp = &fwInfo;
    int result = NAFTPS_SUCCESS;

    result = storeFwData(buf, len);
    if (result == NAFTPS_CONTINUE)
    {
        printf("Not enough memory to store the entire image. Exiting without updating flash!\n");
        fwDlState |= (NA_FWDL_HEAP_FULL | NA_FWDL_FAILED);        
        fwp->accumulator = 0;
        cleanupFwData();
        return NAFTPS_GENERROR;
    }
    else if (result != NAFTPS_SUCCESS)
        goto doneStore;

#ifndef DOTNETMF
    if (fwp->total_len > fwp->exp_len)
    {
        printf("Image size(%d) exceeded the size specified in header(%d)\n",
               (int)fwp->total_len, (int)fwp->exp_len);
        result = NAFTPS_GENERROR;        
        goto doneStore;
    }
#endif
    
    doneStore:
    if (result != NAFTPS_SUCCESS)
    {
        fwDlState |= NA_FWDL_FAILED;
        cleanupFwData();
    }
        
    return result;
}

/*
 * Timer function called to reset the device
 */
static void resetAfterImageDownload (ULONG notUsed)
{
    NA_UNUSED_PARAM(notUsed);
    customizeReset();   /* reset*/
}


/*ad*
 *
 * This function can be used to store the executable/firmware images to flash.
 * Function takes user provided buffer and places it in dynamically allocated 
 * RAM. Function does the CRC calculation as data arrives. If there is not 
 * enough memory to store the buffer then function starts writing the stored 
 * data in flash and skips the CRC check. 
 *
 * Function calls @link NAFlashSectors and @link NAFlashSectorSizes to 
 * determine the number of sectors and size of the sectors for downloading.  
 *
 * This function should be used in conjunction with the naFWControlClose, 
 * which waits for the firmware/flash update to complete and reboots the unit.
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  bufferp  Data buffer
 * @param  buflen   Length of data buffer
 * @param  argp     File name
 * @param  handle   Required to keep track of the session 
 *
 * @return  NAFTPS_SUCCESS   Successfully written to flash/dynamic memory
 * @return  NAFTPS_GENERROR  There could be many reasons for this error. 
 *                        Wrong file name, flash update in progress, invalid
 *                        handle and total length received so far exceeds
 *                        the length specified in the image header
 * @return NAFTPS_FILESYSTEMERR  Failed to access flash
 * 
 * @note Don't change the name or prototype of this API. It is used by the FTP
 *       server to store the firmware image. User can call this function
 *       directly but make sure to call @link naFWControlClose at the end
 *       to update flash and reboot.  
 *
 * @see @link naFtpFSInit
 * @see @link naFtpDlInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWDataClose
 * @see @link naFWControlClose
 *
*ad*/
int naFWStoreImage(char * bufferp, int buflen, char * argp, int handle)
{
    int result; 
    
    if (naFWIsFirmwareFile(argp) == FALSE)
        return NAFTPS_GENERROR;

    if ((result = initializeDownload(argp, handle)) != NAFTPS_SUCCESS)
    {
        fwDlState |= NA_FWDL_FAILED;    
        return result;
    }

    if (handle != fwInfo.handle)
        return NAFTPS_GENERROR;
    
    /* process already in error */
    if (fwDlState & NA_FWDL_FAILED)
        return NAFTPS_GENERROR;

    if (fwDlState & NA_FWDL_CRC_CHECK)
    {
        result = ftpStoreInRam(bufferp, buflen);
        if (result != NAFTPS_CONTINUE) 
            return result;
    }

    if (NABspIsFlashNand() == TRUE)
        result = updateNANDFlash(bufferp, buflen, argp);
    else
        result = updateNORFlash(bufferp, buflen, argp);
    
    return result;
}

/*ad*
 * This function is called after complete firmware image is transferred.
 * If you are downloading through FTP then this function is called during
 * data connection close. This function checks the calculated CRC, the total
 * length of received data with the length specified in the image header and
 * on success it invokes the flash write.
 *
 * This function is also called by @link naFWControlClose just to make sure 
 * that it gets called at least once to kick off the flash write.
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  handle   Required to keep track of the session 
 *
 * @return  NAFTPS_SUCCESS   Received data is valid
 * @return  NAFTPS_GENERROR  Invalid data length or CRC 
 * 
 * @note Don't change the name or prototype of this API. It is called by the FTP
 *       server during data connection close. User can call this function
 *       directly after transferring the firmware data.  
 *
 * @see @link naFtpFSInit
 * @see @link naFtpDlInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWStoreImage
 * @see @link naFWControlClose
 *
*ad*/
int naFWDataClose(int handle)
{
    NaFWDl_t *fwp = &fwInfo;
    int result = NAFTPS_SUCCESS;

    /* not belongs to firmware download */
    if ((handle != fwInfo.handle) || (fwDlState & NA_FWDL_DATA_CLOSED))
    {
        if (fwDlState & NA_FWDL_INVALID_FILENAME)
        {
            /* we reach this point if the downloaded file name is not supported */
            /* so clear the invalid filename bit so the code will print the     */
            /* warning message again for the next unsupported file name         */
            fwDlState &= ~NA_FWDL_INVALID_FILENAME;
        }
        return result;
    }

    if (!(fwDlState & NA_FWDL_FAILED))
    {
        if (fwp->total_len < fwp->exp_len)
        {
#ifndef DOTNETMF
            printf("Image size(%d) is less than the size specified in header(%d)\n",
               (int)fwp->total_len, (int)fwp->exp_len);
            result = NAFTPS_GENERROR;
#endif        
        }
        else if (fwp->total_len > fwp->exp_len)
        {
#ifndef DOTNETMF
            printf("Image size(%d) is more than the size specified in header(%d)\n",
               (int)fwp->total_len, (int)fwp->exp_len);
            result = NAFTPS_GENERROR;
#endif        
        }
        else if (fwp->accumulator)
        {
            printf("Checksum failed\n");
            result = NAFTPS_GENERROR;
        }
        else
        {
            if (fwp->total_len > 0)
            {
                #ifndef DOTNETMF
                printf("Checksum passed, writing to flash...\n");
                #endif
            }
            else
            {
                #ifndef DOTNETMF
                printf("Download complete, writing to flash...\n");
                #endif
            }
        }
    }

    fwDlState |= (result != NAFTPS_SUCCESS) ? NA_FWDL_FAILED : NA_FWDL_UPDATE_FLASH;
    fwDlState |= NA_FWDL_DATA_CLOSED;
    
    return result;
}

/*ad*
 * This function is called after complete firmware image is transferred.
 * If you are downloading through FTP then this function is called when
 * client closes the connection. This function waits until flash download is
 * complete and after successful firmware update it activates the timer to 
 * reboots the unit after 2 seconds. User can update multiple flash images 
 * at one session. Unit reboots only if the last firmware update was 
 * successful.
 *
 * This function will also call @link naFWDataClose just to make sure that 
 * function is called at least once before closing the connection.
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  handle   Required to keep track of the session 
 *
 * @return  NAFTPS_SUCCESS   Always returns success
 * 
 * @note Don't change the name or prototype of this API. It is called by the FTP
 *       server during control connection close. User can call this function
 *       directly after transferring the firmware data.  
 *
 * @see @link naFtpFSInit
 * @see @link naFtpDlInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWStoreImage
 * @see @link naFWDataClose
 *
*ad*/
int naFWControlClose(int handle)
{
    static TX_TIMER resetTimer;
    int result = NAFTPS_SUCCESS;

    /* not belongs to firmware download */
    if (handle != fwInfo.handle)
        return result;
    
    /* close the data connection related cleanups if not done */
    naFWDataClose(handle);

    /* wait until flash update completes... */
    while (!(fwDlState & NA_FWDL_COMPLETED))
    {
        if (fwDlState & NA_FWDL_FAILED)
            break;
        tx_thread_sleep(1);
    }

    if (fwDlState & NA_FWDL_COMPLETED)
    {
        /*  We did an image download -- reset the unit  */
        if (tx_timer_create (&resetTimer, "Reset Timer", resetAfterImageDownload,
			    NOT_USED, (FTP_RESET_DELAY * NABspTicksPerSecond), NOT_USED,
		   	    TX_AUTO_ACTIVATE) == TX_SUCCESS)
        {
            printf("Resetting the system in %d seconds...\n", FTP_RESET_DELAY );
        }
		else
		{
			printf("Failed to start the timer, so user has to reset manually!\n");
		}
    }
    else
        fwDlState = NA_FWDL_INIT;

    return NAFTPS_SUCCESS;
}


/*ad*
 *
 * User can call this function to initiate the FTP server which allows FTP
 * clients to download firmware images. When user downloads application image
 * like image.bin or backup.bin then the FTP server stores the entire image
 * in dynamically allocated memory, checks the CRC and signature and finally
 * writes the image to the flash.
 *
 * Unit reboots 2 seconds after client closes its connection or firmware 
 * update completes whichever comes last. It is possible to download multiple 
 * files in one FTP session. That means user can download both rom.bin 
 * (bootloader file) and image.bin (application file) in one session.
 *
 * If there is not enough memory to store the entire application image, then 
 * the integrity check (CRC) will be dropped and the received data will be 
 * written to flash to clear the dynamically allocated memory. From that point 
 * onwards data will be written to flash sector by sector as we received. 
 *
 * The integrity check mechanism is added only to the application image not
 * for the bootloader image. 
 *
 * @external
 * @include "fservapi.h"
 * @library bsp
 * @platform all
 * @category Networking:FTP_Server
 * @since 7.4
 *
 * @param  nusers  number of clients that can login at a time
 *
 * @return  NAFTPS_SUCCESS  FTP server is initialized and started and is
 *                          ready to receive firmware file.
 * @return  NAFTPS_GENERROR  Either failed to initialize or failed to start
 *                           the server. 
 *
 * @see @link NAFTPS
 * @see @link NAFlashWrite
 * @see @link NAFlashSectors
 * @see @link NAFlashSectorSizes
 * @see @link naFtpFSInit
 *
 * @note Use @link naFtpFSInit if you want the filesystem functionality along
 *       with FTP download capability. User can see the update status on the 
 *       std IO terminal. If the terminal is not connected, then user should 
 *       allow enough time (normally one minute per mega bytes of data) for 
 *       the firmware update to complete. 
 *
 * @see @link naFtpFSInit
 * @see @link naFWIsFirmwareFile
 * @see @link naFWStoreImage
 * @see @link naFWDataClose
 * @see @link naFWControlClose
 *
*ad*/
int naFtpDlInit(int nusers)
{
    int result;
    
    /* initialize ftp server with number of users */
    if ((result = FSInitialize(nusers)) != NAFTPS_SUCCESS)
    {
        printf("FSInitialize failed\n");
        return result;
    }

    /* start ftp server */
    if ((result = FSStartServer()) != NAFTPS_SUCCESS) 
        printf("FSStartServer failed\n");
    
    printf ("FTP download server is ready.\n");        

    NALedBlinkGreen (2, 1);
    return result;    
}
