/*  ============================================================================
 *   Copyright (c) Texas Instruments Inc 2014
 *
 *   Use of this software is controlled by the terms and conditions found in the
 *   license agreement under which this software has been supplied.
* ============================================================================
 Examples to show pacekt processing for GE
* =============================================================================
 *  Revision History
 *  ===============
 *  November 23, 2014 Brighton Feng   File Created
 * ============================================================================
 */

#include <stdio.h>
#include <string.h>
#include "K2_GE_Init_drv.h"
#include "K2_common.h"
#include "GE_PktDMA_Init.h"
#include "GE_test.h"
#include "GE_Interrupts.h"
#include "GE_packets_process.h"
#include "GE_debug.h"

/*number of bytes will be filled an verfied
To fill and verfiy full packet may result in high CPU loading*/
#define NUM_BYTES_VERIFY 	0

#define RECORD_DETAILS 	0

GE_port_test_stat port_test_stat[GE_NUM_ETHERNET_PORT];

unsigned long long ullTestID= 1;
/*fill data into the payload the GE packet before test*/
void InitPayload(Uint8 * ucpBuffer, Uint32 uiNumBytes, Uint8 uiPortNum)
{
	int i;
	GE_Test_Packet_Header *test_header;
	unsigned long long * ullpData;
	if(ucpBuffer)
	{
		test_header= (GE_Test_Packet_Header *)ucpBuffer;
		test_header->payloadNumBytes= uiNumBytes;
		test_header->portNum= uiPortNum;
        CP15_DCacheCleanBuff((unsigned int)ucpBuffer, sizeof(GE_Test_Packet_Header));

#if NUM_BYTES_VERIFY
		//fill the rest of the data with fixed value
		ullpData = (unsigned long long *)(((Uint32)ucpBuffer +sizeof(GE_Test_Packet_Header)+7)&0xFFFFFFF8); //round up
		uiNumBytes = (uiNumBytes-sizeof(GE_Test_Packet_Header)-16)&0xFFFFFFF8; //round down
		if(uiNumBytes>NUM_BYTES_VERIFY)
			uiNumBytes= NUM_BYTES_VERIFY;
		for(i=0; i<uiNumBytes/8; i++)
		{
			ullpData[i]= ullTestID;
		}
        CP15_DCacheCleanBuff((unsigned int)ullpData, uiNumBytes);
#endif
        
		ullTestID++;
	}
}

/*verify data packet after test*/
void VerifyPacket(Uint8 * ucpBuffer, Uint32 uiNumBytes, Uint8 uiPortNum)
{
	int i;
	GE_Test_Packet_Header *test_header;
	unsigned long long * ullpData;
	if(ucpBuffer)
	{
		CP15_DCacheInvalidateBuff((unsigned int)ucpBuffer, 64);
		test_header= (GE_Test_Packet_Header *)ucpBuffer;
		if((test_header->payloadNumBytes != uiNumBytes)
			&&(test_header->payloadNumBytes != (uiNumBytes+4)))
		{
			printf("Port %d, Size info in packet (%d) does not match the actual size of the received packet (%d)\n",
				uiPortNum, test_header->payloadNumBytes, uiNumBytes);
		}
		if(test_header->portNum != uiPortNum)
		{
			printf("Port number (%d) in packet does not match the port received the packet (%d)\n",
				test_header->portNum, uiPortNum);
		}

#if NUM_BYTES_VERIFY
		//verify the rest of the data with fixed value
		ullpData = (unsigned long long *)(((Uint32)ucpBuffer +sizeof(GE_Test_Packet_Header)+7)&0xFFFFFFF8); //round up
		uiNumBytes = (uiNumBytes-sizeof(GE_Test_Packet_Header)-16)&0xFFFFFFF8; //round down
		if(uiNumBytes>NUM_BYTES_VERIFY)
			uiNumBytes= NUM_BYTES_VERIFY;

		CP15_DCacheInvalidateBuff((unsigned int)ullpData, uiNumBytes);
		for(i=1; i<uiNumBytes/8; i++)
		{
			if(ullpData[0] != ullpData[i])
			{
				printf("Data changed at position %d from 0x%8llx to 0x%8llx\n", i, 
					ullpData[0], ullpData[i]);
				return;
			}
		}
#endif
	}
}

/*construct a packet to "uiPortNum" according to "transferParam",
return the pointer of the descriptor*/
HostPacketDescriptor * ConstructPacket(GE_Transfer_Param * transferParam)
{
	Uint32 uiPortNum;
	Uint8 * ucpBuffer;
	HostPacketDescriptor * hostDescriptor;
	Uint32 uiPayloadNumBytes = transferParam->payloadNumBytes;

	hostDescriptor= (HostPacketDescriptor *)
		KeyStone_queuePop(transferParam->sourceQueue);
	if(NULL==hostDescriptor)
	{
		/*If there is no valid packet in the specified FDQ, 
		DDR_HOST_SIZE1_FDQ will be used instead.*/
		printf("Source queue %d is empty, try to get from DDR FDQ %d\n", 
			transferParam->sourceQueue, DDR_HOST_SIZE1_FDQ);
		hostDescriptor= (HostPacketDescriptor *)
			KeyStone_queuePop(DDR_HOST_SIZE1_FDQ);

		if(NULL==hostDescriptor)
		{
			printf("Source queue %d is empty\n", transferParam->sourceQueue);
			GE_Check_Free_Queues(); 	//for debug
			return NULL;
		}
	}

	//for this test, flow ID determines port number, and destination MAC address
	uiPortNum= transferParam->portNum; 	
	
	/*invalid cache before read descriptor RAM*/
	CP15_DCacheCleanInvalidateBuff((unsigned int)hostDescriptor, 64);

#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	/*the SRC_TAG_LO field in the Tx descriptor is used as RX flow ID*/
	hostDescriptor->src_tag_lo= 
		(transferParam->portNum+22);//for PktDMA loopback test

	/*Directed packet to port. Setting these bits to a non-zero value 
	indicates that the packet is a directed packet. Packets with the 
	these bits set will bypass the ALE and send the packet directly 
	to the port indicated.*/
	hostDescriptor->ps_flags= uiPortNum+1;
#else 	//K2E or K2L
	hostDescriptor->ps_flags= 0; //no CRC
	hostDescriptor->dest_tag_lo= uiPortNum+1;
#endif

	/*initialize the source buffer*/
	ucpBuffer= (Uint8 *)hostDescriptor->buffer_ptr;

	/*fill MAC header*/
	Fill_EMAC_header(ucpBuffer, ETHERNET_IPV4_PACKET, 
		Source_MAC_address[uiPortNum], Dest_MAC_address[uiPortNum]);

	InitPayload(ucpBuffer+EMAC_HEADER_LEN, uiPayloadNumBytes, uiPortNum);

	hostDescriptor->packet_length= uiPayloadNumBytes+EMAC_HEADER_LEN;
	hostDescriptor->buffer_len   = uiPayloadNumBytes+EMAC_HEADER_LEN;
	hostDescriptor->ret_push_policy= 0 ; //return to queue tail
	
	/*write back data from cache to descriptor RAM*/
	CP15_DCacheCleanBuff((unsigned int)hostDescriptor, 64);
	CP15_DCacheCleanBuff((unsigned int)ucpBuffer, EMAC_HEADER_LEN);

	return hostDescriptor;
}

/*transfer packets on all masked port*/
void GE_transfer_packets(Uint32 uiTX_port_mask, 
	Uint32 uiNumPackets, Uint32 uiPacketBytes)
{
	int i, j;
	HostPacketDescriptor * hostDescriptor;

	if((uiPacketBytes<46)||(uiPacketBytes>9216))
	{
		printf("Error: invalid packet payload size %d\n", uiPacketBytes);
		return;
	}

	if(uiNumPackets>32)
	{
		printf("Error: too much number of packets %d\n", uiNumPackets);
		return;
	}

	for(i=0; i<GE_NUM_ETHERNET_PORT; i++)
	{
		if((uiTX_port_mask>>i)&1) //port is enabled for TX
		{
			port_test_stat[i].tx_param.portNum= i;
			port_test_stat[i].tx_param.sourceQueue= Tx_FDQ[i];
			port_test_stat[i].tx_param.numPackets= uiNumPackets;
			port_test_stat[i].tx_param.payloadNumBytes= uiPacketBytes;
		}
	}

	for(i=0; i<uiNumPackets; i++)
	{
		for(j=0; j<GE_NUM_ETHERNET_PORT; j++)
			if((uiTX_port_mask>>j)&1) //port is enabled for TX
			{
				hostDescriptor= ConstructPacket(&port_test_stat[j].tx_param);
				if(NULL==hostDescriptor)
					return;

				//record start time for TX
				if(0==port_test_stat[j].uiTxCCNT)
					port_test_stat[j].uiTxCCNT= CP15_read_CCNT();

				/*push the packet descriptor to Packet DMA TX queue*/
				KeyStone_queuePush(GE_DIRECT_TX_QUEUE,
					(Uint32)hostDescriptor|FETCH_SIZE_32);
			}
	}
}


//parser the received packet
void parserRxPacket(HostPacketDescriptor * hostDescriptor, Uint32 uiPortNum)
{
	Uint32 length;
	Ethernet_Packet_Type type;
	unsigned long long sourceMAC, destMAC;
	Uint8 lastData;
	
	/*invalid cache before read descriptor RAM*/
	CP15_DCacheInvalidateBuff((unsigned int)hostDescriptor, 64);

	length= hostDescriptor->packet_length- EMAC_HEADER_LEN;
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
	//K2H/K always include CRC in received packet
	length -= EMAC_CRC_LEN;
#endif

	Get_EMAC_header((Uint8 *)hostDescriptor->buffer_ptr,
		&type, &sourceMAC, &destMAC);

	//check the last lastData
	lastData= *(Uint8 *)(hostDescriptor->buffer_ptr+length+EMAC_HEADER_LEN-1);

	printf("Last external packect from port %d: %d bytes (last byte=0x%x), packet type = 0x%04x, from srouce address 0x%012llx to destination address 0x%012llx\n",
		uiPortNum, length, lastData, type, sourceMAC, destMAC);
}

//last packet received from the other device
HostPacketDescriptor * lastExternalPacket[GE_NUM_ETHERNET_PORT];
#if RECORD_DETAILS
Uint32 uiStartCCNT, uiEndCCNT[128], uiCCNT_index;
Uint8 port_num[128];
#endif
void Rx_packet_process(Uint32 uiPort, Uint32 uiDescriptor)
{
	HostPacketDescriptor * hostDescriptor= (HostPacketDescriptor * )uiDescriptor;

	//port_test_stat[uiPort].uiRxCCNT= GE_INT_CCNT;
	port_test_stat[uiPort].uiRxCCNT= CP15_read_CCNT();
	
#if RECORD_DETAILS
	uiEndCCNT[uiCCNT_index&127]= CP15_read_CCNT();
	port_num[uiCCNT_index&127]= uiPort;
	uiCCNT_index++;
#endif

	port_test_stat[uiPort].uiNumRxPackets++;

	//for 2 devices test, record the last packet recieved
	if((ge_port_cfg[uiPort].loopback_mode== ETHERNET_LOOPBACK_DISABLE)
		||(ge_port_cfg[uiPort].loopback_mode== ETHERNET_SERDES_EXTERNAL_LOOPBACK))
	{
		lastExternalPacket[uiPort]= hostDescriptor;
	}
	else
	{
		/*invalid cache before read descriptor RAM*/
		CP15_DCacheInvalidateBuff((unsigned int)hostDescriptor, 64);

		if(hostDescriptor->packet_length<64)
		{
			printf("Ethernet Port %d, receive packet with length %d\n", 
				uiPort, hostDescriptor->packet_length);
		}
		else
		{
			VerifyPacket((Uint8 *)hostDescriptor->buffer_ptr+EMAC_HEADER_LEN,
#if defined(DEVICE_K2H) || defined(DEVICE_K2K)
				//K2H/K always include CRC in received packet
				hostDescriptor->packet_length- EMAC_HEADER_LEN - EMAC_CRC_LEN,
#else
				hostDescriptor->packet_length- EMAC_HEADER_LEN,
#endif
				uiPort);
		}
	}
	
	/*descriptor Reclamation*/
	KeyStone_queuePush(RECLAMATION_QUEUE, uiDescriptor|FETCH_SIZE_32);
}

void GE_Test_init()
{
#if RECORD_DETAILS
	uiStartCCNT=CP15_read_CCNT(); 
	//uiEndCCNT=0; 
	uiCCNT_index=0;
#endif
	memset((void *)lastExternalPacket, 0, sizeof(lastExternalPacket));
	memset((void *)port_test_stat, 0, sizeof(port_test_stat));
}

//show the result information of the last test interation
int GE_Test_result()
{
	int i, iResult=0;
	Uint32 uiCycles;
	GE_Transfer_Param * tx_param;

#if RECORD_DETAILS
	for(i=0; i<uiCCNT_index; i++)
		printf("port%d:%u ", port_num[i], uiEndCCNT[i]);
	if(uiCCNT_index)
		printf("Test start at %u, end at %u, %u cycles\n", uiStartCCNT, (uiEndCCNT[uiCCNT_index-1]), 
			((unsigned int)((0xFFFFFFFFl+(uiEndCCNT[uiCCNT_index-1]))- (unsigned long long)(uiStartCCNT))+ 1));
#endif
	
	for(i=0; i< GE_NUM_ETHERNET_PORT; i++)
	{
		if(lastExternalPacket[i])
			parserRxPacket(lastExternalPacket[i], i);
	}

	for(i=0; i< GE_NUM_ETHERNET_PORT; i++)
	{
		tx_param= &port_test_stat[i].tx_param;
		if((tx_param->numPackets)&&
			(ETHERNET_EXTERNAL_LINE_LOOPBACK>=ge_port_cfg[i].loopback_mode))
		{
			uiCycles= ((unsigned int)((0xFFFFFFFFl+port_test_stat[i].uiRxCCNT)- 
				(unsigned long long)port_test_stat[i].uiTxCCNT)+ 1);
			port_test_stat[i].uiThroughput_Mbps= 
				(unsigned long long)tx_param->numPackets*tx_param->payloadNumBytes*8*gMain_Core_Speed_Hz/uiCycles/1000000;
			if(port_test_stat[i].uiNumRxPackets)
				printf("Ethernet port %d TX %2d packets x %4d bytes, RX %2d packets, %8u cycles, %5u Mbps\n",
					i, tx_param->numPackets, tx_param->payloadNumBytes, port_test_stat[i].uiNumRxPackets,
					uiCycles, port_test_stat[i].uiThroughput_Mbps);
			else
			{
				printf("Ethernet port %d TX %2d packets x %4d bytes, RX %2d packets!\n",
					i, tx_param->numPackets, tx_param->payloadNumBytes, port_test_stat[i].uiNumRxPackets);
				iResult= -1;
			}
		}
		else if(port_test_stat[i].uiNumRxPackets)
		{
			printf("Ethernet port %d TX %2d packets x %4d bytes, RX %2d packets.\n",
				i, tx_param->numPackets, tx_param->payloadNumBytes, port_test_stat[i].uiNumRxPackets);
		}
	}

	GE_Test_init(); //clear statistics
	
	return iResult;
}
