/*
 *  ======== main.c ========
 */

#include <xdc/std.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>

#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/family/c66/Cache.h>

#include <ti/sdo/edma3/drv/edma3_drv.h>
#include <ti/sdo/edma3/drv/sample/bios6_edma3_drv_sample.h>

#define DDR_ADDR    0x80000000

uint8_t *srcBuff1;
uint8_t *dstBuff1;

volatile short irqRaised1;

/* Callback function 1 */
void callback1(unsigned int tcc, EDMA3_RM_TccStatus status, void *appData) {
	(void) tcc;
	(void) appData;

	switch (status) {
	case EDMA3_RM_XFER_COMPLETE:
		/* Transfer completed successfully */
		irqRaised1 = 1;
		break;
	case EDMA3_RM_E_CC_DMA_EVT_MISS:
		/* Transfer resulted in DMA event miss error. */
		irqRaised1 = -1;
		break;
	case EDMA3_RM_E_CC_QDMA_EVT_MISS:
		/* Transfer resulted in QDMA event miss error. */
		irqRaised1 = -2;
		break;
	default:
		break;
	}
}

/*
 *  ======== taskFxn ========
 */
Void taskFxn(UArg a0, UArg a1)
{
    System_printf("enter taskFxn()\n");

	uint32_t edmaInstance = 0;
	EDMA3_DRV_Result drvEdmaResult = EDMA3_DRV_SOK;

	EDMA3_DRV_Handle hEdma;

    hEdma = edma3init(edmaInstance, &drvEdmaResult);
	if (!hEdma) {
		System_printf("! drvEdma3init fail\n");
		goto THE_END;
	}

	EDMA3_DRV_Result result = EDMA3_DRV_SOK;
	EDMA3_DRV_SyncType syncType = EDMA3_DRV_SYNC_A;
	unsigned int chId = 0;
	unsigned int tcc = 0;
	int i;
	unsigned int count;
	unsigned int numenabled = 0;
	unsigned int BRCnt = 0;
	int srcbidx = 0, desbidx = 0;
	int srccidx = 0, descidx = 0;
	uint32_t acnt = 1024 * 4;
	uint32_t bcnt = 2;
	uint32_t ccnt = 2;

	uint32_t buffSize = acnt * bcnt * ccnt;

	srcBuff1 = (uint8_t*) DDR_ADDR + 0 * buffSize;
	dstBuff1 = (uint8_t*) DDR_ADDR + 1 * buffSize;

	memset(srcBuff1, 0, buffSize);
	memset(dstBuff1, 0, buffSize);

	/* Initalize source and destination buffers */
	for (count = 0u; count < (buffSize / sizeof(uint32_t)); count++) {
		((uint32_t*)srcBuff1)[count] = 0xBEAF0000 | (int) count;
	}

	printf("Copy %d byte [0x%X to 0x%X]\n", buffSize, srcBuff1, dstBuff1);

	/* Flush the Source Buffer */
	if (result == EDMA3_DRV_SOK) {
		result = Edma3_CacheFlush((unsigned int) srcBuff1, buffSize);
	}

	/* Invalidate the Destination Buffer */
	if (result == EDMA3_DRV_SOK) {
		result = Edma3_CacheInvalidate((unsigned int) dstBuff1, buffSize);
	}

	/* Set B count reload as B count. */
	BRCnt = bcnt;

	/* Setting up the SRC/DES Index */
	srcbidx = (int) acnt;
	desbidx = (int) acnt;
	if (syncType == EDMA3_DRV_SYNC_A) {
		/* A Sync Transfer Mode */
		srccidx = (int) acnt;
		descidx = (int) acnt;
	} else {
		/* AB Sync Transfer Mode */
		srccidx = ((int) acnt * (int) bcnt);
		descidx = ((int) acnt * (int) bcnt);
	}

	/* Setup for Channel 1*/
	tcc = EDMA3_DRV_TCC_ANY;
	chId = EDMA3_DRV_DMA_CHANNEL_ANY;

    /* Request any DMA channel and any TCC */
	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_requestChannel(hEdma, &chId, &tcc,
				(EDMA3_RM_EventQueue) 0, &callback1, NULL);
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setSrcParams(hEdma, chId, (unsigned int) (srcBuff1),
				EDMA3_DRV_ADDR_MODE_INCR, EDMA3_DRV_W8BIT);
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setDestParams(hEdma, chId, (unsigned int) (dstBuff1),
				EDMA3_DRV_ADDR_MODE_INCR, EDMA3_DRV_W8BIT);
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setSrcIndex(hEdma, chId, srcbidx, srccidx);
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setDestIndex(hEdma, chId, desbidx, descidx);
	}

	if (result == EDMA3_DRV_SOK) {
		if (syncType == EDMA3_DRV_SYNC_A) {
			result = EDMA3_DRV_setTransferParams(hEdma, chId, acnt, bcnt, ccnt,
					BRCnt, EDMA3_DRV_SYNC_A);
		} else {
			result = EDMA3_DRV_setTransferParams(hEdma, chId, acnt, bcnt, ccnt,
					BRCnt, EDMA3_DRV_SYNC_AB);
		}
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setOptField(hEdma, chId, EDMA3_DRV_OPT_FIELD_TCINTEN,
				1u);
	}

	if (result == EDMA3_DRV_SOK) {
		result = EDMA3_DRV_setOptField(hEdma, chId,
				EDMA3_DRV_OPT_FIELD_ITCINTEN, 1u);
	}

	/*
	 * Since the transfer is going to happen in Manual mode of EDMA3
	 * operation, we have to 'Enable the Transfer' multiple times.
	 * Number of times depends upon the Mode (A/AB Sync)
	 * and the different counts.
	 */
	if (result == EDMA3_DRV_SOK) {
		/*Need to activate next param*/
		if (syncType == EDMA3_DRV_SYNC_A) {
			numenabled = bcnt * ccnt;
		} else {
			/* AB Sync Transfer Mode */
			numenabled = ccnt;
		}

		for (i = 0; i < numenabled; i++) {
			irqRaised1 = 0;

			/*
			 * Now enable the transfer as many times as calculated above.
			 */
			result = EDMA3_DRV_enableTransfer(hEdma, chId,
					EDMA3_DRV_TRIG_MODE_MANUAL);
			if (result != EDMA3_DRV_SOK) {
				printf("edma3_test: EDMA3_DRV_enableTransfer "
						"Failed, error code: %d\r\n", result);
				break;
			}

			// Waiting interrupt
			while (irqRaised1 == 0u) {
				printf("waiting for interrupt...\n");
			}

//			// Pooling
//			EDMA3_DRV_waitAndClearTcc(hEdma, tcc);

			/* Check the status of the completed transfer */
			if (irqRaised1 < 0) {
				/* Some error occured, break from the FOR loop. */
				printf("\r\nedma3_test: Event Miss Occured!!!\r\n");

				/* Clear the error bits first */
				result = EDMA3_DRV_clearErrorBits(hEdma, chId);
				break;
			}
		}
	}

    /* Free the previously allocated channel. */
	result = EDMA3_DRV_freeChannel(hEdma, chId);
	if (result != EDMA3_DRV_SOK) {
		printf("edma3_test: EDMA3_DRV_freeChannel() FAILED, "
				"error code: %d\r\n", result);
	}

	/* Match the Source and Destination Buffers. */
	if (EDMA3_DRV_SOK == result) {
		int res = memcmp(srcBuff1, dstBuff1, buffSize);

		if (0 == res) {
			printf("Copy success\n");
		} else {
			printf("Copy fails\n");
		}
	}

THE_END:
	if (hEdma) {
		edma3deinit(edmaInstance, hEdma);
	}

    System_printf("exit taskFxn()\n");
}

/*
 *  ======== main ========
 */
Int main()
{ 
    Task_Handle task;
    Error_Block eb;

    System_printf("enter main()\n");

    Error_init(&eb);
    task = Task_create(taskFxn, NULL, &eb);
    if (task == NULL) {
        System_printf("Task_create() failed!\n");
        BIOS_exit(0);
    }

    BIOS_start();    /* does not return */
    return(0);
}
