/*
Copyright 2007, Freie Universitaet Berlin. All rights reserved.

These sources were developed at the Freie Universität Berlin, Computer
Systems and Telematics group.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
 
- Neither the name of Freie Universitaet Berlin (FUB) nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

This software is provided by FUB and the contributors on an "as is"
basis, without any representations or warranties of any kind, express
or implied including, but not limited to, representations or
warranties of non-infringement, merchantability or fitness for a
particular purpose. In no event shall FUB or contributors be liable
for any direct, indirect, incidental, special, exemplary, or
consequential damages (including, but not limited to, procurement of
substitute goods or services; loss of use, data, or profits; or
business interruption) however caused and on any theory of liability,
whether in contract, strict liability, or tort (including negligence
or otherwise) arising in any way out of the use of this software, even
if advised of the possibility of such damage.

This implementation was developed by the CST group at the FUB.

For documentation and questions please use the web site
http://scatterweb.mi.fu-berlin.de and the mailinglist
scatterweb@lists.spline.inf.fu-berlin.de (subscription via the Website).
Berlin, 2007
*/

#include <ScatterWeb.System.h>

#if 0
/**
 * @brief	Baudrate settings
 * 
 * triple {UBR01, UBR11, UMCTL1}
 */
const uint8_t	comm_bps_set[8][3] = {
	{0x55, 0x07, 0x09},		// BPS_2400
	{0xAA, 0x03, 0x6D},		// BPS_4800
	{0xD5, 0x01, 0x52},		// BPS_9600
	{0x38, 0x01, 0xEF},		// BPS_14400
	{0xEA, 0x00, 0x5B},		// BPS_19200
	{0x75, 0x00, 0x52},		// BPS_38400
	{0x4E, 0x00, 0x44},		// BPS_57600
	{0x27, 0x00, 0x10}		// BPS_115200
};
#endif

volatile uint8_t serial_buf1[SERIAL_BUFSIZE]; // The serial ISR writes bytes into this buffer.
volatile uint8_t serial_buf2[SERIAL_BUFSIZE];
volatile uint8_t* serial_line;
volatile uint8_t* buf = serial_buf1;
//volatile uint8_t* rts_port;
//volatile uint8_t* cts_port;
volatile uint16_t serial_pos = 0; // Current position in receive buffer ::serial_buffer.
volatile uint8_t	serial_mode = 0;

#ifndef SERIAL_STOP
#define SERIAL_STOP()
#define SERIAL_RESUME()
#endif

//#define SERIAL_STOP()       do{ *cts_port |=  cts_bit; }while(0)  // Set CTS to 1 to stop receiving bytes.
//#define SERIAL_RESUME()     do{ *cts_port &= ~cts_bit; }while(0)  // Set CTS to 0 to resume receiving bytes.

//uint8_t  rts_bit;
//uint8_t  cts_bit;

int putchar(int c) {
	return Comm_writer(c);
}

int _Comm_writer(int c) {
	UART_WAIT_TX();
	UART_TX = c;
	return c;	
}

int Comm_writer(int c) {
	if( UART_MODE_IS(UART_MODE_RS232) ) {
		return _Comm_writer(c);
	} else {
		return -1;
	}
}

void Comm_writeChars(const char* data, const int n) {	
	register const char*	c = data;
	register const char*	d = data + n;

	for(; c < d; c++)
		Comm_writer(*c);
}

bool Comm_printf(const char *fmt, ...) {
	if( uart_lock(UART_MODE_RS232) ) {
		va_list argp;
		va_start(argp, fmt);
		vuprintf(_Comm_writer, fmt, argp);
		va_end(argp);

		uart_unlock(UART_MODE_RS232);
	}
	return true;
}

void Comm_clear() {
	runModule &= ~MF_SERIAL;
	serial_line = 0;
	SERIAL_RESUME();
}

/**
 * @brief	Serial ISR-Routine for receiving bytes.
 */
interrupt(NOVECTOR) serial_rec() {
	uint8_t temp = RXBUF1;
	uint8_t b;

	if( serial_pos == 0 )
		serial_mode = 0;

	switch(temp) {
		case '\r':	// filter out carriage returns
			break;
			  
		case '\n':	// line is complete
			SERIAL_STOP();            // set CTS to 1 to stop receiving bytes
			buf[serial_pos] = '\0'; // terminate
			if(buf == serial_buf1) {
				buf         = serial_buf2;
				serial_line = serial_buf1;
			} else {
				buf         = serial_buf1;
				serial_line = serial_buf2;
			}
			WAKEUPLPM1(MF_SERIAL);
			serial_pos = 0;
			break;
				  
		case '\b':	// backspace
			if( serial_pos > 0 ) {
				if( (serial_mode & 0xFE) == 0 )
					serial_pos--;
				else
					buf[serial_pos] &= 0xF0;
			}
			break;
			  
		default: // data
			if( serial_pos == 0 ) {
				// check first character for marker
				if( temp == '>' ) {
					buf[serial_pos++] = temp;
					serial_mode = 0x02;
					return;
				}
			} else if( serial_pos == SERIAL_BUFSIZE ) {
				// buffer full
				return;   
			}

			if( serial_mode == 0 ) {
				// ASCII mode
				buf[serial_pos++] = temp;
			} else {
				// HEX mode
			  	
			  	b = 0;
				if( (temp >= '0') && (temp <= '9') )
					b = temp - '0';
				else if( (temp >= 'A') && (temp <= 'F') )
					b = temp - 'A' + 10;
			  	
				  if( serial_mode & 0x01 ) {
					  buf[serial_pos++] += b;
					  serial_mode = 0x02;
				  } else {
					  buf[serial_pos] = (b << 4);
					  serial_mode = 0x03;
				  }
			  }
		// end default case
	}
}

interrupt(NOVECTOR) autorxIRQ() {
	uint32_t bps;
	uint8_t temp = UART_RX;
	IE2 &= ~URXIE1; // Disable USART1 RX interrupt
	
  switch (temp) {
    case 0xE6 :
		bps = 57600;
      break;
    case 0x1C :
      bps = 38400;
      break;
    case 0xE0 :
      bps = 19200;
      break;
    case 0x80 :
      bps = 14400;
      break;
    case 0x0D :
    default :
      bps = 115200;
      break;
	}
	uart_set_speed(UART_MODE_RS232, bps);
	UART_RX = 0;    // clear RX Buffer from Dummy-Inputs
	IE2 |= URXIE1; // Enable USART1 RX interrupt
}

bool Comm_init() {

  Comm_clear();
  
  // select rs232
  P3SEL |= 0xC0;
  // configure baudrate
  uart_set_speed(UART_MODE_RS232, 115200);
  //Comm_setSpeed(BPS_115200);
  // configure rs232 receive handler
  uart_set_handler(UART_MODE_RS232, serial_rec);
  
  return true;
}

