//----------------------------------------------------------------------------------
// <copyright file="uart.c" company="LiquidRobotics">
//	Copyright (c) Liquid Robotics Corporation.  All rights reserved.
// </copyright>
//
// <summary>
// 	AVR 2650 UART configuration and queue service functions (ISR's are in UART_ISR.S)
//  Note that the structures and definitions in this file are shared between C and
//  Assembler files.  Changes on one side need to be reflected correspondingly on
//  the other.
// </summary>
//
// <owner>Mike Cookson</owner>
//---------------------------------------------------------------------------------

#ifndef __UART_H__
# define __UART_H__

#define UART_NUM_PORTS (4)						// the number of ports supported by this module
#define UART_RX_BUFFER_SIZE (256)				// the size of the input buffer
#define UART_TX_BUFFER_SIZE (256)				// the size of the output buffer
#define UART_OSC (115200L*16*4)					// frequency of the crystal osciallator
#define UART_BRR(br) (UART_OSC/16L/((br)+1))	// resulting BRR value given baud rate

// data bits, stop bits and parity settings
#define UART_MODE(synch,data,stop,parity) (synch|parity|data|stop)

#define UART_MODE_ASYNCH			(0)
#define UART_MODE_NO_PARITY			(0)
#define UART_MODE_EVEN_PARITY		(_BV(UPM01))
#define UART_MODE_ODD_PARITY		(_BV(UPM01) | _BV(UPM00))
#define UART_MODE_FIVE_DATA_BITS	(0)
#define UART_MODE_SIX_DATA_BITS		(_BV(UCSZ00))
#define UART_MODE_SEVEN_DATA_BITS	(_BV(UCSZ01))
#define UART_MODE_EIGHT_DATA_BITS	(_BV(UCSZ01) | _BV(UCSZ00))
#define UART_MODE_ONE_STOP_BIT		(0)
#define UART_MODE_TWO_STOP_BITS		(_BV(USBS0))

//Flags used for flushing buffers
#define	UART_RX_BUFFER		1
#define	UART_TX_BUFFER		2
#define UART_ALL_BUFFERS	3

// macros for configuring IC's related to various ports
#define UART_SET_REG_MASKS(reg,offbits,onbits) \
				((reg) = ((reg) & ~(offbits)) | (onbits))

#define UART_SET_BY_STATE(st,reg,bits) \
				((reg) = ((st) ? ((reg) | (bits)) : ((reg) &~(bits))))

#define UART_CONFIG_PORT_0(st)	\
				(UART_SET_BY_STATE(!(st), PRR0, _BV(PRUSART0)), \
				 UART_SET_REG_MASKS(DDRE, 1, 0xA), \
				 UART_SET_BY_STATE(st, PORTE, 8))

#define UART_CONFIG_PORT_1(st)	\
				(UART_SET_BY_STATE(!(st), PRR1, _BV(PRUSART1)), \
				 UART_SET_REG_MASKS(DDRD, 4, 8), \
				 UART_SET_REG_MASKS(DDRK, 0, 3), \
				 UART_SET_REG_MASKS(PORTK, 1, 2))

#define UART_CONFIG_PORT_2(st)	\
				(UART_SET_BY_STATE(!(st), PRR1, _BV(PRUSART2)), \
				 UART_SET_REG_MASKS(DDRH, 1, 0x32), \
				 UART_SET_REG_MASKS(PORTH, 0x10, 0x20))

#define UART_CONFIG_PORT_3(st)	\
				(UART_SET_BY_STATE(!(st), PRR1, _BV(PRUSART3)), \
				 UART_SET_REG_MASKS(DDRJ, 1, 2), \
				 UART_SET_REG_MASKS(DDRF, 0, 3), \
				 UART_SET_REG_MASKS(PORTF, 1, 2))


// Here are some additional error codes returned by these routines

#define OS_ERR_UART_NOT_INITIALIZED	150u
#define OS_ERR_UART_ID_INVALID		151u
#define OS_ERR_UART_DUPLICATE_INIT	152u

// This structure (uart_t) defines variables relative to sending and receiving data across the
// UART's

typedef struct s_uart_t
{
	volatile uint16_t    frame_errors;              //  0: incremented on framing error
    volatile uint16_t    uart_overrun_errors;       //  2: incremented on uart overrun error
    volatile uint16_t    fifo_overrun_errors;       //  4: incremented when rx fifo overflows
    volatile uint8_t     rxinptr;					//  6: offset within the associated rxbuffer at which data are being added
    volatile uint8_t     rxoutptr;					//  7: offset within the associated rxbuffer at which data can be removed
    volatile uint8_t     txinptr;					//  8: offset within the associated txbuffer at which data are being added
    volatile uint8_t     txoutptr;					//  9: offset within the associated txbuffer at which data are being removed
    volatile uint16_t    rxcnt;						// 10: the count of data in the rxbuffer
    uint8_t     *uart_base;							// 12: points to the base of the registers used for this port
    OS_EVENT    *prxevent;							// 14: points to the event to trigger on receive and if a task is pending
    volatile uint8_t    stale_EMPTY : 1,			// 16: bit 0 indicates that the receive buffer is empty; bit 1 indicates a intercharacter gap
						stale_STALE : 1;
    OS_EVENT    *ptxevent;							// 17: points to the event to trigger on transmit buffer empty if a task is pending
	volatile uint8_t     txcnt;						// 19: count of bytes in the transmit buffer

	// length is 20

} uart_t;

// define the registers in the UART
typedef struct
{
    volatile uint8_t l;
    volatile uint8_t h;
} volatile_lh_t;

typedef union u_volatile_lhw_t
{
    volatile uint16_t w;
	volatile_lh_t b;
} volatile_lhw_t;

typedef struct {
    volatile uint8_t ucsra; 		// UART control/status register A
    volatile uint8_t ucsrb; 		// UART control/status register A
    volatile uint8_t ucsrc; 		// UART control/status register A
    uint8_t reserved_1;        		// unused
	volatile uint8_t ubrrL;			// lower division of baud rate
	volatile uint8_t ubrrH;			// upper division of baud rate
    volatile uint8_t udr;   		// UART data register
} uart_peripheral_t;

typedef struct {
	uint8_t uart_id;				// the index of this port
	uart_peripheral_t* pper;		// points to the major serial port regiers
	volatile uint8_t* prr;			// points the power control register to be used
	uint8_t prrMask;				// the mask that disables (1) or enables (0) the serial port
} uart_regs_t;

extern uart_t uart_devctl[UART_NUM_PORTS];
extern uart_regs_t uart_regs[UART_NUM_PORTS];

// the RX buffers are aligned on 256 byte boundaries
extern char rxbuffer[UART_NUM_PORTS][UART_RX_BUFFER_SIZE] __attribute__((section(".xbig")));
extern char txbuffer[UART_NUM_PORTS][UART_TX_BUFFER_SIZE] __attribute__((section(".xbig")));

uint8_t OSuartInitPort(uint8_t uart_id, uint32_t baud_rate, uint8_t databits, uint8_t stopbits, uint8_t parity);
uint8_t OSuartUpdatePort(uint8_t uart_id, uint32_t baud_rate, uint8_t databits, uint8_t stopbits, uint8_t parity);
void OSuartDefaultState();
void OSuartEnableDriver(uart_regs_t* pregs, uint8_t state);
void OSuartClearDevctl(uart_t* puart);
uint8_t OSuartGetCharWait(uint8_t uart_id, uint16_t max_wait, uint8_t *err);
uint8_t OSuartPutCharWait(uint8_t uart_id, uint8_t ch, uint16_t timeout);
uint16_t OSuartPutStrWait(uint8_t uart_id, char* pstr, uint16_t timeout, uint8_t* err);
uint16_t OSuartPutBlockWait(uint8_t uart_id, uint8_t* pdata, uint16_t count, uint16_t timeout, uint8_t* err);
uint8_t OSuartGetReadStats(uint8_t uart_id, uint16_t* pRxCount, uint16_t* pFrameErrors, uint16_t* pUARTOverruns, uint16_t* pFIFOOverruns);
uint8_t OSuartGetWriteStats(uint8_t uart_id, uint16_t* pTxCount);
uint8_t OSuartGetBufferOffsets(uint8_t uart_id, uint8_t* pRxIn, uint8_t* pRxOut, uint8_t* pTxIn, uint8_t* pTxOut);
uint8_t OSuartFlushBuffers(uint8_t uart_id, uint8_t flags);
uint8_t OSuartGetRxFifoCount(uint8_t uart_id, uint8_t* pCount);
void OSuartTimerTickHandler(void);


#endif
