/**
 * \file sio2host.c
 *
 * \brief Handles Serial I/O  Functionalities For the Host Device
 *
 * Copyright (c) 2013-2018 Microchip Technology Inc. and its subsidiaries.
 *
 * \asf_license_start
 *
 * \page License
 *
 * Subject to your compliance with these terms, you may use Microchip
 * software and any derivatives exclusively with Microchip products.
 * It is your responsibility to comply with third party license terms applicable
 * to your use of third party software (including open source software) that
 * may accompany Microchip software.
 *
 * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
 * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
 * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
 * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
 * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
 * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
 * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
 * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
 * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
 *
 * \asf_license_stop
 */

/* === INCLUDES ============================================================ */

#include "asf.h"
#include "sio2host.h"
#include "conf_sio2host.h"

/* === TYPES =============================================================== */

/* === MACROS ============================================================== */

/* === PROTOTYPES ========================================================== */

/* === GLOBALS ========================================================== */
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
static struct usart_module host_uart_module;
#else
static usart_serial_options_t usart_serial_options = {
	.baudrate     = USART_HOST_BAUDRATE,
	.charlength   = USART_HOST_CHAR_LENGTH,
	.paritytype   = USART_HOST_PARITY,
	.stopbits     = USART_HOST_STOP_BITS
};
#endif

/**
 * Receive buffer
 * The buffer size is defined in sio2host.h
 */
static uint8_t serial_rx_buf[SERIAL_RX_BUF_SIZE_HOST];

/**
 * Receive buffer head
 */
static uint8_t serial_rx_buf_head;

/**
 * Receive buffer tail
 */
static uint8_t serial_rx_buf_tail;

/**
 * Number of bytes in receive buffer
 */
static uint8_t serial_rx_count;

/* === IMPLEMENTATION ====================================================== */

void sio2host_init(void)
{
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
	struct usart_config host_uart_config;
	/* Configure USART for unit test output */
	usart_get_config_defaults(&host_uart_config);
	host_uart_config.mux_setting = HOST_SERCOM_MUX_SETTING;

	host_uart_config.pinmux_pad0 = HOST_SERCOM_PINMUX_PAD0;
	host_uart_config.pinmux_pad1 = HOST_SERCOM_PINMUX_PAD1;
	host_uart_config.pinmux_pad2 = HOST_SERCOM_PINMUX_PAD2;
	host_uart_config.pinmux_pad3 = HOST_SERCOM_PINMUX_PAD3;
	host_uart_config.baudrate    = USART_HOST_BAUDRATE;
	stdio_serial_init(&host_uart_module, USART_HOST, &host_uart_config);
	usart_enable(&host_uart_module);
	/* Enable transceivers */
	usart_enable_transceiver(&host_uart_module, USART_TRANSCEIVER_TX);
	usart_enable_transceiver(&host_uart_module, USART_TRANSCEIVER_RX);
#else
	stdio_serial_init(USART_HOST, &usart_serial_options);
#endif
	USART_HOST_RX_ISR_ENABLE();
}
void sio2host_deinit(void)
{
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35		
		usart_disable(&host_uart_module);
	
		/* Disable transceivers */
		usart_disable_transceiver(&host_uart_module, USART_TRANSCEIVER_TX);
		usart_disable_transceiver(&host_uart_module, USART_TRANSCEIVER_RX);
#endif	
}
uint8_t sio2host_tx(uint8_t *data, uint8_t length)
{
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
	status_code_genare_t status;
#else
	status_code_t status;
#endif /*SAMD || SAMR21 || SAML21 */

	do {
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
		status
			= usart_serial_write_packet(&host_uart_module,
				(const uint8_t *)data, length);
#elif SAM4S || SAM4E
        status = usart_serial_write_packet((Usart *)USART_HOST,
				(const uint8_t *)data,
				length);
#else
	    status = usart_serial_write_packet(USART_HOST,
				(const uint8_t *)data,
				length);
#endif
	} while (status != STATUS_OK);
	return length;
}

uint8_t sio2host_rx(uint8_t *data, uint8_t max_length)
{
	uint8_t data_received = 0;
	if(serial_rx_buf_tail >= serial_rx_buf_head)
	{
		serial_rx_count = serial_rx_buf_tail - serial_rx_buf_head;
	}
	else
	{
		serial_rx_count = serial_rx_buf_tail + (SERIAL_RX_BUF_SIZE_HOST - serial_rx_buf_head);
	}
	
	if (0 == serial_rx_count) {
		return 0;
	}

	if (SERIAL_RX_BUF_SIZE_HOST <= serial_rx_count) {
		/*
		 * Bytes between head and tail are overwritten by new data.
		 * The oldest data in buffer is the one to which the tail is
		 * pointing. So reading operation should start from the tail.
		 */
		serial_rx_buf_head = serial_rx_buf_tail;

		/*
		 * This is a buffer overflow case. But still only the number of
		 * bytes equivalent to
		 * full buffer size are useful.
		 */
		serial_rx_count = SERIAL_RX_BUF_SIZE_HOST;

		/* Bytes received is more than or equal to buffer. */
		if (SERIAL_RX_BUF_SIZE_HOST <= max_length) {
			/*
			 * Requested receive length (max_length) is more than
			 * the
			 * max size of receive buffer, but at max the full
			 * buffer can be read.
			 */
			max_length = SERIAL_RX_BUF_SIZE_HOST;
		}
	} else {
		/* Bytes received is less than receive buffer maximum length. */
		if (max_length > serial_rx_count) {
			/*
			 * Requested receive length (max_length) is more than
			 * the data
			 * present in receive buffer. Hence only the number of
			 * bytes
			 * present in receive buffer are read.
			 */
			max_length = serial_rx_count;
		}
	}

	data_received = max_length;
	while (max_length > 0) {
		/* Start to copy from head. */
		*data = serial_rx_buf[serial_rx_buf_head];
		data++;
		max_length--;
		if ((SERIAL_RX_BUF_SIZE_HOST - 1) == serial_rx_buf_head) {
			serial_rx_buf_head = 0;
		}
		else
		{
			serial_rx_buf_head++;
		}
	}
	return data_received;
}

uint8_t sio2host_getchar(void)
{
	uint8_t c;
	while (0 == sio2host_rx(&c, 1)) {
	}
	return c;
}

void sio2host_putchar(uint8_t ch)
{
	sio2host_tx(&ch, 1);
}

int sio2host_getchar_nowait(void)
{
	uint8_t c;
	int back = sio2host_rx(&c, 1);
	if (back >= 1) {
		return c;
	} else {
		return (-1);
	}
}

#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
void USART_HOST_ISR_VECT(uint8_t instance)
#else
USART_HOST_ISR_VECT()
#endif
{
	uint8_t temp;
#if SAMD || SAMR21 || SAML21 || SAMR30 || SAMR34 || SAMR35
	usart_serial_read_packet(&host_uart_module, &temp, 1);
#elif SAM4E || SAM4S
	usart_serial_read_packet((Usart *)USART_HOST, &temp, 1);
#else
    usart_serial_read_packet(USART_HOST, &temp, 1);
#endif

	/* Introducing critical section to avoid buffer corruption. */
	cpu_irq_disable();

	/* The number of data in the receive buffer is incremented and the
	 * buffer is updated. */

	serial_rx_buf[serial_rx_buf_tail] = temp;

	if ((SERIAL_RX_BUF_SIZE_HOST - 1) == serial_rx_buf_tail) {
		/* Reached the end of buffer, revert back to beginning of
		 * buffer. */
		serial_rx_buf_tail = 0x00;
	} else {
		serial_rx_buf_tail++;
	}

	cpu_irq_enable();
}

/* EOF */
