/*
 * c't-Bot
 * 
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your
 * option) any later version. 
 * This program is distributed in the hope that it will be 
 * useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 * PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public 
 * License along with this program; if not, write to the Free 
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307, USA.
 * 
 */

/*! @file 	uart.c 
 * @brief 	Routinen zur seriellen Kommunikation
 * @author 	Benjamin Benz (bbe@heise.de)
 * @date 	26.12.05
*/

#ifdef MCU 

#include "ct-Bot.h"

#include <avr/io.h>
#include <avr/interrupt.h>
#ifndef NEW_AVR_LIB
	#include <avr/signal.h>
#endif
#include "ct-Bot.h"
#include "uart.h"
#include "command.h"
#include "log.h"

#ifdef UART_AVAILABLE

#define BAUDRATE	57600

#define UART_RX_BUFFER_SIZE 16	/*!< Größe des UART-Puffers */

#define UART_RX_BUFFER_MASK ( UART_RX_BUFFER_SIZE - 1 )
#if ( UART_RX_BUFFER_SIZE & UART_RX_BUFFER_MASK )
	#error RX buffer size is not a power of 2
#endif

//#define UART_TIMEOUT	20000	/*!< Timeout. Wartet UART_TIMEOUT CPU-Takte */

static uint8 UART_RxBuf[UART_RX_BUFFER_SIZE];	/*!< UART-Puffer */
static volatile uint8 UART_RxHead;				/*!< Zeiger für UART-Puffer */
static volatile uint8 UART_RxTail;				/*!< Zeiger für UART-Puffer */

//char uart_timeout;	/*!< 0, wenn uart_read/uart_send erfolgreich 1, wenn timeout erreicht */

/*!
 * Initialisiere UART
 */
void uart_init(void){	 
	#ifdef __AVR_ATmega644__
		/* Senden und Empfangen ermöglichen + RX Interrupt an */
		UCSR0B= (1<<RXEN0) | (1<<TXEN0)|(1<<RXCIE0); 			
		/* 8 Bit, 1 Stop, Keine Parity */
		UCSR0C=0x86;
	#else
		/* Senden und Empfangen ermöglichen + RX Interrupt an */
		UCSRB= (1<<RXEN) | (1<<TXEN)|(1<<RXCIE); 	
		/* 8 Bit, 1 Stop, Keine Parity */
		UCSRC=0x86;
	#endif
	
	/* UART auf 9600 baud */
//	UBRRH=0;
//	UBRRL= 103;  /* Werte stehen im Datenblatt tabelarisch */
	#ifdef __AVR_ATmega644__
		UBRR0L = (uint8) (( ((uint32)F_CPU) / 16 / ((uint32)BAUDRATE) - 1) & 0xFF);
		UBRR0H = (uint8) (( ((uint32)F_CPU) / 16 / ((uint32)BAUDRATE) - 1) >> 8);
	#else
		UBRRL = (uint8) (( ((uint32)F_CPU) / 16 / ((uint32)BAUDRATE) - 1) & 0xFF);
		UBRRH = (uint8) (( ((uint32)F_CPU) / 16 / ((uint32)BAUDRATE) - 1) >> 8);
	#endif
	
	/* Puffer leeren */
	UART_RxTail = 0;
	UART_RxHead = 0;
}

/*!
 *  Interrupt Handler fuer den Datenempfang per UART
 */
#ifdef __AVR_ATmega644__
	SIGNAL (USART0_RX_vect){
#else
	SIGNAL (SIG_UART_RECV){
#endif

	/* Pufferindex berechnen */
	UART_RxHead++;						/* erhoehen */ 
	UART_RxHead %= UART_RX_BUFFER_MASK; /* Und bei Bedarf umklappen, da Ringpuffer */
	
	if (UART_RxHead == UART_RxTail){
		/* TODO Fehler behandeln !!
		 * ERROR! Receive buffer overflow */
	}
	#ifdef __AVR_ATmega644__
		UART_RxBuf[UART_RxHead] = UDR0; /* Daten lesen und sichern*/
	#else
		UART_RxBuf[UART_RxHead] = UDR; /* Daten lesen und sichern*/	
	#endif	
}

/*! 
 * Prüft, ob daten verfügbar 
 * @return Anzahl der verfuegbaren Bytes
 */
uint8 uart_data_available(void){
	if (UART_RxHead == UART_RxTail) 	/* Puffer leer */
		return 0;		
	else if (UART_RxHead > UART_RxTail)		/* Schreibzeiger vor Lesezeiger */ 
		return UART_RxHead - UART_RxTail; 
	else			/* Schreibzeiger ist schon umgelaufen */
		return UART_RxHead - UART_RxTail + UART_RX_BUFFER_SIZE;
}


/*!
 * Überträgt ein Zeichen per UART
 * Achtung ist noch blockierend!!!!
 * TODO: umstellen auf nicht blockierend und mehr als ein Zeichen
 * @param data Das Zeichen
 */
void uart_send_byte(uint8 data){ // Achtung ist noch blockierend!!!!
	#ifdef __AVR_ATmega644__
		while ((UCSR0A & _BV(UDRE0)) ==0){asm volatile("nop"); }	// warten bis UART sendebereit
		UDR0= data;
	#else
		while ((UCSRA & _BV(UDRE)) ==0){asm volatile("nop"); }	// warten bis UART sendebereit
		UDR= data;	
	#endif
}

/*!
 * Sende Kommando per UART im Little Endian
 * @param cmd Zeiger auf das Kommando
 * @return Anzahl der gesendete Bytes
 */
//#define uart_send_cmd(cmd)  uart_write(cmd,sizeof(command_t));

/* 
int uart_send_cmd(command_t *cmd){
	int i;
	char * ptr = (char*) cmd;
	for (i=0; i<sizeof(command_t); i++)
		uart_send_byte(*ptr++);
		
	return sizeof(command_t);
}
*/

/*!
 * Sende Daten per UART im Little Endian
 * @param data Datenpuffer
 * @param length Groesse des Datenpuffers in bytes
 * @return Anzahl der gesendete Bytes
 */
int uart_write(uint8 * data, int length){
	int i;
	char * ptr = (char*) data;
	for (i=0; i<length; i++)
		uart_send_byte(*ptr++);
		
	return length;	
}

/*!
 * Liest Zeichen von der UART
 * @param data Der Zeiger an die die gelesenen Zeichen kommen
 * @param length Anzahl der zu lesenden Bytes
 * @return Anzahl der tatsaechlich gelesenen Zeichen
 */
int uart_read(void* data, int length){
	uint8 i;
	char* ptr = data;
	
	uint8 count= uart_data_available();

//	LOG_DEBUG(("%d/%d av/sel",count,length));
	
	if (count > length)
		count=length;
		
	for (i=0; i<count; i++){
		UART_RxTail++;
		UART_RxTail %= UART_RX_BUFFER_MASK;
		*ptr++ = UART_RxBuf[UART_RxTail];
		
	}
	
	return count;
}

#endif
#endif