/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_PLATFORM_AVR_UART_INLINE_H
#define ECXX_PLATFORM_AVR_UART_INLINE_H


namespace eCxx {


//
// Compatibility macros for AVR MCUs with different UART/USART register names
//
#if defined(PE) && !defined(UPE)
    #define UPE PE
#endif

#if defined(TXC) && !defined(TXC0)
    #define TXC0   TXC
    #define TXEN0  TXEN
    #define RXEN0  RXEN
    #define RXCIE0 RXCIE
    #define UDRIE0 UDRIE
    #define U2X0   U2X
    #define UPE0   UPE
    #define UDRE0  UDRE
#endif

#if defined(TXC1) && !defined(TXC0)
    #define TXC0   TXC1
    #define TXEN0  TXEN1
    #define RXEN0  RXEN1
    #define RXCIE0 RXCIE1
    #define UDRIE0 UDRIE1
    #define U2X0   U2X1
    #define UPE0   UPE1
    #define UDRE0  UDRE1
#endif


//
// According to the datasheets, the position of the other UARTs' configuration bits are the same as UART0's
//
#if defined(TXC1) && (TXC1 != TXC0 || TXEN1 != TXEN0 || RXEN1 != RXEN0 || RXCIE1 != RXCIE0 || UDRIE1 != UDRIE0 || U2X1 != U2X0 || UPE1 != UPE0 || UDRE1 != UDRE0)
#error "USART/USART1's configuration bits position are not the same as USART/USART0's"
#endif

#if defined(TXC2) && (TXC2 != TXC0 || TXEN2 != TXEN0 || RXEN2 != RXEN0 || RXCIE2 != RXCIE0 || UDRIE2 != UDRIE0 || U2X2 != U2X0 || UPE2 != UPE0 || UDRE2 != UDRE0)
#error "USART/USART2's configuration bits position are not the same as USART/USART0's"
#endif

#if defined(TXC3) && (TXC3 != TXC0 || TXEN3 != TXEN0 || RXEN3 != RXEN0 || RXCIE3 != RXCIE0 || UDRIE3 != UDRIE0 || U2X3 != U2X0 || UPE3 != UPE0 || UDRE3 != UDRE0)
#error "USART/USART3's configuration bits position are not the same as USART/USART0's"
#endif


//
// Private inline member functions of the AVR UART class
//
inline UART::UART(
    volatile uint8_t* UBRRH_, volatile uint8_t* UBRRL_,
    volatile uint8_t* UCSRA_, volatile uint8_t* UCSRB_,
    volatile uint8_t* UCSRC_, volatile uint8_t* UDR_
)
: _UBRRH(UBRRH_)
, _UBRRL(UBRRL_)
, _UCSRA(UCSRA_)
, _UCSRB(UCSRB_)
, _UCSRC(UCSRC_)
, _UDR  (UDR_  )
{}


inline void UART::rxComplete_isr()
{
    // Check for parity error
    if( *_UCSRA & _BV(UPE0) ) {
        *_UDR;
    }
    // Store the data
    else {
        const uint8_t c = *_UDR;
        _rxBuff.put(c);
    }
}


inline void UART::udrEmpty_isr()
{
    // Retrieve data from the Tx buffer
    uint8_t c;

    if( !_txBuff.get(c) ) {
        // Disable this interrupt if no more data can be retrieved
        *_UCSRB &= ~_BV(UDRIE0);
        return;
    }

    // Send the data
    *_UDR = c;

    // Clear the 'Transmit Complete' (TXC) bit to ensure flush() will not return until the byte(s) are really sent
    // (eCxx does not support 'Multi-processor Communication Mode', hence MPCM0 is never set)
    *_UCSRA |= _BV(TXC0);

    // Disable this interrupt if the Tx buffer is empty
    if( _txBuff.empty() ) *_UCSRB &= ~_BV(UDRIE0);
}


} // namespace eCxx


#endif
