/************************************************************************/
/* Copyright 1998-2016 MBARI											*/
/************************************************************************/
/* $Header$		*/
/* Summary	: YModem Protocol Handler for OASIS Mooring Controller		*/
/* Filename : ymodem.c													*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: OASIS Mooring												*/
/* Revision : 1.0														*/
/* Created	: 08/24/2016													*/
/* Comment	: Adapted from xmodem program by Steve Grandi, grandi@noao.edu*/
/*																			*/
/* MBARI provides this documentation and code "as is", with no warranty,	*/
/* express or implied, of its quality or consistency. It is provided without*/
/* support and without obligation on the part of the Monterey Bay Aquarium	*/
/* Research Institute to assist in its use, correction, modification, or	*/
/* enhancement. This information should not be published or distributed to	*/
/* third parties without specific written permission from MBARI.			*/
/*																			*/
/************************************************************************/
/* Modification History:												*/
/* 20aug98 rah, created													*/
/* 24aug2016 rah, created OASIS5 version from OASIS3 version			*/
/* $Log$
*/
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
#include <ymodem.h>						/* YModem protocol definitions		*/
#include <log.h>						/* Log record definitions			*/
#include <otask.h>						/* OASIS Multitasking definitions	*/
#include <file.h>						/* OASIS File I/O Routines			*/
#include <utils.h>						/* OASIS Utility routines			*/
#include <malloc.h>						/* OASIS malloc routines			*/
#include <crc16.h>						/* CRC16-CCITT routines				*/

#include <istdio.h>						/* Our (integer) Standard I/O		*/
#include <stdio.h>						/* Standard I/O						*/
#include <string.h>						/* String functions					*/
#include <time.h>


/************************************************************************/
/* Function	   : ymdmSyncProtocol										*/
/* Purpose	   : Get the protocol sync character						*/
/* Inputs	   : Time to wait in seconds								*/
/* Outputs	   : Sync char or ERROR										*/
/************************************************************************/
MLocal Errno ymdmSyncProtocol(Nat16 waitTime)
{
	Reg Int16	ch;
	Nat16		cnt;

	for(cnt = 0; cnt < waitTime; cnt++)
	{
		ch = xgetc_tmout(1);

		if ((ch == CRCCHR) || (ch == GCHR) || (ch == NAK))
			return(ch);

		if ((ch == CAN) && (xgetc_tmout(2) == CAN))
			return(ERROR);
	}

	return(ERROR);

} /* ymdmSyncProtocol() */


/************************************************************************/
/* Function	   : ymdmSendPkt											*/
/* Purpose	   : Send one YModem packet									*/
/* Inputs	   : Packet, packet number, packet size, flags word			*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno ymdmSendPkt(char *ymdmBuf, Nat16 pktNum, Nat16 pktSize,
						 Word flags, SerDesc *sdp)
{
	Nat16				errCnt, crc, cksum, i;
	Reg Int16			ch;
	Reg unsigned char	*p;

	for (errCnt = 0; errCnt < ERRORMAX; errCnt++)
	{
		xflush_ser(0);
		doputc((pktSize == SMPKTSIZE) ? SOH : STX, sdp);
		doputc(pktNum, sdp);
		doputc(~pktNum, sdp);

		crc = cksum = 0;

		for (i = 0, p = ymdmBuf; i < pktSize; i++)
		{
			doputc(*p, sdp);
			crc = updcrc((*p & 0xff), crc);
			cksum += *p++;
		}

		if (flags & CRCFLAG)
		{
			crc = updcrc(0, updcrc(0, crc));
			doputc(crc >> 8, sdp);
			doputc(crc, sdp);
		}
		else
			doputc(cksum, sdp);

		if ((flags & GFLAG) || ((ch = xgetc_tmout(2)) == ACK))
			return(OK);

		if ((ch == CAN) && (xgetc_tmout(2) == CAN))
			return(ERROR);
	}

	return(ERROR);

} /* ymdmSendPkt() */


/************************************************************************/
/* Function	   : ymdmSendFileName										*/
/* Purpose	   : Start YModem protocol, send pkt 0 with file name		*/
/* Inputs	   : File name, packet buffer								*/
/* Outputs	   : Flag word or ERROR										*/
/************************************************************************/
MLocal Word ymdmSendFileName(char *fileName, FSIZE_t fileLen,
							 char *ymdmBuf, SerDesc *sdp)
{
	Word	flags;
	char	*buff_ptr; 

	switch(ymdmSyncProtocol(NAKMAX))
	{
		case CRCCHR:
			flags = KPKT | CRCFLAG;
			break;

		case GCHR:
			flags = KPKT | CRCFLAG | GFLAG;
			break;

		case NAK:
			return(0);

		default:
			return(ERROR);
	}

	memset(ymdmBuf, 0, YBUFSIZE);
	strcpy(ymdmBuf, fileName);

	buff_ptr = ymdmBuf + strlen(fileName) + 1;

	sprintf(buff_ptr, "%u", fileLen);

	if (ymdmSendPkt(ymdmBuf, 0, SMPKTSIZE, flags, sdp) != OK)
		return(ERROR);

	return(flags);

} /* ymdmSendFileName() */


/************************************************************************/
/* Function	   : ymdmSendFile											*/
/* Purpose	   : Send one file via YMODEM								*/
/* Inputs	   : File name, file size, buffer ptr						*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
MLocal Errno ymdmSendFile(char *name, FSIZE_t fsize, char *ymdmBuf)
{
	FRESULT		fRes;
	Word		flags;
	UINT		bytesToSend;
	char		*pktP;
	Nat16		pktNum;
	SerDesc		*sdp;
	FIL			file;
	
	logFlush();
	sdp = rtosIsRunning() ? pvTaskGetThreadLocalStoragePointer(NULL, ConsoleP) : NULL;
	
	if ((fRes = f_open(&file, name, (FA_READ | FA_OPEN_EXISTING))) != FR_OK)
	{
		fperr(fRes, "Error on f_open");
		return(ERROR);
	}

	/* Send file name, then resync the protocol			*/
	if ((flags = ymdmSendFileName(name, fsize, ymdmBuf, sdp)) == ERROR)
		return( ERROR );

	if (flags)							/* If not XMODEM, resync		*/
		if (ymdmSyncProtocol(RESYNCMAX) == ERROR)
			return(ERROR);
	
	/* Send the data					*/
	pktNum = 1;

	while ((f_read(&file, ymdmBuf, YBUFSIZE, &bytesToSend) == FR_OK) &&
		   (bytesToSend > 0))
	{
		pktP = ymdmBuf;
		if ((flags & KPKT) && (bytesToSend >= (6 * 128)))
		{								/* Normal case					*/
			if (ymdmSendPkt(pktP, pktNum, LGPKTSIZE, flags, sdp) != OK)
				return(ERROR);
			pktNum++;
		}
		else while (bytesToSend > 0)
		{								/* Last data segment, < 1024 bytes */
			if (ymdmSendPkt(pktP, pktNum, SMPKTSIZE, flags, sdp) != OK)
				return(ERROR);
			pktNum++;
			bytesToSend -= SMPKTSIZE;
			pktP += SMPKTSIZE;
		}
	}

	f_close(&file);

	/* Send EOT, wait for ACK			*/
	for (pktNum = 0; pktNum < EOTMAX; pktNum++)
	{
		doputc(EOT, sdp);
		if (xgetc_tmout(2) == ACK)
			return(OK);
	}

	return(ERROR);

} /* ymdmSendFile() */


/************************************************************************/
/* Function	   : dsySendFile											*/
/* Purpose	   : Function passed to dirScan for "YS" command			*/
/* Inputs	   : Directory entry ptr, ymodem buffer ptr					*/
/* Outputs	   : TRUE if OK, FALSE if error								*/
/************************************************************************/
MBool dsSendFile(FILINFO *de, void *ymdmBuf)
{
	Errno	rtn;

	if ((de->fattrib & (AM_DIR | AM_VOL)) == 0)
	{
		takeFileSem();
		rtn = ymdmSendFile(de->fname, de->fsize, (char *)ymdmBuf);
		giveFileSem();
		return(rtn == OK ? TRUE : FALSE);
	}

	return(TRUE);
}

/************************************************************************/
/* Function	   : ymdmSend												*/
/* Purpose	   : Send File(s) in YModem format							*/
/* Inputs	   : Parm Mask, File name(s)								*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn ymdmSend(ParmMask_t pmask, char *fileName)
{
	char		*ymdmBuf;
	SerDesc		*sdp;

	if ((pmask & 1) == 0)
		return(ERROR);

	if ( (ymdmBuf = malloc(YBUFSIZE)) == NULL )
	{
		xprintf("Out of memory\n");
		return(ERROR);
	}

	xprintf("Sending %s via YMODEM protocol\n", fileName);
	xprintf("Please start download on receiver with YMODEM (batch or G) protocol\n");

	memset(ymdmBuf, 0, YBUFSIZE);

	if (dirScan(fileName, dsSendFile, ymdmBuf) > 0)
	{
		sdp = rtosIsRunning() ? pvTaskGetThreadLocalStoragePointer(NULL, ConsoleP) : NULL;
		ymdmSendFileName("", 0, ymdmBuf, sdp);
	}
	else
		xputs("\030\030\030\030\030");

	free(ymdmBuf);
	return(OK);

} /* ymdmSend() */
