// -*- Mode: C++; c-basic-offset: 8; indent-tabs-mode: nil -*-
//
// Interrupt-driven serial transmit/receive library.
//
//      Copyright (c) 2010 Michael Smith. All rights reserved.
//
// Receive and baudrate calculations derived from the Arduino 
// HardwareSerial driver:
//
//      Copyright (c) 2006 Nicholas Zambetti.  All right reserved.
//
// Transmit algorithm inspired by work:
//
//      Code Jose Julio and Jordi Munoz. DIYDrones.com
//
//      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
//

//
// Note that this library does not pre-declare drivers for serial
// ports; the user must explicitly create drivers for the ports they
// wish to use.  This is less friendly than the stock Arduino driver,
// but it saves ~200 bytes for every unused port.
//

#ifndef FastSerial_h
#define FastSerial_h

// disable the stock Arduino serial driver
#ifdef HardwareSerial_h
# error Must include FastSerial.h before the Arduino serial driver is defined.
#endif
#define HardwareSerial_h

#include <inttypes.h>
#include <stdlib.h>
#include <avr/interrupt.h>

#include "BetterStream.h"

//
// Because Arduino libraries aren't really libraries, but we want to
// only define interrupt handlers for serial ports that are actually
// used, we have to force our users to define them using a macro.
//
// Due to the way interrupt vectors are specified, we have to have
// a separate macro for every port.  Ugh.
//
// The macros are:
//
// FastSerialPort0(<port name>)         creates <port name> referencing serial port 0
// FastSerialPort1(<port name>)         creates <port name> referencing serial port 1
// FastSerialPort2(<port name>)         creates <port name> referencing serial port 2
// FastSerialPort3(<port name>)         creates <port name> referencing serial port 3
//

//
// Forward declarations for clients that want to assume that the
// default Serial* objects exist.
//
// Note that the application is responsible for ensuring that these
// actually get defined, otherwise Arduino will suck in the
// HardwareSerial library and linking will fail.
//
extern class FastSerial Serial;
extern class FastSerial Serial1;
extern class FastSerial Serial2;
extern class FastSerial Serial3;

class FastSerial : public BetterStream {
public:
        FastSerial(const uint8_t portNumber,
                   volatile uint8_t *ubrrh,
                   volatile uint8_t *ubrrl,
                   volatile uint8_t *ucsra,
                   volatile uint8_t *ucsrb,
                   volatile uint8_t *udr,
                   const uint8_t u2x,
                   const uint8_t portEnableBits,
                   const uint8_t portTxBits);

        // Serial API
        virtual void    begin(long baud);
        virtual void    begin(long baud, unsigned int rxSpace, unsigned int txSpace);
        virtual void    end(void);
        virtual int     available(void);
        virtual int     read(void);
        virtual int     peek(void);
        virtual void    flush(void);
        virtual void    write(uint8_t c);
        using BetterStream::write;

        // public so the interrupt handlers can see it
        struct Buffer {
                volatile uint16_t head, tail;
                uint16_t        mask;
                uint8_t         *bytes;
        };

private:
        // register accessors
        volatile uint8_t *_ubrrh;
        volatile uint8_t *_ubrrl;
        volatile uint8_t *_ucsra;
        volatile uint8_t *_ucsrb;
        volatile uint8_t *_udr;

        // register magic numbers
        uint8_t         _portEnableBits;        // rx, tx and rx interrupt enables
        uint8_t         _portTxBits;            // tx data and completion interrupt enables
        uint8_t         _u2x;

        // ring buffers
        Buffer          *_rxBuffer;
        Buffer          *_txBuffer;
        bool            _open;

        bool            _allocBuffer(Buffer *buffer, unsigned int size);
        void            _freeBuffer(Buffer *buffer);
};

// Used by the per-port interrupt vectors
extern FastSerial::Buffer	__FastSerial__rxBuffer[];
extern FastSerial::Buffer	__FastSerial__txBuffer[];

// Generic Rx/Tx vectors for a serial port - needs to know magic numbers
#define FastSerialHandler(_PORT, _RXVECTOR, _TXVECTOR, _UDR, _UCSRB, _TXBITS) \
ISR(_RXVECTOR, ISR_BLOCK)                                               \
{                                                                       \
        uint8_t c;                                                      \
        int16_t i;                                                      \
                                                                        \
        /* read the byte as quickly as possible */                      \
        c = _UDR;                                                       \
        /* work out where the head will go next */                      \
        i = (__FastSerial__rxBuffer[_PORT].head + 1) & __FastSerial__rxBuffer[_PORT].mask; \
        /* decide whether we have space for another byte */             \
        if (i != __FastSerial__rxBuffer[_PORT].tail) {                  \
                /* we do, move the head */                              \
                __FastSerial__rxBuffer[_PORT].bytes[__FastSerial__rxBuffer[_PORT].head] = c; \
                __FastSerial__rxBuffer[_PORT].head = i;                 \
        }                                                               \
}                                                                       \
ISR(_TXVECTOR, ISR_BLOCK)                                               \
{                                                                       \
        /* if there is another character to send */                     \
        if (__FastSerial__txBuffer[_PORT].tail != __FastSerial__txBuffer[_PORT].head) { \
                _UDR = __FastSerial__txBuffer[_PORT].bytes[__FastSerial__txBuffer[_PORT].tail]; \
                /* increment the tail */                                \
                __FastSerial__txBuffer[_PORT].tail =                    \
                        (__FastSerial__txBuffer[_PORT].tail + 1) & __FastSerial__txBuffer[_PORT].mask; \
        } else {                                                        \
                /* there are no more bytes to send, disable the interrupt */ \
                if (__FastSerial__txBuffer[_PORT].head == __FastSerial__txBuffer[_PORT].tail) \
                        _UCSRB &= ~_TXBITS;                             \
        }                                                               \
}                                                                       \
struct hack

// Macros defining serial ports
#if defined(__AVR_ATmega1280__)
#define FastSerialPort0(_portName)                                      \
	FastSerial _portName(0,                                         \
                             &UBRR0H,                                   \
                             &UBRR0L,                                   \
                             &UCSR0A,                                   \
                             &UCSR0B,                                   \
                             &UDR0,                                     \
                             U2X0,                                      \
                             (_BV(RXEN0) |  _BV(TXEN0) | _BV(RXCIE0)),  \
                             (_BV(UDRIE0)));                            \
	FastSerialHandler(0, SIG_USART0_RECV, SIG_USART0_DATA, UDR0, UCSR0B, _BV(UDRIE0))
#define FastSerialPort1(_portName)                                      \
	FastSerial _portName(1,                                         \
                             &UBRR1H,                                   \
                             &UBRR1L,                                   \
                             &UCSR1A,                                   \
                             &UCSR1B,                                   \
                             &UDR1,                                     \
                             U2X1,                                      \
                             (_BV(RXEN1) |  _BV(TXEN1) | _BV(RXCIE1)),  \
                             (_BV(UDRIE1)));                            \
	FastSerialHandler(1, SIG_USART1_RECV, SIG_USART1_DATA, UDR1, UCSR1B, _BV(UDRIE1))
#define FastSerialPort2(_portName)                                      \
	FastSerial _portName(2,                                         \
                             &UBRR2H,                                   \
                             &UBRR2L,                                   \
                             &UCSR2A,                                   \
                             &UCSR2B,                                   \
                             &UDR2,                                     \
                             U2X2,                                      \
                             (_BV(RXEN2) |  _BV(TXEN2) | _BV(RXCIE2)),  \
                             (_BV(UDRIE2)));                            \
	FastSerialHandler(2, SIG_USART2_RECV, SIG_USART2_DATA, UDR2, UCSR2B, _BV(UDRIE2))
#define FastSerialPort3(_portName)                                      \
	FastSerial _portName(3,                                         \
                             &UBRR3H,                                   \
                             &UBRR3L,                                   \
                             &UCSR3A,                                   \
                             &UCSR3B,                                   \
                             &UDR3,                                     \
                             U2X3,                                      \
                             (_BV(RXEN3) |  _BV(TXEN3) | _BV(RXCIE3)),  \
                             (_BV(UDRIE3)));                            \
	FastSerialHandler(3, SIG_USART3_RECV, SIG_USART3_DATA, UDR3, UCSR3B, _BV(UDRIE3))
#else
#if defined(__AVR_ATmega8__)
#define FastSerialPort0(_portName)                                      \
	FastSerial _portName(0,                                         \
                             &UBRR0H,                                   \
                             &UBRR0L,                                   \
                             &UCSR0A,                                   \
                             &UCSR0B,                                   \
                             &UDR0,                                     \
                             U2X0,                                      \
                             (_BV(RXEN0) |  _BV(TXEN0) | _BV(RXCIE0)),  \
                             (_BV(UDRIE0)));                            \
	FastSerialHandler(0, SIG_UART_RECV, SIG_UART_DATA, UDR0, UCSR0B, _BV(UDRIE0))
#else
// note no SIG_USART_* defines for the 168, 328, etc.
#define FastSerialPort0(_portName)                                      \
	FastSerial _portName(0,                                         \
                             &UBRR0H,                                   \
                             &UBRR0L,                                   \
                             &UCSR0A,                                   \
                             &UCSR0B,                                   \
                             &UDR0,                                     \
                             U2X0,                                      \
                             (_BV(RXEN0) |  _BV(TXEN0) | _BV(RXCIE0)),  \
                             (_BV(UDRIE0)));                            \
	FastSerialHandler(0, USART_RX_vect, USART_UDRE_vect, UDR0, UCSR0B, _BV(UDRIE0))
#endif
#endif

#endif // FastSerial_h