/**
 * \file
 *
 * \brief Application implement
 *
 * Copyright (c) 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
 *
 */
/*
 * Support and FAQ: visit <a href="https://www.microchip.com/support/">Microchip Support</a>
 */

#include <atmel_start.h>
#include "driver_examples.h"
#include "spi_slave_dma_config.h"

/* DMA channel for SPI Slave TX and RX */
#define CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL 2
#define CONF_SERCOM_6_RECEIVE_DMA_CHANNEL 3

/* Buffer length to transfer/receive */
#define BUFFER_LEN (12)

/* DMA channel Descriptor */
extern DmacDescriptor _descriptor_section[DMAC_CH_NUM];
extern DmacDescriptor _write_back_section[DMAC_CH_NUM];

/* IO descriptor for SPI master */
struct io_descriptor *io_spi_master;

/* DATA transfer/reception completion flags  */
volatile uint8_t spi_slave_tx_complete  = 0;
volatile uint8_t spi_slave_rx_complete  = 0;
volatile uint8_t spi_master_tx_complete = 0;
volatile uint8_t spi_master_rx_complete = 0;
volatile uint8_t cs_pulled_high         = 0;
volatile uint8_t received_data_len      = 0;

static uint8_t rx_buffer[BUFFER_LEN] = {0};
static uint8_t tx_buffer[BUFFER_LEN] = "Hello World!";

/* DMA Transfer complete callback for SPI master TX */
static void spi_master_tx_complete_cb(struct _dma_resource *resource)
{
	/* Transfer completed */
	delay_us(CONF_DELAY_US);
	gpio_set_pin_level(SPI_CS, true);
	spi_master_tx_complete = true;
}

/* DMA Transfer complete callback for SPI master RX */
static void spi_master_rx_complete_cb(struct _dma_resource *resource)
{
	/* Transfer completed */
	gpio_set_pin_level(SPI_CS, true);
	spi_master_rx_complete = true;
}

/* Start SPI master data transfer */
void spi_master_tx(uint8_t *buffer, uint8_t length)
{
	spi_m_dma_enable(&SPI_0);
	gpio_set_pin_level(SPI_CS, false);
	io_write(io_spi_master, buffer, length);
}

/* Start SPI master data Reception from Slave  */
void spi_master_rx(uint8_t *buffer, uint8_t length)
{
	spi_m_dma_enable(&SPI_0);
	gpio_set_pin_level(SPI_CS, false);
	io_read(io_spi_master, buffer, length);
}

/* SPI master IO and Callback Initialization  */
void spi_master_init(void)
{
	spi_m_dma_get_io_descriptor(&SPI_0, &io_spi_master);
	spi_m_dma_register_callback(&SPI_0, SPI_M_DMA_CB_TX_DONE, spi_master_tx_complete_cb);
	spi_m_dma_register_callback(&SPI_0, SPI_M_DMA_CB_RX_DONE, spi_master_rx_complete_cb);
}

/* SPI Slave Chip Select low to high change callback */
static void slave_select_high(void)
{
	received_data_len = (_descriptor_section[CONF_SERCOM_6_RECEIVE_DMA_CHANNEL].BTCNT.reg
	                     - _write_back_section[CONF_SERCOM_6_RECEIVE_DMA_CHANNEL].BTCNT.reg);
	cs_pulled_high    = true;
}

/* DMA Transfer complete callback for SPI Slave RX */
static void spi_slave_rx_complete_cb(struct _dma_resource *const resource)
{
	spi_slave_rx_complete = true;
}

/* DMA Transfer complete callback for SPI Slave TX */
static void spi_slave_tx_complete_cb(struct _dma_resource *const resource)
{
	spi_slave_tx_complete = true;
}

/* Register SPI Slave DMA channel callbacks */
void register_dma_spi_slave_callback(uint16_t channel, void *cb)
{
	struct _dma_resource *resource_rx;
	_dma_get_channel_resource(&resource_rx, channel);
	resource_rx->dma_cb.transfer_done = cb;
}

/* Enable SPI slave slave select interrupt */
void spi_s_sync_enable_ss_detect(void *hw, bool state)
{
	NVIC_DisableIRQ((IRQn_Type)SERCOM6_1_IRQn);
	NVIC_ClearPendingIRQ((IRQn_Type)SERCOM6_1_IRQn);
	NVIC_EnableIRQ((IRQn_Type)SERCOM6_1_IRQn);
	if (state) {
		hri_sercomspi_set_INTEN_TXC_bit(hw);
	} else {
		hri_sercomspi_clear_INTEN_TXC_bit(hw);
	}
}

/* slave slave interrupt handler */
void SERCOM6_1_Handler()
{
	hri_sercomspi_clear_INTEN_TXC_bit((SercomSpi *)(SPI_1.dev.prvt));
	slave_select_high();
}

/* SERCOM SPI Slave configuration */
void spi_slave_init()
{
	_dma_set_source_address(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL,
	                        (uint32_t *)&(((SercomSpi *)(SPI_1.dev.prvt))->DATA.reg));
	_dma_set_destination_address(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL,
	                             (uint32_t *)&(((SercomSpi *)(SPI_1.dev.prvt))->DATA.reg));

	/* callback */
	register_dma_spi_slave_callback(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, spi_slave_tx_complete_cb);
	register_dma_spi_slave_callback(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL, spi_slave_rx_complete_cb);

	/* Enable DMA transfer complete interrupt */
	_dma_set_irq_state(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL, DMA_TRANSFER_COMPLETE_CB, true);
	_dma_set_irq_state(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, DMA_TRANSFER_COMPLETE_CB, true);

	spi_s_sync_enable_ss_detect((SercomSpi *)(SPI_1.dev.prvt), true);
}

/* Start SPI slave data transfer */
void spi_slave_tx(uint8_t *buffer, uint8_t length)
{
	spi_s_sync_enable(&SPI_1);
	_dma_set_source_address(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, (uint32_t *)buffer);
	_dma_srcinc_enable(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, true);
	_dma_set_data_amount(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, (uint32_t)length);
	_dma_enable_transaction(CONF_SERCOM_6_TRANSMIT_DMA_CHANNEL, false);
}

/* Start SPI slave data reception  */
void spi_slave_rx(uint8_t *buffer, uint8_t length)
{
	spi_s_sync_enable(&SPI_1);
	_dma_set_destination_address(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL, (uint32_t *)buffer);
	_dma_set_data_amount(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL, (uint32_t)length);
	_dma_enable_transaction(CONF_SERCOM_6_RECEIVE_DMA_CHANNEL, false);
}

int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	/* Initialize SPI master IO and Callback  */
	spi_master_init();
	/* SPI Slave DMA and Callback configuration */
	spi_slave_init();
	/* Start SPI Slave data reception using DMA */
	spi_slave_rx(rx_buffer, BUFFER_LEN);
	/* Start SPI Master data transfer using DMA */
	spi_master_tx(tx_buffer, BUFFER_LEN);

	while (1) {
		/* Check for SPI Slave is received data from SPI Master */
		if (spi_slave_rx_complete) {
			spi_slave_rx_complete = false;
			printf("Slave Received DATA Length = %d\n\rSlave Received DATA = ", received_data_len);
			/* Print Received data by SPI Slave from SPI Master on Console */
			for (int i = 0; i < BUFFER_LEN; i++) {
				printf("%c", rx_buffer[i]);
			}
			printf("\n\r");

			/* Start SPI Slave data transfer using DMA */
			spi_slave_tx(rx_buffer, BUFFER_LEN);

			/* Start SPI Master data reception using DMA */
			spi_master_rx(tx_buffer, BUFFER_LEN);
		}

		/* Check for SPI Master is received data from SPI Slave */
		if (spi_master_rx_complete) {
			spi_master_rx_complete = false;
			printf("Master Received DATA Length = %d\n\rMaster Received DATA = ", received_data_len);
			/* Print Received data by SPI Master from SPI Slave on Console */
			for (int i = 0; i < BUFFER_LEN; i++) {
				printf("%c", tx_buffer[i]);
			}
			printf("\n\r");
			//delay_ms(500);
			//spi_master_tx(tx_buffer, BUFFER_LEN);
		}
		delay_ms(500);
		spi_slave_rx(rx_buffer, BUFFER_LEN);
		spi_master_tx(tx_buffer, BUFFER_LEN);
	}
	

}
