/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <joerg@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * ----------------------------------------------------------------------------
 *
 * Stdio demo, UART implementation
 *
 * $Id: uart.c,v 1.1.2.1 2005/12/28 22:35:08 joerg_wunsch Exp $
 */

#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

#include "usart.h"


/* Define the stream object that avr-libc's stdio will use */
FILE usart_stream = FDEV_SETUP_STREAM(usart_putchar,
                                      usart_getchar,
                                      _FDEV_SETUP_RW);

void
usart_init(unsigned int baud)
{
    if ((F_CPU < 2000000UL) && (baud > 4800))
    {
        /* Reduce data error by using 2x clk */
        UCSRA = _BV(U2X);
        UBRRH = (uint8_t)(((F_CPU / (8UL * baud)) - 1) >> 8);
        UBRRL = (uint8_t)(F_CPU / (8UL * baud)) - 1;
    }
    else
    {
        UBRRH = (uint8_t)(((F_CPU / (16UL * baud)) - 1) >> 8);
        UBRRL = (uint8_t)(F_CPU / (16UL * baud)) - 1;
    }

    /* Enable receiver and transmitter */
    UCSRB = _BV(RXEN) | _BV(TXEN);

    /* Set frame format: 8N1 */
    UCSRC = _BV(URSEL) | (3<<UCSZ0);

    /* Have stdio and stderr use the USART stream */
    stdin = stdout = stderr = &usart_stream;
}


int
usart_putchar(char c, FILE *stream)
{

#ifdef USE_GENERATE_CRLF
    /* Convert '\n' to '\r\n' */
    if (c == '\n')
    {
        usart_putchar('\r', stream);
    }
#endif

    /* Wait until tx register is empty */
    while (!(UCSRA & _BV(UDRE)));
    
    /* Send the character */
    UDR = c;

    return 0;
}


int
usart_getchar(FILE *stream)
{
    uint8_t c;

    /* Wait for data to be received */
    while (!(UCSRA & _BV(RXC)));

    /* Check error conditions */
    if (UCSRA & _BV(FE)) return _FDEV_EOF;
    if (UCSRA & _BV(DOR)) return _FDEV_ERR;

    /* Return received character */
    c = UDR;

    return c;
}
