#include <al_uart.h>
#include <al_common.h>
#include <avr/io.h>
#include <avr/interrupt.h>

volatile static uint8_t	uart_rx_fifo[UART_RX_FIFO_DEPTH],
			uart_rx_head,
			uart_rx_tail,
			uart_tx_fifo[UART_TX_FIFO_DEPTH],
			uart_tx_head,
			uart_tx_tail;


#ifdef SIG_USART_RECV
SIGNAL(SIG_USART_RECV)
#else
SIGNAL(SIG_UART_RECV)
#endif
{
#ifdef UDR0
    uart_rx_fifo[uart_rx_head++]=UDR0;
#else
    uart_rx_fifo[uart_rx_head++]=UDR;
#endif
    uart_rx_head%=UART_RX_FIFO_DEPTH;

    if (uart_rx_head==uart_rx_tail)
    {
	uart_rx_tail++;
        uart_rx_tail%=UART_RX_FIFO_DEPTH;
    }
}


#ifdef SIG_USART_DATA
SIGNAL(SIG_USART_DATA)
#else
SIGNAL(SIG_UART_DATA)
#endif
{
    if (uart_tx_tail==uart_tx_head)
    {
#ifdef UCSR0B
	cbi(UCSR0B,UDRIE0);
#elif defined(UCSRB)
	cbi(UCSRB,UDRIE);
#else
	cbi(UCR,UDRIE);
#endif
	return;
    }

#ifdef UDR0
    UDR0=uart_tx_fifo[uart_tx_tail++];
#else
    UDR=uart_tx_fifo[uart_tx_tail++];
#endif
    uart_tx_tail%=UART_TX_FIFO_DEPTH;
}


void uart_init()
{
    uart_rx_head=uart_rx_tail=0;
    uart_tx_head=uart_tx_tail=0;

#ifdef UBRR0L
    UBRR0L=LBYTE(UART_BR_DIVIDER);
    UBRR0H=HBYTE(UART_BR_DIVIDER);
#elif defined(UBRRL)
    UBRRL=LBYTE(UART_BR_DIVIDER);
    UBRRH=HBYTE(UART_BR_DIVIDER);
#else
    UBRR=UART_BR_DIVIDER;
#endif

#ifdef UCSR0B
    UCSR0B=_BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0);
#elif defined(UCSRB)
    UCSRB=_BV(RXEN) | _BV(TXEN) | _BV(RXCIE);
#else
    UCR=_BV(RXEN) | _BV(TXEN) | _BV(RXCIE);
#endif
}


void uart_suspend()
{
#ifdef UCSR0B
    UCSR0B=0;
#elif defined(UCSRB)
    UCSRB=0;
#else
    UCR=0;
#endif
}


uint8_t uart_getc()
{
    uint8_t	d;

    while (uart_rx_fifo_empty());

    d=uart_rx_fifo[uart_rx_tail++];
    uart_rx_tail%=UART_RX_FIFO_DEPTH;

    return d;
}


int16_t uart_timeout_getc()
{
    uint8_t	d=0;

    TCNT0=0;
#ifdef TCCR0B
    TCCR0B=0x05;
#else
    TCCR0=0x05;
#endif

    while (uart_rx_fifo_empty())
    {
#ifdef TIFR0
	if (bit_is_set(TIFR0,TOV0))
	{
	    sbi(TIFR0,TOV0);
#else
	if (bit_is_set(TIFR,TOV0))
	{
	    sbi(TIFR,TOV0);
#endif
	    if (++d>=UART_RX_TO_LOOPS)
		return -1;
	}
    }

    d=uart_rx_fifo[uart_rx_tail++];
    uart_rx_tail%=UART_RX_FIFO_DEPTH;

    return d;
}


void uart_putc(uint8_t c_)
{
    while (uart_tx_fifo_full());

    uart_tx_fifo[uart_tx_head++]=c_;
    uart_tx_head%=UART_TX_FIFO_DEPTH;

#ifdef UCSR0B
    sbi(UCSR0B,UDRIE0);
#elif defined(UCSRB)
    sbi(UCSRB,UDRIE);
#else
    sbi(UCR,UDRIE);
#endif
}


void uart_puts(char* s_)
{
    for (;*s_;s_++)
	uart_putc(*s_);
}


uint8_t uart_rx_fifo_empty()
{
    return uart_rx_head==uart_rx_tail;
}


uint8_t uart_tx_fifo_full()
{
    return (uart_tx_head+1)%UART_TX_FIFO_DEPTH==uart_tx_tail;
}


uint8_t uart_tx_fifo_capacity()
{
    if (uart_tx_head>=uart_tx_tail)
	return UART_TX_FIFO_DEPTH-1-(uart_tx_head-uart_tx_tail);

    return uart_tx_tail-uart_tx_head-1;
}


uint8_t uart_tx_complete()
{
    return uart_tx_head==uart_tx_tail
#ifdef UCSR0A
    && bit_is_set(UCSR0A,TXC0);
#elif defined(UCSRA)
    && bit_is_set(UCSRA,TXC);
#else
    ;
#endif
}
