/****************************************************************************/
/* Copyright 2015 MBARI                                                     */
/****************************************************************************/
/* Summary  : Serial I/O for OASIS5 on PIC32MX470F512L                      */
/* Filename : serial.c                                                      */
/* Author   : Robert Herlien (rah)                                          */
/* Project  : OASIS Mooring Replacement (OASIS5)                            */
/* Revision: 1.0                                                            */
/* Created  : 07/13/2015                                                    */
/*                                                                          */
/* 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:                                                    */
/* 13jul2015 rah - created                                                  */
/* 20jun2016 rah - Merged with Oasis3/4 source								*/
/****************************************************************************/

#include <xc.h>
#include <GenericTypeDefs.h>

#include <FreeRTOS.h>
#include <stdint.h>
#include <stdlib.h>
#include <semphr.h>

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <mbariConst.h>					/* MBARI constants					*/
#include <time.h>						/* Std C time.h						*/
#include <oasis.h>						/* OASIS controller definitions		*/

#include <serial.h>
#include <buffer.h>
#include <dig_io.h>
#include <timer.h>

#define SER_POLL	(TICKS_PER_SEC/50)	/* Serial polling rate in ticks		*/


/********************************/
/*		External Data			*/
/********************************/

Extern Parm_t		xoff_tmout;					/* Timeout for xoff			*/


/****************************************/
/*			Global Data					*/
/****************************************/

Global SemaphoreHandle_t	ser_sem[PIC_UARTS];	/* Mutex sems for ser ports */


/****************************************/
/*		Module Local Data				*/
/****************************************/

MLocal StaticSemaphore_t	serSemBuf[PIC_UARTS];	/* Mutex sem buffers	*/

MLocal unsigned int iec1_RxBits[] = 
    {0, _IEC1_U1RXIE_MASK, _IEC1_U2RXIE_MASK, _IEC1_U3RXIE_MASK, 0};

MLocal unsigned int iec2_RxBits[] = 
    {_IEC2_U5RXIE_MASK, 0, 0, 0, _IEC2_U4RXIE_MASK};

MLocal unsigned int iec1_TxBits[] = 
    {0, _IEC1_U1TXIE_MASK, _IEC1_U2TXIE_MASK, 0, 0};

MLocal unsigned int iec2_TxBits[] = 
    {_IEC2_U5TXIE_MASK, 0, 0, _IEC2_U3TXIE_MASK, _IEC2_U4TXIE_MASK};


MLocal ByteBuffer	rxBuff[PIC_UARTS];
MLocal Byte			rxBuffData[PIC_UARTS][RX_BUFF_SIZE];
MLocal Nat32		rxOverFlowCnt[PIC_UARTS];

MLocal ByteBuffer	txBuff[PIC_UARTS];
MLocal Byte			txBuffData[PIC_UARTS][TX_BUFF_SIZE];
MLocal Nat32		txCount[PIC_UARTS];

MLocal Nat32		baudRate[PIC_UARTS];
MLocal MBool		xoff_status[PIC_UARTS];		/* UART Transmitter status */
MLocal MBool		do_xon[PIC_UARTS];			/* TRUE to do XON/XOFF	   */
MLocal time_t		xoff_time[PIC_UARTS];		/* To time out XOFF status */



/************************************************************************/
/* Function    : serInit                                                */
/* Purpose     : Initialize serial I/O Module                           */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void serInit(void)
{
    int i;

    /* UART Setup - Note that dig_io.c sets all the Tx bits to output 0 state */
    /* This will cause the port to go into break when the UART is disabled    */
    /* Map the PPS pins for the UARTs                                         */

    U1RXR = 0x01;                       /* UART1 RX = Ser 1             */
    RPG7R = 0x03;                       /* UART1 TX                     */
    U2RXR = 0x02;                       /* UART2 RX = Ser 2             */
    RPF5R = 0x01;                       /* UART2 TX                     */
    U3RXR = 0x0b;                       /* UART3 RX = Ser 3             */
    RPD14R = 0x01;                      /* UART3 TX                     */
    U4RXR = 0x02;                       /* UART4 RX = Ser 4             */
    RPB15R = 0x02;                      /* UART4 TX                     */
    U5RXR = 0x0b;                       /* UART5 RX = Ser 0 = Console   */
    RPF8R = 0x04;                       /* UART5 TX = Ser 0 = Console   */

    /* initialize serial data */
    for (i = 0; i < PIC_UARTS; i++)
    {
        bufInit(&rxBuff[i], rxBuffData[i], RX_BUFF_SIZE);
        bufInit(&txBuff[i], txBuffData[i], TX_BUFF_SIZE);

		/* Create mutex sems	*/
		ser_sem[i] = xSemaphoreCreateMutexStatic(&serSemBuf[i]);

		serPortInit(i, STD_BAUD, NO_PTY | BIT8 | STOP1);
										        /* Sets up UxMODE and UxSTA */

        rxOverFlowCnt[i] = 0;            /* init flags */
        txCount[i] = 0;
		do_xon[i] = FALSE;
		xoff_status[i] = FALSE;
    }
    

/******************** Setup Serial Port 0 == UART 5 *********************/

    /* Configure uart5 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS2CLR = (_IFS2_U5RXIF_MASK | _IFS2_U5TXIF_MASK);

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC10bits.U5IP = 4;
    IPC10bits.U5IS = 0;

    /* enable receive interrupts, disable transmit interrupts */
    IEC2SET = _IEC2_U5RXIE_MASK;
    IEC2CLR = _IEC2_U5TXIE_MASK;


/******************** Setup Serial Port 1 == UART 1 *********************/

    /* Configure uart1 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U1RXIF_MASK;
    IFS1CLR = _IFS1_U1TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC7bits.U1IP = 4;
    IPC7bits.U1IS = 3;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U1RXIE_MASK;
    IEC1CLR = _IEC1_U1TXIE_MASK;


/******************** Setup Serial Port 2 == UART 2 *********************/

    /* Configure uart2 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U2RXIF_MASK;
    IFS1CLR = _IFS1_U2TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U2IP = 4;
    IPC9bits.U2IS = 3;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U2RXIE_MASK;
    IEC1CLR = _IEC1_U2TXIE_MASK;
    
    
/******************** Setup Serial Port 3 == UART 3 *********************/

    /* Configure uart3 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS1CLR = _IFS1_U3RXIF_MASK;
    IFS2CLR = _IFS2_U3TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U3IP = 4;
    IPC9bits.U3IS = 3;

    /* enable receive interrupts, disable transmit interrupts */
    IEC1SET = _IEC1_U3RXIE_MASK;
    IEC2CLR = _IEC2_U3TXIE_MASK;


/******************** Setup Serial Port 4 == UART 4 *********************/

    /* Configure uart4 receive and transmit interrupt */
    /* clear any pending interrupts just in case */
    IFS2CLR = _IFS2_U4RXIF_MASK;
    IFS2CLR = _IFS2_U4TXIF_MASK;

    /* Set interrupt priority - Rx and Tx share priority    */
    IPC9bits.U4IP = 4;
    IPC9bits.U4IS = 3;

    /* enable receive interrupts, disable transmit interrupts */
    IEC2SET = _IEC2_U4RXIE_MASK;
    IEC2CLR = _IEC2_U4TXIE_MASK;

}


/************************************************************************/
/* Function	   : getSerialPort											*/
/* Purpose	   : Get access to a serial port							*/
/* Input	   : Physical serial port number							*/
/* Output	   : None													*/
/************************************************************************/
void getSerialPort(Port_t serport)
{
	if (isPhysSer(serport))					/* If phys ser port, take sem*/
		xSemaphoreTake(&ser_sem[serport], portMAX_DELAY);

} /* getSerialPort() */


/************************************************************************/
/* Function	   : releaseSerialPort										*/
/* Purpose	   : Release access to a serial port						*/
/* Input	   : Physical serial port number							*/
/* Output	   : None													*/
/************************************************************************/
void releaseSerialPort(Port_t serport)
{
	if (isPhysSer(serport))					/* If physical ser port		*/
		xSemaphoreGive(&ser_sem[serport]);	/* Give up semaphore		*/

} /* releaseSerialPort() */


/************************************************************************/
/* Function	   : serCheckXoffStatus										*/
/* Purpose	   : Check if any port has timed out on XOFF				*/
/* Input	   : None													*/
/* Output	   : None													*/
/* Comment	   : Called twice/second from oasis task main loop			*/
/************************************************************************/
void serCheckXoffStatus(void)
{
  Reg Nat32		i;
  Reg time_t	tod = clkTime();

  for ( i = 0; i < PIC_UARTS; i++ )
	  if ((xoff_status[i]) && ((tod - xoff_time[i]) > xoff_tmout))
	  {											/* Timed out on XOFF	*/
		  xoff_status[i] = FALSE;				/* Turn off xoff status */

		  IEC1SET = iec1_TxBits[i];			    /* Turn on UART Tx ints */
		  IEC2SET = iec2_TxBits[i];
	  }

} /* serCheckXoffStatus() */


/************************************************************************/
/* Function	   : ser_getc												*/
/* Purpose	   : Read one character from serial port					*/
/* Inputs	   : Physical serial port number							*/
/* Output	   : Character, or ERROR if none							*/
/************************************************************************/
int ser_getc(Port_t port)
{
    int     stat;
	Byte	c;

    if (port < PIC_UARTS)               /* Valid port?                  */
    {
        IEC1CLR = iec1_RxBits[port];    /* Turn off ints from the UART  */
        IEC2CLR = iec2_RxBits[port];    /* while manipulating the buffer*/

        stat = bufGet(&c, &rxBuff[port]);

        IEC1SET = iec1_RxBits[port];    /* Turn UART ints back on       */
        IEC2SET = iec2_RxBits[port];    /* Turn UART ints back on       */

        return(stat ? (int)c : ERROR);
    }

    return(ERROR);
}


/************************************************************************/
/* Function	   : ser_putc												*/
/* Purpose	   : Write one character to serial port						*/
/* Inputs	   : Physical port number, character						*/
/* Output	   : MBool, TRUE if character sent							*/
/************************************************************************/
MBool ser_putc(Port_t port, Byte b)
{
    MBool	rtn;

    if (port < PIC_UARTS)               /* Valid port?                  */
    {
        IEC1CLR = iec1_TxBits[port];    /* Turn off ints from the UART  */
        IEC2CLR = iec2_TxBits[port];    /* while manipulating the buffer*/

        rtn = bufPut(b, &txBuff[port]);

		if (!do_xon[port] || !xoff_status[port])
		{
			IEC1SET = iec1_TxBits[port]; /* Turn on UART Tx ints        */
			IEC2SET = iec2_TxBits[port];
		}

        return(rtn);
    }

    return(FALSE);
}


/************************************************************************/
/* Function    : serPutString                                           */
/* Purpose     : Queue a C string for transmission                      */
/* Inputs      : Ser port number, string to send                        */
/* Outputs     : Number of bytes queued                                 */
/************************************************************************/
int serPutString(Port_t port, char* buff)
{
    int         i;
    char        *p;

    if (port < PIC_UARTS)               /* Valid port?                  */
    {
        for (i = 0, p = buff; *p; )
            if (ser_putc(port, *p))
            {
                p++;
                i++;
            }
			else
				vTaskDelay(SER_POLL);

        return(i);
    }

    return(0);
}


/************************************************************************/
/* Function	   : serPortInit											*/
/* Purpose	   : Initialize one serial port								*/
/* Inputs	   : Physical serial port to init							*/
/*				 Initialization word									*/
/*				 Bits 6-7 = Newline mode - what to send on \n to output:*/
/*							  00 = \n, 01 = \r\n, 02 = \r, 03 = none	*/
/*				 Bit 5 = Echo mode: 1 to do local echo, 0 for none		*/
/*				 Bit 4 = Flow control: 1 for XON/XOFF, 0 for none		*/
/*				 Bit 3 = Stop bits: 0 for 1 stop bit, 1 for 2 stop bits */
/*				 Bit 2 = Character len: 0 for 7 bits, 1 for 8 bits		*/
/*				 Bits 0-1 = Parity: 00 = none, 01 = Even, 10 = odd, 11 rsvd*/
/* Outputs	   : MBool, TRUE if OK										*/
/************************************************************************/
MBool serPortInit(Port_t port, Nat32 baud, Nat32 mode)
{
	int		data_par;

	do_xon[port] = (mode & FLOW) ? TRUE : FALSE;
	xoff_status[port] = FALSE;

	if (mode & BIT7)					/* 7 bit mode not supported		*/
		return(FALSE);
	
	switch(mode & PTY_MASK)
	{
	  case ODD_PTY:
		  data_par = D8_ODD;
		  break;

	  case EVEN_PTY:
		  data_par = D8_EVEN;
		  break;
		
	  default:
		  data_par = D8_NONE;
		  break;
	}

    switch(port)
    {
		case SER_PORT_0:
			U5MODE = UxMODE;
			U5MODEbits.PDSEL = data_par;

			if (mode & STOP2)
				U5MODEbits.STSEL = 1;

			if (baud <= 19200L)
				U5BRG = StdBRG(baud);
			else
			{
				U5BRG = FastBRG(baud);
				U5MODEbits.BRGH = 1;
			}

			U5STA = UxSTA;
			break;

		case SER_PORT_1:
			U1MODE = UxMODE;
			U1MODEbits.PDSEL = data_par;

			if (mode & STOP2)
				U1MODEbits.STSEL = 1;

			if (baud <= 19200L)
				U1BRG = StdBRG(baud);
			else
			{
				U1BRG = FastBRG(baud);
				U1MODEbits.BRGH = 1;
			}

			U1STA = UxSTA;
			break;

        case SER_PORT_2:
            U2MODE = UxMODE;
            U2MODEbits.PDSEL = data_par;
                          
			if (mode & STOP2)
                U2MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U2BRG = StdBRG(baud);
            else
            {
                U2BRG = FastBRG(baud);
                U2MODEbits.BRGH = 1;
            }

            U2STA = UxSTA;
            break;

        case SER_PORT_3:
            U3MODE = UxMODE;
            U3MODEbits.PDSEL = data_par;

			if (mode & STOP2)
                U3MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U3BRG = StdBRG(baud);
            else
            {
                U3BRG = FastBRG(baud);
                U3MODEbits.BRGH = 1;
            }

            U3STA = UxSTA;
            break;

        case SER_PORT_4:
            U4MODE = UxMODE;
            U4MODEbits.PDSEL = data_par;

			if (mode & STOP2)
                U4MODEbits.STSEL = 1;

            if (baud <= 19200L)
                U4BRG = StdBRG(baud);
            else
            {
                U4BRG = FastBRG(baud);
                U4MODEbits.BRGH = 1;
            }

            U4STA = UxSTA;
            break;

	    default:
			return(FALSE);
    }

	baudRate[port] = baud;
	return(TRUE);
}


/************************************************************************/
/* Function    : serGetConfig                                           */
/* Purpose     : Return parity and stop bits for serial port            */
/* Inputs      : Ser port number, ptrs to parity and stop bits          */
/* Outputs     : None                                                   */
/************************************************************************/
void serGetConfig(int port, unsigned long *baud, int *data_par, int *stop)
{
    switch ( port )
    {
        case SER_PORT_0:
            *stop = U5MODEbits.STSEL + 1;
            *data_par = U5MODEbits.PDSEL;
            break;

        case SER_PORT_1:
            *stop = U1MODEbits.STSEL + 1;
            *data_par = U1MODEbits.PDSEL;
            break;

        case SER_PORT_2:
            *stop = U2MODEbits.STSEL + 1;
            *data_par = U2MODEbits.PDSEL;
            break;

        case SER_PORT_3:
            *stop = U3MODEbits.STSEL + 1;
            *data_par = U3MODEbits.PDSEL;
            break;

        case SER_PORT_4:
            *stop = U4MODEbits.STSEL + 1;
            *data_par = U4MODEbits.PDSEL;
            break;

        default:
            return;
    }

    *baud = baudRate[port];
}


/************************************************************************/
/* Function	   : ser_sndsts												*/
/* Purpose	   : Get number characters left to send on serial port		*/
/* Inputs	   : Physical serial port number							*/
/* Output	   : Number characters left to send							*/
/* Comments	   : If ring buffer is empty, checks UART to see if it's	*/
/*				 still sending a character.	 This is to ensure that the */
/*				 last byte is sent before this	function returns a 0.	*/
/************************************************************************/
int ser_sndsts(Port_t port)
{
	int		avail;

	if (port >= PIC_UARTS)
		return(0);

	if ((avail = bufAvailable(txBuff[port])) > 0)
		return(avail);

    switch(port)
    {
	  case SER_PORT_0:
		  return(U5STAbits.TRMT ? 0 : 1);

	  case SER_PORT_1:
		  return(U1STAbits.TRMT ? 0 : 1);

	  case SER_PORT_2:
		  return(U2STAbits.TRMT ? 0 : 1);

	  case SER_PORT_3:
		  return(U3STAbits.TRMT ? 0 : 1);

	  case SER_PORT_4:
		  return(U4STAbits.TRMT ? 0 : 1);
	}

	return(0);

} /* ser_sndsts() */


/************************************************************************/
/* Function	   : ser_read												*/
/* Purpose	   : Read bytes from serial port with timeout				*/
/* Inputs	   : Phys serial port, buffer, num bytes to read, timeout	*/
/* Output	   : Number of bytes read									*/
/*				 Operates a byte at a time because PicoDOS TURxGetBlock */
/*				 doesn't work with a timeout of 0, and is inefficient	*/
/*				 with a timeout of 1 ms									*/
/************************************************************************/
int ser_read(Port_t port, char *buffer, Int32 nbytes, TickType_t tmout)
{
	Reg char	*p;						/* Buffer ptr			*/
	Int16		c;						/* Serial character		*/
	Int32		bytesRcvd;				/* Num bytes received	*/
	TickType_t	endTick;				/* When to stop			*/

	p = buffer;
	endTick = xTaskGetTickCount() + tmout;

	for (bytesRcvd = 0; bytesRcvd < nbytes; )
	{
		if ((c = ser_getc(port)) != ERROR)
		{
			*p++ = c;
			bytesRcvd++;
		}
		else if ((Int32)(xTaskGetTickCount() - endTick) < 0)
			vTaskDelay(SER_POLL);
		else
			return(bytesRcvd);
	}

	return(bytesRcvd);

} /* ser_read() */


/************************************************************************/
/* Function	   : ser_write												*/
/* Purpose	   : Write bytes to serial port with timeout				*/
/* Inputs	   : Phys serial port, buffer, num bytes to write, timeout	*/
/* Output	   : Number of bytes sent									*/
/************************************************************************/
int		ser_write(Port_t port, const char *buffer, Int32 nbytes, TickType_t tmout)
{
	const char	*bufp;					/* Buffer ptr			*/
	TickType_t	endTick;				/* When to stop			*/
	Int32		bytesSent;				/* Number bytes sent	*/

	bufp = buffer;
	endTick = xTaskGetTickCount() + tmout;

	for (bytesSent = 0; bytesSent < nbytes; )
	{
		if (ser_putc(port, *bufp))
		{
			bufp++;
			bytesSent++;
		}
		else if ((Int32)(xTaskGetTickCount() - endTick) < 0)
			vTaskDelay(SER_POLL);
		else
			return(bytesSent);
	}

	return(bytesSent);

} /* ser_write() */


/************************************************************************/
/* Function    : serRxCount                                             */
/* Purpose     : Return number of bytes available on serial port        */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
int  serRxCount(Port_t port)
{
    return((port < PIC_UARTS) ? bufAvailable(rxBuff[port]) : 0);
}

/************************************************************************/
/* Function    : serRxFlush                                             */
/* Purpose     : Flush serial input FIFO                                */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serRxFlush(Port_t port)
{
    unsigned int reg;

    if (port < PIC_UARTS)
    {
        __builtin_disable_interrupts();

        bufClear(&rxBuff[port]);

        switch ( port )
        {
		  case 0: while ( U5STAbits.URXDA ) reg = U5RXREG; break;
          case 1: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
          case 2: while ( U2STAbits.URXDA ) reg = U2RXREG; break;
          case 3: while ( U3STAbits.URXDA ) reg = U3RXREG; break;
          case 4: while ( U4STAbits.URXDA ) reg = U4RXREG; break;
        }

        __builtin_enable_interrupts();
    }
}


/************************************************************************/
/* Function    : serTxFlush                                             */
/* Purpose     : Flush serial output FIFO                               */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serTxFlush(Port_t port)
{
    if (port < PIC_UARTS)
    {
        __builtin_disable_interrupts();

        bufClear(&txBuff[port]);

        switch ( port )
        {
          case 0: U5STAbits.UTXEN = 0; Nop();
                  U5STAbits.UTXEN = 1; Nop();
                  break;
          case 1: U1STAbits.UTXEN = 0; Nop(); 
                  U1STAbits.UTXEN = 1; Nop(); 
                  break;
          case 2: U2STAbits.UTXEN = 0; Nop();
                  U2STAbits.UTXEN = 1; Nop();
                  break;
          case 3: U3STAbits.UTXEN = 0; Nop();
                  U3STAbits.UTXEN = 1; Nop();
                  break;
          case 4: U4STAbits.UTXEN = 0; Nop();
                  U4STAbits.UTXEN = 1; Nop();
                  break;
        }

        __builtin_enable_interrupts();
    }
}


/************************************************************************/
/* Function    : serWaitTxComplete                                      */
/* Purpose     : Wait for Tx to complete                                */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serWaitTxComplete(int port)
{
	int		i;

    if (port < PIC_UARTS)
    {
		for (i = 0; i < 100; i++)
        {
            if (bufIsEmpty(txBuff[port]))
                switch ( port )
                {
                  case 0: if (U5STAbits.TRMT) return; break;
                  case 1: if (U1STAbits.TRMT) return; break;
                  case 2: if (U2STAbits.TRMT) return; break;
                  case 3: if (U3STAbits.TRMT) return; break;
                  case 4: if (U4STAbits.TRMT) return; break;
                }

			vTaskDelay(SER_POLL);
        }
    }
}


/************************************************************************/
/* Function	   : serGetRxOvflCnt										*/
/* Purpose	   : Get overflow count for a TPU serial port				*/
/* Inputs	   : Physical serial port number							*/
/* Output	   : None													*/
/************************************************************************/
Nat32	serGetRxOvflCnt(Port_t port)
{
	if (port < PIC_UARTS)
		return(rxOverFlowCnt[port]);
	else
		return(0);
	
} /* serGetRxOvflCnt() */


/************************************************************************/
/* Function	   : ser_break												*/
/* Purpose	   : Send Break signal to a serial port						*/
/* Inputs	   : Physical serial port number, length of break in ticks	*/
/* Output	   : None													*/
/************************************************************************/
void	ser_break(Port_t port, TickType_t count)
{
	switch(port)
	{
	  case 0:
		  U5MODEbits.ON = 0;
		  vTaskDelay(count);
		  U5MODEbits.ON = 1;
		  break;

	  case 1:
		  U1MODEbits.ON = 0;
		  vTaskDelay(count);
		  U1MODEbits.ON = 1;
		  break;

	  case 2:
		  U2MODEbits.ON = 0;
		  vTaskDelay(count);
		  U2MODEbits.ON = 1;
		  break;

	  case 3:
		  U3MODEbits.ON = 0;
		  vTaskDelay(count);
		  U3MODEbits.ON = 1;
		  break;

	  case 4:
		  U4MODEbits.ON = 0;
		  vTaskDelay(count);
		  U4MODEbits.ON = 1;
		  break;
	}

} /* ser_break() */



/****************************************************************************/
/*			Routines to enable wakeup from sleep on Console Rx				*/
/****************************************************************************/

void setupConsoleWakeFromSleep(void)
{
	U5MODESET = _U5MODE_WAKE_MASK;
    IEC2CLR = _IEC2_U5RXIE_MASK;
}

void clearConsoleWakeFromSleep(void)
{
	U5MODECLR = _U5MODE_WAKE_MASK;
    IEC2SET = _IEC2_U5RXIE_MASK;
}


/****************************************************************************/
/*			Routines to get/set xon status, for testing						*/
/****************************************************************************/

/* Bit 0 = enabled, Bit 1 = status */
int serGetXonStatus(Port_t port, time_t *xtime)
{
	if (port >= PIC_UARTS)
		return(0);

	if (xtime)
		*xtime = xoff_time[port];

	return((do_xon[port] ? 1 : 0) | (xoff_status[port] ? 2 : 0));
}

void serSetXonStatus(Port_t port, MBool onoff)
{
	if (port < PIC_UARTS)
		do_xon[port] = (onoff ? TRUE : FALSE);
}



/****************************************************************************/
/*                       Interrupt Service Routines                         */
/* note: the standard buffer functions are not used here as calling external*/
/* functions from an interrupt service routine increases interrupt overhead */
/* However, buffer macros ARE used											*/
/****************************************************************************/

/************************************************************************/
/* Function    : _U5IntHandler                                          */
/* Purpose     : Interrupt handler for UART 5 (Ser Port 0, console)     */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_5_VECTOR)) ) _U5IntHandler(void)
{
    Byte       rcvByte;

    if (IFS2bits.U5RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U5STAbits.URXDA )
        {
			rcvByte = (Byte)U5RXREG;

			if (do_xon[0] && (rcvByte == XON))
			{
				xoff_status[0] = FALSE;
				IEC2SET = _IEC2_U5TXIE_MASK;
			}
			else if (do_xon[0] && (rcvByte == XOFF))
			{
				xoff_status[0] = TRUE;
				xoff_time[0] = clkTime();			// Remember when
				IEC2CLR = _IEC2_U5TXIE_MASK;
			}

            /* if the buffer is full, mark overflow error   */
            else if ( bufIsFull(rxBuff[SER_PORT_0]) )
            {
                rxOverFlowCnt[SER_PORT_0]++;		/* inc overflow count and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_0].inptr++ = rcvByte;
                rxBuff[SER_PORT_0].occupied++;

                if (rxBuff[SER_PORT_0].inptr >= rxBuff[SER_PORT_0].endptr)
                    rxBuff[SER_PORT_0].inptr = rxBuff[SER_PORT_0].startptr;
            }
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U5RXIF_MASK;
    }

    if (IFS2bits.U5TXIF)
    {
        while (IEC2bits.U5TXIE && (U5STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_0].occupied)
            {
                U5TXREG = *txBuff[SER_PORT_0].outptr++;
                txBuff[SER_PORT_0].occupied--;
                txCount[SER_PORT_0]++;

                if (txBuff[SER_PORT_0].outptr >= txBuff[SER_PORT_0].endptr)
                    txBuff[SER_PORT_0].outptr = txBuff[SER_PORT_0].startptr;
            }
            else
                IEC2CLR = _IEC2_U5TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U5TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U1IntHandler                                          */
/* Purpose     : Interrupt handler for UART 1 (Ser Port 1)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_1_VECTOR)) ) _U1IntHandler(void)
{
    Byte       rcvByte;

    if (IFS1bits.U1RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U1STAbits.URXDA )
        {
			rcvByte = (Byte)U1RXREG;

			if (do_xon[1] && (rcvByte == XON))
			{
				xoff_status[1] = FALSE;
				IEC1SET = _IEC1_U1TXIE_MASK;
			}
			else if (do_xon[1] && (rcvByte == XOFF))
			{
				xoff_status[1] = TRUE;
				xoff_time[1] = clkTime();			// Remember when
				IEC1CLR = _IEC1_U1TXIE_MASK;
			}

            /* if the buffer is full, mark overflow error   */
            else if ( bufIsFull(rxBuff[SER_PORT_1]) )
            {
                rxOverFlowCnt[SER_PORT_1]++;		/* inc overflow count and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_1].inptr++ = rcvByte;
                rxBuff[SER_PORT_1].occupied++;

                if (rxBuff[SER_PORT_1].inptr >= rxBuff[SER_PORT_1].endptr)
                    rxBuff[SER_PORT_1].inptr = rxBuff[SER_PORT_1].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U1RXIF_MASK;
    }

    if (IFS1bits.U1TXIF)
    {
        while (IEC1bits.U1TXIE && (U1STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_1].occupied)
            {
                U1TXREG = *txBuff[SER_PORT_1].outptr++;
                txBuff[SER_PORT_1].occupied--;
                txCount[SER_PORT_1]++;

                if (txBuff[SER_PORT_1].outptr >= txBuff[SER_PORT_1].endptr)
                    txBuff[SER_PORT_1].outptr = txBuff[SER_PORT_1].startptr;
            }
            else
                IEC1CLR = _IEC1_U1TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U1TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U2IntHandler                                          */
/* Purpose     : Interrupt handler for UART 2 (Ser Port 2)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_2_VECTOR)) ) _U2IntHandler(void)
{
	Byte		rcvByte;

    if (IFS1bits.U2RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U2STAbits.URXDA )
        {
			rcvByte = (Byte)U2RXREG;

			if (do_xon[2] && (rcvByte == XON))
			{
				xoff_status[2] = FALSE;
				IEC1SET = _IEC1_U2TXIE_MASK;
			}
			else if (do_xon[2] && (rcvByte == XOFF))
			{
				xoff_status[2] = TRUE;
				xoff_time[2] = clkTime();			// Remember when
				IEC1CLR = _IEC1_U2TXIE_MASK;
			}

            /* if the buffer is full, mark overflow error   */
            else if ( bufIsFull(rxBuff[SER_PORT_2]) )
            {
                rxOverFlowCnt[SER_PORT_2]++;		/* inc overflow count and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_2].inptr++ = rcvByte;
                rxBuff[SER_PORT_2].occupied++;

                if (rxBuff[SER_PORT_2].inptr >= rxBuff[SER_PORT_2].endptr)
                    rxBuff[SER_PORT_2].inptr = rxBuff[SER_PORT_2].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U2RXIF_MASK;
    }

    if (IFS1bits.U2TXIF)
    {
        while (IEC1bits.U2TXIE && (U2STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_2].occupied)
            {
                U2TXREG = *txBuff[SER_PORT_2].outptr++;
                txBuff[SER_PORT_2].occupied--;
                txCount[SER_PORT_2]++;

                if (txBuff[SER_PORT_2].outptr >= txBuff[SER_PORT_2].endptr)
                    txBuff[SER_PORT_2].outptr = txBuff[SER_PORT_2].startptr;
            }
            else
                IEC1CLR = _IEC1_U2TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U2TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U3IntHandler                                          */
/* Purpose     : Interrupt handler for UART 3 (Ser Port 3)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_3_VECTOR)) ) _U3IntHandler(void)
{
    Byte       rcvByte;

    if (IFS1bits.U3RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U3STAbits.URXDA )
        {
			rcvByte = (Byte)U3RXREG;

			if (do_xon[3] && (rcvByte == XON))
			{
				xoff_status[3] = FALSE;
				IEC2SET = _IEC2_U3TXIE_MASK;
			}
			else if (do_xon[3] && (rcvByte == XOFF))
			{
				xoff_status[3] = TRUE;
				xoff_time[3] = clkTime();			// Remember when
				IEC2CLR = _IEC2_U3TXIE_MASK;
			}

            /* if the buffer is full, mark overflow error   */
            else if ( bufIsFull(rxBuff[SER_PORT_3]) )
            {
                rxOverFlowCnt[SER_PORT_3]++;		/* inc overflow count and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_3].inptr++ = rcvByte;
                rxBuff[SER_PORT_3].occupied++;

                if (rxBuff[SER_PORT_3].inptr >= rxBuff[SER_PORT_3].endptr)
                    rxBuff[SER_PORT_3].inptr = rxBuff[SER_PORT_3].startptr;
            }
        }

        /* clear the interrupt      */
        IFS1CLR = _IFS1_U3RXIF_MASK;
    }

    if (IFS2bits.U3TXIF)
    {
        while (IEC2bits.U3TXIE && (U3STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_3].occupied)
            {
                U3TXREG = *txBuff[SER_PORT_3].outptr++;
                txBuff[SER_PORT_3].occupied--;
                txCount[SER_PORT_3]++;

                if (txBuff[SER_PORT_3].outptr >= txBuff[SER_PORT_3].endptr)
                    txBuff[SER_PORT_3].outptr = txBuff[SER_PORT_3].startptr;
            }
            else
                IEC2CLR = _IEC2_U3TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U3TXIF_MASK;
    }
}

/************************************************************************/
/* Function    : _U4IntHandler                                          */
/* Purpose     : Interrupt handler for UART 4 (Ser Port 4)              */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
void __attribute__( (interrupt(IPL6SOFT), vector(_UART_4_VECTOR)) ) _U4IntHandler(void)
{
    Byte       rcvByte;

    if (IFS2bits.U4RXIF)
    {
        /* grab all the bytes from the buffer */
        while ( U4STAbits.URXDA )
        {
			rcvByte = (Byte)U4RXREG;

			if (do_xon[4] && (rcvByte == XON))
			{
				xoff_status[4] = FALSE;
				IEC2SET = _IEC2_U3TXIE_MASK;
			}
			else if (do_xon[4] && (rcvByte == XOFF))
			{
				xoff_status[4] = TRUE;
				xoff_time[4] = clkTime();			// Remember when
				IEC2CLR = _IEC2_U4TXIE_MASK;
			}

            /* if the buffer is full, mark overflow error   */
            else if ( bufIsFull(rxBuff[SER_PORT_4]) )
            {
                rxOverFlowCnt[SER_PORT_4]++;		/* inc overflow count and bail */
            }
            else   /* if there is room in the buffer put the byte in it */
            {
                *rxBuff[SER_PORT_4].inptr++ = rcvByte;
                rxBuff[SER_PORT_4].occupied++;

                if (rxBuff[SER_PORT_4].inptr >= rxBuff[SER_PORT_4].endptr)
                    rxBuff[SER_PORT_4].inptr = rxBuff[SER_PORT_4].startptr;
            }
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U4RXIF_MASK;
    }

    if (IFS2bits.U4TXIF)
    {
        while (IEC2bits.U4TXIE && (U4STAbits.UTXBF == 0))
        {
            if (txBuff[SER_PORT_4].occupied)
            {
                U4TXREG = *txBuff[SER_PORT_4].outptr++;
                txBuff[SER_PORT_4].occupied--;
                txCount[SER_PORT_4]++;

                if (txBuff[SER_PORT_4].outptr >= txBuff[SER_PORT_4].endptr)
                    txBuff[SER_PORT_4].outptr = txBuff[SER_PORT_4].startptr;
            }
            else
                IEC2CLR = _IEC2_U4TXIE_MASK;
        }

        /* clear the interrupt      */
        IFS2CLR = _IFS2_U4TXIF_MASK;
    }
}

#ifdef DEBUG_SERIAL_CMD

/************************************************************************/
/* Function	   : serDump												*/
/* Purpose	   : Show status of serial channel							*/
/* Inputs	   : Parm Mask, Channel number								*/
/* Outputs	   : OK														*/
/************************************************************************/
int serDump(Nat32 pmask, Parm_t serchan)
{

  if ((pmask == 0) || (chan >= PIC_UARTS))
	  xprintf("Bad channel number\n");
  else
  {
	  xprintf("Status of Serial Channel %d\n\n", serchan);

	  xprintf("Xon/Xoff %sABLED, current status %s\n",
			  do_xon[chan] ? "EN" : "DIS",
			  xoff_status[chan] ? "XOFF" : "XON");

	  xprintf("Tx buf: %#x  size %d	occupied %d	 inptr %d  outptr %d\n",
			  txBuff[port].startptr, txBuff[port].size, bufAvailable(txBuff[port]),
			  txBuff[port].inptr, txBuff[port].outptr);

	  xprintf("Rx buf: %#x  size %d	occupied %d	 inptr %d  outptr %d\n",
			  rxBuff[port].startptr, rxBuff[port].size, bufAvailable(rxBuff[port]),
			  rxBuff[port].inptr, rxBuff[port].outptr);
  }

  return(OK);

} /* serDump() */

#endif
