/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/

/* ----------------------- Static variables ---------------------------------*/
static UCHAR ucCriticalNesting = 0x00;
static unsigned int rxByte;
/* ----------------------- Start implementation -----------------------------*/

/* bus port API */
void busXcvrOn()
{
}

void busXcvrOff()
{
}

void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    ENTER_CRITICAL_SECTION(  );
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.*/
    /* For RS-232, RX interrupt is always enabled */
    if ( xRxEnable )
        _U4RXIE = 1;
    else
        _U4RXIE = 0;

    if ( xTxEnable )
    {
        /* enable the interrupt */
        _U4TXIE = 1;
        /* set the the interrupt flag to kick start the xmit FSM */
        _U4TXIF = 1;
    }
    else
        _U4TXIE = 0;

    EXIT_CRITICAL_SECTION(  );
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    BOOL status = TRUE;

/*********************************************/
/*           Baud Rate Calculation           */
/*********************************************/
/*                                           */
/* BRGH = 0                                  */
/*                                           */
/*   UxBRG = ( Fcy / ( 16 * baud )) - 1      */
/*                                           */
/* BRGH = 1                                  */
/*                                           */
/*   UxBRG = ( Fcy / ( 4 * baud )) - 1       */
/*                                           */
/*   Current Settings                        */
/*   Fosc = 3.6864 MHz                       */
/*   Fcy = Fosc / 2                          */
/*   BRGH = 1                                */
/*                                           */
/*    ___________________________            */
/*   |  Baud          |  UxBRG  |            */
/*   ----------------------------            */
/*   |  19200        |   23     |            */
/*   ----------------------------            */
/*   |  38400        |   11     |            */
/*   ----------------------------            */
/*   |  57600        |   7      |            */
/*   ----------------------------            */
/*   |  115200       |   3      |            */
/*   ---------------------------             */
/*                                           */
/*********************************************/

    /* set baud to rate */
    U4MODEbits.BRGH = 1;
    
    /* set the baud rate */
    switch ( ulBaudRate )
    {
        case  9600: U4BRG = 47; break;
        case 19200: U4BRG = 23; break;
        case 38400: U4BRG = 11; break;
        case 57600: U4BRG = 7;  break;
        case 115200: U4BRG = 3; break;
        /* if the baud rate is not supported set it 
        to 19200, but return FALSE */
        default:    U4BRG = 23;
                    status = FALSE;
    }

    /* No parity, one stop bit, no autobaud, polled */
    U4MODEbits.UARTEN = 1;
    U4MODEbits.ABAUD = 0;

    /* no parity */
    U4MODEbits.PDSEL0 = 0;
    U4MODEbits.PDSEL1 = 0;

    /* 1 stop bits */
    U4MODEbits.STSEL = 0;
    
    /* UTXINV: Transmit Polarity Inversion, UTXEN: Transmit Enable */
    U4STA = 0x0400;

    /* set tx int mode to interrupt when the shift register is empty */
    U4STAbits.UTXISEL1 = 0;
    U4STAbits.UTXISEL0 = 1;

    /* Configure uart1 receive and transmit interrupt */

    /* clear any pending interrupts just in case */
    _U4RXIF = 0;
    _U4TXIF = 0;

    /* set the interrupt priority for receive and transmit */
    /* NOTE - these priorities must be < 4, to make critical region code work!!!*/
    _U4RXIP = 3;
    _U4TXIP = 2;

    /* make sure interrupts are disabled */
    _U4RXIE = 1;
    _U4TXIE = 0;
    
    return status;
}

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    U4TXREG = ucByte;
    return TRUE;
}

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    /* grab the last byte received */
    *pucByte = (CHAR)rxByte;
    return TRUE;
}

/* This is UART4 transmit ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U4TXInterrupt(void)
{
    _U4TXIF = 0;
    
    pxMBFrameCBTransmitterEmpty();
}

/* This is UART4 receive ISR */
void __attribute__ ((interrupt,no_auto_psv)) _U4RXInterrupt(void)
{
    unsigned int perr_flag = 0;
    unsigned int ferr_flag = 0;
    
    _U4RXIF = 0;

    /* if you get an overrun error clear it and bail out */
    if ( U4STAbits.OERR )
    {
        /* clear the overrun */
        U4STAbits.OERR = 0;
        
        /* empty the FIFO */
        while ( U4STAbits.URXDA )
            rxByte = U4RXREG;

        return;
    }
    
    /* read bytes out while data is available */
    while ( U4STAbits.URXDA )
    {
        /* set local parity error flag */
        if ( U4STAbits.PERR )
            perr_flag = 1;

        /* set local framing error flag */
        if ( U4STAbits.FERR )
            ferr_flag = 1;
        
        rxByte = U4RXREG;

        /* notify the RX FSM that a byte has been received 
        if there are no framing errors*/
        if ( ferr_flag == 0 )
            pxMBFrameCBByteReceived();
    }
}

void EnterCriticalSection( void )
{
    /* disable ints < 4, which are all the Modbus stack interrupts */
    SRbits.IPL = 4;

    /* increment critical section nesting count */
    ++ucCriticalNesting;
}

void ExitCriticalSection( void )
{
    /* if you've nested all the way out  of critical sections, enable ints*/
    if( --ucCriticalNesting == 0 )
	SRbits.IPL = 0;
}

