/*! \file edp.c \brief Emerald Data Protocol System. */
//*****************************************************************************
//
// File Name	: 'edp.c'
// Title		: Emerald Data Protocol System
// Author		: Pascal Stang - Copyright (C) 2003
// Created		: 2003.07.01
// Revised		: 2003.07.21
// Version		: 0.1
// Target MCU	: Atmel AVR series
// Editor Tabs	: 4
//
// This code is distributed under the GNU Public License
//		which can be found at http://www.gnu.org/licenses/gpl.txt
//
//*****************************************************************************

//----- Include Files ---------------------------------------------------------
#include <avr/io.h>		// include I/O definitions (port names, pin names, etc)
#include <avr/interrupt.h>	// include interrupt support
#include <avr/pgmspace.h>	// include program-space support

#include "global.h"		// include our global settings
#include "i2c.h"		// include I2C support
#include "rprintf.h"	// include printf function library

#include "edp.h"

// globals
// EDP master/command: response code and reply buffer
u08 EdpCommandResponseCode;
//u08 EdpCommandReplyLength;
u08 EdpCommandReplyBuffer[EDP_REPLY_BUFFER_SIZE];
u08 EdpCommandReplyChecksum;
// EDP slave: response code and reply buffer
u08 EdpSlaveResponseCode;
u08 EdpSlaveReplyLength;
u08 EdpSlaveReplyBuffer[EDP_REPLY_BUFFER_SIZE];
// EDP slave request handler function pointer
EdpSlaveHandlerFuncType edpSlaveHandlerFunc;

// functions
void edpInit(void)
{
	// initialize i2c interface and function library
	i2cInit();
	// set i2c bit rate to 30KHz
	i2cSetBitrate(30);
	// set the Slave Receive Handler function
	// (this function will run whenever a master somewhere else on the bus
	//  writes data to us as a slave)
	i2cSetSlaveReceiveHandler( edpSlaveReceiveService );
	// set the Slave Transmit Handler function
	// (this function will run whenever a master somewhere else on the bus
	//  attempts to read data from us as a slave)
	i2cSetSlaveTransmitHandler( edpSlaveTransmitService );
}

void edpSetSlaveHandler(EdpSlaveHandlerFuncType edpSlaveHandlerFunction)
{
	edpSlaveHandlerFunc = edpSlaveHandlerFunction;
}

// ************ EDP Master operations ************
u08 edpSendCommand(u08 deviceAddr, u08 cmdLength, EdpCommand* edpCommand)
{
	EdpReply* edpCommandReply = (EdpReply*)EdpCommandReplyBuffer;
	u08* sendData;
	u08* replyData;
	u08 replyLength;
	u08 checksum;

	// initialize response variables
	edpCommandReply->Length = 0;
	EdpCommandReplyChecksum = 0;

	#ifdef EDP_DEBUG
	rprintf("\r\nBegin EdpSendCommand, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// disable TWI interrupt
	cbi(TWCR, TWIE);

	// clear TWI interface
	//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK));

	// send start condition
	i2cSendStart();
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Start, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send device address with write
	i2cSendByte( (deviceAddr&0xFE) );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Device Address+Write, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// check if device is present and live
	if( i2cGetStatus() != TW_MT_SLA_ACK)
	{
		// device did not ACK it's address, command will not continue
		// transmit stop condition
		// leave with TWEA on for slave receiving
		i2cSendStop();
		while( !(inb(TWCR) & BV(TWSTO)) );
		#ifdef EDP_DEBUG
		rprintf("No Device!, Sent Stop, TWSR:0x%x\r\n",inb(TWSR));
		#endif
		// enable TWI interrupt
		sbi(TWCR, TWIE);
		// return error
		return EDP_COMMAND_NODEV;
	}

	// send data
	sendData = (u08*)edpCommand;
	checksum = 0;
	while(cmdLength)
	{
		i2cSendByte( *sendData );
		i2cWaitForComplete();
		#ifdef EDP_DEBUG
		rprintf("Sent Data, TWSR:0x%x\r\n",inb(TWSR));
		#endif
		checksum += *sendData++;
		cmdLength--;
	}

	// send the checksum
	i2cSendByte( ~checksum );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Checksum, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send repeated start condition
	i2cSendStart();
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Repeated Start, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send device address with read
	i2cSendByte( deviceAddr|0x01 );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Device Address+Read, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// read response code, return NACK
	i2cReceiveByte(FALSE);
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Read Data, TWSR:0x%x\r\n",inb(TWSR));
	#endif
	EdpCommandResponseCode = i2cGetReceivedByte();

	if(EdpCommandResponseCode==EDP_RESP_DATA_REPLY)
	{
		// a data reply is being sent

		// send repeated start condition
		i2cSendStart();
		i2cWaitForComplete();
		
		// send device address with read
		i2cSendByte( deviceAddr|0x01 );
		i2cWaitForComplete();

		// get length, return ACK
		i2cReceiveByte(TRUE);
		i2cWaitForComplete();
		edpCommandReply->Length = i2cGetReceivedByte();
		// set temp variables
		replyLength = edpCommandReply->Length;
		replyData = edpCommandReply->Data;

		// get data, return ACKs
		// preset checksum with the datalength byte
		checksum = replyLength;
		while(replyLength > 1)
		{
			i2cReceiveByte(TRUE);	// receive data byte and return ACK
			i2cWaitForComplete();
			*replyData = i2cGetReceivedByte();
			checksum += *replyData++;
			replyLength--;
		}

		// get last data (actually the checksum), return NACK (last-byte signal)
		i2cReceiveByte(FALSE);
		i2cWaitForComplete();
		*replyData = i2cGetReceivedByte();
		// add received checksum+1 to our checksum, the result should be zero
		checksum += (*replyData) + 1;
		// save the reply checksum
		EdpCommandReplyChecksum = checksum;
	}
	
	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();
	while( !(inb(TWCR) & BV(TWSTO)) );
	#ifdef EDP_DEBUG
	rprintf("Sent Stop, TWSR:0x%x\r\n",inb(TWSR));
	#endif
	
	// enable TWI interrupt
	sbi(TWCR, TWIE);

	return EDP_COMMAND_OK;
}

// get the response code and reply from last command
u08 edpGetCommandReply(u08* responseCode, EdpReply** edpReply)
{
	u08 retval=EDP_REPLY_OK;

	// get the response code from last command
	*responseCode = EdpCommandResponseCode;
	// get the reply from last command
	*edpReply = (EdpReply*)EdpCommandReplyBuffer;

	// check response code
	if(EdpCommandResponseCode == EDP_RESP_DATA_REPLY)
	{
		// there was a reply, check the checksum
		// if it's non-zero, data corruption is present
		if(EdpCommandReplyChecksum)
			retval = EDP_REPLY_BADCHKSUM;
	}
	return retval;
}

/*
u08 edpSendCommand(u08 deviceAddr, u08 sendLength, u08* sendData)
{
	u08* replyData = EdpCommandReplyBuffer;
	u08 replyLength;
	u08 checksum;

	// initialize response variables
	EdpCommandReplyLength = 0;
	EdpCommandReplyChecksum = 0;

	#ifdef EDP_DEBUG
	rprintf("\r\nBegin EdpSendCommand, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// disable TWI interrupt
	cbi(TWCR, TWIE);

	// clear TWI interface
	//outb(TWCR, (inb(TWCR)&TWCR_CMD_MASK));

	// send start condition
	i2cSendStart();
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Start, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send device address with write
	i2cSendByte( (deviceAddr&0xFE) );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Device Address+Write, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// check if device is present and live
	if( i2cGetStatus() != TW_MT_SLA_ACK)
	{
		// device did not ACK it's address, command will not continue
		// transmit stop condition
		// leave with TWEA on for slave receiving
		i2cSendStop();
		while( !(inb(TWCR) & BV(TWSTO)) );
		#ifdef EDP_DEBUG
		rprintf("No Device!, Sent Stop, TWSR:0x%x\r\n",inb(TWSR));
		#endif
		// enable TWI interrupt
		sbi(TWCR, TWIE);
		// return error
		return EDP_COMMAND_NODEV;
	}

	// send data
	checksum = 0;
	while(sendLength)
	{
		i2cSendByte( *sendData );
		i2cWaitForComplete();
		#ifdef EDP_DEBUG
		rprintf("Sent Data, TWSR:0x%x\r\n",inb(TWSR));
		#endif
		checksum += *sendData++;
		sendLength--;
	}

	// send the checksum
	i2cSendByte( ~checksum );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Checksum, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send repeated start condition
	i2cSendStart();
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Repeated Start, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// send device address with read
	i2cSendByte( deviceAddr|0x01 );
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Sent Device Address+Read, TWSR:0x%x\r\n",inb(TWSR));
	#endif

	// read response code, return NACK
	i2cReceiveByte(FALSE);
	i2cWaitForComplete();
	#ifdef EDP_DEBUG
	rprintf("Read Data, TWSR:0x%x\r\n",inb(TWSR));
	#endif
	EdpCommandResponseCode = i2cGetReceivedByte();

	if(EdpCommandResponseCode==EDP_RESP_DATA_REPLY)
	{
		// a data reply is being sent

		// send repeated start condition
		i2cSendStart();
		i2cWaitForComplete();
		
		// send device address with read
		i2cSendByte( deviceAddr|0x01 );
		i2cWaitForComplete();

		// get length, return ACK
		i2cReceiveByte(TRUE);
		i2cWaitForComplete();
		replyLength = i2cGetReceivedByte();
		EdpCommandReplyLength = replyLength;

		// get data, return ACKs
		// preset checksum with the datalength byte
		checksum = replyLength;
		while(replyLength > 1)
		{
			i2cReceiveByte(TRUE);	// receive data byte and return ACK
			i2cWaitForComplete();
			*replyData = i2cGetReceivedByte();
			checksum += *replyData++;
			replyLength--;
		}

		// get last data (actually the checksum), return NACK (last-byte signal)
		i2cReceiveByte(FALSE);
		i2cWaitForComplete();
		*replyData = i2cGetReceivedByte();
		// add received checksum+1 to our checksum, the result should be zero
		checksum += (*replyData) + 1;
		// save the reply checksum
		EdpCommandReplyChecksum = checksum;
	}
	
	// transmit stop condition
	// leave with TWEA on for slave receiving
	i2cSendStop();
	while( !(inb(TWCR) & BV(TWSTO)) );
	#ifdef EDP_DEBUG
	rprintf("Sent Stop, TWSR:0x%x\r\n",inb(TWSR));
	#endif
	
	// enable TWI interrupt
	sbi(TWCR, TWIE);

	return EDP_COMMAND_OK;
}

u08 edpGetCommandReply(u08* responseCode, u08* replyLength, u08** replyData)
{
	u08 retval=EDP_REPLY_OK;

	// get the response code and reply data from last command
	*responseCode = EdpCommandResponseCode;
	// get the reply length from last command
	*replyLength = EdpCommandReplyLength;
	// get the reply data from last command
	*replyData = EdpCommandReplyBuffer;

	// check response code
	if(EdpCommandResponseCode == EDP_RESP_DATA_REPLY)
	{
		// there was a reply, check the checksum
		// if it's non-zero, data corruption is present
		if(EdpCommandReplyChecksum)
			retval = EDP_REPLY_BADCHKSUM;
	}
	return retval;
}
*/

// ************ EDP Slave operations ************

// this function will run when a master somewhere else on the bus
// addresses us and wishes to write data to us
void edpSlaveReceiveService(u08 receiveDataLength, u08* receiveData)
{
	u08 i,checksum;

	// initialize the reply length from this command
	EdpSlaveReplyLength = 0;
	// verify the checksum
	// initialize the checksum with 1
	checksum = 0x01;
	// sum all the data in the packet and the data's checksum
	for(i=0; i<receiveDataLength; i++)
	{
		checksum += receiveData[i];
	}
	// if the checksum is non-zero, then the data is corrupt
	if(checksum)
	{
		// set reply code
		// [FIX] which should it be?
		EdpSlaveResponseCode = EDP_RESP_DATA_CHK_ERROR;
		//EdpSlaveResponseCode = EDP_RESP_CMD_CHK_ERROR;
		return;
	}

	// make an EDP command pointer to the received I2C data
	EdpCommand* edpCommand = (EdpCommand*)receiveData;

	// if a slave handler is defined
	if(edpSlaveHandlerFunc)
	{
		// then use it
		EdpSlaveResponseCode = edpSlaveHandlerFunc(
			receiveDataLength, edpCommand, 
			EDP_REPLY_BUFFER_SIZE, (EdpReply*)EdpSlaveReplyBuffer);
	}
	else
	{
		// otherwise reply with unknown command
		EdpSlaveResponseCode = EDP_RESP_UNKWN_CMD;
	}
}

// this function will run when a master somewhere else on the bus
// addresses us and wishes to read data from us
u08 edpSlaveTransmitService(u08 transmitDataLengthMax, u08* transmitData)
{
	u08 i;
	u08 checksum;
	u08 transmitDataLength = 0;

	EdpReply* edpReply = (EdpReply*)EdpSlaveReplyBuffer;

	if(EdpSlaveResponseCode)
	{
		// reply code is non-zero, we must send it
		*transmitData = EdpSlaveResponseCode;
		transmitDataLength = 1;
		// reset the reply code to flag that we've sent it
		EdpSlaveResponseCode = 0;
	}
	else
	{
		// reply code already sent, now send data (if any)
		// copy length of reply to transmit buffer (+1 for checksum)
		*transmitData++ = edpReply->Length+1;
		// initialize checksum
		checksum = edpReply->Length+1;
		// copy reply buffer to the transmit buffer
		for(i=0; i<edpReply->Length; i++)
		{
			*transmitData++ = edpReply->Data[i];
			checksum += edpReply->Data[i];
		}
		// copy checksum to transmit buffer
		*transmitData++ = ~checksum;
		// set number of bytes to transmit
		transmitDataLength = edpReply->Length+2;
	}

	// return number of bytes written to transmit buffer
	return transmitDataLength;
}
