/****************************************************************************/
/* Copyright 2009 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
#include "typedefs.h"
#include "serial.h"

#include "buffer.h"
#include "time_keeping.h"
#include "periph.h"
#include "config_vars.h"

#include <xc.h>
#include <uart.h>

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/****************************************************************************/
/*                            Serial Buffer Data                            */
/****************************************************************************/
#define TOTAL_UARTS     4
/* Note: buffer size must be power of 2 for buffer handling code to work */
#define RX_BUFF_SIZE    256
#define TX_BUFF_SIZE    128

static ByteBuffer rxBuff[TOTAL_UARTS]; //typedefed as volatile
static volatile unsigned char rxBuffData[TOTAL_UARTS][RX_BUFF_SIZE];
static volatile int rxOverFlowFlag[TOTAL_UARTS];

static ByteBuffer txBuff[TOTAL_UARTS]; //typedefed as volatile
static volatile unsigned char txBuffData[TOTAL_UARTS][TX_BUFF_SIZE];
static volatile int txOverFlowFlag[TOTAL_UARTS];

/* Baud rate generation */
/*  with UART_BRGH_FOUR - must be used for rates > 9600 */
#define BRG4_VAL(X)    ((FCY/(X))/4)-1
/*  with UART_BRGH_SIXTEEN  */
#define BRG16_VAL(X)    ((FCY/(X))/16)-1

/*  Public data         */
char Msg_Buff[MSG_BUFF_SZ];

/**********************************************************
 * serInit: setup software buffers,
 *              enable Rx ISRs, and open ports
 ***********************************************************/
void serInit() {
    int i;

    /* initialize software buffers*/
    for (i = 0; i < TOTAL_UARTS; ++i) {
        /* init buffers */
        rxBuff[i].data = rxBuffData[i];
        rxBuff[i].head = 0;
        rxBuff[i].tail = 0;
        rxBuff[i].ptr_mask = (RX_BUFF_SIZE - 1);
        txBuff[i].data = txBuffData[i];
        txBuff[i].head = 0;
        txBuff[i].tail = 0;
        txBuff[i].ptr_mask = (TX_BUFF_SIZE - 1);

        /* init flags */
        rxOverFlowFlag[i] = FALSE;
        txOverFlowFlag[i] = FALSE;
    }

    /*UART1 - HOST SERIAL*/
    U1RX_Clear_Intr_Status_Bit;
    U1TX_Clear_Intr_Status_Bit;
    ConfigIntUART1(UART_RX_INT_EN | UART_RX_INT_PR4 |
            UART_TX_INT_DIS | UART_TX_INT_PR2);
    OpenUART1(UART_EN | UART_IDLE_STOP | UART_BRGH_FOUR,
            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(38400));

    /*UART2 -  LICOR SERIAL*/
    U2RX_Clear_Intr_Status_Bit;
    U2TX_Clear_Intr_Status_Bit;
    ConfigIntUART2(UART_RX_INT_EN | UART_RX_INT_PR4 |
            UART_TX_INT_DIS | UART_TX_INT_PR2);
    OpenUART2(UART_EN | UART_IDLE_STOP | UART_BRGH_FOUR,
            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));

    /*UART3 - SPARE SERIAL*/
    U3RX_Clear_Intr_Status_Bit;
    U3TX_Clear_Intr_Status_Bit;
    ConfigIntUART3(UART_RX_INT_EN | UART_RX_INT_PR4 |
            UART_TX_INT_DIS | UART_TX_INT_PR2);
    OpenUART3(UART_EN | UART_IDLE_STOP | UART_BRGH_FOUR,
            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));

    /*UART4 - CONSOLE SERIAL*/
    U4RX_Clear_Intr_Status_Bit;
    U4TX_Clear_Intr_Status_Bit;
    ConfigIntUART4(UART_RX_INT_EN | UART_RX_INT_PR4 |
            UART_TX_INT_DIS | UART_TX_INT_PR2);
    OpenUART4(UART_EN | UART_IDLE_CON | UART_BRGH_FOUR,
            UART_INT_TX_BUF_EMPTY | UART_TX_ENABLE, BRG4_VAL(9600));

}

/*************************************************************
 * Change the baudrate of a port
 * No protection,so it might gargble any ongoing transmitions
 * input: port#, new baudrate
 * output: errno
 ***************************************************************/
int serSetBaud(int port, UINT32 baud) {
    UINT16 brgVal;


    if (0 == baud) return (ERR_FAILURE);
    brgVal = BRG4_VAL(baud);

    switch (port) {
        case SER_HOST:
            U1BRG = brgVal;
            break;
        case SER_LICOR:
            U2BRG = brgVal;
            break;
        case SER_SPARE:
            U3BRG = brgVal;
            break;
        case SER_CONS:
            U4BRG = brgVal;
            break;
        default:
            return (ERR_FAILURE);
    }
    return (ERR_SUCCESS);
}


/****************************************************************************/
/*                       READ FUNCTIONS                                     */
/****************************************************************************/

/***************************************************************
 * get the next byte from port's Rx buffer
 * input:   port#, pointer to char to fill
 * ouput:   True if char is read, else
 *          false and char is set to 0
 ***************************************************************/
int serGetByte(int port, unsigned char* b) {
    int stat;
    /* make sure the port is valid */
    if (0 > port || 3 < port) {
        *b = 0;
        return FALSE;
    }
    /* try to get a byte from the buffer */
    enterCriticalSection(); //stop more bytes from comming in
    stat = bufGet(b, &rxBuff[port]);
    exitCriticalSection();

    return stat;
}

/***************************************************************
 * reurn the next byte from port's Rx buffer, leaves it there
 * input:   port#
 * ouput:   next byte in buffer or 0
 ***************************************************************/
unsigned char serPeek(int port) {
    if (0 > port || 3 < port) {
        return (0);
    }
    if (serIsAvail(port))
        return (rxBuff[port].data[rxBuff[port].head]);
    else
        return (0);
}

/***************************************************************
 *  get bytes from a port for "period" milliseconds
 * or until buffer us full
 * returns the number of bytes read.
 ***************************************************************/
int serGetSTO(int port, char* buffer, int buflen, UINT32 period) {
    Timer tmr;
    int i = 0;
    BYTE in=0;

    tkTmrSet(&tmr, period);
    tkTmrStart(&tmr);

    while (tkTmrUpdate(&tmr)) {
        while (serIsAvail(port) && (buflen - 1 > i)) {
            serGetByte(port, &in);
            if (in == '\r' || in == '\n') {
                buffer[i] = '\0';
                return (i);
            } else {
                buffer[i++] = in;
            }
        }
    }
    buffer[i] = '\0'; //end the string
    return (i);
}

/***************************************************************
 * fill a buffer until the string in "term" is found or buffer is full
 * null terminates string before "term", if found
 * returns total number of bytes read
 ***************************************************************/
int serGetUntil(int port, const char* term, char* buffer, int buflen) {
    unsigned char inchar;
    int j = 0;
    int i = 0;

    while (serGetByte(port, &inchar) && buflen > i + 1) {
        if (inchar == term[j++]) {
            if ('\0' == term[j]) {
                buffer[i - j] = '\0';
                return (i);
            }
        } else {
            j = 0;
        }
        buffer[i++] = inchar;
    }
    buffer[i] = '\0';

    return (i);
}

/***************************************************************
 * Throws away input until "targ" or "term" string is found or
 * everything has been read from Rx buffer
 * returns true if "targ" is found or false if "term" is found
 ***************************************************************/
int serScanUntil(int port, char* targ, const char* term) {
    unsigned char inchar;
    int i = 0;
    int j = 0;

    while (serGetByte(port, &inchar)) {
        if (inchar == targ[i++]) {
            if ('\0' == targ[i]) {
                return (TRUE);
            }
        } else {
            i = 0;
        }

        if (inchar == term[j++]) {
            if ('\0' == term[j]) {
                return (FALSE);
            }
        } else {
            j = 0;
        }
    }
    return (FALSE);
}

/***************************************************************
 * Throws away input until "targ" string is found or timeout
 * returns true if "targ" is found or false otherwise
 ***************************************************************/
int serScanTO(int port, char *targ, UINT32 period) {
    Timer tmr;
    BYTE inC;
    int i = 0;

    tkTmrSet(&tmr, period);
    tkTmrStart(&tmr);

    while (tkTmrUpdate(&tmr)) {
        if (serGetByte(port, &inC)) {
            if (inC == targ[i++]) {
                if ('\0' == targ[i]) {
                    return (TRUE);
                }
            } else {
                i = 0;
            }
        }
    }
    return (FALSE);
}


/****************************************************************************/
/*                       WRITE FUNCTIONS                                    */
/****************************************************************************/

/***********************************************************
 * Put a byte in port's software TXbuffer and turn on that
 * TXinterrupt, returns FALSE if buffer is full or bad port#
 ***********************************************************/
int serPutChar(int port, char b) {
    /* make sure the port is valid */
    if (port < 0 || port > 3) {
        return (FALSE);
    }
    if (!bufPut((BYTE) b, &txBuff[port])) {
        return (FALSE);
    }
    switch (port) {
        case 0: EnableIntU1TX;
            break;
        case 1: EnableIntU2TX;
            break;
        case 2: EnableIntU3TX;
            break;
        case 3: EnableIntU4TX;
            break;
        default: return (FALSE);
    }
    return (TRUE);
}

/***************************************************************
 * send bytes to port's SW Tx buffer until null is found
 * if the SW Tx buffer fills up this will keep trying while
 * bytes are transmitted and space made available
 ***************************************************************/
int serPutString(int port, char* buff) {
    int i = 0;
    /* make sure the port is valid */
    if (port < 0 || port > 3) {
        return (0);
    }
    while (buff[i] != '\0') {
        if (serPutChar(port, buff[i]))
            ++i;
    }
    return i;
}
/****************************************************************
 * send string to console if "TERSE" is not 0
 * is blocking to make sure message gets out.
 ****************************************************************/
int serPutSDebug(char* buff){
    int cfgVal;

    if(ERR_FAILURE == cfgGet("TERSE", &cfgVal)){
        serPutString(SER_CONS, "debug fail");
        return(ERR_FAILURE);
    }
    if(0 == cfgVal){
        cfgVal = serPutString(SER_CONS, buff);
        serTxWait(SER_CONS);
        return(cfgVal);
    }
    return(0);
}
/***************************************************************
 * send one byte directly to port's Tx HW buffer bypassing SW buffer
 *  FAILS IF BUFFER IS FULL!
 ***************************************************************/
int serSetByte(int port, BYTE b) {

    switch (port) {
        case 0:
            if (!U1STAbits.UTXBF) { //not Tx Buffer Full
                U1TXREG = b; //fill Tx reg
                return (TRUE);
            }
            break;
        case 1:
            if (!U2STAbits.UTXBF) {
                U2TXREG = b;
                return (TRUE);
            }
            break;
        case 2:
            if (!U3STAbits.UTXBF) {
                U3TXREG = b;
                return (TRUE);
            }
            break;
        case 3:
            if (!U4STAbits.UTXBF) {
                U4TXREG = b;
                return (TRUE);
            }
            break;
        default:
            break;
    }
    return FALSE;
}


/****************************************************************************/
/*                       CONTROL FUNCTIONS                                  */
/****************************************************************************/

/***************************************************************
 * returns the number of bytes in port's Rx buffer
 ***************************************************************/
int serIsAvail(int port) {
    /* make sure the port is valid */
    if (0 > port || 3 < port) {
        return 0;
    }
    return (bufContains(&rxBuff[port]));
}

/***************************************************************
 * returns whether or nor port's Rx buffer is full
 ***************************************************************/
int serIsRxFull(int port) { //UNUSED
    int stat;
    /* make sure the port is valid */
    if (0 > port || 3 < port) {
        return FALSE;
    }
    /* get the buffer status */
    enterCriticalSection();
    stat = bufIsFull(&rxBuff[port]);
    exitCriticalSection();

    return stat;
}

/***************************************************************
 * returns whether or nor port's HW Tx buffer is full
 ***************************************************************/
int serIsTxFull(int port) { //UNUSED
    /* return the hardware FIFO status */
    switch (port) {
        case 0:
            return (U1STAbits.UTXBF);
        case 1:
            return (U2STAbits.UTXBF);
        case 2:
            return (U3STAbits.UTXBF);
        case 3:
            return (U4STAbits.UTXBF);
        default:
            return (TRUE);
    }
}

/***************************************************************
 * has port's overflow flag been set?
 ***************************************************************/
int serGetRxOverFlow(int port) { //UNUSED
    if (0 > port || 3 < port) {
        return FALSE;
    }
    return (rxOverFlowFlag[port]);
}

/***************************************************************
 * clear port's overflow flag
 ***************************************************************/
void serClrRxOverFlow(int port) {
    if (0 > port || 3 < port) {
        return;
    }
    rxOverFlowFlag[port] = FALSE;
}

/***************************************************************
 * reset the software buffer and clear the hardware regs
 ***************************************************************/
void serRxFlush(int port) {
    unsigned reg; //dummy to clear HW Rx buffer
    /* make sure the port is valid */
    if (0 > port || 3 < port) {
        return;
    }
    enterCriticalSection();
    /* clear out the software FIFO */
    bufClear(&rxBuff[port]);
    /* clear out the hardware FIFO */
    switch (port) {
        case 0: while (U1STAbits.URXDA) reg = U1RXREG;
            break;
        case 1: while (U2STAbits.URXDA) reg = U2RXREG;
            break;
        case 2: while (U3STAbits.URXDA) reg = U3RXREG;
            break;
        case 3: while (U4STAbits.URXDA) reg = U4RXREG;
            break;
        default: break;
    }

    exitCriticalSection();

    return;
}

/***************************************************************
 * clear out the SW and HW Tx buffers, does not send what's in them
 ***************************************************************/
void serTxFlush(int port) { //UNUSED
    if (0 > port || 3 < port) {
        return;
    }
    enterCriticalSection();
    bufClear(&txBuff[port]);
    /*UTXEN = Transmit Enable bit*/
    switch (port) {
        case 0:
            U1STAbits.UTXEN = 0;
            Nop();
            U1STAbits.UTXEN = 1;
            break;
        case 1:
            U2STAbits.UTXEN = 0;
            Nop();
            U2STAbits.UTXEN = 1;
            break;
        case 2:
            U3STAbits.UTXEN = 0;
            Nop();
            U3STAbits.UTXEN = 1;
            break;
        case 3:
            U4STAbits.UTXEN = 0;
            Nop();
            U4STAbits.UTXEN = 1;
            break;
        default:
            break;
    }
    exitCriticalSection();
    return;
}

/****************************************************************
 * wait untill all bytes in port's Tx buffers are written
 ****************************************************************/
void serTxWait(int port) { //UNUSED
    /*TRMT = Transmit Shift Register is Empty bit*/
    switch (port) {
        case 0:
            while (!U1STAbits.TRMT) Nop();
            break;
        case 1:
            while (!U2STAbits.TRMT) Nop();
            break;
        case 2:
            while (!U3STAbits.TRMT) Nop();
            break;
        case 3:
            while (!U4STAbits.TRMT) Nop();
            break;
        default:break;
    }
}


/****************************************************************************/
/*                       Interrupt Service Routines                         */
/****************************************************************************/
/* note: the functions in buffer.c are not used here
 * as calling an external function from and interrupt
 * service routine increases interrupt overhead
 */

/***************************************************************
 *              UART1 receive ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U1RXInterrupt(void) {
    while (U1STAbits.URXDA) { //while data available
        if (((rxBuff[0].head + 1) & rxBuff[0].ptr_mask) == rxBuff[0].tail) {
            rxOverFlowFlag[0] = U1RXREG; //read byte to move it out off HW buffer
            rxOverFlowFlag[0] = TRUE; //set overflow flag and bail
        } else { //there is space in SW buffer
            rxBuff[0].data[rxBuff[0].head] = (unsigned char) U1RXREG;
            rxBuff[0].head = (rxBuff[0].head + 1) & rxBuff[0].ptr_mask;
        }
    }
    U1RX_Clear_Intr_Status_Bit;
    return;
}

/***************************************************************
 *              UART2 receive ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt(void) {
    while (U2STAbits.URXDA) {
        if (((rxBuff[1].head + 1) & rxBuff[1].ptr_mask) == rxBuff[1].tail) {
            rxOverFlowFlag[1] = U2RXREG;
            rxOverFlowFlag[1] = TRUE;
        } else {
            rxBuff[1].data[rxBuff[1].head] = (unsigned char) U2RXREG;
            rxBuff[1].head = (rxBuff[1].head + 1) & rxBuff[1].ptr_mask;
        }
    }
    U2RX_Clear_Intr_Status_Bit;
    return;
}

/***************************************************************
 *              UART3 receive ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U3RXInterrupt(void) {
    while (U3STAbits.URXDA) {
        if (((rxBuff[2].head + 1) & rxBuff[2].ptr_mask) == rxBuff[2].tail) {
            rxOverFlowFlag[2] = U3RXREG;
            rxOverFlowFlag[2] = TRUE;
        } else {
            rxBuff[2].data[rxBuff[2].head] = (unsigned char) U3RXREG;
            rxBuff[2].head = (rxBuff[2].head + 1) & rxBuff[2].ptr_mask;
        }
    }
    U3RX_Clear_Intr_Status_Bit;
    return;
}

/***************************************************************
 *              UART4 receive ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U4RXInterrupt(void) {
    while (U4STAbits.URXDA) {
        if (((rxBuff[3].head + 1) & rxBuff[3].ptr_mask) == rxBuff[3].tail) {
            rxOverFlowFlag[3] = U4RXREG;
            rxOverFlowFlag[3] = TRUE;
        } else {
            rxBuff[3].data[rxBuff[3].head] = (unsigned char) U4RXREG;
            rxBuff[3].head = (rxBuff[3].head + 1) & rxBuff[3].ptr_mask;
        }
    }
    U4RX_Clear_Intr_Status_Bit;
    return;
}

/***************************************************************
 *              UART1 transmit ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U1TXInterrupt(void) {
    while (!U1STAbits.UTXBF) { //not HW buffer full
        if (txBuff[0].tail == txBuff[0].head) { //buffer is empty
            DisableIntU1TX; //turn off transmit, leave flag set for next time
            break;
        } else {
            U1TX_Clear_Intr_Status_Bit; //only clear flag if byte is written
            U1TXREG = txBuff[0].data[txBuff[0].tail]; //push byte
            txBuff[0].tail = (txBuff[0].tail + 1) & txBuff[0].ptr_mask; //inc. tail
        }
    }
}

/***************************************************************
 *              UART2 transmit ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U2TXInterrupt(void) {
    while (!U2STAbits.UTXBF) { //not HW buffer full
        if (txBuff[1].tail == txBuff[1].head) { //buffer is empty
            DisableIntU2TX; //turn off transmit
            break;
        } else {
            U2TX_Clear_Intr_Status_Bit;
            U2TXREG = txBuff[1].data[txBuff[1].tail]; //push byte
            txBuff[1].tail = (txBuff[1].tail + 1) & txBuff[1].ptr_mask; //inc. tail
        }
    }
}

/***************************************************************
 *              UART3 transmit ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U3TXInterrupt(void) {
    while (!U3STAbits.UTXBF) { //not HW buffer full
        if (txBuff[2].tail == txBuff[2].head) { //buffer is empty
            DisableIntU3TX; //turn off transmit
            break;
        } else {
            U3TX_Clear_Intr_Status_Bit;
            U3TXREG = txBuff[2].data[txBuff[2].tail]; //push byte
            txBuff[2].tail = (txBuff[2].tail + 1) & txBuff[2].ptr_mask; //inc. tail
        }
    }
}

/***************************************************************
 *          UART4 transmit ISR
 ***************************************************************/
void __attribute__((interrupt, no_auto_psv)) _U4TXInterrupt(void) {
    while (!U4STAbits.UTXBF) { //not HW buffer full
        if (txBuff[3].tail == txBuff[3].head) { //buffer is empty
            DisableIntU4TX; //turn off transmit
            break;
        } else {
            U4TX_Clear_Intr_Status_Bit;
            U4TXREG = txBuff[3].data[txBuff[3].tail]; //push byte
            txBuff[3].tail = (txBuff[3].tail + 1) & txBuff[3].ptr_mask; //inc. tail
        }
    }
}



