//*****************************************************************************
//
// bl_ymodem.c    : The file holds the main control loop of the boot loader.
// Author         : QJ Wang. qjwang@ti.com
// Date           : 2-16-2016
//
// Copyright (c) 2006-2011 Texas Instruments Incorporated.  All rights reserved.
// Software License Agreement
//
// Texas Instruments (TI) is supplying this software for use solely and
// exclusively on TI's microcontroller products. The software is owned by
// TI and/or its suppliers, and is protected under applicable copyright
// laws. You may not combine this software with "viral" open-source
// software in order to form a larger program.
//
// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
//
//*****************************************************************************
/*  YMODEM MINIMUM REQUIREMENTS

    All programs claiming to support YMODEM must meet the following minimum
    requirements:
       + The sending program shall send the pathname (file name) in block 0.
       + The pathname shall be a null terminated ASCII string as described below.
            + Unless specifically requested, only the file name portion is sent.
            + No drive letter is sent.
            + Systems that do not distinguish between upper and lower case
              letters in filenames shall send the pathname in lower case only.

       + The receiving program shall use this pathname for the received file
         name, unless explicitly overridden.
       + 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.
       + The sending program shall use CRC-16 in response to a "C" pathname
         nak, otherwise use 8 bit checksum.
       + The receiving program must accept any mixture of 128 and 1024 byte
         blocks within each file it receives.  Sending programs may
         arbitrarily switch between 1024 and 128 byte blocks.
       + The sending program must not change the length of an unacknowledged
         block.
       + At the end of each file, the sending program shall send EOT up to ten
         times until it receives an ACK character.  (This is part of the
         XMODEM spec.)
       + The end of a transfer session shall be signified by a null (empty)
         pathname, this pathname block shall be acknowledged the same as other
         pathname blocks.
*/

/*
   SENDER                                               RECEIVER
   ======                                               ========
                                                           C
                                                           C
   SOH 00 FF Filename NULs CRC CRC
                                                          ACK
                                                           C
   STX 01 FD Data[1024] CRC1 CRC2
                                                          ACK
   SOH 02 FC Data[128] CRC1 CRC2
                                                          ACK
   SOH 03 FB Data[100] ^Z[28] CRC1 CRC2
                                                          ACK
   EOT
                                                          NAK
   EOT
                                                          ACK
                                                           C
   SOH 00 FF NUL[128] CRC CRC
                                                          ACK

   Use a sequence of two consecutive CAN (Hex 18) characters as
   a transfer abort command.
 */

#include "bl_config.h"

#if defined(UART_ENABLE_UPDATE)
#include "sci_common.h"
#include "bl_flash.h"
#include "sci.h"
#include "bl_ymodem.h"
#include "sys_common.h"
//#include "sys_pmu.h"

char fileName[FILENAME_LEN];
extern unsigned int g_pulUpdateSuccess[8];
extern unsigned int g_ulUpdateStatusAddr;
extern uint32_t g_ulUpdateBufferSize;          //32 bytes or 8 32-bit words
extern void delay(unsigned int delayval);


int Ymodem_Receive (sciBASE_t *sci, char *buf)
{
	unsigned char packet_data[PACKET_1K_SIZE + PACKET_HEADER + PACKET_CRC]={NULL};
	unsigned char fileSize[FILESIZE_LEN], *pFile;
	char *pcBuf;
	int i, file_done, packets_received, errors, imageSize = 0, key;
	unsigned int oReturnCheck;
	unsigned char ucBank=0;
    unsigned short packet_size, loop=1;
    unsigned int FlashDestination = APP_START_ADDRESS ;            /* Flash user program offset */

	g_pulUpdateSuccess[1] = FlashDestination;
	enum rcvstates              // states when receiving
	{
	     AskHeader,             // ask for a header
	     WaitHeader,            // await header 1st char
	     EOTHeader,             // EOT process
	     EndSession,
	     CANHeader,
		 AbortHeader,
		 PacketErrors,
		 GetPacketData,
		 Packet0,
		 DataPacket
	} state;

	while(loop)
	{
		file_done = 0;
		packets_received = 0;
		pcBuf = buf;
		errors = 0;
		state = AskHeader;

		while(file_done == 0 )
		{
			switch (state)
			{
				/* The receiver starts by sending an “C” (0x43) to the sender indicating it wishes
				 * to use the CRC method of block validating. After sending the initial “C” the receiver waits
				 * for either a 3 second time out or until a buffer full flag is set. If the receiver is
				 * timed out then another “C” is sent to the sender and the 3 second time out starts again.
				 * This process continues until the receiver receives a complete 133-byte or 1029-byte packet.
				 */
				case AskHeader:
					 UART_txByte(sci, CRC);
					 state = WaitHeader;
					 break;
				case WaitHeader:
					 key = UART_getChar(sci, WAIT_TIMEOUT);
					 if (key < 0 ){
						 state = PacketErrors;  //timeout
						 break;
					 }

					 /* Byte 1 of the Ymodem packet can only have a value of SOH, STX, EOT, CAN
					  * anything else is an error
					  */
					 switch (key)
					 {
						 case SOH:
							  packet_size = PACKET_SIZE;
							  state = GetPacketData;
							  break;
						 case STX:
							  packet_size = PACKET_1K_SIZE;
							  state = GetPacketData;
							  break;
						 case EOT:
							  state = EOTHeader;
							  break;
						 case CAN:  //Cancel transfer.
							  key = UART_getChar(sci, WAIT_TIMEOUT);
							  if (key == CAN)  {
								  state = CANHeader;
							  }
						 default:  /* unexpected key, treat it as cancelled by sender */
							 state = PacketErrors;
							 break;
					 }
					 break;
				case EOTHeader:   /* End of transmission */
					 UART_txByte(sci, ACK);
					 file_done = 1;
					 break;
				case EndSession:
					 UART_txByte(sci, ACK);
					 file_done = 1;
					 loop = 0;  //sessioin done
					 break;
				case CANHeader:     /* Cancelled by sender */
					 UART_txByte(sci, ACK);  //for get 2 CAN from sender
					 return 0;
				case AbortHeader:  /* cancelled by receiver: errors, timeout, etc*/
					 UART_txByte(sci, CAN);
					 UART_txByte(sci, CAN);
					 return 0;
				case PacketErrors:  /* 1: CRC error; 2:Packet # != (it's complement ^ 0xFF) */
									/* 3: timeout */
					 if (packets_received != 0)
					 {
						 errors ++;
						 if(errors >= MAX_ALLOWED_ERRORS){
							state = AbortHeader;
							break;
						 }
					 }
					 UART_txByte(sci, CRC);
					 state = WaitHeader;
					 break;
				case GetPacketData:
					 *packet_data = (char) key;  //data[0] = SOH, STX

					 /* The protocol NAKs the following conditions:
					  * 1. Framing error on any byte : serious hardware failures, baudrate etc
					  * 2. Overrun error on any byte : serious hardware failures, baudrate etc
					  * 3. Duplicate packet: the sender getting an ACK garbled and re-transmitting the packet.
					  * 4. CRC error : noisy environments
					  * 5. Receiver timed out (didn't receive packet within x second)
					  * On any NAK, the sender will re-transmit the last packet.
					  */
					 for (i = 1; i < (packet_size +  PACKET_HEADER + PACKET_CRC); i++)
					 {
						 key = UART_getChar(sci, WAIT_TIMEOUT);
						 if(key < 0) {
							 state = PacketErrors;
							 break;
						  }
						 packet_data[i] = (char) key;
					 }
					 if ( (packet_data[PACKET_SEQNUM_LOC] + packet_data[PACKET_SEQNUM_LOC+1]) != 255 )
					 {
						 state = PacketErrors;
						 break;
					 }
					 if (_cal_crc16_(packet_data + PACKET_HEADER, packet_size + PACKET_CRC) != 0) {
						 state = PacketErrors;
						 break;
					 }
					 /* Duplicate packet */
					 if ((packet_data[PACKET_SEQNUM_LOC] & 0xff) != (packets_received & 0xff)) {
						 UART_txByte(sci, NAK);
						 state = WaitHeader;
					 }
					 else
					 {
						if (packets_received == 0)
						{
							state = Packet0;
						}else{
							state = DataPacket;
						}
						packets_received ++;
					 }
					 break;

				case Packet0:
					 /* Filename packet:
					  * If directories are included, they are delimited by / rather than \
					  * The filename is sent as a NUL terminated ASCII string
					  * No spaces are included in the pathname or filename
					  */
					 if (packet_data[PACKET_HEADER] != 0)  /* The filename is not NULL*/
					 {
						/* Filename packet has valid data */
						for (i = 0, pFile = packet_data + PACKET_HEADER; (*pFile != 0) && (i < FILENAME_LEN);)
						{
							fileName[i++] = *pFile++;
						}
						fileName[i++] = '\0';
						for (i = 0, pFile ++; (*pFile != ' ') && (i < FILESIZE_LEN);)
						{
						  fileSize[i++] = *pFile++;
						}
						fileSize[i++] = '\0';
						Str2Int(fileSize, &imageSize);
						g_pulUpdateSuccess[2] = (uint32_t) imageSize;
						/* Test the size of the image to be sent */
						/* Image size is greater than Flash size */
						/* Erase the FLASH pages */
						if(!BLInternalFlashStartAddrCheck(FlashDestination,  imageSize))
						{
							state = AbortHeader;
						}
						/* Initialize the Flash Wrapper registers */
						oReturnCheck = 0;
						oReturnCheck = Fapi_BlockErase( ucBank, FlashDestination, imageSize);
						// Return an error if an access violation occurred.
						if(oReturnCheck)
						{
							state = AbortHeader;
						}
						//Tell sender we get the header successfully
						UART_txByte(sci, ACK);
						delay(5000000);     //0.5s
						UART_txByte(sci, CRC);
						state = WaitHeader;
					 }
					 /* packet_data[PACKET_HEADER] = 0: Filename packet is empty, end session */
					 else {
							state = EndSession;
					 }  //end of "if (packet_data[PACKET_HEADER] != 0), else"
					 break;

				case DataPacket:
					 oReturnCheck = 0;
					 memcpy(pcBuf, packet_data + PACKET_HEADER, packet_size);
					 oReturnCheck = Fapi_BlockProgram( ucBank, FlashDestination, (unsigned long)buf, packet_size);
					 if(oReturnCheck)
					 {
						// Indicate that the flash programming failed.
						state = AbortHeader;
						break;
					 }
					 // Now update the address to program.
					 FlashDestination += packet_size;
					 UART_txByte(sci, ACK);
					 state = WaitHeader;
					 break;
			}
		}//while(file_done)
	}//while(loop)


	if( errors == 0)
    {
    	oReturnCheck = Fapi_BlockProgram( ucBank, g_ulUpdateStatusAddr, (unsigned long)&g_pulUpdateSuccess, g_ulUpdateBufferSize);
    }
	return (int)imageSize;
}


/**
  * Description: Transmit a single packet using the ymodem protocol
  * Parameters:  sci port, data, length
  * Return value:  None
  */
static void SendPacket(sciBASE_t *sci, unsigned char *data, unsigned int packetNum)
{
	  unsigned short i = 0;
	  int crc16, packet_size;
	  char ch;

	  if (packetNum==0){
		  packet_size = PACKET_SIZE;
	  }else{
		  packet_size = PACKET_1K_SIZE;
	  }

	  crc16 = _cal_crc16_(data, packet_size);

	  if (packetNum == 0)
		  UART_txByte(sci, SOH);
	  else
		  UART_txByte(sci, STX);

	  ch = packetNum & 0xFF;
	  UART_txByte(sci, ch);
	  UART_txByte(sci, 0xFF-ch);

	  for(i=0; i < packet_size; i++)
	  {
		  UART_txByte(sci, data[i]);
	  }
	  UART_txByte(sci, (crc16 >> 8) & 0xFF);
	  UART_txByte(sci, crc16 & 0xFF);
}

void SendDataPacket(sciBASE_t *sci, unsigned char *data, unsigned int size)
{
	  unsigned int send_size, i;
	  int blockno =1;
	  int key, ch;
	  unsigned char temp[PACKET_SIZE];

	  while (size > 0)
	  {
		  if (size > PACKET_1K_SIZE)
			  send_size = PACKET_1K_SIZE;
		  else
			  send_size = size;

          //try upto 5 times
		  do
		  {
			  SendPacket(sci, data, blockno);

			  key = UART_getChar(sci, WAIT_TIMEOUT);

			  switch (key)
			  {
				 case ACK:
					  blockno++;
					  data += send_size;
					  size -= send_size;
					  i=5;
					  break;
				 case CRC:
					  i++;
			          break;
				 case NAK:
					  i++;
					  break;
				 case CAN:
					  return;
				 default:
					  return;
			  }//switch case
		  }while(i<5);
	  }//while loop

	  i=0;
      while (i < PACKET_SIZE) {
              temp[i++] = 0;
      }

	  //Wait for receiver's ACKnowledge
      //And send a null packet
	  while(1)
	  {
		  UART_txByte(sci,EOT);
		  key = UART_getChar(sci, WAIT_TIMEOUT);
		  switch(key)
		  {
		  	  case ACK:
		      	  ch = UART_getChar(sci, WAIT_TIMEOUT);
		      	  if (ch == CRC){
		      		  do{
		      			  SendPacket(sci, temp, 0);
		      			  ch = UART_getChar(sci, WAIT_TIMEOUT);
		      		  }while((ch !=ACK) && (ch != -1));
		          }
		      	  return;
		  	  case NAK:
		  		  continue;
		  	  case -1:
		  		  continue;
		  	  default:
		  		  return;
		  }
	  }
}//funtion end


/********************************************************************
   SENDER                                               RECEIVER
   ======                                               ========
                                                           C
                                                           C
   SOH 00 FF Filename NULs CRC CRC
                                                          ACK
                                                           C
   STX 01 FD Data[1024] CRC1 CRC2
                                                          ACK
   SOH 02 FC Data[128] CRC1 CRC2
                                                          ACK
   SOH 03 FB Data[100] ^Z[28] CRC1 CRC2
                                                          ACK
   EOT
                                                          NAK
   EOT
                                                          ACK
                                                           C
   SOH 00 FF NUL[128] CRC CRC
                                                          ACK
 ********************************************************************/

char Ymodem_Transmit (sciBASE_t *sci, unsigned char *pImage, char* fileName, unsigned int imageSize)
{
	  int key, c;
	  unsigned int isFirst = 1;
      unsigned long i = 0;
      unsigned char packet_data[PACKET_SIZE];
      const char* sizeStr;
	  unsigned int blocknum = 0;

	  for(;;)
	  {
			/* Send file name Packet */
		    blocknum = 0;  //first block for file name and file size, 128 bytes long
		    if (fileName)
		    {    /*Packet0 like this: SOH 00 FF filename NULL filesize 00 00 00 ...*/
		         while (*fileName && (i < FILENAME_LEN))
		         {
		              packet_data[i++] = *fileName++;
		         }
		         packet_data[i++] = 0;
		         sizeStr = U32toStr(imageSize);
		         while(*sizeStr) {
		                packet_data[i++] = *sizeStr++;
		         }
		    }
 	        while (i < PACKET_SIZE) {
 	        	packet_data[i++] = 0;
		    }
		    /* Send the file packet which contain filename and file size */
		    /* The packet number is 0 */
		    SendPacket(sci, packet_data, blocknum);

    		/* Wait for ACK and 'C' after send file packet
    		 * Wait for ACK only for sending data packet
    		 */
			key = UART_getChar(sci, WAIT_TIMEOUT);
			if (key == ACK)
			{
				 c = UART_getChar(sci, WAIT_TIMEOUT);
				 if (c == CRC)
				 {
					 SendDataPacket(sci, pImage, imageSize);
					 return 0;
				 }
			}//end of if (key == ACK)
			else if ((key == CRC) && (isFirst == 1))
			{
					isFirst = 0;
					continue;       //jump back to for(;;)
			}
			else if (( key != NAK) || (isFirst == 1))
			{
					break;   //terminate the for(;;) loop
			}
	  }

	  UART_txByte(sci, CAN);
	  UART_txByte(sci, CAN);
	  return 0;
}

#endif

