/*
        This file sets up USART0 stream as stdin / stdout / stderror

        Confirmed for:
        	 atmega: 168, 328, 1284
        	 Should work without mods for many others

        Features:
        	Buffered, Interrupt driven
        	Allows differing in / out buffer sizes up to 256 bytes

        Usage:
        	Specify non-default buffer sizes and baud, if desired
        	#include this file

        Gotchas:
        	app must explicitly call sei() before using stdio

        Example:

        	#define STD_USART_INSIZE 32
        	#define STD_USART_OUTSIZE 8
        	#define BAUD 9600
        	#include <std_usart.h>

        	int main(void){
        		sei();

        		puts("Hello, world!");

        	}


*/

#ifndef STD_USART_H
#define STD_USART_H

#ifndef STD_USART_INSIZE
#define STD_USART_INSIZE 16
#endif

#ifndef STD_USART_OUTSIZE
#define STD_USART_OUTSIZE 16
#endif

#ifndef BAUD
#define BAUD 38400
#endif
















/*=============================================================================================*/
#include <stdio.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <avr/io.h>



FILE std_usart_file;

/* ring buffers */
volatile uint8_t std_usart_inbuffer[STD_USART_INSIZE];
volatile uint8_t std_usart_in_head = 0;
volatile uint8_t std_usart_in_tail = 0;
volatile uint8_t std_usart_in_count = 0;

volatile uint8_t std_usart_outbuffer[STD_USART_OUTSIZE];
volatile uint8_t std_usart_out_head = 0;
volatile uint8_t std_usart_out_tail = 0;
volatile uint8_t std_usart_out_count = 0;


int std_usart_putc(char, FILE * );
int std_usart_getc( FILE * );

/*
	Init and end functions are declared 'naked'... because they aren't really functions.
	Declared 'used' to prevent them from being optimized away by pesky compilers
*/

void std_usart_init(void) __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".init8")));



/* The init code runs 'invisibly' before main() */
void std_usart_init(void) {

/* we use the avr-libc baud rate setting macro*/
#include <util/setbaud.h>
  UBRR0H = UBRRH_VALUE;
  UBRR0L = UBRRL_VALUE;
#if USE_2X
  UCSR0A |= (1 << U2X);
#endif


	/* Enable USART transmitter and receiver, and the receive interrupt */
  UCSR0B = _BV(TXEN0) | _BV(RXEN0) | _BV(RXCIE0);

  /* Set for 8, N, 1 */
  UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);

	/* link into the avr-libc stdio implementation */
  fdev_setup_stream	(&std_usart_file, std_usart_putc, std_usart_getc, _FDEV_SETUP_RW);

	/* setup as stdin/stdout/stderr */
  stdout = &std_usart_file;
  stdin = &std_usart_file;
  stderr = &std_usart_file;

}




/* we 'put()' at the head of the transmit buffer */
int std_usart_putc(char c, FILE * f){
  uint8_t i, sr;

  /* block while buffer is full */
  do{
			i = ( std_usart_out_count == STD_USART_OUTSIZE );
	} while(i);


	/* store 'character' in buffer, and increment head */
	std_usart_outbuffer[std_usart_out_head++] = c;
	if( std_usart_out_head == STD_USART_OUTSIZE ) std_usart_out_head = 0;
	sr = SREG;
	cli();
	std_usart_out_count++;
	SREG = sr;

	/* enable data register empty interrupt */
	UCSR0B |= _BV(UDRIE0);

	/* at this point, the Data Register Empty interrupt will fire */
	return 0;
}



/* We transmit from the tail of the transmit buffer */
#ifdef USART0_UDRE_vect
ISR(USART0_UDRE_vect){ /* atmega1284 and kin */
#else
ISR(USART_UDRE_vect){  /* atmega328 and kin */
#endif

	/* if the transmit buffer is not empty, transmit the next byte */
  if( std_usart_out_count ){

  	UDR0 = std_usart_outbuffer[std_usart_out_tail++];
  	if(std_usart_out_tail == STD_USART_OUTSIZE) std_usart_out_tail = 0;
    std_usart_out_count--;

  }

  else{ /* The transmit buffer is empty, disable the Data Register Empty interrupt */
  	UCSR0B = _BV(RXCIE0) | _BV(TXEN0) | _BV(RXEN0);
  }
}




/*
	We receive at the head of the receive buffer.
	If there is no room, we have to drop the byte received
*/
#ifdef USART0_RX_vect
ISR(USART0_RX_vect){
#else
ISR(USART_RX_vect){
#endif
uint8_t c;

  c = UDR0;

  if(std_usart_in_count < STD_USART_INSIZE){
    std_usart_inbuffer[std_usart_in_head++] = c;
    std_usart_in_count++;
    if(std_usart_in_head == STD_USART_INSIZE) std_usart_in_head = 0;
  }
  else{ /* Set the streams error flag */
  	std_usart_file.flags |= __SERR;
  }
}





/*
	We 'get()' from the tail of the receive buffer.
*/
int std_usart_getc( FILE * f){
	uint8_t sr;

	// return EOF if buffer is empty
  int c = _FDEV_EOF;

  if( std_usart_in_count ){

  	c = std_usart_inbuffer[std_usart_in_tail++];
    if(  std_usart_in_tail == STD_USART_INSIZE ) std_usart_in_tail = 0;
    sr = SREG;
  	cli();
    std_usart_in_count--;
    SREG = sr;

  }

  return c;
}





/*
			The standard exit disables interrupts and goes into an infinite loop. We want to keep
			interrupts enabled so that all characters in std_usart_outbuffer are transmitted. We also
			want to loop in sleep mode, rather than waste power. When the transmit buffer, no further
			interrupts will occur, causing the mcu to sleep 'forever'.
*/

void std_usart_end(void) __attribute__ ((naked)) __attribute__((used)) __attribute__ ((section (".fini1")));

#include <avr/sleep.h>

void std_usart_end(void){
	set_sleep_mode(0); /* 'Idle' sleep mode allows USART to continue working */
	do{
		sleep_mode();
	}while(1);
}


#endif /* STD_USART_H */
