/* 
 * Copyright (c) 2012, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * */

/*
 *  ======== BufferPacketizer.c ========
 */
#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Gate.h>
#include <xdc/runtime/Log.h>
#include <xdc/runtime/Memory.h>
#include <xdc/runtime/Startup.h>
#include <xdc/runtime/Types.h>
#include <xdc/runtime/Diags.h>
#include <xdc/runtime/Timestamp.h>
#include <ti/uiactools/runtime/IUIAPacketTransfer.h>
#include <ti/uiactools/runtime/UIAPacket.h>


#include <stdio.h>


#ifdef xdc_target__isaCompatible_64P
#include <c6x.h>
#else

static UInt32 DNUM=0;
//#include <ti/ctools/etbLib/CSETB.c>
#endif

#include <stdlib.h>
#include <string.h>

#define HDR_OFFSET_IN_WORDS 7        /* Size of the UIA trace packet header */
#define BYTES_IN_WORD 4

#include "package/internal/BufferPacketizer.xdc.h"
/*
#define HDR_OFFSET_IN_WORDS 7        // Size of the UIA trace packet header /
#define BYTES_IN_WORD 4
#define MAU_TO_BITS32(mau)          ((mau) / sizeof(Bits32))
#define EDMA_BFR_CNT  2
#define C66x 1
#include <ti/ctools/etbLib/ETBAddr.h>
#include <ti/ctools/etbLib/edma_dev-c66xx.h>
 */
static int gNumLoops = 0;


extern UInt32 ETB_getATBMsgHdrPointer (int index);
extern void stm_config_for_etb(void);


/*
 *  ======== BufferPacketizer_getFreeSize =========
 */
SizeT BufferPacketizer_getUnsentSize(BufferPacketizer_Object *obj)
{
	return (obj->pPacketEndPlusOne - obj->rdUploadBuffer);
}


/*
 *  ======== BufferPacketizer_Module_startup ========
 */
Int BufferPacketizer_Module_startup(Int phase)
{
	Int i;
	BufferPacketizer_Object *obj;

	/*
	 *  Loop over all instances of the BufferPacketizer module
	 */
	for (i = 0; i < BufferPacketizer_Object_count(); i++) {
		obj = BufferPacketizer_Object_get(NULL, i);
		if (obj->primeFxn != NULL) {
			obj->pPacketBuffer = (UInt32*)obj->primeFxn(obj);
			if (obj->pPacketBuffer != NULL) {

				obj->wrPacket = obj->pPacketBuffer + HDR_OFFSET_IN_WORDS;
				obj->pPacketEndPlusOne = obj->pPacketBuffer
						+ (obj->packetSizeInMAUs/sizeof(UInt32));
			}
			obj->primeStatus = TRUE;

		}
	}

#ifdef xdc_target__isaCompatible_64P
	/* Start the timestamp */
	TSCL = 0;
#endif
	return (Startup_DONE);


}

/*
 *  ======== Instance_init =========
 *  TODO not completely tested.
 */
Int BufferPacketizer_Instance_init(BufferPacketizer_Object *obj,
		const BufferPacketizer_Params *prms, Error_Block *eb)
{

	obj->uploadBufferSizeInWords = 0; 
	obj->packetSizeInMAUs = prms->packetSizeInMAUs;
	obj->enabled = TRUE;
	obj->snapshotNumber = 1;
	obj->serial = 1;
	obj->isFirstPacket = TRUE;
	obj->endpointNumber = prms->endpointNumber;
	if (obj->endpointNumber == 0xFF){
		obj->endpointNumber = DNUM;
	}
	obj->printDebugMessages = FALSE;
	obj->exchangeFxn = prms->exchangeFxn;
	obj->primeFxn = prms->primeFxn;
	obj->etbCoreId = prms->etbCoreId;
	obj->uploadBufferSizeInWords = 0;
	obj->pUploadBuffer = NULL;
	obj->bufType = prms->bufferType;
	switch (obj->bufType) {
	case BufferPacketizer_BufferType_CPUTRACE_ETB:
		obj->traceHdrType = UIAPacket_HdrType_CPUTrace;
		break;
	case BufferPacketizer_BufferType_STM_ETB:
		break;
	default:
		obj->traceHdrType = UIAPacket_HdrType_MemoryBuffer;
		break;
	}
	return (0); /* status passed to finalize on error */
}

Void BufferPacketizer_enableDebugMessages(BufferPacketizer_Object *obj, Bool enable){
	obj->printDebugMessages = enable;
}
/*
 *  ======== BufferPacketizer_Instance_finalize ========
 *  TODO not completely tested.
 */
Void BufferPacketizer_Instance_finalize(BufferPacketizer_Object *obj, Int status)
{
}


/*******************************************************************************
 * Public Function Definitions
 ******************************************************************************/


/*
 * ======== setBufferToUpload ========
 */
Bool BufferPacketizer_setBufferToUpload(BufferPacketizer_Object *obj, UInt32* pUploadBuffer, UInt32 bufferSizeInWords, Ptr pUploaderContext){
	UInt32 availableWords = bufferSizeInWords;
	UInt32 maxReadSize = obj->packetSizeInMAUs - (HDR_OFFSET_IN_WORDS + 1)*BYTES_IN_WORD;
	Bool result = FALSE;
	if ((obj->tracePktDownCount == 0)&&(obj->tracePktUpCount == 0)){
		result = TRUE;
		obj->pUploadBuffer = pUploadBuffer;
		obj->uploadBufferSizeInWords = bufferSizeInWords;
		obj->rdUploadBuffer = pUploadBuffer;
		obj->pUploaderContext = pUploaderContext;
		if (pUploadBuffer != NULL){
			if (!obj->isInitialized){
				if (obj->endpointNumber == 0xFF){
					obj->endpointNumber = DNUM;
				}
				obj->isInitialized = TRUE;
			}
		}
		if (obj->isInitialized){
			obj->tracePktDownCount = 0;
			obj->tracePktUpCount = 0;

			if (obj->printDebugMessages){
				printf("BufferPacketizer_transferETBData: availableWords = %d\n",availableWords);
			}
			obj->tracePktDownCount = ((availableWords * BYTES_IN_WORD) / maxReadSize);
			obj->isFirstPacket = TRUE;
			if (((availableWords * BYTES_IN_WORD)%maxReadSize) == 0){
				obj->tracePktDownCount--;
			}
			if (obj->bufType == BufferPacketizer_BufferType_STM_ETB){
				obj->tracePktDownCount++; /* add one extra packet to carry the STM config data */
			}
			obj->snapshotNumber++;
		}
	}
	return(result);
}

/*
 *  ======== BufferPacketizer_enable ========
 */
Bool BufferPacketizer_enable(BufferPacketizer_Object *obj)
{
	Bool prev;
	IArg key;

	key = Gate_enterSystem();

	prev = obj->enabled;
	obj->enabled = TRUE;

	Gate_leaveSystem(key);

	return (prev);
}

/*
 *  ======== BufferPacketizer_disable ========
 */
Bool BufferPacketizer_disable(BufferPacketizer_Object *obj)
{
	Bool prev;
	IArg key;

	key = Gate_enterSystem();

	prev = obj->enabled;
	obj->enabled = FALSE;

	Gate_leaveSystem(key);

	return (prev);
}



/*
 * ======== BufferPacketizer_getTransferType ========
 *  Returns whether the logger events can be sent over lossy transports or
 *  requires reliable transport
 // TODO need to see if this makes sense...
 */
IUIAPacketTransfer_TransferType BufferPacketizer_getTransferType(
		BufferPacketizer_Object *obj)
{
	return(obj->transferType);
}



/*
 * ======== BufferPacketizer_isEmpty ========
 * Returns true if the transfer packetBuffer has no unread data
 */
Bool BufferPacketizer_isEmpty(BufferPacketizer_Object *obj)
{
	Bool result;

	if (obj->wrPacket == (obj->pPacketBuffer + HDR_OFFSET_IN_WORDS)) {
		result = TRUE;
	}
	else {
		result = FALSE;
	}

	return (result);
}

/*
 * ======== BufferPacketizer_isEmpty ========
 * Returns true if the transfer packetBuffer has no unread data
 */
Bool BufferPacketizer_isFirstPacketFlagSet(BufferPacketizer_Object *obj)
{
	return (obj->isFirstPacket);
}




/*
 *  ======== BufferPacketizer_flush ========
 */
Void BufferPacketizer_sendPacket(BufferPacketizer_Object *obj)
{
	Bits32 pktLength;
	UInt32 adrs;
	int i;
	if ((obj->pPacketBuffer != NULL)&&(obj->isInitialized)){
		pktLength = (Bits32)((obj->wrPacket - obj->pPacketBuffer) * BYTES_IN_WORD);
		/* If there is data in the packetBuffer */
		if (obj->rdUploadBuffer
				> (obj->pPacketBuffer + HDR_OFFSET_IN_WORDS)) {
			/* Set UIA packet length and sequence number */
			UIAPacket_setEventLength((UIAPacket_Hdr*)obj->pPacketBuffer,pktLength);
			UIAPacket_setSequenceCount((UIAPacket_Hdr*)obj->pPacketBuffer,
					obj->serial);
			UIAPacket_initTraceHdrFields((UIAPacket_Hdr7 *)obj->pPacketBuffer, obj->traceHdrType,obj->endpointNumber,
					obj->isFirstPacket,obj->tracePktDownCount,obj->snapshotNumber);
			if (obj->printDebugMessages){
				printf("Packet Sequence Number=%d: obj->isFirstPacket = %d. obj->tracePktDownCount = %d\n",
						obj->serial,obj->isFirstPacket,obj->tracePktDownCount);
				printf("PktHdr.isFirstPkt = 0x%x PktHdr.tracePktDownCount=0x%x\n",
						UIAPacket_getFirstPktFlag((UIAPacket_Hdr7 *)obj->pPacketBuffer),
						UIAPacket_getTracePktDownCount((UIAPacket_Hdr7 *)obj->pPacketBuffer));
				adrs = (UInt32)obj->pPacketBuffer;
				for (i=0;i<7;i++){
					printf("   Hdr[%d] = 0x%x (address= 0x%x)\n",i,*(UInt32*)adrs,adrs);
					adrs += 4;
				}
			}
			/*
			 *  If there is empty space at the end of the packetBuffer, to let UIA know it should ignore this we
			 *  add a 32 bit Invalid UIA header with the length of the empty space.
			 */
			if (pktLength < obj->packetSizeInMAUs){
				UIAPacket_setInvalidHdr(obj->wrPacket,
						(obj->pPacketEndPlusOne - obj->wrPacket) * BYTES_IN_WORD);
			}
			/* Set the module write ptr to NULL to prevent log calls in exchange */
			obj->wrPacket = NULL;

			/* Send filled packetBuffer to exchange function */
			obj->pPacketBuffer = (UInt32*)obj->exchangeFxn((Ptr)obj,(Ptr)obj->pPacketBuffer);

			/* Update ptrs to new packetBuffer */
			obj->wrPacket = obj->pPacketBuffer + HDR_OFFSET_IN_WORDS;
			obj->pPacketEndPlusOne =	obj->pPacketBuffer	+ (obj->packetSizeInMAUs/sizeof(UInt32));
			obj->serial++;
			if (obj->printDebugMessages){
				printf("New packet: serial=%d, address = 0x%x\n",obj->serial,(UInt32)obj->pPacketBuffer);
			}
		}
	}
}

/*
 *  ======== BufferPacketizer_Prime =========
 */
Bool BufferPacketizer_prime(BufferPacketizer_Object *obj, Ptr packetBuffer)
{
	if (obj->primeStatus == FALSE) {
		obj->pPacketBuffer = (UInt32*)packetBuffer;

		obj->wrPacket = (UInt32*)packetBuffer + HDR_OFFSET_IN_WORDS;
		obj->pPacketEndPlusOne = (UInt32*)packetBuffer + obj->packetSizeInMAUs/sizeof(UInt32);
		obj->primeStatus = TRUE;//BufferPacketizer_init(obj);
		return (obj->primeStatus);
	}
	return (FALSE);
}



/*
 * ======== EtbDrain_readyToSend ========
 * returns true if the ETB drain packetBuffer contains unsent data
 *
 */
Bool BufferPacketizer_readyToSend(BufferPacketizer_Object *obj){
	if (obj->isInitialized){
		return (obj->tracePktDownCount > 0);
	}
	return(FALSE);
}

/*
 *   ======== poll ========
 *  Check if data is ready and, if so, trigger buffer exchange to send the data as packets.
 *  Call readyToSend after this call to check if there is data available to send.
 * @a(returns)  0 if success, else error code
 */
UInt32 BufferPacketizer_poll(BufferPacketizer_Object *obj){
	return(0);
}

/****************************************************************************************
 * Status of the STM - this is required to decode the compressed data
 ****************************************************************************************/
Void BufferPacketizer_transferSTMConfig(BufferPacketizer_Object *obj)
{
	/* ATB Message Header Pointer
	 *  Value stored from the ATB_POINTER register to provide information to determine
	 *  how to interpret the ETB data.
	 */
	int index = obj->snapshotNumber & 1;

	// first word is the value read for the transfer that is being uploaded
	*obj->wrPacket++ = ETB_getATBMsgHdrPointer(index) ; //(obj->atbMsgHdrPointer[index] & 0x0FFFF) | (obj->dmaStatus[index].isWrapped << 16);
	index = (index+1) & 1;
	// second word is the value for the previous (or following) transfer.  Can be used to detect changes in status.
	*obj->wrPacket++ = ETB_getATBMsgHdrPointer(index); //(obj->atbMsgHdrPointer[index] & 0x0FFFF) | (obj->dmaStatus[index].isWrapped << 16);
	BufferPacketizer_sendPacket(obj);
	return;
}

/****************************************************************************************
 * Read and Transfer ETB data
 * move the binary data to PC host via event packets for further
 * decoding and analysis
 ****************************************************************************************/
Bool BufferPacketizer_transferData(BufferPacketizer_Object *obj, UInt32 maxNumPackets)
{
	uint32_t retSize=0;
	UInt32 maxReadSize = obj->packetSizeInMAUs - (HDR_OFFSET_IN_WORDS + 1)*BYTES_IN_WORD;
	int32_t pktCnt;
	int32_t index = ((obj->snapshotNumber-1) & 1);
	Bool done = FALSE;
	uint32_t numWordsTransferred = 0;
	UInt32 	availableWords = obj->uploadBufferSizeInWords - (obj->rdUploadBuffer - obj->pUploadBuffer);

	if((obj->pPacketBuffer != NULL)&&((obj->tracePktDownCount > 0)||(obj->tracePktUpCount > 0)))
	{
		/* Loop through the necessary number of packets to read the data
		 *  from the ETB drain packetBuffer.
		 * - 16 1kB packets.
		 * After the packet has been successfully read, it is written to
		 *  the specified output file.
		 */

		pktCnt = 0;

		while ((!done)&&(pktCnt < maxNumPackets)) {
			if (availableWords > maxReadSize) {
				retSize = maxReadSize;
			} else {
				retSize = availableWords;
			}

			numWordsTransferred += retSize;
			obj->wrPacket += retSize;
			if ((obj->tracePktDownCount == 0)&&(obj->bufType == BufferPacketizer_BufferType_STM_ETB)){
				/* the last packet needs to contain the STM configuration data */
				BufferPacketizer_transferSTMConfig(obj);
			} else {
				BufferPacketizer_sendPacket(obj);
			}
			obj->isFirstPacket = FALSE;
			pktCnt++;
			if (obj->tracePktDownCount == 0){
				// Finished uploading the capture packetBuffer.
				// Re-configure the DMA and re-enable the ETB
				done = TRUE;
				obj->tracePktUpCount = 0;

			} else {
				if (obj->printDebugMessages){
					printf("pktCnt=%d: numWordsTransferred = %d out of %d\n",
							obj->tracePktUpCount,(obj->tracePktUpCount*maxReadSize)/sizeof(Int32),availableWords);
				}
				obj->tracePktDownCount--;
				obj->tracePktUpCount++;
			}

		}

	}
	return(TRUE);
}



