text: #ifndef AVRLIB_ASYNC_USART_HPP
  #define AVRLIB_ASYNC_USART_HPP
  
  #include <stdint.h>
  #include "nobootseq.hpp"
  #include "buffer.hpp"
  #include "usart_base.hpp"
  #include "null_pin.hpp"
  
  #include <Stream.h>
  
  namespace avrlib {
  
  template <typename Usart, int RxBufferSize, int TxBufferSize, typename Bootseq = nobootseq, typename Overflow = uint32_t, typename Dir = null_pin<0> >
  class async_usart: public Stream
  {
  public:
  	typedef Usart usart_type;
  	typedef Overflow overflow_type;
  	typedef typename usart_type::value_type value_type;
  
  	typedef Bootseq bootseq_type;
  
  	async_usart()
  		 :m_overflow(0), m_transmitted(true)
  	{
  	}
  
  	template <typename T1>
  	async_usart(T1 const & t1)
  		: m_overflow(0), m_transmitted(true)
  	{
  		m_usart.open(t1);
  	}
  
  	template <typename T1, typename T2>
  	async_usart(T1 const & t1, T2 const & t2)
  		: m_overflow(0), m_transmitted(true)
  	{
  		m_usart.open(t1, t2);
  	}
  
  	typename buffer<value_type, RxBufferSize>::index_type available() const
  	{
  		return m_rx_buffer.size();
  	}
  
  	bool empty() const
  	{
  		return m_rx_buffer.empty();
  	}
  
  	bool tx_empty() const
  	{
  		return m_tx_buffer.empty();
  	}
  
  	bool tx_ready() const
  	{
  		return !m_tx_buffer.full();
  	}
  	
  	bool transmitted()
  	{
  		return m_transmitted;
  	}
  
  	int peek()  // because of compatibility with Arduino Stream
  	{
  		return m_rx_buffer.empty() ? -1 : m_rx_buffer.top();
  	}
  
  	//value_type read()
  	int read() // because of compatibility with Arduino Stream
  	{
  		while (m_rx_buffer.empty())
  		{
  			cli();
  			this->process_rx();
  			sei();
  		}
  		
  		value_type res = m_rx_buffer.top();
  		m_rx_buffer.pop();
  		return res;
  	}
  
  	uint8_t read_size() const
  	{
  		return m_rx_buffer.size();
  	}
  
  	//void write(value_type v)
  	size_t write(value_type v) // because of compatibility with Arduino Stream
  	{
  		m_transmitted = false;
  		if(TxBufferSize != 0)
  		{
  			while (m_tx_buffer.full())
  			{
  				cli();
  				this->process_tx();
  				sei();
  			}
  			m_tx_buffer.push(v);
  			if(m_usart.is_dre_interrupt_set())
  				m_usart.enable_dre_interrupt();
  		}
  		else
  		{
  			while(!m_usart.tx_empty())
  			{
  				cli();
  				__asm__ volatile ("nop");
  				sei();
  			}
  			m_usart.send(v);
  		}
  		return 1;
  	}
  
  	int available() // because of compatibility with Arduino Stream
  	{
  		return m_rx_buffer.size();
  	}
  	
  	void flush()
  	{
  		while (!m_transmitted)
  		{
  			cli();
  			this->process_tx();
  			sei();
  		}
  	}
  
  	void process_rx()
  	{
  		if (!m_usart.rx_empty())
  			this->intr_rx();
  	}
  
  	bool intr_rx()
  	{
  		if(m_usart.overflow())
  			++m_overflow;
  		if(m_usart.frame_error())
  		{
  			m_usart.recv();
  			return false;
  		}
  		value_type v = m_bootseq.check(m_usart.recv());
  		if(!m_rx_buffer.push(v))
  		{
  			++m_overflow;
  		}
  		return true;
  	}
  
  	bool process_tx()
  	{
  		if (!m_tx_buffer.empty() && m_usart.tx_empty())
  		{
  			Dir::set_high();
  			nop();
  			m_usart.send(m_tx_buffer.top());
  			m_tx_buffer.pop();
  			return true;
  		}
  		else if(m_usart.transmitted())
          {
              Dir::set_low();
  			m_transmitted = true;
          }
  
  		// TODO: flush the underlying port
  		return false;
  	}
  	
  	bool intr_tx()
  	{
  		if (!m_tx_buffer.empty())
  		{
  			m_usart.send(m_tx_buffer.top());
  			m_tx_buffer.pop();
  			return true;
  		}
  		else
  		{
  			m_usart.disable_dre_interrupt();
  			m_transmitted = false;
  		}
  		return false;
  	}
  
  	bool tx_reserve(uint8_t size)
  	{
  		return TxBufferSize - m_tx_buffer.size() > size;
  	}
  	
  	overflow_type overflow() const { return m_overflow; }
  	void clear_overflow() { m_overflow = 0; }
  
  	typedef buffer<value_type, RxBufferSize> rx_buffer_type;
  	rx_buffer_type & rx_buffer() { return m_rx_buffer; }
  		
  	typedef buffer<value_type, TxBufferSize> tx_buffer_type;
  	tx_buffer_type & tx_buffer() { return m_tx_buffer; }
  
  	usart_type & usart() { return m_usart; }
  	usart_type const & usart() const { return m_usart; }
  		
  	void async_tx(const bool& en)
  	{
  		m_usart.dre_interrupt(en, false);
  	}
  	void async_tx(const uart_interrupt_priority_t& priority)
  	{
  		m_usart.dre_interrupt(priority, false);
  	}
  	bool async_tx() const { return m_usart.is_dre_interrupt_set(); }
  
  private:
  	usart_type m_usart;
  	buffer<value_type, RxBufferSize> m_rx_buffer;
  	buffer<value_type, TxBufferSize==0?1:TxBufferSize> m_tx_buffer;
  	bootseq_type m_bootseq;
  	volatile overflow_type m_overflow;
  	volatile bool m_transmitted;
  };
  
  }
  
  #endif
  