// 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
* 
* Changelog:
* - v 1.2 (2012-04-02)
*  - added Loop Back option
*  - id-filter now implemented by application. pmc_init() expects a pointer to a filter function
*  - fixed bug with handling of crc errors -> bad packets where not discarded
* - v 1.1 (2012-03-30)
*  - fixed error in collision detection which caused the main process to lock up.
*  - moved pmc_rx_int to pmc.c
* 
*****************************************************************************/

#ifndef _PMC_H_
#define _PMC_H_ 1

#include "pmc.config.h"

typedef enum {
	PMC_MODE_IDLE,
	PMC_MODE_RX_CTRL,
	PMC_MODE_RX_DATA,
	PMC_MODE_TX_REQUEST,
	PMC_MODE_TX,
	PMC_MODE_WAIT,
	PMC_MODE_ERROR
}PMC_MODE;

#define PMC_ERROR_RX_BUFFER_FULL	0xE1
#define PMC_ERROR_RX_CRC_FAILED		0xE2
#define PMC_ERROR_TX_DATA_INVALID	0xE3
#define PMC_ERROR_TX_SEND_FAILED	0xE4
#define PMC_ERROR_TX_BUFFER_FULL	0xE5
#define PMC_ERROR_LINK_BAD			0xE6

extern volatile PMC_MODE pmc_mode;
extern const uint8_t pmc_crc_table[256];

/** \brief Initialize PMC Modules
 * 
 * Call this function to initialize the PMC Stack. The application has to provide
 * a pointer to a filter functions that decides if a message-id should be handled.
 * 
 * An example filter function could look like this:
 * \code
uint8_t filter(uint8_t data) {
	if(data == PMC_ID) {
		return 1;
	}
	return 0;
}
 * \endcode
 * 
 * pmc_init should be called like this:
 *\code
	pmc_init(&filter);
 * \endcode

 * \param pointer to filter function
 * \return void
 */
void pmc_init(uint8_t (*filter)(uint8_t data));

/** \brief PMC Main Process
 * 
 * Call this function regularly in your main-loop.
 * It does all the complex calculation stuff to keep the ISR small and fast.
 * - Initalise a transmission if there is data in the TX Buffer. Therfore we have to wait some time and listen on the bus. We dont want do this in pmc_send_message().
 * - If we are currently receiving a message: keep track of the timeout and reset the RX Buffer if we don't receive data for a long time.
 * 
 * 
 * \param void
 * \return Error Code if there was an error.
 */
uint8_t pmc_process(void);

/** \brief Send a message
 * 
 * \param id Message ID
 * \param subid User definable value. The lengths is 4 bits and the data has to be in the lower nibble.
 * \param datalength number of data bytes in the message. Valid values are between 0 an 15.
 * \param data Pointer to a buffer containing the data bytes.
 * \return true if buffer is empty and the message has been moved to TX Buffer, false if buffer is full and doesn't get empty for a while.
 */
uint8_t pmc_send_message(uint8_t id, uint8_t subid, uint8_t datalength, uint8_t *data);

/** \brief Send a message and wait until all pending messages have been sent.
 * 
 * same as pmc_send_message() but will not return until the tx buffer is empty
 * 
 * \param id Message ID
 * \param subid User definable value. The lengths is 4 bits and the data has to be in the lower nibble.
 * \param datalength number of data bytes in the message. Valid values are between 0 an 15.
 * \param data Pointer to a buffer containing the data bytes.
 * \return true if buffer is empty and the message has been moved to TX Buffer, false if buffer is full and doesn't get empty for a while.
 */
uint8_t pmc_send_message_wait(uint8_t id, uint8_t subid, uint8_t datalength, uint8_t *data);

/** \brief Pull a new message from Buffer
 * 
 * Call this function regularly in your main-loop to avoid a buffer overflow.
 * The function checks the RX Buffer. If there is a new message, it calculates the checksum and move the message id, control bits and data to the buffer given as parameter. 
 * 
 * \param id Message ID
 * \param subid User definable value. The lengths is 4 bits and the data has to be in the lower nibble.
 * \param datalength number of data bytes in the message. Valid values are between 0 an 15.
 * \param data Pointer to a buffer containing the data bytes.
 * \return 1 if a message was moved to the given buffer. 0 if there was no new message.
 */
uint8_t pmc_get_message(uint8_t *id, uint8_t *subid, uint8_t *datalength, uint8_t *data);

/** \brief Flush RS Buffer
 * 
 * just sets tail and head pointer equal.
 * 
 */
void pmc_flush_rx_buffer(void);

/** \struct pmc_buffer
 *  \brief a simple fifo buffer
 * 
 * This structure is used by the PMC implementation as FIFO Buffer for 
 * Transmission and Reception.
 */ 
struct pmc_buffer {
	uint8_t head; ///< Head index for write operation
	uint8_t tail; ///< Tail index for read operation
	/** 19 Bytes of fifo buffer
	 * - 1 Byte ID
	 * - 1 Byte Control bits and Data length
	 * - up to 15 Bytes data
	 * - 1 Byte CRC Checksum
	 */
	uint8_t buffer[18];
};

/** \brief PMC RX Interrupt Process
 * 
 * This function should be called by the ISR to handle the received data.
 * 
 * \param mark High Byte containing the ninth Bit
 * \param data Low Byte of the transmission.
 * \return void
 */
void pmc_rx_int(uint8_t mark, uint8_t data);

/** \brief PMC Timer Interrupt Process
 * 
 * This function should be called regularly by a Timer Interrupt.
 * The frequency depends on the BAUD rate and should around BAUD/11
 * e.g. For 38400 BAUD it should be called every 250µs. 
 * 
 * \param void
 * \return void
 */
inline void pmc_timer_interrupt(void) {
	extern volatile uint8_t pmc_tick;
	extern volatile uint8_t pmc_timeout;
	
	pmc_tick++;
	pmc_timeout++;
}

/** \brief Calculate CRC
 *  
 * Look-Up-Table based calculation of an 8-Bit CRC checksum.
 * The Implementation is based on the Maxim 1-Wire CRC calculation as described in
 * http://www.maxim-ic.com/app-notes/index.mvp/id/27
 * 
 * The Polynomial is X^8 + x^5 + x^4 +1
 * 
 * \param crc 
 * \param x 
 * \return updated crc value
 */
inline uint8_t pmc_crc_update(uint8_t crc, uint8_t x) {
	return pmc_crc_table[crc ^ x];
}

#endif /* _PMC_H_ */
