--- /dev/null
+/* interrupt-driven UART routines */
+
+#include <stdio.h>
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#include "uart.h"
+
+static volatile uint8_t uart_txbuf[UART_TXBUFSIZE];
+static volatile uint8_t uart_tx_rp = 0;
+static volatile uint8_t uart_tx_wp = 0;
+static volatile uint8_t uart_rxbuf[UART_RXBUFSIZE];
+static volatile uint8_t uart_rx_rp = 0;
+static volatile uint8_t uart_rx_wp = 0;
+
+/* global, external accessable variables */
+volatile uint8_t uart_break = 0; /* 0: no break detected */
+volatile uint8_t uart_echo = 0; /* 0: disabled, otherwise enabled */
+
+void uart_init(void)
+{
+ UBRR0 = ((F_CPU + BAUDRATE * 8) / (BAUDRATE * 16) - 1);
+ UCSR0B = (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);
+ UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
+}
+
+uint8_t uart_rx_state(void)
+{
+ /* number of bytes in Rx buffer */
+ return((uint8_t)(uart_rx_wp - uart_rx_rp) % UART_RXBUFSIZE);
+}
+
+uint8_t uart_tx_state(void)
+{
+ /* number of bytes in Tx buffer */
+ return((uint8_t)(uart_tx_wp - uart_tx_rp) & UART_TXBUFSIZE);
+}
+
+int uart_putc(char c, FILE *stream)
+{
+ if(c == '\n') uart_putc('\r', stream);
+
+ uint8_t tmp = (uart_tx_wp + 1) % UART_TXBUFSIZE;
+
+ /* buffer full -> block */
+ while(tmp == uart_tx_rp);
+
+ /* enqueue */
+ uart_txbuf[uart_tx_wp] = c;
+ uart_tx_wp = tmp;
+
+ /* enable UDRE interrupt */
+ UCSR0B |= (1<<UDRIE0);
+
+ return 0;
+}
+
+ISR(USART_UDRE_vect)
+{
+ uint8_t tmp;
+
+ if(uart_tx_rp != uart_tx_wp) {
+ /* dequeue */
+ tmp = (uart_tx_rp + 1) % UART_TXBUFSIZE;
+ UDR0 = uart_txbuf[uart_tx_rp];
+ uart_tx_rp = tmp;
+ } else {
+ /* buffer empty -> disable interrupt */
+ UCSR0B &= ~(1<<UDRIE0);
+ }
+}
+
+int uart_getc(FILE *stream)
+{
+ uint8_t tmp = (uart_rx_rp + 1) % UART_RXBUFSIZE;
+
+ /* empty buffer -> block */
+ while(uart_rx_rp == uart_rx_wp);
+
+ /* dequeue data */
+ uart_rx_rp = tmp;
+ unsigned char c = uart_rxbuf[uart_rx_rp];
+
+ if(uart_echo) {
+ /* echo */
+ if(c == '\r') uart_putc('\n', stream);
+ uart_putc(c, stream);
+ }
+
+ return c;
+}
+
+ISR(USART_RX_vect) {
+ uint8_t tmp = (uart_rx_wp + 1) % UART_RXBUFSIZE;
+ uint8_t ferr = UCSR0A & (1<<FE0);
+ uint8_t data = UDR0;
+
+ /* check for BREAK */
+ if(ferr && (data == 0)) {
+ uart_break = 1;
+ return;
+ }
+
+ /* enqueue if space in buffer */
+ if(uart_rx_rp != tmp) {
+ uart_rx_wp = tmp;
+ uart_rxbuf[uart_rx_wp] = data;
+ }
+}
+
+FILE uart_out = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
+FILE uart_in = FDEV_SETUP_STREAM(NULL, uart_getc, _FDEV_SETUP_READ);