/*******************************************************************************
 *  emeter-communication.c -
 *
 *  Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ 
 *  ALL RIGHTS RESERVED
 * 
*/

#include <inttypes.h>
#include <stdlib.h>
#include <stdbool.h>
#if defined(__MSP430__)
#include <msp430.h>
#endif

#include "emeter-template.h"

#include <emeter-toolkit.h>
#include <emeter-metrology.h>

#include "homogenize.h"
#include "emeter-communication.h"
#if defined(IEC62056_SUPPORT)
#include "iec62056_46_user.h"
#endif
#if defined(DLT645_SUPPORT)
#include "emeter-dlt645.h"
#endif

#if defined(IEC62056_SUPPORT)
#define SERIAL_INACTIVITY_TIMEOUT_SUPPORT
#endif

/* We either start with a simple MAX_UART_PORT value, and map this to the
   individual enabled UARTs, or we start with the individual enabled UART
   defines, and work out the value of MAX_UART_PORT. */
#if defined(MAX_UART_PORT)
    #if MAX_UART_PORT >= 1
        #define UART_0_SUPPORT
    #endif
    #if MAX_UART_PORT >= 2
        #define UART_1_SUPPORT
    #endif
    #if MAX_UART_PORT >= 3
        #define UART_2_SUPPORT
    #endif
    #if MAX_UART_PORT >= 4
        #define UART_3_SUPPORT
    #endif
#endif

#if defined(__MSP430__)  &&  defined(IEC62056_21_SUPPORT)
uint8_t iec62056_21_address[] = "Node 1";
uint8_t iec62056_21_address_len = 6;
#endif

#if defined(UART_0_DMA_SUPPORT)
uint8_t dma_rx_buffer0[128];
#endif
#if defined(UART_1_DMA_SUPPORT)
uint8_t dma_rx_buffer1[128];
#endif

struct uart_port_s ports[MAX_UART_PORT];

#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
/* Timer management data */
struct
{
    uint32_t epoch;
    uint16_t last_timeout;
} timer_control;
#endif

#if defined(__MSP430_HAS_UART0__)  ||  defined(__MSP430_HAS_EUSCI_A0__)
/* Dither/modulation patterns to get close to an exact bit rate */
static const uint8_t mod_patterns[] =
{
    0x00, 0x01, 0x11, 0x51, 0x55, 0x5D, 0xDD, 0xDF
};
#endif

#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
int serial_configure_timeouts(int port, uint16_t inter_octet_limit, uint16_t inactivity_limit)
{
    ports[port].inactivity_limit = (32768UL*inactivity_limit)/1000UL;
    ports[port].inter_octet_limit = (32768UL*inter_octet_limit)/1000UL;
    return 0;
}
/*- End of function --------------------------------------------------------*/
#endif

int serial_configure(int port, int mode, uint32_t bit_rate)
{
    int32_t bitrate_divider;
    uint16_t ctl0;
    uint16_t ctl1;
#if !defined(__MSP430_HAS_EUSCI_A0__)
    /* If the device has EUSCI ports the bit rate control register can be accessed as
       a single 16 bit operation. The other serial modules need to be accessed with two
       8 bit operations. */
    uint8_t br0;
    uint8_t br1;
#endif
    uint8_t mctl;

#if !defined(COMMON_RX_TX_BUFFER_SUPPORT)
    ports[port].tx_msg.len = 0;
    ports[port].tx_msg.ptr = 0;
#endif
    ports[port].rx_msg.ptr = 0;
    ports[port].rx_frame_pending = false;
    ports[port].tx_in_progress = false;
#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
    ports[port].next_inactivity_timeout = 0;
    ports[port].next_octet_timeout = 0;
#endif

    /* Set some defaults */
#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
    serial_configure_timeouts(port, 1, 1);
#endif

    /* We only need two modes for the serial characters in the work we have done to date.
       Just support those for the time being */
    if (mode == 0)
    {
        /* 7-bit character, even parity */
#if defined(__MSP430_HAS_UART0__)
        ctl0 = PENA | PEV | SWRST;
#else
        ctl0 = UC7BIT | UCPEN;
#endif
    }
    else
    {
        /* 8-bit character, no parity */
#if defined(__MSP430_HAS_UART0__)
        ctl0 = CHAR | SWRST;
#else
        ctl0 = 0;
#endif
    }
    /* Use ACLK for slow bit rates. Use SMCLK for higher bit rates */
    if (bit_rate <= 4800L)
    {
        bitrate_divider = (32768L*16L)/bit_rate;
#if defined(__MSP430_HAS_UART0__)
        ctl1 = SSEL0;               /* ACLK */
#else
        ctl1 = UCSSEL_1 | UCSWRST;  /* ACLK */
#endif
    }
    else
    {
#if defined(__MSP430F6736__)  ||  defined(__MSP430F6749__)  ||  defined(__MSP430F67491__)  ||  defined(__MSP430F6779__)  ||  defined(__MSP430F67791__)
        /* 24MHz clock */
        bitrate_divider = (32768L*768L*16L)/bit_rate;
#elif defined(__MSP430F4794__)  ||  defined(__MSP430F47197__)
        /* 16MHz clock */
        bitrate_divider = (32768L*512L*16L)/bit_rate;
#elif defined(__MSP430i2040__)  ||  defined(__MSP430i2041__)  || defined(__MSP430i2021__) 
        /* 16MHz clock */
        bitrate_divider = (16384000*16L)/bit_rate;

#elif defined(__MSP430AFE251__)  ||  defined(__MSP430AFE252__)  ||  defined(__MSP430AFE253__)
        /* Exactly 8MHz clock */
        bitrate_divider = (8000000L*16L)/bit_rate;
#else
        /* 8MHz clock */
        bitrate_divider = (32768L*256L*16L)/bit_rate;
#endif
#if defined(__MSP430_HAS_UART0__)
        ctl1 = SSEL1;               /* SMCLK */
#else
        ctl1 = UCSSEL_2 | UCSWRST;  /* SMCLK */
#endif
    }
    bitrate_divider++;
#if defined(__MSP430_HAS_EUSCI_A0__)  ||  defined(__MSP430_HAS_UART0__)
    mctl = (bitrate_divider & 0x0E) >> 1;
    mctl = mod_patterns[mctl];
#else
    mctl = bitrate_divider & 0x0E;
#endif
    bitrate_divider >>= 4;
#if !defined(__MSP430_HAS_EUSCI_A0__)
    br1 = bitrate_divider >> 8;
    br0 = bitrate_divider;
#endif

    switch (port)
    {
#if defined(UART_0_SUPPORT)
    case 0:
    #if defined(__MSP430_HAS_USCI_AB0__)  ||  defined(__MSP430_HAS_USCI_A0__)  ||  defined(__MSP430_HAS_EUSCI_A0__)
        /* Configure the port with the reset bit held high */
        UCA0CTL1 |= UCSWRST;
        UCA0CTL0 = ctl0;
        UCA0CTL1 = ctl1;
        #if defined(__MSP430_HAS_EUSCI_A0__)
        UCA0BRW = bitrate_divider;
        UCA0MCTLW = ((uint16_t) mctl << 8);
        #else
        UCA0BR1 = br1;
        UCA0BR0 = br0;
        UCA0MCTL = mctl;
        UCA0STAT = 0;
        #endif
        UCA0TXBUF = 0;
        UCA0CTL1 &= ~UCSWRST;
        #if defined(UART_0_DMA_SUPPORT)
        //DMACTL0 |= (DMA1TSEL_3 | DMA2TSEL_4);
        #endif
        #if defined(__MSP430_HAS_USCI_AB0__)
        UC0IE |= UCA0RXIE;
        #else
        UCA0IE |= UCRXIE;
        #endif
    #elif defined(__MSP430_HAS_UART0__)
        UCTL0 = ctl0;
        UTCTL0 = ctl1;
        UBR10 = br1;
        UBR00 = br0;
        UMCTL0 = mctl;
        UCTL0 &= ~SWRST;
        /* Enable USART0 TXD/RXD */
        U0ME |= (UTXE0 | URXE0);
        U0IE |= URXIE0;
        /* If you do not initially kick the Tx port the TXEPT bit is not set. */
        TXBUF0 = 0;
    #endif
        return 0;
#endif
#if defined(UART_1_SUPPORT)
    case 1:
    #if defined(__MSP430_HAS_USCI_AB1__)  ||  defined(__MSP430_HAS_USCI_A1__)  ||  defined(__MSP430_HAS_EUSCI_A1__)
        /* Configure the port with the reset bit held high */
        UCA1CTL1 |= UCSWRST;
        UCA1CTL0 = ctl0;
        UCA1CTL1 = ctl1;
        #if defined(__MSP430_HAS_EUSCI_A1__)
        UCA1BRW = bitrate_divider;
        UCA1MCTLW = ((uint16_t) mctl << 8);
        #else
        UCA1BR1 = br1;
        UCA1BR0 = br0;
        UCA1MCTL = mctl;
        UCA1STAT = 0;
        #endif
        UCA1TXBUF = 0;
        UCA1CTL1 &= ~UCSWRST;
        #if defined(UART_1_DMA_SUPPORT)
        DMACTL0 |= (DMA1TSEL_9 | DMA2TSEL_10);
        #endif
        #if defined(__MSP430_HAS_USCI_AB1__)
        UC1IE |= UCA1RXIE;
        #else
        UCA1IE |= UCRXIE;
        #endif
    #elif defined(__MSP430_HAS_UART1__)
        UCTL1 = ctl0;
        UTCTL1 = ctl1;
        UBR11 = br1;
        UBR01 = br0;
        UMCTL1 = mctl;
        UCTL1 &= ~SWRST;
        /* Enable USART1 TXD/RXD */
        ME2 |= (UTXE1 | URXE1);
        IE2 |= URXIE1;
        /* If you do not initially kick the Tx port, the TXEPT bit is not set. */
        TXBUF1 = 0;
    #endif
        return 0;
#endif
#if defined(UART_2_SUPPORT)
    case 2:
    #if defined(__MSP430_HAS_USCI_AB2__)  ||  defined(__MSP430_HAS_USCI_A2__)  ||  defined(__MSP430_HAS_EUSCI_A2__)
        /* Configure the port with the reset bit held high */
        UCA2CTL1 |= UCSWRST;
        UCA2CTL0 = ctl0;
        UCA2CTL1 = ctl1;
        #if defined(__MSP430_HAS_EUSCI_A2__)
        UCA2BRW = bitrate_divider;
        UCA2MCTLW = ((uint16_t) mctl << 8);
        #else
        UCA2BR1 = br1;
        UCA2BR0 = br0;
        UCA2MCTL = mctl;
        UCA2STAT = 0;
        #endif
        UCA2TXBUF = 0;
        UCA2CTL1 &= ~UCSWRST;
        #if defined(__MSP430_HAS_USCI_AB2__)
        UC2IE |= UCA2RXIE;
        #else
        UCA2IE |= UCRXIE;
        #endif
    #endif
        return 0;
#endif
#if defined(UART_3_SUPPORT)
    case 3:
    #if defined(__MSP430_HAS_USCI_AB3__)  ||  defined(__MSP430_HAS_USCI_A3__)  ||  defined(__MSP430_HAS_EUSCI_A3__)
        /* Configure the port with the reset bit held high */
        UCA3CTL1 |= UCSWRST;
        UCA3CTL0 = ctl0;
        UCA3CTL1 = ctl1;
        #if defined(__MSP430_HAS_EUSCI_A3__)
        UCA3BRW = bitrate_divider;
        UCA3MCTLW = ((uint16_t) mctl << 8);
        #else
        UCA3BR1 = br1;
        UCA3BR0 = br0;
        UCA3MCTL = mctl;
        UCA3STAT = 0;
        #endif
        UCA3TXBUF = 0;
        UCA3CTL1 &= ~UCSWRST;
        #if defined(__MSP430_HAS_USCI_AB3__)
        UC3IE |= UCA3RXIE;
        #else
        UCA3IE |= UCRXIE;
        #endif
    #endif
        return 0;
#endif
    }
    return -1;
}
/*- End of function --------------------------------------------------------*/

int serial_write(int port, const uint8_t buf[], int len)
{
    ports[port].tx_in_progress = true;
    ports[port].tx_msg.ptr = 0;
    ports[port].tx_msg.len = len;
    switch (port)
    {
    #if defined(UART_0_SUPPORT)
    case 0:
        #if defined(UART_0_DMA_SUPPORT)
        __data20_write_long((uint32_t) &DMA2SA, (uint32_t) buf);
        __data20_write_long((uint32_t) &DMA2DA, (uint32_t) &UCA0TXBUF);
        DMA2SZ = len;
        /* Enable, source address incremented, single transfer. */
        DMA2CTL = DMADT_0 | DMASRCINCR_3 | DMASRCBYTE | DMADSTBYTE | DMAEN;
        /* Kick things, so the DMA starts rolling */
        UC0IFG &= ~UCA0TXIFG;
        UC0IFG |= UCA0TXIFG;
        #elif defined(__MSP430_HAS_UART0__)
        //ports[port].tx_msg.buf = buf;
        U0IE |= UTXIE0;
        #elif defined(__MSP430_HAS_USCI_AB0__)
        //ports[port].tx_msg.buf = buf;
        UC0IE |= UCA0TXIE;
        #else
        //ports[port].tx_msg.buf = buf;
        UCA0IE |= UCTXIE;
        #endif
        break;
    #endif
    #if defined(UART_1_SUPPORT)
    case 1:
        #if defined(UART_1_DMA_SUPPORT)
        __data20_write_long((uint32_t) &DMA2SA, (uint32_t) buf);
        __data20_write_long((uint32_t) &DMA2DA, (uint32_t) &UCA1TXBUF);
        DMA2SZ = len;
        /* Enable, source address incremented, single transfer. */
        DMA2CTL = DMADT_0 | DMASRCINCR_3 | DMASRCBYTE | DMADSTBYTE | DMAEN;
        /* Kick things, so the DMA starts rolling */
        UC1IFG &= ~UCA1TXIFG;
        UC1IFG |= UCA1TXIFG;
        #elif defined(__MSP430_HAS_UART1__)
        //ports[port].tx_msg.buf = buf;
        U1IE |= UTXIE1;
        #elif defined(__MSP430_HAS_USCI_AB1__)
        //ports[port].tx_msg.buf = buf;
        UC1IE |= UCA1TXIE;
        #else
        //ports[port].tx_msg.buf = buf;
        UCA1IE |= UCTXIE;
        #endif
        break;
    #endif
    #if defined(UART_2_SUPPORT)
    case 2:
        //ports[port].tx_msg.buf = buf;
        #if defined(__MSP430_HAS_USCI_AB2__)
        UC2IE |= UCA2TXIE;
        #else
        UCA2IE |= UCTXIE;
        #endif
        break;
    #endif
    #if defined(UART_3_SUPPORT)
    case 3:
        //ports[port].tx_msg.buf = buf;
        #if defined(__MSP430_HAS_USCI_AB3__)
        UC3IE |= UCA3TXIE;
        #else
        UCA3IE |= UCTXIE;
        #endif
        break;
    #endif
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
void serial_rx_inactivity_timer_cancel(int port)
{
    ports[port].next_inactivity_timeout = 0;
}
/*- End of function --------------------------------------------------------*/

void serial_rx_inactivity_timer_restart(int port)
{
    ports[port].next_inactivity_timeout = current_time() + ports[port].inactivity_limit;
}
/*- End of function --------------------------------------------------------*/
#endif

void uart_rx_core(int port, uint8_t rx_char)
{
    #if defined(IEC62056_21_SUPPORT)
    /* Deal with character timeout */
    if (inter_char_timeout == 0)
        iec62056_21_rx_restart(port);
    inter_char_timeout = SAMPLES_PER_10_SECONDS/200;
    if (!ports[port].tx_in_progress)
        iec62056_21_rx_byte(rx_char & 0x7F);
    #endif
    #if defined(DLT645_SUPPORT)
        #if defined(COMMON_RX_TX_BUFFER_SUPPORT)
    dlt645_rx_byte(port, rx_char);
        #else
    if (!ports[port].tx_in_progress)
        dlt645_rx_byte(port, rx_char);
        #endif
    #endif
}
/*- End of function --------------------------------------------------------*/

#if defined(UART_0_DMA_SUPPORT)  ||  defined(UART_1_DMA_SUPPORT)  ||  defined(UART_2_DMA_SUPPORT)  ||  defined(UART_3_DMA_SUPPORT)
void uart_tx_dma_core(int port)
{
    if (ports[port].tx_msg.len == 0)
        return;
    ports[port].tx_msg.len = 0;
#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
    serial_tx_complete(port);
#endif
}
/*- End of function --------------------------------------------------------*/
#endif

uint16_t uart_tx_core(int port)
{
    uint8_t tx;

    if (!ports[port].tx_in_progress)
        return 0x8000;
    tx = ports[port].tx_msg.buf.uint8[ports[port].tx_msg.ptr++];
    if (ports[port].tx_msg.ptr >= ports[port].tx_msg.len)
    {
#if defined(COMMON_RX_TX_BUFFER_SUPPORT)
        /* Let receive mode have access to the buffer */
        ports[port].rx_msg.ptr = 0;
        ports[port].rx_msg.len = 0;
#else
        ports[port].tx_msg.ptr = 0;
        ports[port].tx_msg.len = 0;
#endif
        ports[port].tx_in_progress = false;
#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
        serial_tx_complete(port);
#endif
        return 0x8000 | tx;
    }
    return tx;
}
/*- End of function --------------------------------------------------------*/

/* We need a separate set of interrupt routines for the 4xx and 5xx devices. Although the body of the
   USCI module is functionally similar in the 4xx and 5xx devices, the interrupt scheme is quite different. */
#if defined(UART_0_SUPPORT)
    #if defined(__MSP430_HAS_UART0__)
ISR(USART0RX, USART0_rx_isr)
{
    uart_rx_core(0, RXBUF0);
}
/*- End of function --------------------------------------------------------*/

ISR(USART0TX, USART0_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(0);
    TXBUF0 = tx & 0xFF;
    if (tx & 0x8000)
        U0IE &= ~UTXIE0;
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_USCI_AB0__)
ISR(USCIAB0RX, USCI_AB0_rx_isr)
{
    uart_rx_core(0, UCA0RXBUF);
}
/*- End of function --------------------------------------------------------*/

        #if !defined(UART_0_DMA_SUPPORT)
ISR(USCIAB0TX, USCI_AB0_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(0);
    UCA0TXBUF = tx & 0xFF;
    if (tx & 0x8000)
        UC0IE &= ~UCA0TXIE;
}
/*- End of function --------------------------------------------------------*/
        #endif
    #elif defined(__MSP430_HAS_USCI_A0__)
ISR(USCI_A0, USCI_A0_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA0IV, USCI_UART_UCTXIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(0, UCA0RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(0);
        UCA0TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA0IE &= ~UCTXIE;
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_EUSCI_A0__)
ISR(USCI_A0, USCI_A0_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA0IV, USCI_UART_UCTXCPTIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(0, UCA0RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(0);
        UCA0TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA0IE &= ~UCTXIE;
        break;
    case USCI_UART_UCSTTIFG:
        break;
    case USCI_UART_UCTXCPTIFG:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #else
        #error Device does not have a UART port 0
    #endif
#endif

#if defined(UART_1_SUPPORT)
    #if defined(__MSP430_HAS_UART1__)
ISR(USART1RX, USART1_rx_isr)
{
    uart_rx_core(1, RXBUF1);
}
/*- End of function --------------------------------------------------------*/

ISR(USART1TX, USART1_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(1);
    TXBUF1 = tx & 0xFF;
    if (tx & 0x8000)
        U1IE &= ~UTXIE1;
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_USCI_AB1__)
ISR(USCIAB1RX, USCI_AB1_rx_isr)
{
    uart_rx_core(1, UCA1RXBUF);
}
/*- End of function --------------------------------------------------------*/

        #if !defined(UART_1_DMA_SUPPORT)
ISR(USCIAB1TX, USCI_AB1_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(1);
    UCA1TXBUF = tx & 0xFF;
    if (tx & 0x8000)
        UC1IE &= ~UCA1TXIE;
}
/*- End of function --------------------------------------------------------*/
        #endif
    #elif defined(__MSP430_HAS_USCI_A1__)
ISR(USCI_A1, USCI_A1_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA1IV, USCI_UART_UCRXIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(1, UCA1RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(1);
        UCA1TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA1IE &= ~UCTXIE;
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_EUSCI_A1__)
ISR(USCI_A1, USCI_A1_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA1IV, USCI_UART_UCTXCPTIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(1, UCA1RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(1);
        UCA1TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA1IE &= ~UCTXIE;
        break;
    case USCI_UART_UCSTTIFG:
        break;
    case USCI_UART_UCTXCPTIFG:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #else
        #error Device does not have a UART port 1
    #endif
#endif

#if defined(UART_2_SUPPORT)
    #if defined(__MSP430_HAS_USCI_AB2__)
ISR(USCIAB2RX, USCI_AB2_rx_isr)
{
    uart_rx_core(2, UCA2RXBUF);
}
/*- End of function --------------------------------------------------------*/

        #if !defined(UART_2_DMA_SUPPORT)
ISR(USCIAB2TX, USCI_AB2_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(2);
    UCA2TXBUF = tx & 0xFF;
    if (tx & 0x8000)
        UC2IE &= ~UCA2TXIE;
}
/*- End of function --------------------------------------------------------*/
        #endif
    #elif defined(__MSP430_HAS_USCI_A2__)
ISR(USCI_A2, USCI_A2_isr)
{
    uint16_t tx;

    now = current_time();
    switch (__even_in_range(UCA2IV, USCI_UART_UCTXIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(2, UCA2RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(2);
        UCA2TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA2IE &= ~UCTXIE;
        break;
    default:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_EUSCI_A2__)
ISR(USCI_A2, USCI_A2_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA2IV, USCI_UART_UCTXCPTIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(2, UCA2RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(2);
        UCA2TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA2IE &= ~UCTXIE;
        break;
    case USCI_UART_UCSTTIFG:
        break;
    case USCI_UART_UCTXCPTIFG:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #else
        #error Device does not have a UART port 2
    #endif
#endif

#if defined(UART_3_SUPPORT)
    #if defined(__MSP430_HAS_USCI_AB3__)
ISR(USCIAB3RX, USCI_AB3_rx_isr)
{
    uart_rx_core(3, UCA3RXBUF);
}
/*- End of function --------------------------------------------------------*/

        #if !defined(UART_3_DMA_SUPPORT)
ISR(USCIAB3TX, USCI_AB3_tx_isr)
{
    uint16_t tx;

    tx = uart_tx_core(3);
    UCA3TXBUF = tx & 0xFF;
    if (tx & 0x8000)
        UC3IE &= ~UCA3TXIE;
}
/*- End of function --------------------------------------------------------*/
        #endif
    #elif defined(__MSP430_HAS_USCI_A3__)
ISR(USCI_A3, USCI_A3_isr)
{
    uint16_t tx;

    now = current_time();
    switch (__even_in_range(UCA3IV, USCI_UART_UCTXIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(3, UCA3RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(3);
        UCA3TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA3IE &= ~UCTXIE;
        break;
    default:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #elif defined(__MSP430_HAS_EUSCI_A3__)
ISR(USCI_A3, USCI_A3_isr)
{
    uint16_t tx;

    switch (__even_in_range(UCA3IV, USCI_UART_UCTXCPTIFG))
    {
    case USCI_NONE:
        break;
    case USCI_UART_UCRXIFG:
        uart_rx_core(3, UCA3RXBUF);
        break;
    case USCI_UART_UCTXIFG:
        tx = uart_tx_core(3);
        UCA3TXBUF = tx & 0xFF;
        if (tx & 0x8000)
            UCA3IE &= ~UCTXIE;
        break;
    case USCI_UART_UCSTTIFG:
        break;
    case USCI_UART_UCTXCPTIFG:
        break;
    }
}
/*- End of function --------------------------------------------------------*/
    #else
        #error Device does not have a UART port 3
    #endif
#endif

#if defined(UART_0_DMA_SUPPORT)  ||  defined(UART_1_DMA_SUPPORT)  ||  defined(UART_2_DMA_SUPPORT)  ||  defined(UART_3_DMA_SUPPORT)
ISR(DMA, dma_interrupt)
{
    switch (__even_in_range(DMAIV, DMAIV_DMA2IFG))
    {
    case DMAIV_DMA0IFG:
        DMA0CTL &= ~ DMAIFG;
        break;
    case DMAIV_DMA1IFG:
        DMA1CTL &= ~ DMAIFG;
        break;
    case DMAIV_DMA2IFG:
        DMA2CTL &= ~ DMAIFG;
        break;
    }
}
/*- End of function --------------------------------------------------------*/
#endif

#if defined(SERIAL_INACTIVITY_TIMEOUT_SUPPORT)
#if defined(USE_TIMERA0_FOR_TIMEOUTS)
ISR(TIMER0_A0, TIMER0_A0_isr)
#elif defined(USE_TIMERA1_FOR_TIMEOUTS)
ISR(TIMER1_A0, TIMER1_A0_isr)
#endif
{
    uint32_t now;
    uint16_t i;

    now = current_time();
#if defined(USE_TIMERA0_FOR_TIMEOUTS)
    TA0CCR0 += 0xC00;
#elif defined(USE_TIMERA1_FOR_TIMEOUTS)
    TA1CCR0 += 0xC00;
#endif
    for (i = 0;  i < MAX_UART_PORT;  i++)
    {
        if (ports[i].next_inactivity_timeout  &&  (now >= ports[i].next_inactivity_timeout))
        {
            /* Wake up the main code, and let is service anything which has timed out. */
            ports[i].next_inactivity_timeout = 0;
            serial_rx_inactivity_timeout(i);
        }
    }
}
/*- End of function --------------------------------------------------------*/

uint32_t current_time(void)
{
    uint16_t snapshot;

#if defined(USE_TIMERA0_FOR_TIMEOUTS)
    snapshot = TA0R;
#elif defined(USE_TIMERA1_FOR_TIMEOUTS)
    snapshot = TA1R;
#endif
    if (snapshot < timer_control.last_timeout)
        timer_control.epoch += 0x10000L;
    timer_control.last_timeout = snapshot;
    return timer_control.epoch + snapshot;
}
/*- End of function --------------------------------------------------------*/

void serial_timer_init(void)
{
    timer_control.epoch = 0;
    timer_control.last_timeout = 0;

#if defined(USE_TIMERA0_FOR_TIMEOUTS)
    /* Set up timer A to use as a timeout interrupt function */
    /* TACCR0 determines the next interrupt, clocked from ACLK */
    TA0R = 0;
    TA0CCR0 = 0;
    TA0CCTL0 = CCIE;
    TA0CTL = TACLR | MC_2 | TASSEL_1;
#elif defined(USE_TIMERA1_FOR_TIMEOUTS)
    /* Set up timer A to use as a timeout interrupt function */
    /* TACCR0 determines the next interrupt, clocked from ACLK */
    TA1R = 0;
    TA1CCR0 = 0;
    TA1CCTL0 = CCIE;
    TA1CTL = TACLR | MC_2 | TASSEL_1;
#endif
}
/*- End of function --------------------------------------------------------*/
#endif
/*- End of file ------------------------------------------------------------*/
