/****************************************************************************/
/* Copyright 2015-2018 MBARI                                                */
/****************************************************************************/
/* Summary  : Serial I/O for BEDS2 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								*/
/* 20feb2018 rah - Modified for BEDS2										*/
/****************************************************************************/

#include <xc.h>

#include <mbariTypes.h>					/* MBARI type definitions			*/
#include <time.h>						/* Std C time.h						*/
#include <beds.h>						/* BEDS2 controller definitions		*/

#include <serial.h>
#include <buffer.h>
#include <dig_io.h>
#include <clock.h>

#define SER_POLL	(TICKS_PER_SEC/50)	/* Serial polling rate in ticks		*/


/********************************/
/*		External Data			*/
/********************************/

Extern Int32	modemPort, modemBaud, debugBaud, usrTmout;
Extern MBool	localEcho;


/****************************************/
/*		Module Local Data				*/
/****************************************/

MLocal SerPort	serPortInUse = 1;

MLocal unsigned int iec1_RxBits[] = 
    {0, _IEC1_U1RXIE_MASK, _IEC1_U2RXIE_MASK, _IEC1_U3RXIE_MASK, 0};

MLocal unsigned int iec1_TxBits[] = 
    {0, _IEC1_U1TXIE_MASK, _IEC1_U2TXIE_MASK, 0, 0};


/************************************************************************/
/* Function    : serInit                                                */
/* Purpose     : Initialize serial I/O Module                           */
/* Inputs      : None                                                   */
/* Outputs     : None                                                   */
/************************************************************************/
Errno serInit(void)
{
    int			i;
	SerStruct	*ssp;

    /* 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                     */

	setSerialBaud(((modemPort == 1) ? 2 : 1), debugBaud);
	setSerialBaud(modemPort, modemBaud);	/* Sets up UxMODE and UxSTA */


/******************** 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 */
    IEC1CLR = _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 */
    IEC1CLR = _IEC1_U2RXIE_MASK;
    IEC1CLR = _IEC1_U2TXIE_MASK;
    
	return(OK);
}


/************************************************************************/
/* Function	   : getSerialStruct										*/
/* Purpose	   : Get ptr to SerStruct									*/
/* Input	   : Physical serial port number							*/
/* Output	   : Ptr to SerStruct, or NULL								*/
/************************************************************************/
SerStruct *getSerialStruct(SerPort serport)
{
	return(NULL);
	
} /* getSerialStruct() */


/************************************************************************/
/* Function	   : setConsole												*/
/* Purpose	   : Set the user I/F console								*/
/* Inputs	   : SerPort enumeration, boolean if on modem				*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno setConsole(SerPort port)
{
	if ((port==0) || (port > PIC_UARTS))	
		return(ERROR);

	serPortInUse = port;
	return(OK);
}


/************************************************************************/
/* Function	   : getConsole												*/
/* Purpose	   : Get the user I/F console								*/
/* Inputs	   : None													*/
/* Outputs	   : Serial port number										*/
/************************************************************************/
SerPort getConsole(void)
{
	return(serPortInUse);
}


/************************************************************************/
/* Function	   : setSerialBaud											*/
/* Purpose	   : Set Baud rate on one serial port						*/
/* Inputs	   : Serial port to init, baud rate							*/
/* Outputs	   : OK or ERROR											*/
/************************************************************************/
Errno setSerialBaud(SerPort port, Nat32 baud)
{
    switch(port)
    {
	    case SER_PORT_1:
			U1MODE = UxMODE;
			if (baud <= 19200L)
				U1BRG = StdBRG(baud);
			else
			{
				U1BRG = FastBRG(baud);
				U1MODEbits.BRGH = 1;
			}

			U1STA = UxSTA;
			break;

        case SER_PORT_2:
            U2MODE = UxMODE;

            if (baud <= 19200L)
                U2BRG = StdBRG(baud);
            else
            {
                U2BRG = FastBRG(baud);
                U2MODEbits.BRGH = 1;
            }

            U2STA = UxSTA;
            break;

	    default:
			return(ERROR);
    }

	return(OK);
}


/************************************************************************/
/* Function	   : ser_putc												*/
/* Purpose	   : Write one character to serial port						*/
/* Inputs	   : Physical port number, character						*/
/* Output	   : MBool, TRUE if character sent							*/
/************************************************************************/
MBool ser_putc(SerPort port, Byte b)
{
    MBool			rtn;

	switch(port)
	{
	  case SER_PORT_1:
		if (U1STAbits.UTXBF) return(FALSE);
		U1TXREG = b;
		return(TRUE);

	  case SER_PORT_2:
		if (U2STAbits.UTXBF) return(FALSE);
		U2TXREG = b;
		return(TRUE);
	}

	return(FALSE);
}


/************************************************************************/
/* Function	   : ser_getc												*/
/* Purpose	   : Read one character from serial port					*/
/* Inputs	   : Physical serial port number							*/
/* Output	   : Character, or ERROR if none							*/
/************************************************************************/
int ser_getc(SerPort port)
{
	switch(port)
	{
	  case SER_PORT_1:
		if (U1STAbits.URXDA)
			return(U1RXREG);
		return(ERROR);

	  case SER_PORT_2:
		if (U2STAbits.URXDA)
			return(U2RXREG);
		return(ERROR);
	}

	return(ERROR);
}

/************************************************************************/
/* Function	   : ser_getc_tmout											*/
/* Purpose	   : Read one character from serial port with timeout		*/
/* Inputs	   : Physical serial port number, timeout in ticks			*/
/* Output	   : Character, or ERROR if none							*/
/************************************************************************/
int ser_getc_tmout(SerPort port, Nat32 ticks)
{
	Nat32	stTick;
	Byte	c;

	if ((port==0) || (port > PIC_UARTS))	
		return(ERROR);

	if (ticks == 0)
		return(ser_getc(port));
	
	for (stTick = clkGetTick(); (clkGetTick() - stTick) < ticks; )
	{
		if ((c = ser_getc(port)) != ERROR)
			return(c);
	}

	return(ERROR);
}


/************************************************************************/
/* Function	   : _mon_putc												*/
/* Purpose	   : Write one character to serial port						*/
/* Inputs	   : Character												*/
/* Output	   : None													*/
/* Comment     : Used by stdio for printf(), putc(), etc				*/
/************************************************************************/
void _mon_putc(char c)
{
	while(!ser_putc(serPortInUse, c))
		clkDelayMs(SER_POLL);					/* Wait for space avail */

} /* _mon_putc() */    


/************************************************************************/
/* Function	   : _mon_getc												*/
/* Purpose	   : Read one character from serial port					*/
/* Inputs	   : canblock???  defined by std library					*/
/* Output	   : Character												*/
/* Comment     : Used by stdio for scanf(), getc(), etc					*/
/************************************************************************/
int _mon_getc(int canblock)
{
    int		c;

	while ((c = ser_getc(serPortInUse)) == ERROR)
		clkDelayMs(SER_POLL);					/* Wait for char		*/

	return(c);
}


/************************************************************************/
/* 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)
{
    switch(port)
    {
	  case SER_PORT_1:
		  return(U1STAbits.TRMT ? 0 : 1);

	  case SER_PORT_2:
		  return(U2STAbits.TRMT ? 0 : 1);
	}

	return(0);

} /* ser_sndsts() */


/************************************************************************/
/* Function    : serRxCount                                             */
/* Purpose     : Return number of bytes available on serial port        */
/* Inputs      : Serial port number                                     */
/* Outputs     : Bytes available										*/
/************************************************************************/
int  serRxCount(SerPort port)
{
    switch(port)
    {
	  case SER_PORT_1:
		  return(U1STAbits.URXDA ? 1 : 0);

	  case SER_PORT_2:
		  return(U2STAbits.URXDA ? 1 : 0);
	}

	return(0);
}


/************************************************************************/
/* Function    : kbhit													*/
/* Purpose     : Return number of bytes available on serial port in use	*/
/* Inputs      : None													*/
/* Outputs     : Non-zero if bytes available, zero if none				*/
/************************************************************************/
int kbhit(void)
{
	return(serRxCount(serPortInUse) != 0);
}


/************************************************************************/
/* Function    : serRxFlush                                             */
/* Purpose     : Flush serial input FIFO                                */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serRxFlush(SerPort port)
{
    unsigned int reg;

    if (port && (port < PIC_UARTS))
    {
        __builtin_disable_interrupts();

        switch ( port )
        {
          case 1: while ( U1STAbits.URXDA ) reg = U1RXREG; break;
          case 2: while ( U2STAbits.URXDA ) reg = U2RXREG; break;
        }

        __builtin_enable_interrupts();
    }
}


/************************************************************************/
/* Function    : serTxFlush                                             */
/* Purpose     : Flush serial output FIFO                               */
/* Inputs      : Serial port number                                     */
/* Outputs     : None                                                   */
/************************************************************************/
void serTxFlush(SerPort port)
{
    if (port && (port < PIC_UARTS))
    {
        __builtin_disable_interrupts();

        switch ( port )
        {
          case 1: U1STAbits.UTXEN = 0; Nop(); 
                  U1STAbits.UTXEN = 1; Nop(); 
                  break;
          case 2: U2STAbits.UTXEN = 0; Nop();
                  U2STAbits.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 && (port < PIC_UARTS))
    {
		for (i = 0; i < 100; i++)
        {
			switch ( port )
			{
			  case 1: if (U1STAbits.TRMT) return; break;
			  case 2: if (U2STAbits.TRMT) return; break;
			}

			clkDelayMs(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)

{
	return(0);
	
} /* serGetRxOvflCnt() */
