/*
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
*/

/**
 * @file		ScatterWeb.Uart.c
 * @ingroup		uart1
 * @brief		UART interface
 * @author		Michael Baar	<baar@inf.fu-berlin.de>
 *
 * UART switch for RS232 and SPI protocols on UART1 written for
 * ScatterWeb MSB boards. Compatible to ScatterWeb EOS,
 * ScatterWeb Bootloader.
 */

// Note: this file is also used for the bootloader without libScatterweb
#include <string.h>
#if SCATTERWEB_BLD
	#include <msp430x16x.h>
	#include <signal.h>
	#include <ScatterWeb.TypeDefs.h>
	#include <ScatterWeb.Uart.h>
#else
	#include <ScatterWeb.System.h>
#endif

void _uart_configure(uint8_t mode);
void _uart_set_mode(uint8_t mode);

volatile uint8_t 		uart_mode;
volatile uint8_t		uart_lockcnt;

static uint8_t				_uart_speed_br0[UART_NUM_MODES];
static uint8_t				_uart_speed_br1[UART_NUM_MODES];
static uint8_t				_uart_speed_bmn[UART_NUM_MODES];
static fp_uart_handler		_uart_handler[UART_NUM_MODES];

static uint8_t calc_umctl(uint16_t br) {
	// from TI slaa049
	register uint8_t CMOD = 256 * br - 256 * (br + 1) / 2;
	register uint8_t c = 0;
	register int i = 0;
	register uint8_t a = CMOD;
	a <<= 1;
	do {
		if( a & 0x80 ) {		// Overflow to integer?
			a = a - 128 + CMOD;	// Yes, subtract 1.000000
			c |= 0x80;
		} else {
			a += CMOD;			// No, add fraction
		}
		if( i == 7 )
			return c;
		i++;
		c >>= 1;
	} while(1);
}
/*---------------------------------------------------------------------------*/
void
uart_init()
{
	uart_mode = UART_MODE_RESET;
	uart_lockcnt = 0;
	memset(_uart_handler, 0, sizeof(_uart_handler));
}
/*---------------------------------------------------------------------------*/
void
uart_set_speed(uint8_t mode, uint32_t baudrate)
{
	uint16_t br = (uint16_t)(mclock / baudrate);
	
	// store setting
	if( mode == UART_MODE_SPI ) {
		if( br < 2 ) br = 2;
		_uart_speed_bmn[mode] = 0;				// modulation
	} else {
		_uart_speed_bmn[mode] = calc_umctl(br);	// modulation
	}
	_uart_speed_br0[mode] = br;					// baudrate
	_uart_speed_br1[mode] = br>>8;				// baudrate

	// reconfigure, if mode active
	if( uart_mode == mode )
		_uart_configure(mode);
}
/*---------------------------------------------------------------------------*/
bool
uart_lock(uint8_t mode)
{
	// already locked?
	if( (mode != uart_mode) && (uart_lockcnt) ) {
		return false;
	} else {
		// increase lock count
		uart_lockcnt++;
		// switch mode (if neccessary)
		_uart_set_mode(mode);
		return true;
	}
}
/*---------------------------------------------------------------------------*/
bool
uart_lock_wait(uint8_t mode) {
	while( UART_WAIT_LOCK(mode) ) { _NOP(); }
	return uart_lock(mode);
}
/*---------------------------------------------------------------------------*/
bool
uart_unlock(uint8_t mode)
{
	/*
	Do we wan't strict checking?
	if( (uart_lockcnt == 0) || (mode != uart_mode) )
		return false;
	*/

	// mode is currently not used, touch it to prevent warning
	mode = mode;

	// decrement lock
	if( uart_lockcnt > 0 )	{
		uart_lockcnt--;

		// if no more locks, switch back to default mode
		if( uart_lockcnt == 0 )
			_uart_set_mode(UART_MODE_DEFAULT);
		return true;
	}
	return false;
}
/*---------------------------------------------------------------------------*/
void
_uart_configure(uint8_t mode)
{
	_DINT();									// disable interrupts

	UART_WAIT_TXDONE();							// wait till all buffered data has been transmitted

	// configure
	if( mode == UART_MODE_RS232 ) {
		P5OUT |= 0x01;							// unselect SPI
		P3SEL |= 0xC0;							// select rs232
		// to RS232 mode
		UCTL1 = SWRST | CHAR;          			// 8-bit character
		UTCTL1 |= SSEL1;						// UCLK = MCLK
		// activate
		U1ME |= UTXE1 | URXE1;					// Enable USART1 TXD/RXD
	} else if( mode == UART_MODE_SPI ) {
		P3SEL &= ~0xC0;							// unselect RS232
		// to SPI mode
		UCTL1 = SWRST | CHAR | SYNC | MM;		// 8-bit SPI Master
		UTCTL1 |= CKPH | SSEL1 | SSEL0 | STC;	// SMCLK, 3-pin mode, clock idle low, data valid on rising edge, UCLK delayed
		// activate
		U1ME |= USPIE1;							// Enable USART1 SPI
	}
	// restore speed settings
	UBR01  = _uart_speed_br0[mode];				// set baudrate
	UBR11  = _uart_speed_br1[mode];
	UMCTL1 = _uart_speed_bmn[mode];				// set modulation

	UCTL1 &= ~SWRST;							// clear reset flag

	_EINT();									// enable interrupts
}
/*---------------------------------------------------------------------------*/
#ifndef SCATTERWEB_BLD

interrupt(NOVECTOR) _uart_rx(void);
interrupt(NOVECTOR) _uart_rx(void) {}

/*---------------------------------------------------------------------------*/
void
uart_set_handler(uint8_t mode, fp_uart_handler fpHandler)
{
	// store setting
	_uart_handler[mode] = fpHandler;
}

#else
	// compiling the bootloader
#endif
/*---------------------------------------------------------------------------*/
void
_uart_set_mode(uint8_t mode)
{
	// do nothing if mode already set
	if( mode == uart_mode )
		return;

	IE2 &= ~(URXIE1 | UTXIE1);					// disable irq
	_uart_configure(mode);						// configure uart parameters
	uart_mode = mode;

	// restore irq
	#ifndef SCATTERWEB_BLD
		if( _uart_handler[mode] ) {
			SET_ISR(UART1RX_VECTOR, _uart_handler[mode]);
			IE2 |= URXIE1;							// Enable USART1 RX interrupt
		} else {
			SET_ISR(UART1RX_VECTOR, _uart_rx);		// Catch and ignore interrupt
			/* Note:
			   This interrupt gets called event though URXIE1 flag is not set. Why?
			*/
		}
	#else
		// no scatterweb -> no set_isr
	#endif
}
