/*! \file stxetx.c \brief STX/ETX Packet Protocol Implementation Library. */
//*****************************************************************************
//
// File Name	: 'stxetx.c'
// Title		: STX/ETX Packet Protocol Implementation Library
// Author		: Pascal Stang - Copyright (C) 2002
// Created		: 10/9/2002
// Revised		: 6/30/2003
// Version		: 0.1
// Target MCU	: any
// Editor Tabs	: 4
//
// Description	: This library provides a set of functions needed to send and
//		receive STX/ETX packets.  STX/ETX is a simple packet protocol that can
//		be wrapped around user data for one or more of the following reasons:
//
//			1. packetization is needed
//				- Using packets can be helpful if your data naturally forms 
//				little "bunches" or if different types of data must be sent
//				over the same channel (a serial cable, for example).  If your
//				data forms "bunches", you can send user data inside STX/ETX
//				packets with a predetermined structure, like an array of A/D
//				conversion results.  If you need a way to tell the receiver
//				what kind of data you're sending, you can use the TYPE field
//				in the STX/ETX packet.
//			2. error checking is needed
//				- STX/ETX packets will add a checksum to your data.  This
//				allows the receiver to verify that data was received correctly
//				and is error-free.  Packets which are corrupted in transmission
//				and fail the the checksum test are automatically discarded.
//				Error checking is especially useful when the data transmission
//				channel is unreliable or noisy (examples: radio, infrared, long
//				cables, etc)
//	
//		STX/ETX packets have the following structure:
//
//		[STX][status][type][length][user data...][checksum][ETX]
//
//		All fields are 1 byte except for user data which may be 0-255 bytes.
//		Uppercase fields are constant (STX=0x02, ETX=0x03), lowercase fields
//		vary.  The length field is the number of bytes in the user data area.
//		The checksum is the 8-bit sum of all bytes between but not including
//		STX/ETX.
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

#include "global.h"
#include "stxetx.h"
//#include "rprintf.h"

// function pointer to data output routine
static void (*stxetxDataOut)(unsigned char data);

// received packet data buffer
unsigned char stxetxRxPacket[STXETX_MAXRXPACKETSIZE];

// functions


// Initialize STX/ETX packet protocol library
void stxetxInit(void (*dataout_func)(unsigned char data))
{
	stxetxDataOut = dataout_func;
}

// Send/Create STX/ETX packet
void stxetxSend(unsigned char status, unsigned char type, unsigned char datalength, unsigned char* dataptr)
{
	unsigned char checksum = 0;
	unsigned short i;
	
	// write packet header
	stxetxDataOut(STX);
	stxetxDataOut(status);
	stxetxDataOut(type);
	stxetxDataOut(datalength);
	// update checksum
	checksum += status + type + datalength;
	// copy data into packet
	for(i = 0; i < datalength; i++)
	{
		stxetxDataOut(*dataptr);
		checksum += *dataptr;
		dataptr++;
	}
	// write packet trailer
	stxetxDataOut(checksum);
	stxetxDataOut(ETX);
}

// process buffer containing STX/ETX packets
unsigned char stxetxProcess(cBuffer* rxBuffer)
{
	unsigned char foundpacket = FALSE;
	unsigned short i;
	unsigned char length, checksum;
	//unsigned char type;

	// process the buffer
	// go through buffer looking for packets
	// the STX must be located at least STXETX_HEADERLENGTH+STXETX_TRAILERLENGTH from end
	// otherwise we must not have a complete packet
	while( rxBuffer->datalength >= ((u16)STXETX_HEADERLENGTH+(u16)STXETX_TRAILERLENGTH) )
	{
		// look for a potential start of packet
		if(bufferGetAtIndex(rxBuffer, 0) == STX)
		{
			// if this is a start, then get the length
			length = bufferGetAtIndex(rxBuffer, STXETX_LENGTHOFFSET);

			// now we must have at least STXETX_HEADERLENGTH+length+STXETX_TRAILERLENGTH in buffer to continue
			if(rxBuffer->datalength >= ((u16)STXETX_HEADERLENGTH+length+(u16)STXETX_TRAILERLENGTH))
			{
				// check to see if ETX is in the right position
				if(bufferGetAtIndex(rxBuffer, STXETX_HEADERLENGTH+length+STXETX_TRAILERLENGTH-1) == ETX)
				{
					// found potential packet
					// test checksum
					checksum = 0;
					// sum data between STX and ETX, not including checksum itself
					// (u16) casting needed to avoid unsigned/signed mismatch
					for(i = 0; i<((u16)STXETX_HEADERLENGTH+length+(u16)STXETX_TRAILERLENGTH-(u16)STXETX_NOETXSTXCHECKSUM); i++)
					{
						checksum += bufferGetAtIndex(rxBuffer, i+STXETX_STATUSOFFSET);
					}
					// compare checksums
					if(checksum == bufferGetAtIndex(rxBuffer, STXETX_CHECKSUMOFFSET+length))
					{
						//we have a packet!
						foundpacket = TRUE;
					
						// copy data to buffer
						// (don't copy STX, ETX, or CHECKSUM)
						for(i = 0; i < ((u16)STXETX_HEADERLENGTH+length-1); i++)
						{
							stxetxRxPacket[i] = bufferGetAtIndex(rxBuffer, i+1);
						}

						// debug
						//rprintf("STXETX Received packet type: 0x%x\n", bufferGetAtIndex(rxBuffer, STXETX_TYPEOFFSET));

						// dump this packet from the
						bufferDumpFromFront(rxBuffer, STXETX_HEADERLENGTH+length+STXETX_TRAILERLENGTH);

						// done with this processing session
						break;
					}
					else
					{
						// checksum bad
						//rprintf("STXETX Received packet with bad checksum\r\n");
						// for now, we dump these
						// dump this STX
						bufferGetFromFront(rxBuffer);
					}
				}
				else
				{
					// no ETX or ETX in wrong position
					// dump this STX
					bufferGetFromFront(rxBuffer);
				}
			}
			else
			{
				// not enough data in buffer to decode pending packet
				// wait until next time
				break;
			}
		}
		else
		{
			// this is not a start, dump it
			bufferGetFromFront(rxBuffer);
		}
	}

	// check if receive buffer is full with no packets decoding
	// (ie. deadlocked on garbage data or packet that exceeds buffer size)
	if(!bufferIsNotFull(rxBuffer))
	{
		// dump receive buffer contents to relieve deadlock
		bufferFlush(rxBuffer);
	}
	
	return foundpacket;
}

unsigned char stxetxGetRxPacketStatus(void)
{
	// return the packet's status
	// (subtract 1 from the offset because the STX has already been discarded)
	return stxetxRxPacket[STXETX_STATUSOFFSET-1];
}

unsigned char stxetxGetRxPacketType(void)
{
	// return the packet's type
	// (subtract 1 from the offset because the STX has already been discarded)
	return stxetxRxPacket[STXETX_TYPEOFFSET-1];
}

unsigned char stxetxGetRxPacketDatalength(void)
{
	// return the packet's datalength
	// (subtract 1 from the offset because the STX has already been discarded)
	return stxetxRxPacket[STXETX_LENGTHOFFSET-1];
}

unsigned char* stxetxGetRxPacketData(void)
{
	// return a pointer to the packet's data payload
	// (subtract 1 from the offset because the STX has already been discarded)
	return stxetxRxPacket+STXETX_DATAOFFSET-1;
}
