/*
 * Copyright (c) 2010-2012 Digi International Inc.,
 * All rights not expressly granted are reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 * =======================================================================
 */
/**
	@addtogroup hal_posix
	@{
	@file xbee_serial_msp430_spi.c
	Serial Interface for XBee Module (MSP430 Platform)

*/
// NOTE: Documentation for these functions can be found in xbee/serial.h.

#include <limits.h>
#include <errno.h>
//#include <fcntl.h>
#include <stdio.h>
//#include <termios.h>
//#include <unistd.h>
//#include <sys/ioctl.h>
//#include BOARD_HEADER
#include "cavalier_board_cfg_rev2.h"
#include "usci_b_spi.h"
#include "ucs.h"
#include "serial.h"

//#define XBEE_SERIAL_VERBOSE		// *** DEBUG

/**************************************************************************************************
*                                           TYPEDEFS / ENUMS
**************************************************************************************************/

typedef struct
{
    uint16_t head;
    uint16_t tail;
    uint16_t size;
    uint8_t *buf;
} queue_t;


#define RX_SPI_BUF_SIZE		600		// SWAG
#define TX_SPI_BUF_SIZE		270     // SWAG

#define XBEE_SER_CHECK(ptr)	\
	do { if (xbee_ser_invalid(ptr)) return -EINVAL; } while (0)

/**************************************************************************************************
*                                           LOCAL VARIABLES
**************************************************************************************************/

/*static*/ queue_t rx_queue;
/*static*/ queue_t tx_queue;	// not currently used

/*static*/ uint8_t	rxSpiBuffer[RX_SPI_BUF_SIZE];
/*static*/ uint8_t	txSpiBuffer[TX_SPI_BUF_SIZE];

/**************************************************************************************************
*                                       PROTOTYPES
**************************************************************************************************/
static inline Result_t dequeue(queue_t *q, uint8_t *data);
static inline Result_t enqueue(queue_t *q, uint8_t data);
static inline int16_t queue_used(queue_t *q);
static inline Result_t queue_flush(queue_t *q);
static void spi_wait_tx_empty( void );
static void spi_wait_rx_received( void );

/**************************************************************************************************
*                                       PUBLIC API
**************************************************************************************************/

/**
	@brief
	Helper function used by other xbee_serial functions to
	validate the \a serial parameter.

	Confirms that it is non-\c NULL and is set to a valid port.

	@param[in]	serial	XBee serial port

	@retval	1 \a serial is not a valid XBee serial port
	@retval	0 \a serial is a valid XBee serial port
*/
bool_t xbee_ser_invalid( xbee_serial_t *serial )
{
	//if (serial && serial->fd >= 0)
	if (serial)
	{
		return 0;
	}

	#ifdef XBEE_SERIAL_VERBOSE
		if (serial == NULL)
		{
			printf( "%s: serial=NULL\n", __FUNCTION__);
		}
		else
		{
			printf( "%s: serial=%p, serial->fd=%d (invalid)\n", __FUNCTION__,
				serial, serial->fd);
		}
	#endif

	return 1;
}

/**
	@brief
	Returns a human-readable string describing the serial port.

	For example, on a Windows machine this will be "COM1" or "COM999".
	On a Rabbit, it will be a single letter, "A" through "F".
	On Freescale HCS08, it will be something like "SCI1" or "SCI2".
	On POSIX, the name of the device (e.g., "/dev/ttyS0")

	Returns "(invalid)" if \a serial is invalid or not configured.

	@param[in]	serial	port

	@return	null-terminated string describing the serial port
*/
const char *xbee_ser_portname( xbee_serial_t *serial)
{
	if (serial == NULL)
	{
		return "(invalid)";
	}

	//return serial->device;
	return "SPI";
}

/**
	@brief
	Transmits \a length bytes from \a buffer to the XBee serial
	port \a serial.

	@param[in]	serial	XBee serial port

	@param[in]	buffer	source of bytes to send

	@param[in]	length	number of bytes to write (may =0 to just receive bytes)

	@retval	>=0		The number of bytes successfully written to XBee serial port.

	@retval	-EINVAL		serial is not a valid XBee serial port.
	@retval	-EIO		I/O error attempting to write to serial port.

	@see	xbee_ser_read(), xbee_ser_putchar(), xbee_ser_getchar()
*/
int xbee_ser_write( xbee_serial_t *serial, const void FAR *buffer, int length )
{
	int result = 0;
	int	i;
	uint8_t	recv_char;
	uint8_t *buf_pt = (uint8_t *)buffer;

	XBEE_SER_CHECK( serial);
	if (length < 0) {
		return -EINVAL;
	}

	SELECT_XBEE_SPI();
    TA1CTL &= ~(0x0002);  // turn timer interrupts off (Timer Interrupt is bit 0x0002)
	if ( length > 0 ) {
		// Send out all TX bytes over SPI.  Queue any received bytes
		for( i=0; i<length; i++ ) {
			spi_wait_tx_empty();
			USCI_B_SPI_transmitData( XBEE_SPI_PORT, *buf_pt++ );
			spi_wait_rx_received();
			recv_char = USCI_B_SPI_receiveData(XBEE_SPI_PORT);
			if( XBEE_ATTN_ASSERTED() ) { // ?? Is this a good thing?  Should we parse the stream instead?
				enqueue(&rx_queue, recv_char);
			}
		}
	}

	TA1CTL |= (0x0002);  // turn timer interrupt back on (Timer Interrupt is bit 0x0002)
//	milli_wait(1); // give timer a chance to get into the routine
//    TA1CTL &= ~(0x0002);  // turn timer interrupts off (Timer Interrupt is bit 0x0002)

	// check for additional bytes to receive.  Send dummy TX to get them.
	while( XBEE_ATTN_ASSERTED() ) { // ?? Is this a good thing?  Should we parse the stream instead?
		spi_wait_tx_empty();
		// transmit dummy 'ff' data to finish receive
		USCI_B_SPI_transmitData( XBEE_SPI_PORT, 0xff );
		spi_wait_rx_received();
		recv_char = USCI_B_SPI_receiveData(XBEE_SPI_PORT);
		enqueue(&rx_queue, recv_char);
	}
    TA1CTL |= (0x0002);  // turn timer interrupt back on (Timer Interrupt is bit 0x0002)
	DESELECT_XBEE_SPI();


	if (result < 0) {
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: error %d trying to write %d bytes\n", __FUNCTION__,
				errno, length);
		#endif
		return -errno;
	}

	#ifdef XBEE_SERIAL_VERBOSE
		printf( "%s: wrote %d of %d bytes\n", __FUNCTION__, length, length);
		hex_dump( buffer, length, HEX_DUMP_FLAG_TAB);
	#endif

	return result;
}

/**
	@brief
	Reads up to \a bufsize bytes from XBee serial port \a serial
	and into \a buffer.

	If there is no data available when the function is
	called, it will return immediately.

	@param[in]	serial	XBee serial port

	@param[out]	buffer	buffer to hold bytes read from XBee serial port

	@param[in]	bufsize	maximum number of bytes to read

	@retval	>=0		The number of bytes read from XBee serial port.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.
	@retval	-EIO		I/O error attempting to write to serial port.

	@see	xbee_ser_write(), xbee_ser_putchar(), xbee_ser_getchar()
*/
int xbee_ser_read( xbee_serial_t *serial, void FAR *buffer, int bufsize )
{
	int result = 0;
	int	i;
	uint8_t	recv_char;
	uint8_t *buf_pt = (uint8_t *)buffer;

	XBEE_SER_CHECK( serial);
	if (! buffer || bufsize < 0) {
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: buffer=%p, bufsize=%d; return -EINVAL\n", __FUNCTION__,
				buffer, bufsize);
		#endif
		return -EINVAL;
	}

	// First we do a check to see if we need to receive bytes into queue
	if( XBEE_ATTN_ASSERTED() ) {
        SELECT_XBEE_SPI();
	    TA1CTL &= ~(0x0002);  // turn timer interrupts off (Timer Interrupt is bit 0x0002)
		// check for additional bytes to receive.  Send dummy TX to get them.
		while( XBEE_ATTN_ASSERTED() ) { // ?? Is this a good thing?  Should we parse the stream instead?
			spi_wait_tx_empty();
			// transmit dummy 'ff' data to finish receive
			USCI_B_SPI_transmitData( XBEE_SPI_PORT, 0xff );
			spi_wait_rx_received();
			recv_char = USCI_B_SPI_receiveData(XBEE_SPI_PORT);
			enqueue(&rx_queue, recv_char);
		}
	    TA1CTL |= (0x0002);  // turn timer interrupt back on (Timer Interrupt is bit 0x0002)
		DESELECT_XBEE_SPI();
	}

	// Now read off bytes from RX queue
	for( i=0; i<bufsize; i++ ) {
		if( dequeue(&rx_queue, buf_pt++ ) != RESULT_SUCCESS )
			break;
		result++;
	}

	if (result == -1)
	{
		if (errno == EAGAIN)
		{
			return 0;
		}
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: error %d trying to read %d bytes\n", __FUNCTION__,
				errno, bufsize);
		#endif
		return -errno;
	}

	#ifdef XBEE_SERIAL_VERBOSE
	if( result > 0) {
		printf( "%s: read %d bytes\n", __FUNCTION__, result);
		hex_dump( buffer, result, HEX_DUMP_FLAG_TAB);
	}
	#endif

	return result;
}

/**
	@brief
	Transmits a single character, \a ch, to the XBee serial
	port \a serial.

	@param[in]	serial	XBee serial port

	@param[in]	ch			character to send

	@retval	0			Successfully sent (queued) character.
	@retval	-ENOSPC	The write buffer is full and the character wasn't sent.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_read(), xbee_ser_write(), xbee_ser_getchar()
*/
int xbee_ser_putchar( xbee_serial_t *serial, uint8_t ch)
{
	int retval;

	retval = xbee_ser_write( serial, &ch, 1);
	if (retval == 1) {
		return 0;
	} else if (retval == 0) {
		return -ENOSPC;
	} else {
		return retval;
	}
}

/**
	@brief
	Reads a single character from the XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	0-255	character read from XBee serial port
	@retval	-ENODATA	There aren't any characters in the read buffer.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_read(), xbee_ser_write(), xbee_ser_getchar()
*/
int xbee_ser_getchar( xbee_serial_t *serial)
{
	uint8_t ch = 0;
	int retval;

	retval = xbee_ser_read( serial, &ch, 1);
	if (retval != 1) {
		return retval ? retval : -ENODATA;
	}

	return ch;
}

/**
	@brief
	Returns the number of bytes of unused space in the serial
	transmit buffer for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	INT_MAX	The buffer size is unlimited (or unknown).
	@retval	>=0		The number of bytes it would take to fill the XBee
							serial port's serial transmit buffer.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_rx_free(), xbee_ser_rx_used(), xbee_ser_rx_flush(),
				xbee_ser_tx_used(), xbee_ser_tx_flush()
*/
int xbee_ser_tx_free( xbee_serial_t *serial)
{
	XBEE_SER_CHECK( serial);

	return INT_MAX;		//***TBD
}

/**
	@brief
	Returns the number of queued bytes in the serial transmit buffer
	for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	0			The buffer size is unlimited (or space used is unknown).
	@retval	>0			The number of bytes queued in the XBee
							serial port's serial transmit buffer.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_rx_free(), xbee_ser_rx_used(), xbee_ser_rx_flush(),
				xbee_ser_tx_free(), xbee_ser_tx_flush()
*/
int xbee_ser_tx_used( xbee_serial_t *serial)
{
	XBEE_SER_CHECK( serial);

	//return( queue_used( tx_queue ) );	// not yet implemented
	return 0;		// ***TBD
}

/**
	@brief
	Flushes (i.e., deletes and does not transmit) characters in the
	serial transmit buffer for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	0			success
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_rx_free(), xbee_ser_rx_used(), xbee_ser_rx_flush(),
				xbee_ser_tx_free(), xbee_ser_tx_used()
*/
int xbee_ser_tx_flush( xbee_serial_t *serial)
{
	XBEE_SER_CHECK( serial);

	//queue_flush( tx_queue );	// not yet implemented

	return 0;
}

/**
	@brief
	Returns the number of bytes of unused space in the serial
	receive buffer for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	INT_MAX	The buffer size is unlimited (or unknown).
	@retval	>=0		The number of bytes it would take to fill the XBee
							serial port's serial receive buffer.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_tx_free(), xbee_ser_tx_used(), xbee_ser_tx_flush(),
				xbee_ser_rx_used(), xbee_ser_rx_flush()
*/
int xbee_ser_rx_free( xbee_serial_t *serial)
{
	XBEE_SER_CHECK( serial);

	return INT_MAX;		// ***TBD - who cares?
}

/**
	@brief
	Returns the number of queued bytes in the serial receive buffer
	for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	>=0		The number of bytes queued in the XBee
							serial port's serial transmit buffer.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_tx_free(), xbee_ser_tx_used(), xbee_ser_tx_flush(),
				xbee_ser_rx_free(), xbee_ser_rx_flush()

	@note Unlike xbee_ser_tx_used(), this function MUST return the number
			of bytes available.  Some layers of the library wait until enough
			bytes are ready before continuing.

			We may expand on or replace this API.  On some platforms (like
			Win32) we have to do some of our own buffering in order to
			peek at data in the serial receive buffer.  Most of the driver
			only requires a check to see if some number of bytes are available
			or not.  Consider changing the API to something like:

	@code	int xbee_ser_rx_avail( xbee_serial_t *serial, uint8_t count)
	@endcode
			returns TRUE if \c count bytes are available, FALSE otherwise.
*/
int xbee_ser_rx_used( xbee_serial_t *serial )
{
	XBEE_SER_CHECK( serial);

	return( queue_used( &rx_queue ) );
}

/**
	@brief
	Deletes all characters in the serial receive buffer for XBee serial
	port \a serial.

	@param[in]	serial	XBee serial port

	@retval	0			success
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_tx_free(), xbee_ser_tx_used(), xbee_ser_tx_flush(),
				xbee_ser_rx_free(), xbee_ser_rx_used()
*/
int xbee_ser_rx_flush( xbee_serial_t *serial)
{
	XBEE_SER_CHECK( serial);

	queue_flush( &rx_queue );
	return 0;
}

/**
	@brief
	Change the baud rate of XBee serial port \a serial to
	\a baudrate bits/second.

	@param[in]	serial	XBee serial port

	@param[in]	baudrate	Bits per second of serial data transfer speed.

	@retval	0			Opened serial port within 5% of requested baudrate.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.
	@retval	-EIO		Can't open serial port within 5% of requested baudrate.

	@see xbee_ser_open(), xbee_ser_close(), xbee_ser_break()
*/
#define _BAUDCASE(b)		case b: baud = B ## b; break
int xbee_ser_baudrate( xbee_serial_t *serial, uint32_t baudrate)
{
#if 0			// not needed for SPI
	struct termios options;
	unsigned int baud;

	XBEE_SER_CHECK( serial);

	switch (baudrate)
	{
		_BAUDCASE(0);
		_BAUDCASE(9600);
		_BAUDCASE(19200);
		_BAUDCASE(38400);
		_BAUDCASE(57600);
		_BAUDCASE(115200);
		default:
			return -EINVAL;
	}

	// Get the current options for the port...
	if (tcgetattr( serial->fd, &options) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: %s failed (%d)\n", __FUNCTION__, "tcgetattr", errno);
		#endif
		return -errno;
	}

	// Set the baud rates...
	cfsetispeed( &options, baud);
	cfsetospeed( &options, baud);

	// Enable the receiver and set local mode...
	options.c_cflag |= (CLOCAL | CREAD);

	// Set 8-bit mode
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;

	// Set the new options for the port, waiting until buffered data is sent
	if (tcsetattr( serial->fd, TCSADRAIN, &options) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: %s failed (%d)\n", __FUNCTION__, "tcsetattr", errno);
		#endif
		return -errno;
	}
#endif
	return 0;
}

/**
	@brief
	Opens the serial port connected to XBee serial port \a serial at
	\a baudrate bits/second.

	@param[in]	serial	XBee serial port

	@param[in]	baudrate	Bits per second of serial data transfer speed.

	@retval	0			Opened serial port within 5% of requested baudrate.
	@retval	-EINVAL	\a serial is not a valid XBee serial port.
	@retval	-EIO		Can't open serial port within 5% of requested baudrate.

	@see xbee_ser_baudrate(), xbee_ser_close(), xbee_ser_break()
*/
int xbee_ser_open( xbee_serial_t *serial, uint32_t baudrate )
{
	//int err;
    USCI_B_SPI_initMasterParam param = {0};
    queue_t *q;

	if (serial == NULL)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: NULL parameter, return -EINVAL\n", __FUNCTION__);
		#endif
		return -EINVAL;
	}

    //Initialize SPI Master
    param.selectClockSource = USCI_B_SPI_CLOCKSOURCE_SMCLK;
    param.clockSourceFrequency = UCS_getSMCLK();
    param.desiredSpiClock = XBEE_SPICLK_FREQ;
    param.msbFirst = USCI_B_SPI_MSB_FIRST;
    param.clockPhase = USCI_B_SPI_PHASE_DATA_CAPTURED_ONFIRST_CHANGED_ON_NEXT;
    param.clockPolarity = USCI_B_SPI_CLOCKPOLARITY_INACTIVITY_LOW;
    if( USCI_B_SPI_initMaster( XBEE_SPI_PORT, &param ) != STATUS_SUCCESS )
    {
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "xbee_ser_open failed \n" );
		#endif
		return -EIO;
    }

    // We gonna just ignore baudrate for now
    (void)baudrate;

    q = &rx_queue;
    q->size = RX_SPI_BUF_SIZE;
    q->buf = rxSpiBuffer;
    queue_flush( q );

    q = &tx_queue;
    q->size = TX_SPI_BUF_SIZE;
    q->buf = txSpiBuffer;
    queue_flush( q );

	//if (err)
	//{
    //	return err;
	//}

    USCI_B_SPI_enable(XBEE_SPI_PORT);

	return 0;
}

/**
	@brief
	Close the serial port attached to XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@retval	0			closed serial port
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_open(), xbee_ser_baudrate(), xbee_ser_break()
*/
int xbee_ser_close( xbee_serial_t *serial)
{
	int result = 0;
#if 0		// can't close SPI
	XBEE_SER_CHECK( serial);

	if (close( serial->fd) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: close(%d) failed (errno=%d)\n", __FUNCTION__,
				serial->fd, errno);
		#endif
		result = -errno;
	}
	serial->fd = -1;
#endif
	return result;
}

/**
	@brief
	Disable the serial transmit pin and pull it low to send a break
	to the XBee serial port.

	@param[in]	serial	XBee serial port

	@param[in]	enabled	Set to 1 to start the break or 0 to end the break (and
								resume transmitting).

	@retval	0	Success
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_open(), xbee_ser_close()
*/
int xbee_ser_break( xbee_serial_t *serial, bool_t enabled)
{
#if 0 	// no breaks on SPI
	XBEE_SER_CHECK( serial);

	/* Devnote regarding use of ioctl() over tcsendbreak():
		"The effect of a nonzero duration with tcsendbreak() varies. SunOS
		specifies a break of duration * N seconds, where N is at least 0.25,
		and not more than 0.5. Linux, AIX, DU, Tru64 send a break of duration
		milliseconds. FreeBSD and NetBSD and HP-UX and MacOS ignore the value
		of duration. Under Solaris and UnixWare, tcsendbreak() with nonzero
		duration behaves like tcdrain()."
		- http://linux.die.net/man/3/tcsendbreak
	*/

	if (ioctl( serial->fd, enabled ? TIOCSBRK : TIOCCBRK) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: ioctl %s failed (errno=%d)\n", __FUNCTION__,
				"TIOCMGET", errno);
		#endif
		return -errno;
	}
#endif
	return 0;
}

/**
	@brief
	Enable or disable hardware flow control (CTS/RTS) on the serial
	port for XBee serial port \a serial.

	@param[in]	serial	XBee serial port

	@param[in]	enabled	Set to 0 to disable flow control or non-zero to enable
								flow control.

	@retval	0	Success
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_set_rts(), xbee_ser_get_cts()

*/
int xbee_ser_flowcontrol( xbee_serial_t *serial, bool_t enabled)
{
#if 0		// No Flow Control
	struct termios options;

	XBEE_SER_CHECK( serial);

	// Get the current options for the port...
	if (tcgetattr( serial->fd, &options) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: %s failed (%d)\n", __FUNCTION__, "tcgetattr", errno);
		#endif
		return -errno;
	}

	if (enabled)
	{
		options.c_cflag |= CRTSCTS;
	}
	else
	{
		options.c_cflag &= ~CRTSCTS;
	}

	// Set the new options for the port immediately
	if (tcsetattr( serial->fd, TCSANOW, &options) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: %s failed (%d)\n", __FUNCTION__, "tcsetattr", errno);
		#endif
		return -errno;
	}
#endif
	return 0;
}

/**
	@brief
	Disable hardware flow control and manually set the RTS (ready to
	send) pin on the XBee device's serial port.

	Typically used to enter the XBee device's boot loader and initiate
	a firmware update.

	@param[in]	serial	XBee serial port

	@param[in]	asserted	Set to 1 to assert RTS (ok for XBee to send to us)
								or 0 to deassert RTS (tell XBee not to send to us).

	@retval	0	Success
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_flowcontrol(), xbee_ser_get_cts()
*/
int xbee_ser_set_rts( xbee_serial_t *serial, bool_t asserted)
{
#if 0		// No Flow Control
	int status;

	XBEE_SER_CHECK( serial);

	// disable flow control so our manual setting will stick
	xbee_ser_flowcontrol( serial, 0);

	if (ioctl( serial->fd, TIOCMGET, &status) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: ioctl %s failed (errno=%d)\n", __FUNCTION__,
				"TIOCMGET", errno);
		#endif
		return -errno;
	}

	if (asserted)
	{
		status |= TIOCM_RTS;
	}
	else
	{
		status &= ~TIOCM_RTS;
	}

	if (ioctl( serial->fd, TIOCMSET, &status) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: ioctl %s failed (errno=%d)\n", __FUNCTION__,
				"TIOCMSET", errno);
		#endif
		return -errno;
	}
#endif
	return 0;
}


/**
	@brief
	Read the status of the /CTS (clear to send) pin on the serial
	port connected to XBee serial port \a serial.

	Note that this
	function doesn't return the value of the pin -- it returns
	whether it's asserted (i.e., clear to send to the XBee serial
	port) or not.

	@param[in]	serial	XBee serial port

	@retval	1			it's clear to send
	@retval	0			it's not clear to send
	@retval	-EINVAL	\a serial is not a valid XBee serial port.

	@see	xbee_ser_flowcontrol(), xbee_ser_set_rts()
*/
int xbee_ser_get_cts( xbee_serial_t *serial)
{
#if 0		// No Flow Control
	int status;

	XBEE_SER_CHECK( serial);

	if (ioctl( serial->fd, TIOCMGET, &status) == -1)
	{
		#ifdef XBEE_SERIAL_VERBOSE
			printf( "%s: ioctl %s failed (errno=%d)\n", __FUNCTION__,
				"TIOCMGET", errno);
		#endif
		return -errno;
	}

	return (status & TIOCM_CTS) ? 1 : 0;
#else
	return 1;		// Always clear to send
#endif
}

/**************************************************************************************************
*                                          LOCAL FUNCTIONS
**************************************************************************************************/

/**********************************************************************************************//**
 * @brief   Enqueue a data byte for transmission
 *
 * @param   q - destination queue
 * @param   data - Byte to add
 *
 * @return  Result_t
 ***************************************************************************************************/
static inline Result_t enqueue(queue_t *q, uint8_t data)
{
    //halIntState_t intState;
    if( !q )
    {
        return RESULT_FAIL;
    }

    //HAL_ENTER_CRITICAL_SECTION( intState );
    uint16_t nextHead = q->head + 1;
    if ( nextHead == q->size )
    {
        nextHead = 0;
    }

    if ( nextHead == q->tail )
    {
        //HAL_EXIT_CRITICAL_SECTION( intState );
        return RESULT_FAIL;
    }

    q->head = nextHead;

    q->buf[ q->head ] = data;

    //HAL_EXIT_CRITICAL_SECTION( intState );
    return RESULT_SUCCESS;
}

/**********************************************************************************************//**
 * @brief   Dequeue the data buffer
 *
 * @param   q - source queue
 * @param   data - Byte read from the queue
 *
 * @return  Result_t
 ***************************************************************************************************/
static inline Result_t dequeue(queue_t *q, uint8_t *data)
{
    //halIntState_t intState;

    if( !q || !data )
    {
        return RESULT_FAIL;
    }

    //HAL_ENTER_CRITICAL_SECTION( intState );

    if( q->tail == q->head )
    {
        //HAL_EXIT_CRITICAL_SECTION( intState );
        return RESULT_FAIL;
    }

    q->tail++;
    if( q->tail == q->size )
    {
        q->tail = 0;
    }

    *data = q->buf[ q->tail ];

    //HAL_EXIT_CRITICAL_SECTION( intState );
    return RESULT_SUCCESS;
}

/**********************************************************************************************//**
 * @brief   Flush queue of all data
 *
 * @param   q - destination queue
 * @param   data - Byte to add
 *
 * @return  Result_t
 ***************************************************************************************************/
static inline Result_t queue_flush(queue_t *q)
{
    //halIntState_t intState;

    if( !q )
    {
        return RESULT_FAIL;
    }

    //HAL_ENTER_CRITICAL_SECTION( intState );
    q->head = q->tail = 0;
    //HAL_EXIT_CRITICAL_SECTION( intState );

    return RESULT_SUCCESS;
}

/**********************************************************************************************//**
 * @brief   Return current number of used elements in queue
 *
 * @param   q - destination queue
 * @param   data - Byte to add
 *
 * @return  Result_t
 ***************************************************************************************************/
static inline int16_t queue_used( queue_t *q )
{
	int16_t		used;
    //halIntState_t intState;

    if( !q )
    {
        return -1;
    }

    //HAL_ENTER_CRITICAL_SECTION( intState );
    used = q->head - q->tail;
    //HAL_EXIT_CRITICAL_SECTION( intState );

    return( (used<0)? used + q->size : used );

}

/**********************************************************************************************//**
 * @brief   Wait for TX buffer to empty (w/ timeout)
 *
 * @return  none
 ***************************************************************************************************/
static void spi_wait_tx_empty( void )
{
	uint16_t timeout = 1000;	// ~1ms

	// Me no likee infinite loops
	while(timeout--)
	{
		if( USCI_B_SPI_getInterruptStatus(XBEE_SPI_PORT, UCTXIFG) )
			break;

		//DELAY_USEC(10);
	}
}

/**********************************************************************************************//**
 * @brief   Wait for RX buffer to be filled (w/ timeout)
 *
 * @return  none
 ***************************************************************************************************/
static void spi_wait_rx_received( void )
{
	uint16_t timeout = 1000;	// ~1ms

	// Me no likee infinite loops
	while(timeout--)
	{
		if( USCI_B_SPI_getInterruptStatus(XBEE_SPI_PORT, UCRXIFG) )
			break;

		//DELAY_USEC(10);
	}
}

