/************************************************************************/
/* Copyright 1998 MBARI													*/
/************************************************************************/
/* $Header: /home/cvs/oasis4/src/controller/system/ymodem.c,v 1.2 2015/02/12 00:11:04 bobh Exp $						*/
/* Summary	: YModem Protocol Handler for OASIS Mooring Controller		*/
/* Filename : ymodem.c													*/
/* Author	: Robert Herlien (rah)										*/
/* Project	: OASIS Mooring												*/
/* $Revision: 1.2 $														*/
/* Created	: 08/20/98													*/
/* 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													*/
/* $Log: ymodem.c,v $
/* Revision 1.2	 2015/02/12 00:11:04  bobh
/* Take fileSem in ymodem
/*
/* Revision 1.1	 2011/10/25 22:39:22  bobh
/* Initial oasis4 revision
/*
/* Revision 1.1	 2009/03/25 20:34:41  bobh
/* Initial checkin
/*
/* Revision 1.2	 2005/03/01 00:54:01  mrisi
/* added file length to ymdFileSend to get rid of the trailing garbage packet on the end of the file
/*
/* Revision 1.1	 2003/09/03 17:48:52  bobh
/* YS - Ymodem send command
/*
 * Revision 4.4	 2001/06/19	 12:16:21  12:16:21	 oasisa (Oasis users)
 * New Repository; 6/19/2001 (klh)
 * 
 * Revision 1.1	 2001/06/19	 11:45:17  11:45:17	 oasisa (Oasis users)
 * Initial revision
 * 
 * Revision 4.2	 98/09/09  10:55:44	 10:55:44  bobh (Bob Herlien)
 * Sept/Oct '98 deployments of M1, Eqpac 1 & 2
 * 
*/
/************************************************************************/

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <oasis.h>						/* OASIS controller definitions		*/
//#include <custom.h>						/* Customizations for implementation*/
#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 <istdio.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 Int16
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 Int16
ymdmSendPkt( char *ymdmBuf, Nat16 pktNum, Nat16 pktSize, Word flags )
{
	Nat16		errCnt, crc;
	Reg Int16	ch;
	
	if ( flags & CRCFLAG )
		crc = CRC16Block(ymdmBuf, (ulong)pktSize, 0);
	else
		crc = CheckSum16Block(ymdmBuf, pktSize, 0);
	
	for ( errCnt = 0; errCnt < ERRORMAX; errCnt++ )
	{
		xflush_ser( 0 );
		doputc( (pktSize == SMPKTSIZE) ? SOH : STX );
		doputc( pktNum );
		doputc( ~pktNum );
		xputn( ymdmBuf, pktSize );

		if ( flags & CRCFLAG )
			doputc( crc >> 8 );
		doputc( crc );
		
		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, long fileLen, char *ymdmBuf )
{
	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, "%ld", fileLen);

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

	return( flags );

} /* ymdmSendFileName() */


/************************************************************************/
/* Function	   : ymdmSendFile											*/
/* Purpose	   : Send one file via YMODEM								*/
/* Inputs	   : File name, buffer ptr									*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
		MLocal Int16
ymdmSendFile( char *name, char *ymdmBuf)
{
	FILE		*fp;
	Word		flags;
	Nat16		pktNum;
	Int16		bytesToSend;
	char		*pktP;
	
	int		fd;
	long	len;
	struct	stat info;

	logFlush();
	
	/* get file des */
	fd = open(name, O_RDONLY );
   
	/* determine the file length */
	if ( fstat(fd, &info) )
	{
	  xprintf("Couldn't determine size of %s\n", name);
	  close(fd);
	  return(ERROR);
	}

	len = info.st_size;

	/* close file */
	close(fd);

	if ((fp = fopen(name, "r")) == NULL)
	{
	  xprintf("Couldn't open %s\n", name);
	  return(ERROR);
	}

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

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

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

	fclose(fp);

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

	return( ERROR );

} /* ymdmSendFile() */


/************************************************************************/
/* Function	   : ymdmSend												*/
/* Purpose	   : Send File(s) in YModem format							*/
/* Inputs	   : Parm Mask, File name(s)								*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
CmdRtn ymdmSend( Nat16 pmask, char *fileName )
{
#pragma unused(pmask)
  char			*ymdmBuf;
  char			*pathstr = FILEPATH;
  struct dirent de;
  Int16			rtn;
	
  if ( (ymdmBuf = malloc(YBUFSIZE)) == NULL )
  {
	xprintf("Out of memory\n");
	return( ERROR );
  }

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

  memset( ymdmBuf, 0, YBUFSIZE );

  if (DIRFindFirst(pathstr, &de) != dsdEndOfDir)
  {
	do
	{
	  if ((DIRMatchName((char *) de.d_name, fileName)) && 
		  ((de.d_attr & (_A_SUBDIR | _A_VOLID)) == 0))
	  {
		takeFileSem();
		rtn = ymdmSendFile(de.d_name, ymdmBuf);
		giveFileSem();
		if (rtn != OK )
		{
		  free(ymdmBuf);
		  return(rtn);
		}
	  }
	} while (DIRFindNext(&de) != dsdEndOfDir);

	DIRFindEnd(&de);
	ymdmSendFileName("", 0, ymdmBuf);
  }
  else
	xputs("\030\030\030\030\030");

  free( ymdmBuf );
  return( OK );

} /* ymdmSend() */
