char usart_buffer[USART_BUFFER_SIZE];
volatile unsigned char usart_buffer_pos_first = 0, usart_buffer_pos_last = 0;
volatile unsigned char usart_buffer_overflow = 0;

void usart_init(unsigned char baud_divider) {

  // Baud rate selection
  UBRRH = 0x00;       
  UBRRL = baud_divider;

  // USART setup
  UCSRA = 0x02;        // 0000 0010
                       // U2X enabled
  UCSRC = 0x86;        // 1000 0110
                       // Access UCSRC, Asyncronous 8N1
  UCSRB = 0x98;        // 1001 1000
                       // Receiver enabled, Transmitter enabled
                       // RX Complete interrupt enabled
  sei();               // Enable interrupts globally
}

void usart_putc(char data) {
    while (!(UCSRA & 0x20)); // Wait untill USART data register is empty
    // Transmit data
    UDR = data;
}

void usart_puts(char *data) {
  int len, count;
  
  len = strlen(data);
  for (count = 0; count < len; count++) 
    usart_putc(*(data+count));
}

char usart_getc(void) {
  // Wait untill unread data in ring buffer
  if (!usart_buffer_overflow)
    while(usart_buffer_pos_first == usart_buffer_pos_last);
  usart_buffer_overflow = 0;
  // Increase first pointer
  if (++usart_buffer_pos_first >= USART_BUFFER_SIZE) 
    usart_buffer_pos_first = 0;
  // Get data from the buffer
  return usart_buffer[usart_buffer_pos_first];
}

unsigned char usart_unread_data(void) {
  if (usart_buffer_overflow)
    return USART_BUFFER_SIZE;
  if (usart_buffer_pos_last > usart_buffer_pos_first)
    return usart_buffer_pos_last - usart_buffer_pos_first;
  if (usart_buffer_pos_last < usart_buffer_pos_first)
    return USART_BUFFER_SIZE-usart_buffer_pos_first 
      + usart_buffer_pos_last;
  return 0;
}

SIGNAL(SIG_UART_RECV) {
  // Increase last buffer 
  if (++usart_buffer_pos_last >= USART_BUFFER_SIZE)
    usart_buffer_pos_last = 0;
  if (usart_buffer_pos_first == usart_buffer_pos_last) 
    usart_buffer_overflow++;
  // Put data to the buffer
  usart_buffer[usart_buffer_pos_last] = UDR;
}

