/*
 * ymodem.c
 *
 *  Created on: Feb 5, 2014
 *      Author: rob
 */

/* ymodem for RTD Serial Recovery (rtdsr)
 *
 * copyright (c) 2011 Pete B. <xtreamerdev@gmail.com>
 *
 * based on ymodem.c for bootldr, copyright (c) 2001 John G Dorsey
 * baded on ymodem.c for reimage, copyright (c) 2009 Rich M Legrand
 * crc16 function from PIC CRC16, by Ashley Roll & Scott Dattalo
 * crc32 function from crc32.c by Craig Bruce
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
 */

/*
 * These Ymodem calls are aimed at embedded software and tailored to
 * work against Microsoft's HyperTerminal. Some of the Ymodem protocol
*  operations have been voluntarily left out.
 *
 * To be able to use these functions, you must provide:
 * o int _getchar(int timeout): A serial getchar() call, with a
 *   timeout expressed in seconds. Negative means infinite timeout.
 *   should return the read character, as an int, or negative on
 *   error/timeout.
 * o void _putchar(int c): A serial putchar() call
 * o printf(), thought all the printfs can be removed if needed.
 *
 * **********************
 *2/2014 Robert Glatts
 * rglatts@ucsd.edu
 *
 * Modified source code to work with SeapHOx Tivaware and FatFs function calls.
 */

#include "config.h"
#include "system.h"
#include "ymodem.h"
#include "user_io.h"
#include "microsd.h"
#include "uartstdio.h"	// User local version with larger RX buffer

// Declarations
enum ymodem_error {UNDEFINED_YMODEM_ERROR, FILE_OPEN_ERROR, FILE_WRITE_ERROR, FILE_SIZE_ERROR, PACKET_TIMEOUT_ERROR, PACKET_CRC_ERRORS, BAD_SEQUENCE_NUMBER, CANCELED_BY_SENDER, EXPECTED_EOT_NOT_FOUND};

// Global variables
extern FIL FileObject;
static unsigned char packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD];

// Function prototypes
unsigned short crc16(unsigned char *buf, unsigned long count);
const char *u32_to_str(unsigned int val);
unsigned long str_to_u32(char* str);
int receive_packet(int packet_size);
void send_packet(unsigned char *data, int block_no, int packet_size);
void send_packet0(const char* filename, unsigned long size);
int send_data_packets(unsigned long file_size, int packet_size);

/* ASCII control codes: */
#define SOH (0x01)      /* start of 128-byte data packet */
#define STX (0x02)      /* start of 1024-byte data packet */
#define EOT (0x04)      /* end of transmission */
#define ACK (0x06)      /* receive OK */
#define NAK (0x15)      /* receiver error; retry */
#define CAN (0x18)      /* two of these in succession aborts transfer */
#define CRC (0x43)      /* use in place of first NAK for CRC mode */
#define CTRL_C (0x03)   /* added to terminate ymodem 9 Oct 2023 */

char dbgLogEnabled = 0;

#define DEBUG_YMODEM_SEND
#define DEBUG_YMODEM_RECEIVE

#ifdef DEBUG_YMODEM_RECEIVE

// For debugging YMODEM receive issues the file pointer (_fRO) is inside the scope of the receiver main loop
// _xxr error result is inside the main receive loop as well

#define DBGR_LOG_START(fn) FIL _fRO; if (dbgLogEnabled) {\
    FRESULT _xxr = f_open(&_fRO,fn,FA_READ|FA_WRITE|FA_OPEN_ALWAYS);\
    if(_xxr != FR_OK) {\
    uprintf("f_open error: %s\r\n", StringFromFresult(_xxr)); return -2; } }

#define DBGR_PRINTF(...)   if (dbgLogEnabled) { sd_fprintf(&_fRO,  __VA_ARGS__); }
#define DBGR_LOG_FINISH()  if (dbgLogEnabled) { FRESULT _xxr = f_close(&_fRO); \
    if (_xxr != FR_OK)\
    uprintf("f_close error on logfile: %s\r\n",StringFromFresult(_xxr)); }
#else
#define DBGR_LOG_START(...)
#define DBGR_PRINTF(...)
#define DBGR_LOG_FINISH()
#endif

// For debugging YMODEM send issues the file pointer must be outside the scope of the send functions
// xxs error result is inside the main ymodem_send function
#ifdef DEBUG_YMODEM_SEND
FIL     _fSO;

#define DBGS_LOG_START(fn)  if (dbgLogEnabled) { FRESULT _xxs = f_open(&_fSO,fn,FA_READ|FA_WRITE|FA_OPEN_ALWAYS); \
        if(_xxs != FR_OK) {\
        uprintf("f_open error: %s\r\n", StringFromFresult(_xxs)); return -2; } }

#define DBGS_PRINTF(...)  if (dbgLogEnabled) { sd_fprintf(&_fSO,  __VA_ARGS__); }

#define DBGS_LOG_FINISH()   if (dbgLogEnabled) { FRESULT _xxs = f_close(&_fSO); \
        if(_xxs != FR_OK)\
        uprintf("f_close error on logfile: %s\r\n",StringFromFresult(_xxs)); }
#else

#define DBGS_LOG_START(...)
#define DBGS_PRINTF(...)
#define DBGS_LOG_FINISH()

#endif

/* http://www.ccsinfo.com/forum/viewtopic.php?t=24977 */
unsigned short crc16(unsigned char *buf, unsigned long count)
{
        unsigned short crc = 0;
        int i;

        while(count--) {
                crc = crc ^ *buf++ << 8;

                for (i=0; i<8; i++) {
                        if (crc & 0x8000) {
                                crc = crc << 1 ^ 0x1021;
                        } else {
                                crc = crc << 1;
                        }
                }
        }
        return crc;
}

const char *u32_to_str(unsigned int val)
{
        /* Maximum number of decimal digits in u32 is 10 */
        static char num_str[11];
        int  pos = 10;
        num_str[10] = 0;

        if (val == 0) {
                /* If already zero then just return zero */
                return "0";
        }

        while ((val != 0) && (pos > 0)) {
                num_str[--pos] = (val % 10) + '0';
                val /= 10;
        }

        return &num_str[pos];
}

unsigned long str_to_u32(char* str)
{
        const char *s = str;
        unsigned long acc;
        int c;

        /* strip leading spaces if any */
        do {
                c = *s++;
        } while (c == ' ');

        for (acc = 0; (c >= '0') && (c <= '9'); c = *s++) {
                c -= '0';
                acc *= 10;
                acc += c;
        }
        return acc;
}

int ymodem_receive(int argc, char *argv[])
{
	unsigned char *file_ptr, *ext_ptr;
	int c, i, cancan;
	unsigned int packet_size, packets_received;
	char file_name[FILE_NAME_LENGTH], file_size[FILE_SIZE_LENGTH];
	unsigned long size = 0;
	enum ymodem_error error = UNDEFINED_YMODEM_ERROR;
	FRESULT fresult;
	UINT bytes_written;

	// remember if we need to close file at end
	bool file_opened = false;

    dbgLogEnabled = 0;
    int argIdx = 1;
    while (argIdx < argc)
    {
        if (strcmp(argv[argIdx], "-d") == 0)
            dbgLogEnabled = 1;
        argIdx++;
    }

	file_name[0] = 0;
	i = 0;
	cancan = 0;
	packets_received = 0;

	UARTEchoSet(false);		// Disable console echo

    DBGR_LOG_START ("ymodem.log")

	while(1)
	{
		UARTCharPut(UART0_BASE, CRC);       //'C'
		MAP_SysCtlDelay(MILLISECOND*1000);
		if( UARTRxBytesAvail() ) break;
		if( i > 60 ) goto abort;		// Give sender one minute to start
	}

	// Receive packets
	while(1)
	{
        // Get the first character
		while(1)
		{
			c = getchar_timed(PACKET_TIMEOUT);
			DBGR_PRINTF("rx: %02X\n\r", c);

			if (c < 0)
			{
			    DBGR_PRINTF("Timeout Error\n\r");
				error = PACKET_TIMEOUT_ERROR;
				goto abort;	// Timeout
			}

			// Look at the first character and decide what to do
			switch(c)
			{
			    // case added for abort 9 Oct 2023
                case 'Q':
                case 'q':
                case 'S':
                case 's':
                case 0x03:      //CTRL-C
                    error = PACKET_TIMEOUT_ERROR;
                    goto abort; // Timeout
#ifdef WARNING_ABATEMENT_UNREACHABLE
                    break;
#endif

				case SOH:
				    DBGR_PRINTF("Packet size 128\n\r");
					packet_size = PACKET_SIZE;
					break;

				case STX:
				    DBGR_PRINTF("PacketSize 1K\n\r");
					packet_size = PACKET_1K_SIZE;
					break;

				case EOT:
					UARTCharPut(UART0_BASE, NAK);	// NAK first EOT character
					c = getchar_timed(PACKET_TIMEOUT);
					if (c == EOT)
					{
						UARTCharPut(UART0_BASE, ACK);	// ACK second EOT character
						f_close(&FileObject);			// Got a good file, close it

						UARTCharPut(UART0_BASE, CRC);	// Send a 'C' to request second file if there is one
						packets_received = 0;			// Reset packet counter since next packet should be filename type
						continue;
					}
					else
					{
						UARTCharPut(UART0_BASE, NAK);	// Unexpected character, NAK it
						error = EXPECTED_EOT_NOT_FOUND;
						continue;
					}

				case CAN:
					c = getchar_timed(PACKET_TIMEOUT);
					if (c == CAN)
					    cancan++;
					if (cancan >= 2)
					{
                        error = CANCELED_BY_SENDER;
					    goto abort;	        // Got two CAN's in a row
					}
					else
					{
						UARTCharPut(UART0_BASE, NAK);	// Unexpected character, NAK it
						continue;
					}

			}	// End of switch

			if(c == SOH || c == STX) break; // End first character loop if a packet is coming
		}

        packet_data[0] = (unsigned char)c;  // Store the first character

        // Get the rest of the packet
    	for(i = 1; i < (packet_size + PACKET_OVERHEAD); i++)
    	{
    		c = getchar_timed(PACKET_TIMEOUT);
            #ifdef DEBUG_YMODEM
    		if (c > 31 && c < 128)
    		    DBGR_PRINTF("%c", c);
    		else
    		    DBGR_PRINTF(".%02X.", c);
            #endif
    		if (c < 0) {
    		    DBGR_PRINTF("\n\rTimeout!\n\r");
				error = PACKET_TIMEOUT_ERROR;
   				goto abort;
    		}

    		else packet_data[i] = (unsigned char)c;
    	}
        #ifdef DEBUG_YMODEM
    	DBGR_PRINTF("\n\rGot Packet\n\r");
        #endif

    	// Check the packet CRC
    	if (crc16(packet_data + PACKET_HEADER, packet_size + PACKET_TRAILER) != 0)
    	{
			UARTCharPut(UART0_BASE, NAK);	// Bad CRC, ask for a resend
			error = PACKET_CRC_ERRORS;
    	}

		// Check if sequence number matches packets received so far
		else if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
		{
			UARTCharPut(UART0_BASE, NAK);	// Bad sequence number, ask for a resend
			error = BAD_SEQUENCE_NUMBER;
		}
    	// Good packet and sequence number
    	else
		{
			packets_received++;		// Got a good packet

			// Is this the first packet?
			if (packets_received == 1)
			{
				/* The spec suggests that the whole data section should
				 * be zeroed, but I don't think all senders do this. If
				 * we have a NULL filename and the first few digits of
				 * the file length are zero, we'll call it empty.
				 */

				// Count the number of zeros in packet
				for (i = PACKET_HEADER; i < PACKET_HEADER + 4; i++)
				{
					if (packet_data[i] != 0)
					{
						break;
					}
				}

				// Filename packet has data
				if (i < PACKET_HEADER + 4)
				{
					// Get the file name
					for (file_ptr = packet_data + PACKET_HEADER, i = 0; *file_ptr && i < FILE_NAME_LENGTH; )
					{
						file_name[i++] = *file_ptr++;
					}
					file_name[i++] = '\0';

					// Get the file size string
					for (++file_ptr, i = 0; *file_ptr != ' ' && i < FILE_SIZE_LENGTH; )
					{
						file_size[i++] = *file_ptr++;
					}
					file_size[i++] = '\0';

                    // Convert file size string to unsigned long
                    size = str_to_u32(file_size);

					//thomthom
					if(calFileFlg == 1)
					{
					    // if the file extension is *.cal
					    ext_ptr = (unsigned char*)strrchr(file_name, '.');      // ext_ptr is pointing at the extension
					    if(ext_ptr != NULL)
					        c = strncmp((char*)ext_ptr, ".cal", 4);

					    //uprintf("Cal File Detected\r\n");

					    // if the file is greater than 500 (actual is 384 bytes) or the extension is not *.cal
					    if(size > 500 || c != 0)
					    {
					        calFileFlg = 0; // too big, not a valid cal file
					        //uprintf("Error: calibration file size is greater than 500 bytes, aborting\r\n");
					        goto abort;
					    }
					}

					// File size too big?  Too big is greater than 1GB
					if (size > MAX_RX_FILE_SIZE * 1024)
					{
						error = FILE_SIZE_ERROR;
						goto abort;
					}

					// Good file size, open the file
					else
					{
					    if(calFileFlg == 0)
					    {
					        DBGR_PRINTF("Got filename: %s\r\n", file_name);

                            // Open the file for writing. Overwrite existing file.
                            fresult = f_open(&FileObject, file_name, FA_WRITE | FA_READ | FA_CREATE_ALWAYS);
                            if(fresult != FR_OK)
                            {
                                file_opened = false;
                                uprintf("f_open error: %s", StringFromFresult(fresult) ); prn_nl();
                                error = FILE_OPEN_ERROR;
                                goto abort;
                            }
                            file_opened = true;
					    }

					}

					UARTCharPut(UART0_BASE, ACK);
					UARTCharPut(UART0_BASE, CRC);
				}

				// Filename packet is empty; end session
				else
				{
						UARTCharPut(UART0_BASE, ACK);	// Acknowledge the end of session

						// Close and open file to avoid FR_INVALID_OBJECT error from f_lseek()
						f_close(&FileObject);
						file_opened = false;

						fresult = f_open(&FileObject, file_name, FA_WRITE | FA_READ | FA_CREATE_ALWAYS);
						if(fresult != FR_OK)
						{
							printf("f_open error: %s\n", StringFromFresult(fresult) );
							error = FILE_OPEN_ERROR;
							goto abort;
						}
						file_opened = true;

						// Truncate file to correct size indicated by sender
						fresult = f_lseek(&FileObject, size);	// Move the file pointer to correct end
						if(fresult != FR_OK)
						{
							printf("f_lseek error: %s\n", StringFromFresult(fresult) );
						}

						fresult = f_truncate(&FileObject);	// Get rid of the padding
						if(fresult != FR_OK)
						{
							printf("f_truncate error: %s\n", StringFromFresult(fresult) );
						}

						goto success;	// Close file and return
				}

	            DBGR_PRINTF("First Pkt Rxd fn=%s, len=%d\n\r", file_name, size);
			}	// End first packet

			// It's a data packet, so store it
			else
			{
			    if(calFileFlg == 1)
			    {
			        parse_cal_packet((char*)&packet_data[PACKET_HEADER]);  // defd in config.c
			        set_cal_filename(file_name);        // defd in config.c
			        goto abort;
			    }
			    else
			    {
	                f_write (&FileObject, &packet_data[PACKET_HEADER], packet_size, &bytes_written);
	                if(fresult != FR_OK)
	                {
	                    uprintf("f_write error: %s", StringFromFresult(fresult) ); prn_nl();
	                    error = FILE_WRITE_ERROR;
	                    goto abort;
	                }
			    }

				UARTCharPut(UART0_BASE, ACK);	// Tell the sender to send next packet

                DBGR_PRINTF("Pkt %d received\n\r", packets_received);
			}

		}  // End good sequence number

	}  /* End of receive packets loop */


abort:
	UARTCharPut(UART0_BASE, CAN);
	UARTCharPut(UART0_BASE, CAN);
	MAP_SysCtlDelay(MILLISECOND*1000);
	if(calFileFlg == 0)
	{
	    uprintf("\r\nYmodem aborted: ");
	    DBGR_PRINTF("Ymodem aborted: ");
	}
	switch(error)
	{
		case FILE_OPEN_ERROR:
			uprintf("File open error\r\n");
			DBGR_PRINTF("File open error\r\n");
			break;

		case FILE_WRITE_ERROR:
			uprintf("File write error");
			DBGR_PRINTF("File write error");
			break;

		case FILE_SIZE_ERROR:
			uprintf("File size larger than: %d kB\r\n", MAX_RX_FILE_SIZE);
			DBGR_PRINTF("File size larger than: %d kB\r\n", MAX_RX_FILE_SIZE);
			break;

		case PACKET_TIMEOUT_ERROR:
		    uprintf("Packet timeout error");
		    DBGR_PRINTF("Packet timeout error");
		    break;

		case PACKET_CRC_ERRORS :
            uprintf("CRC errors");
            DBGR_PRINTF("CRC errors");
            break;

		case BAD_SEQUENCE_NUMBER :
            uprintf("BAD Sequence number");
            DBGR_PRINTF("BAD Sequence number");
            break;

		case CANCELED_BY_SENDER :
            uprintf("Cancelled by Sender");
            DBGR_PRINTF("Cancelled by Sender");
            break;

		case EXPECTED_EOT_NOT_FOUND :
            uprintf("Expected EOT not found");
            DBGR_PRINTF("Expected EOT not found");
            break;

		default :
		    uprintf("Unk error %d\r\n", error);
		    DBGR_PRINTF("Unk error %d\r\n", error);
		    break;
	}

    if (!((error == FILE_OPEN_ERROR) || (error == FILE_SIZE_ERROR)))
    {
        uprintf("... after %d packets\r\n", packets_received);
        DBGR_PRINTF("... after %d packets\r\n", packets_received);
    }

success:
    if (dbgLogEnabled)
    {
        uprintf("Debug log file \"ymodem.log\" was created.");
        prn_nl();
    }

    DBGR_LOG_FINISH()
#if 0
    // Close the log file
    fresult = f_close( &fileObject);
    if(fresult != FR_OK)
    {
        uprintf("f_close error on logfile: %s\r\n", StringFromFresult(fresult));
    }
#endif

    if(file_opened)
    {
        f_close(&FileObject);
    }

    uprintf("File received %s", file_name);
    prn_nl();

	UARTEchoSet(true);		// Enable console echo
	calFileFlg = 0;         // NEW 15 Sep 2023 filefree parsing of cal file
	return 0;
}

void send_packet(unsigned char *data, int block_no, int packet_size)
{
        int count, crc;

        DBGS_PRINTF("send packet %d packet_size:%d\n\r", block_no, packet_size);

        crc = crc16(data, packet_size);
        /* 128 byte packets use SOH, 1K use STX */
        if (packet_size == PACKET_1K_SIZE)
            UARTCharPut(UART0_BASE, (block_no==0)?SOH:STX);
        else
            UARTCharPut(UART0_BASE, SOH);

        UARTCharPut(UART0_BASE, block_no & 0xFF);
        UARTCharPut(UART0_BASE, ~block_no & 0xFF);

        for (count=0; count<packet_size; count++) {
        	UARTCharPut(UART0_BASE, data[count]);
        }
        UARTCharPut(UART0_BASE, (crc >> 8) & 0xFF);
        UARTCharPut(UART0_BASE, crc & 0xFF);
}

/* Send block 0 (the filename block). filename might be truncated to fit. */
void send_packet0(const char* filename, unsigned long size)
{
        unsigned long count = 0;
        unsigned char block[PACKET_SIZE];
        const char* num;

        if (filename) {
                while (*filename && (count < PACKET_SIZE-FILE_SIZE_LENGTH-2)) {
                        block[count++] = *filename++;
                }
                block[count++] = 0;

                num = u32_to_str(size);
                while(*num) {
                        block[count++] = *num++;
                }
        }

        while (count < PACKET_SIZE) {
                block[count++] = 0;
        }

        send_packet(block, 0, PACKET_SIZE);
}


int send_data_packets(unsigned long file_size, int packet_size)
{
        int blockno = 1;
        int ch, tries;
        unsigned int send_size;
        UINT br;		// Bytes read
        FRESULT fresult;

        // Send the file packets
        do
        {
        	fresult = f_read(&FileObject, packet_data, packet_size, &br);	// Read a packet from file
        	if(fresult != FR_OK)
        	{
                DBGS_PRINTF("f_read error: %s\r\n", StringFromFresult(fresult))
                uprintf("f_read error: %s\r\n", StringFromFresult(fresult) );
        		return -1;
        	}

        	send_size = br;

			// Send packet until ACK received.
        	tries = 0;

        	do
			{
				send_packet(packet_data, blockno, packet_size);		// Send the packet
				DBGS_PRINTF("sent block %d, %d bytes\n\r", blockno, send_size);
				ch = getchar_timed(PACKET_TIMEOUT);
				DBGS_PRINTF("response = %02X\n\r", ch);

				if (ch == ACK)				// Receiver got the packet okay, send next packet
				{
					blockno++;
			    	break;
				}
				else
				{
					if((ch == CAN) || (tries++ > 10))	// Receiver sent CANCEL command or didn't respond
					{
						return -1;
					}
				}
			} while(1);

        } while(send_size == packet_size);	// Last packet sent was less than full sized, so end

        // Send two EOTs to signal end of data packets. Receiver should NAK first EOT and ACK C second EOT
        do
        {
        	UARTCharPut(UART0_BASE, EOT);			// Send End of Transmission command
            ch = getchar_timed(PACKET_TIMEOUT);
        } while((ch != ACK) && (ch != -1));		// Keep sending until receiver ACKs or doesn't respond

        /* Send last data packet */
        if (ch == ACK) {
                ch = getchar_timed(PACKET_TIMEOUT);
                if (ch == CRC) {
                        do {
                                send_packet0(0, 0);
                                ch = getchar_timed(PACKET_TIMEOUT);
                        } while((ch != ACK) && (ch != -1));
                }
        }

        return 0;
}

// Send file. Returns -1 on error
int ymodem_send(int argc, char *argv[])
{
        if(argc < 2)
        {
            return -1;
        }

        int packetSize = PACKET_1K_SIZE;        // JLD - set default to 1K byte packets, use 128 for Y-modem std protocol
        int ch, tries=0, error=0;
        unsigned long file_size;
        FRESULT fresult;
        char const* filename = argv[1];
        int argIdx = 2;
        int packetSizeSet = 0;
        dbgLogEnabled = 0;
        errno = 0;
        while (argIdx < argc)
        {
            if (strcmp(argv[argIdx], "-d") == 0)
                dbgLogEnabled = 1;
            else if (!packetSizeSet)
            {
                packetSize = strtol(argv[argIdx],0,10);
                if ((packetSize != PACKET_SIZE) && (packetSize != PACKET_1K_SIZE))
                {
                    uprintf("Packet size not supported: %d, using %d", packetSize, PACKET_1K_SIZE);
                    prn_nl();
                    packetSize = PACKET_1K_SIZE;    // if its not 128 use Y-modem 1K
                }
                if (errno == 0)
                    packetSizeSet = 1;
            }
            argIdx++;
        }

        DBGS_LOG_START("ymsend.log")

    	UARTEchoSet(false);		// Disable console echo

		DBGS_PRINTF("\n\r\n\rfilename = %s\npacket size = %d\n\r", filename, packetSize);

    	// Open the file for reading.
    	fresult = f_open(&FileObject, filename, FA_READ);
    	if(fresult != FR_OK)
    	{
            uprintf("f_open error: %s\n\r", StringFromFresult(fresult) ); prn_nl();
    		DBGS_PRINTF("f_open error: %s\n\r", StringFromFresult(fresult) );
    		error = -1;
    		goto abort;
    	}

    	file_size = f_size(&FileObject);  // Set file size
    	DBGS_PRINTF("File size = %lu\n", file_size);

    	DBGS_PRINTF("Ymodem sending %s, %lu bytes, packetSize=%d...\r\n", filename, file_size, packetSize);    //uprintf("Ymodem sending %s...\r\n", filename);
    	UARTFlushTx(false);

        /* Flush the RX FIFO, after a cool off delay */
        MAP_SysCtlDelay(MILLISECOND*1000);
        UARTFlushRx();

        /* Send 'C' and look for 'C' response */
        do
        {
        	UARTCharPut(UART0_BASE, CRC);	// Sending 'C' is not in YMODEM spec, but lets us know sender is waiting
            ch = getchar_timed(1);
			tries++;
           if(tries >= 30) goto abort;

        } while (ch != CRC);		// Look for response for 30 seconds

		// Got the right response, send the file
		tries = 0;

		do
		{
			send_packet0(filename, file_size);
			/* When the receiving program receives this block and successfully
			 * opened the output file, it shall acknowledge this block with an ACK
			 * character and then proceed with a normal XMODEM file transfer
			 * beginning with a "C" or NAK tranmsitted by the receiver.
			 */
			ch = getchar_timed(PACKET_TIMEOUT);     // 10sec

			if (ch == ACK)
			{
				ch = getchar_timed(PACKET_TIMEOUT);

				if (ch == CRC)	// Receiver got packet 0 okay, send the rest
				{
					error = send_data_packets(file_size, packetSize);
					if(error)
					{
						goto abort;
					}

					else
					{
						DBGS_PRINTF("\r\nSent: %s\r\n", filename);
                        uprintf("\r\nSent: %s\r\n", filename);
						goto success;
					}
				}
			}

		} while(tries++ < 10);	// Try sending packet0 for ten seconds

abort:
		UARTCharPut(UART0_BASE, CAN);
		UARTCharPut(UART0_BASE, CAN);
        MAP_SysCtlDelay(MILLISECOND*1000);
        DBGS_PRINTF("\r\nYmodem aborted\n");
        uprintf("\nYmodem aborted\n");

success:
        f_close(&FileObject);
    	UARTEchoSet(true);		// Enable console echo


        DBGS_LOG_FINISH()
    	if (dbgLogEnabled)
    	{
    	    uprintf("Debug log file \"ymsend.log\" was created.");
    	    prn_nl();
        }
		return error;
}

