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

/* DMA channel for SPI Slave TX and RX */
#define CONF_SERCOM_5_RECEIVE_DMA_CHANNEL 2
#define CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL 3

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

#define SLAVE_BUFFER_SIZE	32

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


static uint8_t rx_buffer[SLAVE_BUFFER_SIZE] = {0};
static uint8_t tx_buffer[SLAVE_BUFFER_SIZE] =  {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,
												19,20,21,22,23,24,25,26,27,28,29,30,31};

/* 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 select interrupt */
void spi_s_sync_enable_ss_detect(void *hw, bool state)
{
	NVIC_DisableIRQ((IRQn_Type)SERCOM5_IRQn);
	NVIC_ClearPendingIRQ((IRQn_Type)SERCOM5_IRQn);
	NVIC_EnableIRQ((IRQn_Type)SERCOM5_IRQn);
	if (state) {
		hri_sercomspi_set_INTEN_TXC_bit(hw);
		} else {
		hri_sercomspi_clear_INTEN_TXC_bit(hw);
	}
}


/* DMA Transfer complete callback for SPI Slave RX */
static void spi_slave_rx_complete_cb(struct _dma_resource *const resource)
{
	//hri_sercomspi_clear_INTEN_RXC_bit((SercomSpi *)(SPI_0.dev.prvt));
	//tx_buffer[63] += 1;
	_dma_enable_transaction(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, false);
	_dma_enable_transaction(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, false);
	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)
{
	tx_buffer[SLAVE_BUFFER_SIZE] += 1;
	//hri_sercomspi_clear_INTEN_TXC_bit((SercomSpi *)(SPI_0.dev.prvt));
	_dma_enable_transaction(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, false);
	_dma_enable_transaction(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, false);
	spi_slave_tx_complete = true;
}

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

/* slave slave interrupt handler */
void SERCOM5_Handler()
{
	hri_sercomspi_clear_INTEN_TXC_bit((SercomSpi *)(SPI_0.dev.prvt));
	//_dma_enable_transaction(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, false);
	//_dma_enable_transaction(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, false);
	//slave_select_high();
}


/* SERCOM SPI Slave configuration */
void spi_slave_init()
{
	_dma_set_source_address(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL,
	(uint32_t *)&(((SercomSpi *)(SPI_0.dev.prvt))->DATA.reg));
	_dma_set_destination_address(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, rx_buffer+SLAVE_BUFFER_SIZE);
	_dma_set_data_amount(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, SLAVE_BUFFER_SIZE);
	
	_dma_set_destination_address(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL,
	(uint32_t *)&(((SercomSpi *)(SPI_0.dev.prvt))->DATA.reg));
	_dma_set_source_address(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, tx_buffer+SLAVE_BUFFER_SIZE);
	_dma_set_data_amount(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, SLAVE_BUFFER_SIZE);
	
	/* callback */
	struct _dma_resource *resource_rx, *resource_tx;
	_dma_get_channel_resource(&resource_rx, CONF_SERCOM_5_RECEIVE_DMA_CHANNEL);
	_dma_get_channel_resource(&resource_tx, CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL);
	resource_rx->dma_cb.transfer_done = spi_slave_rx_complete_cb;
	resource_tx->dma_cb.transfer_done = spi_slave_tx_complete_cb;

	/* Enable DMA transfer complete interrupt */
	_dma_set_irq_state(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, DMA_TRANSFER_COMPLETE_CB, true);
	_dma_set_irq_state(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, DMA_TRANSFER_COMPLETE_CB, true);

	spi_s_sync_enable_ss_detect((SercomSpi *)(SPI_0.dev.prvt), true);
	hri_sercomspi_set_CTRLB_PLOADEN_bit(SPI_0.dev.prvt);
	spi_s_sync_enable(&SPI_0);
}

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

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



int main(void)
{
	/* Initializes MCU, drivers and middleware */
	atmel_start_init();
	//printf("Init Complete \n\r");
	
	/* SPI Slave DMA and Callback configuration */
	spi_slave_init();
	/* Start SPI Slave data reception using DMA */
	//spi_slave_rx(rx_buffer, SLAVE_BUFFER_SIZE);
	//spi_slave_tx(tx_buffer, SLAVE_BUFFER_SIZE);
	printf("Init Complete \n\r");
	
	//spi_s_sync_enable(&SPI_0);
	_dma_enable_transaction(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, false);
	_dma_enable_transaction(CONF_SERCOM_5_TRANSMIT_DMA_CHANNEL, false);
	//_dma_enable_transaction(CONF_SERCOM_5_RECEIVE_DMA_CHANNEL, false);

	/* Replace with your application code */
	while (1) {
		delay_ms(1000);
		//tx_buffer[4] = tx_buffer[4]+1;
		//spi_master_rx(rx_buffer, BUFFER_LEN);
					
		printf("Slave Recived DATA = ");
		/* Print Received data by SPI Master from SPI Slave on Console */
		for (int i = 0; i < SLAVE_BUFFER_SIZE; i++) {
			printf("%u, ", rx_buffer[i]);
		}
		printf("\n\r");
				
		printf("Slave Sent DATA = ");
			/* Print Received data by SPI Master from SPI Slave on Console */
		for (int i = 0; i < SLAVE_BUFFER_SIZE; i++) {
			printf("%u, ", tx_buffer[i]);
		}
		printf("\n\r");
	}
}
