/****************************************************************************/
/* Summary  : Serial I/O routines for BEDS				    */
/* Filename : serial.c							    */
/* Author   : Robert Herlien (rah)					    */
/* Project  : BEDS (Benthic Event Detection System)			    */
/* Revision : 1.0							    */
/* Created  : 10/02/2012						    */
/****************************************************************************/
/* Modification History:						    */
/* 02oct2012 rah - created						    */
/* $Log$
 */
/****************************************************************************/

#include <mbariTypes.h>		/* MBARI type definitions		*/
#include <mbariConst.h>		/* MBARI constants			*/
#include <cfxpico.h>		// Persistor PicoDOS Definitions
#include <beds.h>		/* BEDS general definitions		*/
#include <serial.h>		/* BEDS serial I/O definitions		*/
#include <io.h>			/* BEDS I/O definitions			*/
#include <modem.h>		/* BEDS acoustic modem software		*/
#include <syslog.h>		/* System logger			*/

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>


/********************************/
/*	External Data		*/
/********************************/

Extern Int32	modemBaud, debugBaud, usrTmout;
Extern MBool	localEcho;


/********************************/
/*	Module Local Data	*/
/********************************/

MLocal TUPort	*tpuPort = NULL;
MLocal SerPort	serPortInUse = SER_NONE;

MLocal TUChParams tpuParms =
    {8, 'N', true, 38400L, tpuAutoPriority, tpuAutoPriority, 256, 256, 512};

MLocal MBool	conSwitched = FALSE, onModemPort = TRUE;
MLocal vfptr	ORGgetq, ORGiflush, ORGgetc, ORGputc, ORGoflush;
MLocal vfptr	ORGdrain, ORGgetclp, ORGgetTmout;
MLocal vfptr	ORGSCITxP, ORGSCIRxQ, ORGSCIRxG; /* Extra ones needed for Ymodem*/


/************************************************************************/
/* Function    : serInit						*/
/* Purpose     : Initialize this module					*/
/* Inputs      : None							*/
/* Outputs     : Pointer to TUPort for aux chan				*/
/************************************************************************/
TUPort *serInit(Void)
{
    SCIConfigure(modemBaud, 'N', TRUE);
    SCIRxSetBuffered(TRUE);
    SCITxSetBuffered(TRUE);

    TUInit(calloc, free);
    tpuParms.baud = debugBaud;
    tpuPort = TUOpen(12, 13, debugBaud, &tpuParms);
    return(tpuPort);

} /* serInit() */


/************************************************************************/
/* Function    : getTPUPort						*/
/* Purpose     : Get the Aux TPU port					*/
/* Inputs      : None							*/
/* Outputs     : TUPort							*/
/************************************************************************/
TUPort	*getTPUPort(Void)
{
    return(tpuPort);
}


/************************************************************************/
/* Function    : setConsoleBaud						*/
/* Purpose     : Set the baud rate for the console port			*/
/* Inputs      : Baud rate						*/
/* Outputs     : None							*/
/************************************************************************/
Void setConsoleBaud(Int32 baud)
{
    Int32	oldBaud;
    char	parity;

    SCIGetConfig(&oldBaud, &parity);

    if (baud != oldBaud)
	SCIConfigure(baud, 'N', TRUE);
}


/************************************************************************/
/* Function    : setTpuBaud						*/
/* Purpose     : Set the baud rate for the aux TPU port			*/
/* Inputs      : Baud rate						*/
/* Outputs     : TUPort pointer (will have changed)			*/
/************************************************************************/
TUPort *setTpuBaud(Int32 baud)
{
    if (baud != tpuParms.baud)
    {
	if (tpuPort != NULL)
	    TUClose(tpuPort);

	tpuParms.baud = baud;
	tpuPort = TUOpen(12, 13, baud, &tpuParms);
    }

    return(tpuPort);
}


/************************************************************************/
/* Function    : setConsole						*/
/* Purpose     : Set the user I/F console				*/
/* Inputs      : SerPort enumeration, boolean if on modem		*/
/* Outputs     : OK							*/
/************************************************************************/
Errno setConsole(SerPort port, MBool isModem)
{
    onModemPort = isModem;

    if (port == serPortInUse)
	return(OK);

    cdrain();
    Delay1ms();	// let final character dribble out;

    if (port == SER_CONSOLE)
    {
	if (conSwitched)
	{
	    BIOSPatchInsert(CIOgetq  , ORGgetq);
	    BIOSPatchInsert(CIOiflush, ORGiflush);
	    BIOSPatchInsert(CIOgetc  , ORGgetc);
	    BIOSPatchInsert(CIOputc  , ORGputc);
	    BIOSPatchInsert(CIOoflush, ORGoflush);
	    BIOSPatchInsert(CIOdrain , ORGdrain);
	    BIOSPatchInsert(CIOgetclp, ORGgetclp);
	    BIOSPatchInsert(SCIRxGetCharWithTimeout, ORGgetTmout);

	    // Following add'l patches needed for YModem, taken from TUAltConsole2.c
	    BIOSPatchInsert(SCITxPutChar, ORGSCITxP);
	    BIOSPatchInsert(SCIRxQueuedCount, ORGSCIRxQ);
	    BIOSPatchInsert(SCIRxGetChar, ORGSCIRxG);
	}

	EIAForceOff(FALSE);			// Turn on console Tx
#ifndef RECIPE_PROTO
	altSerEnable(FALSE);			// Turn off TPU Tx
#endif
	conSwitched = FALSE;
    }
    else if (port == SER_TPU)
    {
	if (!conSwitched)
	{
	    ORGgetq   = BIOSPatchInsert(CIOgetq  , TIOgetq);  
	    ORGiflush = BIOSPatchInsert(CIOiflush, TIOiflush);
	    ORGgetc   = BIOSPatchInsert(CIOgetc  , TIOgetc);  
	    ORGputc   = BIOSPatchInsert(CIOputc  , TIOputc);  
	    ORGoflush = BIOSPatchInsert(CIOoflush, TIOoflush);
	    ORGdrain  = BIOSPatchInsert(CIOdrain , TIOdrain); 
	    ORGgetclp = BIOSPatchInsert(CIOgetclp, TIOgetclp);
	    ORGgetTmout = BIOSPatchInsert(SCIRxGetCharWithTimeout, TIOgetByteTmout);

	    // Following add'l patches needed for YModem, taken from TUAltConsole2.c
	    ORGSCITxP = BIOSPatchInsert(SCITxPutChar, TIO2putc);
	    ORGSCIRxQ = BIOSPatchInsert(SCIRxQueuedCount, TIOgetq);
	    ORGSCIRxG = BIOSPatchInsert(SCIRxGetChar, TIOgetc);
	}

	altSerEnable(TRUE);			// Turn on TPU Tx
	EIAForceOff(TRUE);			// Turn off console Tx
	conSwitched = TRUE;
    }

    ciflush();
    serPortInUse = port;
    return(OK);

} /* setConsole() */


/************************************************************************/
/* Function    : setSerTxEnable						*/
/* Purpose     : Enable Tx for the serial port in use			*/
/* Inputs      : None							*/
/* Outputs     : None							*/
/* Side Effect : Always enables the Rx					*/
/************************************************************************/
Void setSerTxEnable(Void)
{
    if (serPortInUse == SER_CONSOLE)
	EIAForceOff(FALSE);
    else if (serPortInUse == SER_TPU)
	PIOSet(AUX_TX_OFF);

} /* setSerTxEnable() */


/************************************************************************/
/* Function    : consoleIsSwitched					*/
/* Purpose     : Return console switched status				*/
/* Inputs      : None							*/
/* Outputs     : TRUE if alt port is now console, else FALSE		*/
/************************************************************************/
MBool consoleIsSwitched(Void)
{
    return(conSwitched);
} 


/************************************************************************/
/* Function    : consolePrintf						*/
/* Purpose     : Printf to console port, regardless whether port is switched*/
/* Inputs      : Format string, arguments				*/
/* Outputs     : Number of chars printed				*/
/* Comment     : Used by syslog if echoing to console			*/
/************************************************************************/
Int32 consolePrintf(const char *fmt, ...)
{
    va_list	ap;
    Int32	rtn;
    vfptr	patchedPutc;

    va_start(ap, fmt);

    if (conSwitched)
    {
	patchedPutc = BIOSPatchInsert(CIOputc, ORGputc);
	rtn = vprintf(fmt, ap);
	ORGputc = BIOSPatchInsert(CIOputc, patchedPutc);  
	return(rtn);
    }
    else
	return(vprintf(fmt, ap));

} /* consolePrintf() */


/************************************************************************/
/* Function    : gets_lpt						*/
/* Purpose     : gets in low power (if using SCI) and timeout		*/
/* Inputs      : line buffer ptr, length of buffer			*/
/* Outputs     : Length of string or TMOUT				*/
/************************************************************************/
short gets_lpt(char *linebuf, short len)
{
    char	*p;
    short	got, ch;
    time_t	now, endTime;

    endTime = time(&now) + usrTmout;

    for (p = linebuf, got = 0; got < len-1; )
    {
	while (CIOgetq() > 0)
	{
	    switch(ch = CIOgetc())
	    {
	      case '\n':
	      case '\r':
		  *p = '\0';
//		  if (localEcho)
//		      CIOputc(ch);
		  return(got);

	      case '\b':
		  if (got > 0)
		  {
		      if (localEcho)
			  CIOprintf("\b \b");
		      got--;
		      *--p = '\0';
		  }
		  break;

	      default:
		  *p++ = ch;
		  got++;
		  if (localEcho)
		      CIOputc(ch);
		  break;
	    }

	    endTime = time(&now) + usrTmout;
	}

	if (time(&now) >= endTime)
	    return(TMOUT);

	checkModemBatt();			/* Check modem batt, if time */
//	if (serPortInUse == SER_CONSOLE)	/* Will wake up on first of  */
//	    ioSleep(TICKS_PER_SECOND/10);	/* ser char or 1/10 second   */
						/* But only works for SCI    */
	Delay1ms();				/* Don't know why I need this*/
#ifdef WATCHDOG
	TickleSWSR();				/* Keep watchdog running     */
#endif
    }

    return(got);

} /* gets_lpt() */


/******************************************************************************\
**	TIOgetq	// 	Return non-zero if input data is available
\******************************************************************************/
short TIOgetq(void)
{
    return(TURxQueuedCount(tpuPort));
}


/******************************************************************************\
**	TIOiflush	// 	Flush any pending input data
\******************************************************************************/
short TIOiflush(void)
{
//    return(TURxGetByteWithTimeout(tpuPort, 1) != -1);
    TURxFlush(tpuPort);
    return(0);
}


/******************************************************************************\
**	TIOgetc	// 	Wait for, and return the next character
\******************************************************************************/
short TIOgetc(void)
{
    return(tgetc(tpuPort));
}


/******************************************************************************\
**	TIOputc	// 	Send character
\******************************************************************************/
void TIOputc(char c)
{
    tputc(tpuPort, c);
}


/******************************************************************************\
**	TIO2putc	// 	Send character
\******************************************************************************/
void TIO2putc(ushort c)
{
    tputc(tpuPort, c);
}


/******************************************************************************\
**	TIOoflush	// 	Discard any queued transmit characters
\******************************************************************************/
void TIOoflush(void)
{
    toflush(tpuPort);
}


/******************************************************************************\
**	TIOdrain	// 	Wait for all transmission to complete
\******************************************************************************/
void TIOdrain(void)
{
    tdrain(tpuPort);
}


/******************************************************************************\
**	TIOgetclp	// 	Wait in low power for, and return the next character 
\******************************************************************************/
short TIOgetclp(bool )
{
    return(tgetc(tpuPort));		/* No equiv low-power getc for TPUs	*/
}



/******************************************************************************\
**	TIOgetByteTmout	// 	Get byte with timeout
\******************************************************************************/
short TIOgetByteTmout(short millisecs)
{
    return(TURxGetByteWithTimeout(tpuPort, millisecs));
}


