// This file has been prepared for Doxygen automatic documentation generation.
/** \file ********************************************************************
*
* Implementation of PoorMansCan Stack
*
* \date 1.4.2012
* \author			  : Dominic Rathje (dominic.rathje@uni-ulm.de)
* \version 1.2
* \note
* - Supported devices : any AVR CPU
* 
* 
*****************************************************************************/

#define _PMC_C_ 1

#include <avr/io.h>
#include <stdlib.h>
#include <string.h>
#include "pmc.h"

//global variables
volatile PMC_MODE pmc_mode;					///< status of the state machine
volatile uint8_t pmc_error;					///< error code
volatile uint8_t pmc_tick;					///< time tick. This is used to generate delay between the last received data an the beginning of an transmission. It should be increased every 500µs by timer interrupt.
volatile uint8_t pmc_timeout;				///< timeout counter. This is used to trigger an timout while waiting for sth. It.should be increased every 500µs by timer interrupt.
volatile uint8_t pmc_crc_errorcount;		///< CRC Error Counter.
uint8_t pmc_random_delay;					///< Random Delay before a Message is sent.

struct pmc_buffer pmc_rx_buffer[PMC_RX_BUFFERS];
struct pmc_buffer pmc_tx_buffer[PMC_TX_BUFFERS];

volatile uint8_t pmc_rx_head=0, pmc_rx_tail=0;
volatile uint8_t pmc_tx_head=0, pmc_tx_tail=0;

volatile uint8_t pmc_rx_counter=0;

volatile uint8_t pmc_rx_int_head=0;			///< head index used in RX ISR
volatile uint8_t pmc_rx_int_tail=0;			///< tail index used in RX ISR
volatile uint8_t *pmc_rx_int_buffer=NULL;	///< buffer pointer used in RX ISR

uint8_t (*pmc_filter)(uint8_t data);

const uint8_t pmc_crc_table[256] = {
	0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
	157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
	35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
	190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
	70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
	219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
	101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
	248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
	140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
	17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
	175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
	50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
	202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
	87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
	233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
	116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53};

void pmc_init(uint8_t (*filter)(uint8_t data)){
	
	pmc_mode = PMC_MODE_IDLE;
	pmc_error = 0;
	pmc_tick = 0;
	pmc_timeout = 0;
	pmc_crc_errorcount = 0;
	pmc_mpcm_en();
	
	pmc_rx_head=0;
	pmc_rx_tail=0;
	pmc_tx_head=0;
	pmc_tx_tail=0;
	
	pmc_rx_counter=0;
	pmc_rx_int_head=0;
	pmc_rx_int_tail=0;	
	
	pmc_filter = filter;

}

uint8_t pmc_process(void) {	
	switch(pmc_mode) {
		case PMC_MODE_IDLE: 
		
			//if there is data to be transmitted
			if( (pmc_tx_tail != pmc_tx_head)) {		
					
				
				// switch mode to TX_REQUEST
				pmc_mode = PMC_MODE_TX_REQUEST; 
				
				// disable mpcm to listen on the bus
				pmc_mpcm_dis();		
				
			}
			
			break;
			
		case PMC_MODE_TX_REQUEST:
		
			
			// wait for tick>2+random
			if(pmc_tick> 2 + pmc_random_delay) {
				
				// switch mode to TX
				pmc_mode = PMC_MODE_TX; 
				
				//set index and buffer variables for ISR
				pmc_rx_int_buffer = pmc_tx_buffer[pmc_tx_tail].buffer;
				pmc_rx_int_head = pmc_tx_buffer[pmc_tx_tail].head;
				pmc_rx_int_tail = pmc_tx_buffer[pmc_tx_tail].tail;
				
				//sendid
				pmc_sendid(pmc_rx_int_buffer[pmc_rx_int_tail]);	
			}
		
			break;
				
		case PMC_MODE_RX_CTRL:
		case PMC_MODE_RX_DATA:
	
			//timeout: if pmc_tick>100 -> reset
			if(pmc_tick>100) {
				pmc_rx_buffer[pmc_rx_head].head=0;
				pmc_mpcm_en();
				pmc_mode=PMC_MODE_IDLE;
			}
			break;
								
		case PMC_MODE_TX:
		
			//timeout: if pmc_tick>100 -> reset
			if(pmc_tick>10) {
				pmc_mode=PMC_MODE_ERROR;
				pmc_error=PMC_ERROR_TX_DATA_INVALID;
			}
			break;
		
		case PMC_MODE_ERROR:
			switch(pmc_error) {
				
				case PMC_ERROR_RX_BUFFER_FULL:
					pmc_error = 0;
					pmc_mode = PMC_MODE_IDLE;
					return PMC_ERROR_RX_BUFFER_FULL;
					
				case PMC_ERROR_RX_CRC_FAILED:
					if(pmc_crc_errorcount < 100) {
						pmc_crc_errorcount+=10;
						pmc_error = 0;
						pmc_mode = PMC_MODE_IDLE;
						return PMC_ERROR_RX_CRC_FAILED;
					} else {
						return PMC_ERROR_LINK_BAD;
					}
					break;
				
				case PMC_ERROR_TX_DATA_INVALID:

					//set delay to wait before an error frame is sent
					pmc_tick = 0;
					pmc_random_delay += pmc_random();

					//reset tail index, so that the message will be transmitted again.
					pmc_tx_buffer[pmc_tx_tail].tail = 0;

					//send error frame (id=0) and wait until this was recognized by pmc_rx_int
					//which will change mode to PMC_MODE_WAIT
					while(pmc_mode == PMC_MODE_ERROR) {
						if(pmc_tick>pmc_random_delay) {
							pmc_sendid(0);
							pmc_random_delay += pmc_random();
							pmc_tick = 0;
						}
					}
					
					
					//set mode to PMC_MODE_ERROR in order to send a second error frame
					pmc_mode = PMC_MODE_ERROR;
					
					//set delay to wait before an error frame is sent
					pmc_tick = 0;
					pmc_random_delay = pmc_random();
					
					
					//send a second error frame
					while(pmc_mode == PMC_MODE_ERROR) {
						if(pmc_tick>pmc_random_delay) {
							pmc_sendid(0);
							pmc_random_delay += pmc_random();
							pmc_tick = 0;
						}
					}
					
					
					//reset state machine
					pmc_tick = 0;
					pmc_error = 0;
					
					break;
				
				case PMC_ERROR_TX_SEND_FAILED:
					//this error must not occur. so we stop work here and rest in this endless loop.
					break;
				
			}
			break;

		case PMC_MODE_WAIT:
			if(pmc_tick > 10) {
				pmc_tick=0;
				pmc_mode = PMC_MODE_IDLE;
			}
				
	}
	
	return pmc_error;
	
}

void pmc_rx_int(uint8_t mark, uint8_t data) {

	extern volatile uint8_t pmc_rx_counter;
	extern struct pmc_buffer pmc_rx_buffer[PMC_RX_BUFFERS];
	//extern struct pmc_buffer pmc_tx_buffer[PMC_TX_BUFFERS];
	extern volatile uint8_t pmc_rx_head, pmc_rx_tail;
	extern volatile uint8_t pmc_tx_tail;
	extern volatile PMC_MODE pmc_mode;
	extern volatile uint8_t pmc_error;
	extern volatile uint8_t pmc_tick;
	
	extern volatile uint8_t pmc_rx_int_head;
	extern volatile uint8_t pmc_rx_int_tail;
	extern volatile uint8_t *pmc_rx_int_buffer;

	pmc_tick=0;

	if(mark==1 && data==0) {
		pmc_rx_buffer[pmc_rx_head].head=0;
		pmc_mpcm_en();
		pmc_mode=PMC_MODE_WAIT;
		pmc_tick = 0;
		return;
	}

	
	switch(pmc_mode) {
		case PMC_MODE_IDLE:
		case PMC_MODE_TX_REQUEST:
				
				if(mark) {
					if(	pmc_filter(data) ) {
							if(pmc_rx_head != ((pmc_rx_tail-1)&(PMC_RX_BUFFERS-1)) ) {
								pmc_mpcm_dis();
								pmc_mode=PMC_MODE_RX_CTRL;	
								
								//copy data to buffer
								pmc_rx_int_head = 0;
								pmc_rx_int_buffer = pmc_rx_buffer[pmc_rx_head].buffer;
								pmc_rx_int_buffer[pmc_rx_int_head++] = data;
								
								
							} else {
								pmc_mode=PMC_MODE_ERROR;
								pmc_error=PMC_ERROR_RX_BUFFER_FULL;
							}
							
					  }
				}
				break;
				
		case PMC_MODE_RX_CTRL:
				//init counter (number of databytes + number of CRC bytes)
				pmc_rx_counter=(data & 0x0F)+1;
				
				//copy data to buffer
				pmc_rx_int_buffer[pmc_rx_int_head++] = data;
				
				pmc_mode = PMC_MODE_RX_DATA;
			
				break;
				
		case PMC_MODE_RX_DATA:
				//copy data to buffer
				pmc_rx_int_buffer[pmc_rx_int_head++] = data;
				
				//dec counter
				pmc_rx_counter--;
				
				//if last byte, counter==0, MODE_IDLE, enable MPCM, 
				if(pmc_rx_counter==0) {
					pmc_mpcm_en();
					pmc_mode=PMC_MODE_IDLE;
					
					// write head and tail pointer
					pmc_rx_buffer[pmc_rx_head].head=pmc_rx_int_head;
					pmc_rx_buffer[pmc_rx_head].tail=0;
					
					//increase buffer index, to tell the world that this buffer contains valid data
					pmc_rx_head++;
					pmc_rx_head &= (PMC_RX_BUFFERS-1);
				}
				break;
				
		case PMC_MODE_TX:
				//check if the received byte equals the send byte
				//increase tail index
				if( data != pmc_rx_int_buffer[pmc_rx_int_tail++]) {
					pmc_mode=PMC_MODE_ERROR;
					pmc_error=PMC_ERROR_TX_DATA_INVALID;
					break;
				}
				
				//check if there is any data left to send
				if(pmc_rx_int_tail == pmc_rx_int_head) {
					
					#ifdef PMC_LOOPBACK
					if(pmc_rx_head != ((pmc_rx_tail-1)&(PMC_RX_BUFFERS-1)) ) {
						//copy data from tx butter to rx buffer
						memcpy((void *)pmc_rx_buffer[pmc_rx_head].buffer,(const void *)pmc_rx_int_buffer,pmc_rx_int_head+1) ;
						pmc_rx_buffer[pmc_rx_head].head=pmc_rx_int_head;
						pmc_rx_buffer[pmc_rx_head].tail=0;
						pmc_rx_head++;
						pmc_rx_head &= (PMC_RX_BUFFERS-1);
					}
					#endif
					
					pmc_tx_tail++;
					pmc_tx_tail &= (PMC_TX_BUFFERS-1);
					pmc_mpcm_en();
					pmc_mode=PMC_MODE_IDLE;
					break;
				}
				
				//send the next byte
				if(pmc_senddata(pmc_rx_int_buffer[pmc_rx_int_tail])==0) {
					pmc_mode=PMC_MODE_ERROR;
					pmc_error=PMC_ERROR_TX_SEND_FAILED;
				}
				break;

		default:
				break;

	}
}

uint8_t pmc_send_message_wait(uint8_t id, uint8_t subid, uint8_t datalength, uint8_t *data) {
	if(pmc_send_message(id,subid,datalength,data)==0) {
		return 0;	
	}
	
	while(pmc_tx_head != pmc_tx_tail) {
		pmc_process();
	}
	return 1;
}

uint8_t pmc_send_message(uint8_t id, uint8_t subid, uint8_t datalength, uint8_t *data) {
		
	
	uint8_t crc;
	uint8_t tmp;
	
	//check for space in buffer and wait if necessary.
	pmc_timeout=0;
	while(pmc_tx_head == ((pmc_tx_tail-1)&(PMC_TX_BUFFERS-1)) ) {
		if(pmc_timeout>100) {
			return 0;
		}
	}
	//validate parameters
	if(datalength>15 || subid>15) {
		return 0;
	}
	
	
	//initialize crc value
	crc=0;
	
	uint8_t *buffer;
	uint8_t head=0;
	
	buffer = pmc_tx_buffer[pmc_tx_head].buffer;
	
	//move data to tx buffer and calculate crc
	buffer[head++]=id;
	crc = pmc_crc_update(crc,id);
	
	tmp =  (subid<<4) | datalength;
	buffer[head++]=tmp;
	crc = pmc_crc_update(crc,tmp);
	
	for(;datalength>0;datalength--) {
		buffer[head++]=*data;
		crc = pmc_crc_update(crc,*data);
		data++;
	}
	
	//copy crc to buffer
	buffer[head++]=crc;

	
	
	// write head and tail pointer
	pmc_tx_buffer[pmc_tx_head].head=head;
	pmc_tx_buffer[pmc_tx_head].tail=0;
	
	
	//increase head index. this will trigger an tx request.
	pmc_tx_head++;
	pmc_tx_head &= (PMC_TX_BUFFERS-1);
	
	
	//reset pmc_tick
	pmc_tick=0;
	
	//set delay
	pmc_random_delay = pmc_random();

	return 1;
}

uint8_t pmc_get_message(uint8_t *id, uint8_t *subid, uint8_t *datalength, uint8_t *data) {
	
	uint8_t crc=0;
	uint8_t tmp;
	
	if(pmc_rx_head == pmc_rx_tail) {
		return 0;
	}
	
	uint8_t *buffer;
	uint8_t tail, head;
	buffer = pmc_rx_buffer[pmc_rx_tail].buffer;
	tail = 0;
	head = pmc_rx_buffer[pmc_rx_tail].head;
	
	// check crc
	while(tail != head) {
		crc = pmc_crc_update(crc,buffer[tail++]);
	}
	
	if(crc) {
		pmc_error = PMC_ERROR_RX_CRC_FAILED;
		pmc_mode = PMC_MODE_ERROR;
		pmc_rx_tail++;
		pmc_rx_tail &= (PMC_RX_BUFFERS-1);
		return 0;
	}
	
	if(pmc_crc_errorcount) pmc_crc_errorcount--;
	
	// copy data to destination
	tail=0;
	*id = buffer[tail++];
	tmp = buffer[tail++];
	*subid = (tmp>>4) & 0x0F;
	*datalength = tmp & 0x0F;
	
	head=head-1;
	while(tail != head) {
		*data = buffer[tail++];
		data++;
	}

	
	// mark buffer as empty
	pmc_rx_tail++;
	pmc_rx_tail &= (PMC_RX_BUFFERS-1);

	return 1;
}

void pmc_flush_rx_buffer(void) {
	
	pmc_rx_tail = pmc_rx_head;
	
}



