/**
* @file icss_iSwitchDrv.c
*
* @brief Contains the Rx and Tx functions for packet processing on ARM including the ISR for two PRU
*
* \par
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* \par
*/
/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include <stdio.h>
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/Log.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/sysbios/hal/Hwi.h>
#include <evmAM335x.h>
#include "icss_iSwitchDrv.h"
#include "icss_switch.h"
#include "icss_pruInit.h"
#include "osdrv_prussdefs.h"
#include "icss_learning.h"
#include "icss_switch_emac.h"
#include "icss_statistics.h"
#include "icss_nimu_eth.h"
#include "osdrv_ndkdeviceconfig.h"
#include <icss_ethdriver.h>

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */


/* ========================================================================== */
/*                            Global Variables                                */
/* ========================================================================== */

/** port params for three ports, two physical and one for host*/
t_portParams switchPort[3];
/** Number of collisions occured*/
int num_of_collision_occured = 0;
/** Number of packets dropped as a result of collision not resolved*/
int collision_pkt_dropped =0;
/** Callback to be registered to process protocol specific packets*/
static icssCallBack_t protRxCall;
/** Callback to be registered to filter out packets*/
static icssCallBackRet_t txPacketExceptionCall;
/** user specific callback argument */
static void *protRxUser;
/** Variable containing list of implemented protocols*/
Uint16 numImplementedProtocols = NUM_PROTOCOLS_IMPLEMENTED;
/**list of identified protocol types, rest initialized to zero*/
Uint16 protocol_impl[MAX_NUM_PROTOCOL_IMPLEMENTED] = {IP4_PROT_TYPE, ARP_PROT_TYPE};

#ifdef USE_RX_TASK
/** Semaphore for the Rx Task*/
Semaphore_Handle RxSemaphoreHandle;
/**Rx Task handle*/
Task_Handle rxpendTask;
#endif

/**
* @def DEFAULT_PACING_TIMER_VAL
*        Pacing timer Value
*/
#define DEFAULT_PACING_TIMER_VAL 100
/**Timer Handle for pacing*/
Timer_Handle pacingTimerHandle;


/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
/**
* @internal
* @brief EMAC Packet Receive API
*
* @param none
*
* @retval none
*/
extern void HwIntRx(Int32);

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
/**
* @internal
* @brief Clears Rx interrupt
*
* @param none
*
* @retval none
*/
inline void ClearRxIRQ0(void)
{
	HWREG(RX_INT_CLR_REG) = 0x00100000;
}

/**
* @internal
* @brief Main Rx interrupt service routine
*
* @param args arguments if any
*
* @retval none
*/
void RxInterruptHandler(void *args)
{
#ifndef USE_RX_TASK
	Int32 port_number;
	Int32 queue_number, new_port;
	Int16 pLength;
	Int32 All_queues_empty = 0;
	char  dest_address[MAXMTU];
	Int32 more;


	
	while(All_queues_empty != 1)
	{
		pLength = RxPktInfo(&port_number, &queue_number);
		if(pLength > 0)
		{
			/*NRT Ethernet packet will be put into QUEPRIO3 & QUEPRIO4 only
			 * Give the packet to ndk stack*/
				if(queue_number >= switch_configuration.ethPrioQueue)
				{
					HwIntRx(queue_number);
				}
				else
				{
					// other queues not processed yet
					if(protRxCall != NULL)
						protRxCall(&queue_number, protRxUser);
					else
						RxPktGet((Uint32)dest_address, queue_number, &port_number, &more);   // just dump the packet here so we do no stall the queues/
				}
		}
		else
			All_queues_empty =1;

		ClearRxIRQ0();
	}
#else
	if(enablePacing == switch_configuration.enableIntrPacing) {
		/* disable Rx interrupt trigger from PRU */
		disableRxInterrupt();
		Timer_start(pacingTimerHandle);
	}
	ClearRxIRQ0();

	Semaphore_post(RxSemaphoreHandle);
#endif
}

/**
* @internal
* @brief ISR for Pacing DM Timer
*
* @param none
*
* @retval none
*/
void pacingISR(void *args)
{
	enableRxInterrupt();
}

/**
* @internal
* @brief Function which pends on Rx semaphore.Gets the Rx packet info for processing
*
* @param none
*
* @retval none
*/
#ifdef USE_RX_TASK
void RxTask(UArg a0, UArg a1)
{

	Int32 port_number;
	Int32 queue_number;
	Int16 pLength;
	Int32 All_queues_empty = 0;
	char  dest_address[MAXMTU];
	Int32 more;
	while(1)
	{
		Semaphore_pend(RxSemaphoreHandle, BIOS_WAIT_FOREVER);

		while(All_queues_empty != 1)
		{
			pLength = RxPktInfo(&port_number, &queue_number);
			if(pLength > 0)
			{
				/*NRT Ethernet packet will be put into QUEPRIO3 & QUEPRIO4 only
				 * Give the packet to ndk stack*/
				if(queue_number >= switch_configuration.ethPrioQueue)
				{
					HwIntRx(queue_number);
				}
				else
				{
					if(protRxCall != NULL)
						protRxCall(&queue_number, protRxUser);
					else
						RxPktGet((Uint32)dest_address, queue_number, &port_number, &more);   // just dump the packet here so we do no stall the queues/

				}
			}
			else
				All_queues_empty = 1;
		}
		All_queues_empty = 0;
	}
}
#endif

/**
* @brief Function which does learning for Tx packets and puts it into TxQueues
*
* @param srcAddress	   memory address to copy packet data from
* @param portNumber	   port from which data will be transmitted
* @param queuePriority	   which queue to write data to
* @param lengthOfPacket	length of the packet
*
* @retval Success -   0
*         Failure -  -1
*/
Int32 TxPacket(const Uint8* srcAddress, Int32 portNumber, Int32 queuePriority, Int32 lengthOfPacket)
{
	Int32 ret=-1;
	const Uint8*		macId;
    Uint8 txPort, trigTx1, trigTx2;
    Int32 rettest=0;

    //Gratitous ARP workaround Check
    if( txPacketExceptionCall != NULL)
    {
    	rettest = txPacketExceptionCall((void *)srcAddress, NULL);
		if(rettest)
		{
			return 0;
		}
    }

    macId = srcAddress;

	if (portNumber == PORT0)	// special case to use MAC learning
	{
		// get the Port number from MAC tables..
		txPort = findMAC(macId);

		switch (txPort)
		{
		case 0:			// need to send on both ports
			trigTx1 = 1;
			trigTx2 = 1;
			break;
		case 1:			// PORT1
			trigTx1 = 1;
			trigTx2 = 0;
			break;
		case 2:			// PORT2
			trigTx1 = 0;
			trigTx2 = 1;
			break;
		default:		// unexpected value from learning engine
			trigTx1 = 1;
			trigTx2 = 0;
			break;
		}
		if(trigTx1 == 1)
		{
			ret = TxPacketEnqueue(srcAddress, PORT1, queuePriority, lengthOfPacket);
		}
		if(trigTx2 == 1)
		{
			if (!ret)
				TxPacketEnqueue(srcAddress, PORT2, queuePriority, lengthOfPacket);
			else
				ret = TxPacketEnqueue(srcAddress, PORT2, queuePriority, lengthOfPacket);
		}

	} else
	{
		ret = TxPacketEnqueue(srcAddress, portNumber, queuePriority, lengthOfPacket);
	}

	if(!ret)
		return 0;
	else
		return -1;
}

/**
* @internal
* @brief Function to enqueue packet for transmission
*
* @param src_address	   memory address to copy packet data from
* @param port_number	   port from which data will be transmitted
* @param queue_priority	   which queue to write data to
* @param length_of_packet	length of the packet
*
* @retval Success -   0
*         Failure -  -1
*/
Int32 TxPacketEnqueue (const Uint8* srcAddress,Int32 portNumber, Int32 queuePriority, Int32 lengthOfPacket)
{
	Uint16 buffer_offset_computed =0;
	Uint32 buffer_des = 0;
	Uint16 queue_wr_ptr =0;
	Uint16 wrk_queue_wr_ptr =0;
	Uint16 size = 0;
	Uint16 queue_rd_ptr =0;
	Uint16 num_of_bytes =0;
	Uint16 new_packet_length =0;
	Uint32 temp =0;
	Uint16 i =0;
	Uint32 collision_queue_selected =0;
    Uint16   collision_status =0;
	Uint16	col_queue_already_occupied =0;
	Uint16 original_length_of_packet =0;
	Uint16 packet_min_size_padding = 0;
	Uint16 remaining_valid_frame_data_length =0;
	Uint8 *macAddr;

//	Uint32 pruSharedMem = 0;

	if((portNumber != PORT1) && (portNumber != PORT2))
		return SWITCH_INVALID_PORT;
	if((queuePriority < QUEPRIO1) || (queuePriority > QUEPRIO4))
		return SWITCH_INVALID_PARAM;
	if(lengthOfPacket > MAXMTU)
		return SWITCH_ERR_BADPACKET;
	if(lengthOfPacket < MINMTU)
			return SWITCH_ERR_BADPACKET;
	
	if(	lengthOfPacket < ETHERNET_FRAME_SIZE_60)
    {
		original_length_of_packet = lengthOfPacket;
		packet_min_size_padding =1;
		lengthOfPacket = ETHERNET_FRAME_SIZE_60;
	
	}else
	{
	    packet_min_size_padding = 0;
	}

	macAddr = (Uint8*)srcAddress;

	/*check link status*/
	// FW: from icss_ethdriver
	if(GetLinkStatus(portNumber) == FALSE_VAL)
	{
		return SWITCH_ERR_TX_NO_LINK;
	}

	t_queParams *txQueue = &(switchPort[portNumber].queue[queuePriority]);
	// Check whether Queue is busy.If yes then put the packet in the collision Queue. Set the busy_s bit because Host is always a Slave.
	temp =  HWREG( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4);
	collision_queue_selected =  (temp & 0x00000100);  // Check the "busy_m" bit
	if(collision_queue_selected != 0)
	{
	   num_of_collision_occured = num_of_collision_occured +1;
	   // Queue is busy  .. put the packet in the collision Queue
	   txQueue = &(switchPort[portNumber].queue[COLQUEUE]);
	   if(portNumber == PORT1)
		{
			col_queue_already_occupied = HWREGB(DATARAM1_PHYS_BASE + COLLISION_STATUS_ADDR + 1);
		} else
		{
			col_queue_already_occupied = HWREGB(DATARAM1_PHYS_BASE + COLLISION_STATUS_ADDR + 2);
		}
		if(col_queue_already_occupied != 0)
		{
		  collision_pkt_dropped ++;
		  return SWITCH_ERR_COLLISION_FAIL;   //No space in collision queue
		}
	}
	else
	{
	 // Queue is Not Busy .. Acquire the Queue by setting "busy_s" bit
		HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 1; // Set the "busy_s" bit
		//Again check if host acquired the queue successfully by checking the busy_m bit
		temp =  HWREG( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4);
		collision_queue_selected =  (temp & 0x00000100);  // Check the "busy_m" bit
		if(collision_queue_selected != 0)
		{
			num_of_collision_occured = num_of_collision_occured +1;
			HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0; // Clear the busy_s bit
			// Queue is busy  .. put the packet in the collision Queue
			txQueue = &(switchPort[portNumber].queue[COLQUEUE]);
		}
	}
	/*  Compute the buffer descriptor ..length is from bit 18 to 28 */
	buffer_des = (lengthOfPacket << 18);

	/*  Read the write pointer from Queue descriptor */
	temp =  HWREG( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset);
	queue_wr_ptr = (temp >> 16);
	queue_rd_ptr = (temp & 0x0000ffff);

	wrk_queue_wr_ptr = (lengthOfPacket >> 5);  /* Divide by 32 */
	wrk_queue_wr_ptr = (wrk_queue_wr_ptr << 2);  /* Multiply by 4 ..as one descriptor represents 32 bytes and BD takes 4 bytes */
	if((lengthOfPacket & 0x0000001f) != 0)
		wrk_queue_wr_ptr = wrk_queue_wr_ptr + 4;

	/* Add to get the value of new queue write pointer */
	wrk_queue_wr_ptr = wrk_queue_wr_ptr + queue_wr_ptr;
	size = txQueue->queue_size;
	/*Check if queue is full and there is an wrap around */
	if(((queue_wr_ptr + 4) % size) == 0)
	{
		if(queue_rd_ptr == txQueue->buffer_desc_offset) /* Since queue is not starting from 0. */
		{
			txQueue->qStat.errCount++;
			HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0;
			return SWITCH_ERR_TX_OUT_OF_BD; 									/* No space in queue */
		}
	}
	/* Check if the Queue is already full */
	if((queue_wr_ptr + 4) == queue_rd_ptr)
	{
		txQueue->qStat.errCount++;
		HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0;
		return SWITCH_ERR_TX_OUT_OF_BD; 										/* No space in queue */
	}
	/* Three cases arise between wr_ptr and rd_ptr */
	if(queue_wr_ptr == queue_rd_ptr)
	{
		/*Check for wrap around */
		if(wrk_queue_wr_ptr >=  size)
		{
			wrk_queue_wr_ptr = (wrk_queue_wr_ptr % size);
			/*Add offset as queue doesn't start from 0. */
			wrk_queue_wr_ptr = wrk_queue_wr_ptr + txQueue->buffer_desc_offset;
		}
	}
	else if(queue_wr_ptr > queue_rd_ptr)
	{
		/*Check for wrap around */
		if(wrk_queue_wr_ptr >=  size)
		{
			wrk_queue_wr_ptr = (wrk_queue_wr_ptr % size);
			wrk_queue_wr_ptr = wrk_queue_wr_ptr + txQueue->buffer_desc_offset;
			if(wrk_queue_wr_ptr >= queue_rd_ptr)
			{
				txQueue->qStat.errCount++;
				HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0;
				return SWITCH_ERR_TX_OUT_OF_BD;  					/* No space in queue */
			}
		}
	}
	else if(queue_wr_ptr < queue_rd_ptr)
	{
		if(wrk_queue_wr_ptr >= queue_rd_ptr)
		{
			txQueue->qStat.errCount++;
			HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0;
			return SWITCH_ERR_TX_OUT_OF_BD;  						/* No space in queue */
		}
	}
	 /* Compute the offset of buffer descriptor in ICSS shared RAM */
	buffer_offset_computed = txQueue->buffer_offset +
			((queue_wr_ptr - txQueue->buffer_desc_offset)*8);    /* queue_wr_ptr points to currently available free buffer */

	/* Check if queue wrap around has happened. If yes then data can't be stored sequentially. */
	if( (wrk_queue_wr_ptr < queue_wr_ptr) &&  (wrk_queue_wr_ptr != txQueue->buffer_desc_offset))
	{
		num_of_bytes = (size - queue_wr_ptr);
		num_of_bytes *= 8;					// divide by 4 * 32!
		// check if Padding has to be done. If yes then Pad with Zero's to reach the minimum size for the ethernet frame.
		if(packet_min_size_padding == 1)
		{
			if( num_of_bytes <= original_length_of_packet)
			{
				memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed), (Int32*) srcAddress, num_of_bytes);
			}
			else
			{
				// Copy the valid packet data first and then Pad with zero's. 
				memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed), (Int32*) srcAddress, original_length_of_packet);
				// Padd the remaining bytes with Zero's
				for(i=0; i< (num_of_bytes - original_length_of_packet); i++)
				{
					HWREGB(L3_OCMC_BASE_ADDRESS + buffer_offset_computed + original_length_of_packet + i) = 0;
				}
			}
		}
		else
		{
			memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed), (Int32*) srcAddress, num_of_bytes);
		}
		new_packet_length = lengthOfPacket - num_of_bytes;
        srcAddress = srcAddress + num_of_bytes;
        if(collision_queue_selected != 0)
        	buffer_offset_computed = buffer_offset_computed + num_of_bytes;
        else
        	buffer_offset_computed = txQueue->buffer_offset;
        if( packet_min_size_padding == 1)
		{
        	if(	original_length_of_packet <= num_of_bytes)
        	{
				// Pad all the remaining bytes with Zero's
        		for(i=0; i< new_packet_length; i++)
				{
					HWREGB(L3_OCMC_BASE_ADDRESS + buffer_offset_computed + i) = 0;
				}
			}
			else
			{
				// Fill the frame data 
				remaining_valid_frame_data_length = (original_length_of_packet- num_of_bytes);
				memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed),(Int32*) srcAddress, remaining_valid_frame_data_length);
				//Pad the remaining bytes with Zero's
				for(i=0; i< (new_packet_length - remaining_valid_frame_data_length); i++)
				{
					HWREGB(L3_OCMC_BASE_ADDRESS + buffer_offset_computed + remaining_valid_frame_data_length+ i) = 0;
				}
			}
		}	
		else
		{
			memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed),(Int32*) srcAddress, new_packet_length);
		}
	}
	else
	{
		if( packet_min_size_padding  == 1)
		{
			memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed), (Int32*)srcAddress, original_length_of_packet);
			// Padd the remaining bytes with Zero's
			for(i=0; i< (lengthOfPacket - original_length_of_packet); i++)
			{
				HWREGB(L3_OCMC_BASE_ADDRESS + buffer_offset_computed + original_length_of_packet + i) = 0;
			}
		}
		else
		{
			memcpy((Int32*)(L3_OCMC_BASE_ADDRESS + buffer_offset_computed), (Int32*)srcAddress, lengthOfPacket);
		}
	}
	HWREG( PRUSS_SHAREDRAM_BASE + queue_wr_ptr ) = buffer_des;
	/* Write new wr_ptr in the queue descriptor */
	HWREGH( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset +2) = wrk_queue_wr_ptr;
	txQueue->qStat.rawCount++;

	// Release the Queue. Clear the "busy_s" bit .. even if collision queue was selected then below line won't have any impact.
	//busy_s_bit =  0x00;
	HWREGB( DATARAM1_PHYS_BASE + txQueue->queue_desc_offset + 4) = 0;
	// If packet was put in collision queue then indiciate it to collision task
	if(collision_queue_selected != 0)
	{
		if(portNumber ==PORT1)
		{
			collision_status = 	queuePriority;
			collision_status = (collision_status << 1);
			collision_status = (collision_status | 0x0001);
			HWREGB( DATARAM1_PHYS_BASE + COLLISION_STATUS_ADDR +1) = collision_status;
		}
		else
		{
			collision_status = 	queuePriority;
    		collision_status = (collision_status << 1);
    		collision_status = (collision_status | 0x0001);
    		HWREGB( DATARAM1_PHYS_BASE + COLLISION_STATUS_ADDR + 2) = collision_status;
		}
	} 
	UpdateTxstats(macAddr,(unsigned int)(lengthOfPacket+4), portNumber-1);
	return 0;
}

/**
* @brief Function to get the Rx packet info
*
* @param portNumber	   port number of Rx packet
* @param queueNumber	   queue number of Rx packet
*
* @retval rd_packet_length length of the packet received
*/
Int32 RxPktInfo(Int32* portNumber, Int32* queueNumber)
{
	Uint16 queue_rd_ptr;
	Uint16 queue_wr_ptr;
	Uint32 rd_buf_desc=0;
	Uint16 rd_packet_length;
	Int32 packet_found =0;
	Int32 i=0;
	Queue *qDesc;

	while((packet_found == 0) && (i < 4))
	{
		qDesc = (Queue *)(DATARAM1_PHYS_BASE + P0_QUEUE_DESC_OFFSET + (i*8));
		queue_wr_ptr = qDesc->wr_ptr;
		queue_rd_ptr = qDesc->rd_ptr;
		if(qDesc->overflow_cnt > 0)
		{
			switchPort[PORT0].queue[i].qStat.errCount += qDesc->overflow_cnt;		// increment missed packets to error counter
			qDesc->overflow_cnt = 0;										            // reset to zero as limited to 256 anyway
		}
		if(queue_rd_ptr != queue_wr_ptr)
		{
			(*queueNumber) = i;
			rd_buf_desc = HWREG( PRUSS_SHAREDRAM_BASE + queue_rd_ptr);
			/* Take out the port number */
			(*portNumber) = (0x00030000 & rd_buf_desc)>>16;
			/* Get the length */
			rd_packet_length = (0x1ffc0000 & rd_buf_desc)>>18;
			packet_found = 1;
		}
		i++;
	}
	/* Received IRQ but can't find the packet in any queue */
	if(packet_found == 0)
	{
		rd_packet_length = 0;
	}
	return rd_packet_length;
}
/**
* @brief Function which copy a packet to ndk buffer/destiantion packet structure
*
* @param destAddress	  ndk packet buffer  address to copy packet data
* @param queueNumber	  Rx queue from which packet data is to be copyied
* @param port	          Rx port number
* @param more	          set to one if more packets in queue already
*
* @retval rd_packet_length length of the packet received
*/
int RxPktGet(Uint32 destAddress, Int32 queueNumber, Int32* port, Int32* more)
{
	Uint16 queue_rd_ptr;
	Uint16 queue_wr_ptr;
	Uint16 rd_buf_desc_num;
	Uint32 rd_buf_desc=0;
	Uint16 rd_packet_length;
	Uint32 rd_buffer_l3_addr;
	Uint16 size =0;
	Uint16 update_rd_ptr=0;
	Uint16 rx_num_of_bytes=0;
	Uint16 new_size =0;
	t_queParams *rxQueue;
	Queue *qDesc;
	Uint16	shadow=0;

	Uint8* srcMacId;
	Uint8* destMacId;

	Uint8 portNum;

	Uint16 *typeProt;
	Uint16  typeProt1;
	Uint16  typeProt2;

	qDesc = (Queue *)(DATARAM1_PHYS_BASE + P0_QUEUE_DESC_OFFSET + (queueNumber*8));
	queue_wr_ptr = qDesc->wr_ptr;
	queue_rd_ptr = qDesc->rd_ptr;
	if(qDesc->overflow_cnt > 0)
	{
		switchPort[PORT0].queue[queueNumber].qStat.errCount += qDesc->overflow_cnt;		// increment missed packets to error counter
		qDesc->overflow_cnt = 0;
		/* reset to zero as limited to 256 anyway */
	}
	rd_buf_desc = HWREG( PRUSS_SHAREDRAM_BASE + queue_rd_ptr);
	rxQueue = &(switchPort[PORT0].queue[queueNumber]);
	/* Determine the address of the first buffer descriptor from the rd_ptr */
	//Check if Packet was received in collision queue or not
	shadow = ((rd_buf_desc & 0x00004000) >> 14);
	if(shadow != 0)
	{ 
	  // Pick the data from collision buffer's
		rd_buffer_l3_addr = (L3_OCMC_BASE_ADDRESS + P0_COL_BUFFER_OFFSET);
	}
	else {
	rd_buf_desc_num = (queue_rd_ptr - rxQueue->buffer_desc_offset) >> 2;
	rd_buffer_l3_addr = (L3_OCMC_BASE_ADDRESS + (rd_buf_desc_num * 32) + rxQueue->buffer_offset);
	}
	/* Take out the port number - it may have changed */
	*port = (0x00030000 & rd_buf_desc)>>16;
	portNum = (*port) - 1;

	srcMacId = (Uint8*)(rd_buffer_l3_addr + 6);
	destMacId = (Uint8*)rd_buffer_l3_addr;

	rd_packet_length = (0x1ffc0000 & rd_buf_desc)>>18;

	size = (rd_packet_length >>2);
	if( (rd_packet_length & 0x00000003) != 0 )
		size = size +1;

	/*Compute number of buffer desc required & update rd_ptr in queue */
	update_rd_ptr = ((rd_packet_length >> 5)*4) + queue_rd_ptr;
	if( (rd_packet_length & 0x0000001f) != 0) /* checks multiple of 32 else need to increment by 4 */
		update_rd_ptr += 4;
	/*Check for wrap around */
	if(update_rd_ptr >= rxQueue->queue_size)
	{
		update_rd_ptr = update_rd_ptr - (rxQueue->queue_size - rxQueue->buffer_desc_offset);
	}
	if(rd_packet_length <= MAXMTU)		/* make sure we do not have too big packets */
	{

#ifdef ALE_IMPLEMENT
		updateHashTable(srcMacId, portNum);
#endif
		/* Copy the data from switch buffers to DDR */
		if( (update_rd_ptr < queue_rd_ptr) && (update_rd_ptr != rxQueue->buffer_desc_offset))
		{
			typeProt = (Uint16*)rd_buffer_l3_addr + 6;
			typeProt1 = (*typeProt << 8);
			typeProt2 = (*typeProt >> 8);
			typeProt1 = typeProt1 | typeProt2;
			rx_num_of_bytes = (rxQueue->queue_size - queue_rd_ptr);
			rx_num_of_bytes = (rx_num_of_bytes >> 2);
			rx_num_of_bytes = (rx_num_of_bytes << 5);

			memcpy((Int32*)destAddress, (Int32*)rd_buffer_l3_addr, rx_num_of_bytes);
		  	destAddress = destAddress + rx_num_of_bytes;
		  	new_size = rd_packet_length - rx_num_of_bytes;
		  	if(shadow != 0)
		  		rd_buffer_l3_addr = rd_buffer_l3_addr + rx_num_of_bytes;
		  	else
		  		rd_buffer_l3_addr = (L3_OCMC_BASE_ADDRESS + rxQueue->buffer_offset);

		  	memcpy((Int32*)destAddress, (Int32*)rd_buffer_l3_addr, new_size);
		}
		else
		{

			memcpy((Int32*)destAddress, (Int32*)rd_buffer_l3_addr, rd_packet_length);
			typeProt = (Uint16*)destAddress + 6;
			typeProt1 = (*typeProt << 8);
			typeProt2 = (*typeProt >> 8);
			typeProt1 = typeProt1 | typeProt2;
		}
	}
	else  /* wrong packet size (exceeds MAXMTU)*/
	{
		rxQueue->qStat.errCount++;
		return -1;
	}
	/* Write back to queue */
	HWREGH(DATARAM1_PHYS_BASE + P0_QUEUE_DESC_OFFSET + (queueNumber*8)) = update_rd_ptr;
	rxQueue->qStat.rawCount++;
	// Check if Host needs to change the wr_ptr for collision queue as well
	if(shadow != 0)
	{
		Queue *qDescCol = (Queue *)(DATARAM1_PHYS_BASE + P0_COL_QUEUE_DESC_OFFSET);
		//Write back to collision queue desc
		HWREGH(DATARAM1_PHYS_BASE + P0_COL_QUEUE_DESC_OFFSET +2) = qDescCol->rd_ptr;
	}
	*more = 0;
	/* get new pointer data in case new packets received in meantime - experimental.. */
	qDesc = (Queue *)(DATARAM1_PHYS_BASE + P0_QUEUE_DESC_OFFSET + (queueNumber*8));
	queue_wr_ptr = qDesc->wr_ptr;

	if(update_rd_ptr != queue_wr_ptr)
		*more = 1;
	UpdateRxstats(destMacId, rd_packet_length + 4, typeProt1, portNum);

	return rd_packet_length;
}

/**
* @brief Function to find the max fill level of a queue
*
* @param portNumber	  port on which the queue belongs
* @param queuePriority	  priority of queue
* @param queueOverflowCount	    number of times queue has overflown
*
* @retval queueMaxFillLevel max fill level of the queue in terms of 32 byte blocks
*/
Int32 GetMaxQueueFillLevel(Int32 portNumber, Int32 queuePriority, Int32* queueOverflowCount)
{
     Int32 queueMaxFillLevel =0;

	if((portNumber != PORT0) && (portNumber != PORT1) && (portNumber != PORT2))
		return SWITCH_INVALID_PORT;
	if((queuePriority < QUEPRIO1) || (queuePriority > QUEPRIO4))
		return SWITCH_INVALID_PARAM;

	t_queParams *Queue = &(switchPort[portNumber].queue[queuePriority]);
	//Read the max fill level for the queue
	queueMaxFillLevel =  HWREGB( DATARAM1_PHYS_BASE + Queue->queue_desc_offset + Q_MAX_FILL_LEVEL_OFFSET);
	
	*queueOverflowCount = HWREGB( DATARAM1_PHYS_BASE + Queue->queue_desc_offset + Q_OVERFLOW_CNT_OFFSET);

	return queueMaxFillLevel;
}

/**
* @internal
* @brief Function to clear queue statistics(internal)
*
* @param none
*
* @retval none
*/
void ClearStatistics(void)
{
	Int32 i, j;
	for (j=0; j<3; j++)
	{
		switchPort[j].errCount = 0;
		switchPort[j].rawCount = 0;
		for (i=0; i<NUMQUEUES; i++)
		{
			switchPort[j].queue[i].qStat.errCount = 0;
			switchPort[j].queue[i].qStat.rawCount = 0;
		}
	}
}

/**
* @internal
* @brief Function to initialize all Port Queue params
*
* @param none
*
* @retval 0 on success
*/
Int32 PortInit(void)
{
	/* Clear counters */
	ClearStatistics();
	/* Initialize port 0*/
	switchPort[PORT0].queue[QUEUE1].buffer_offset 	  = P0_Q1_BUFFER_OFFSET;
	switchPort[PORT0].queue[QUEUE1].buffer_desc_offset = P0_Q1_BD_OFFSET;
	switchPort[PORT0].queue[QUEUE1].queue_desc_offset  = P0_QUEUE_DESC_OFFSET;
	switchPort[PORT0].queue[QUEUE1].queue_size         = (HOST_QUEUE_1_SIZE << 2) + P0_Q1_BD_OFFSET;		// really the end of Queue

	switchPort[PORT0].queue[QUEUE2].buffer_offset 	  = P0_Q2_BUFFER_OFFSET;
	switchPort[PORT0].queue[QUEUE2].buffer_desc_offset = P0_Q2_BD_OFFSET;
	switchPort[PORT0].queue[QUEUE2].queue_desc_offset  = P0_QUEUE_DESC_OFFSET + 8;
	switchPort[PORT0].queue[QUEUE2].queue_size         = (HOST_QUEUE_2_SIZE << 2) + P0_Q2_BD_OFFSET;

	switchPort[PORT0].queue[QUEUE3].buffer_offset 	  = P0_Q3_BUFFER_OFFSET;
	switchPort[PORT0].queue[QUEUE3].buffer_desc_offset = P0_Q3_BD_OFFSET;
	switchPort[PORT0].queue[QUEUE3].queue_desc_offset  = P0_QUEUE_DESC_OFFSET + 16;
	switchPort[PORT0].queue[QUEUE3].queue_size         = (HOST_QUEUE_3_SIZE << 2) + P0_Q3_BD_OFFSET;

	switchPort[PORT0].queue[QUEUE4].buffer_offset 	  = P0_Q4_BUFFER_OFFSET;
	switchPort[PORT0].queue[QUEUE4].buffer_desc_offset = P0_Q4_BD_OFFSET;
	switchPort[PORT0].queue[QUEUE4].queue_desc_offset  = P0_QUEUE_DESC_OFFSET + 24;
	switchPort[PORT0].queue[QUEUE4].queue_size         = (HOST_QUEUE_4_SIZE << 2) + P0_Q4_BD_OFFSET;

	/* Initialize port 1*/
	switchPort[PORT1].queue[QUEUE1].buffer_offset 	  = P1_Q1_BUFFER_OFFSET;
	switchPort[PORT1].queue[QUEUE1].buffer_desc_offset = P1_Q1_BD_OFFSET;
	switchPort[PORT1].queue[QUEUE1].queue_desc_offset  = P1_QUEUE_DESC_OFFSET;
	switchPort[PORT1].queue[QUEUE1].queue_size         = (QUEUE_1_SIZE << 2) + P1_Q1_BD_OFFSET;

	switchPort[PORT1].queue[QUEUE2].buffer_offset 	  = P1_Q2_BUFFER_OFFSET;
	switchPort[PORT1].queue[QUEUE2].buffer_desc_offset = P1_Q2_BD_OFFSET;
	switchPort[PORT1].queue[QUEUE2].queue_desc_offset  = P1_QUEUE_DESC_OFFSET + 8;
	switchPort[PORT1].queue[QUEUE2].queue_size         = (QUEUE_2_SIZE << 2) + P1_Q2_BD_OFFSET;

	switchPort[PORT1].queue[QUEUE3].buffer_offset 	  = P1_Q3_BUFFER_OFFSET;
	switchPort[PORT1].queue[QUEUE3].buffer_desc_offset = P1_Q3_BD_OFFSET;
	switchPort[PORT1].queue[QUEUE3].queue_desc_offset  = P1_QUEUE_DESC_OFFSET + 16;
	switchPort[PORT1].queue[QUEUE3].queue_size         = (QUEUE_3_SIZE << 2) + P1_Q3_BD_OFFSET;

	switchPort[PORT1].queue[QUEUE4].buffer_offset 	  = P1_Q4_BUFFER_OFFSET;
	switchPort[PORT1].queue[QUEUE4].buffer_desc_offset = P1_Q4_BD_OFFSET;
	switchPort[PORT1].queue[QUEUE4].queue_desc_offset  = P1_QUEUE_DESC_OFFSET + 24;
	switchPort[PORT1].queue[QUEUE4].queue_size         = (QUEUE_4_SIZE << 2) + P1_Q4_BD_OFFSET;
	
	//Collision Queue
	switchPort[PORT1].queue[COLQUEUE].buffer_offset 	  	= P1_COL_BUFFER_OFFSET;
	switchPort[PORT1].queue[COLQUEUE].buffer_desc_offset = P1_COL_BD_OFFSET;
	switchPort[PORT1].queue[COLQUEUE].queue_desc_offset  = P1_COL_QUEUE_DESC_OFFSET;
	switchPort[PORT1].queue[COLQUEUE].queue_size         = (COLLISION_QUEUE_SIZE << 2) + P1_COL_BD_OFFSET;

	/* Initialize port 2*/
	switchPort[PORT2].queue[QUEUE1].buffer_offset 	  = P2_Q1_BUFFER_OFFSET;
	switchPort[PORT2].queue[QUEUE1].buffer_desc_offset = P2_Q1_BD_OFFSET;
	switchPort[PORT2].queue[QUEUE1].queue_desc_offset  = P2_QUEUE_DESC_OFFSET;
	switchPort[PORT2].queue[QUEUE1].queue_size         = (QUEUE_1_SIZE << 2) + P2_Q1_BD_OFFSET;

	switchPort[PORT2].queue[QUEUE2].buffer_offset 	  = P2_Q2_BUFFER_OFFSET;
	switchPort[PORT2].queue[QUEUE2].buffer_desc_offset = P2_Q2_BD_OFFSET;
	switchPort[PORT2].queue[QUEUE2].queue_desc_offset  = P2_QUEUE_DESC_OFFSET + 8;
	switchPort[PORT2].queue[QUEUE2].queue_size         = (QUEUE_2_SIZE << 2) + P2_Q2_BD_OFFSET;

	switchPort[PORT2].queue[QUEUE3].buffer_offset 	  = P2_Q3_BUFFER_OFFSET;
	switchPort[PORT2].queue[QUEUE3].buffer_desc_offset = P2_Q3_BD_OFFSET;
	switchPort[PORT2].queue[QUEUE3].queue_desc_offset  = P2_QUEUE_DESC_OFFSET + 16;
	switchPort[PORT2].queue[QUEUE3].queue_size         = (QUEUE_3_SIZE << 2) + P2_Q3_BD_OFFSET;

	switchPort[PORT2].queue[QUEUE4].buffer_offset 	  = P2_Q4_BUFFER_OFFSET;
	switchPort[PORT2].queue[QUEUE4].buffer_desc_offset = P2_Q4_BD_OFFSET;
	switchPort[PORT2].queue[QUEUE4].queue_desc_offset  = P2_QUEUE_DESC_OFFSET + 24;
	switchPort[PORT2].queue[QUEUE4].queue_size         = (QUEUE_4_SIZE << 2) + P2_Q4_BD_OFFSET;
	
	//Collision Queue
	switchPort[PORT2].queue[COLQUEUE].buffer_offset 	  	= P2_COL_BUFFER_OFFSET;
	switchPort[PORT2].queue[COLQUEUE].buffer_desc_offset = P2_COL_BD_OFFSET;
	switchPort[PORT2].queue[COLQUEUE].queue_desc_offset  = P2_COL_QUEUE_DESC_OFFSET;
	switchPort[PORT2].queue[COLQUEUE].queue_size         = (COLLISION_QUEUE_SIZE << 2) + P2_COL_BD_OFFSET;

	return 0;
}


/**
* @internal
* @brief Function to create Rx task (to receive packet)
*
* @param none
*
* @retval Success -   0
*         Error   -   <0
*/
Int8 CreateRXtask()
{
#ifdef USE_RX_TASK

	Task_Params taskParams;
    Semaphore_Params semParams;

    Semaphore_Params_init(&semParams);
    semParams.mode = Semaphore_Mode_BINARY;
    RxSemaphoreHandle = Semaphore_create(0, &semParams, NULL);
    if(RxSemaphoreHandle==NULL)
    {
    	return -1;
    }
    Task_Params_init(&taskParams);
    taskParams.priority = 10;
    taskParams.instance->name = "rxTask";
    rxpendTask = Task_create(RxTask, &taskParams, NULL);
    if(rxpendTask==NULL)
    {
    	return -2;
    }
#endif // USE_RX_RTASK

    if(enablePacing == switch_configuration.enableIntrPacing) {
		/*Timer initialization variables*/
		Timer_Params timerParams;
		Error_Block eb;
		/*Pacing DM Timer configuration*/
		DMTimer3ModuleClkConfig(); /*Pacing Timer*/
		/*Timer setup init*/
		Error_init(&eb);
		Timer_Params_init(&timerParams);

		/*This is irrelevant since timeout value is received from the supervisor*/
		timerParams.period = DEFAULT_PACING_TIMER_VAL;
		timerParams.periodType = Timer_PeriodType_MICROSECS;
		timerParams.runMode = Timer_RunMode_ONESHOT;
		timerParams.arg = 1;
		/*24 Mhz, care must be taken to ensure that this value
		 * is modified if the source clock frequency is modified */
		timerParams.extFreq.lo = DMTIMER_SRC_CLK_FREQ;
		timerParams.extFreq.hi = 0;
		timerParams.startMode = Timer_StartMode_USER;

		pacingTimerHandle = Timer_create(DMTIMER3_ID, (ti_sysbios_interfaces_ITimer_FuncPtr)pacingISR, &timerParams,  &eb); /* DMTIMER3 */
		if(pacingTimerHandle == NULL) {
			System_abort("Pacing Timer creation failed");
		}
    }
	// following line moved to PnInit() as was overwritten otherwise
	//HWREGB(PRUSS_SHAREDRAM_BASE + RX_INT_STATUS_OFFSET) = 0x01;
    return 0;
}
/**
* @internal
* @brief Function to delete Rx task (to receive packet)
*
* @param none
*
* @retval Success -   0
*         Error   -   <0
*/
Int8 DeleteRXtask()
{
	Task_delete(&rxpendTask);
	Semaphore_delete(&RxSemaphoreHandle);
	return 0;
}

/**
* @brief Function to register callback function to process protocol specific packets
*
* @param callBack	  Callback function pointer
* @param userArg	  user argument if any
*
* @retval none
*/
void RegisterProtocolCallback(icssCallBack_t callBack, void *userArg)
{
	protRxCall = callBack;
	protRxUser = userArg;
}

/**
* @brief Function to register callback function for TX exception
*
* @param callBack	  Callback function pointer
*
* @retval none
*/
void RegisterTxExceptionCallback(icssCallBackRet_t callBack)
{
	txPacketExceptionCall = callBack;
}

void addWord(Uint8 *src, Uint32 word) {
    *(src) = (word >> 24) & 0xff;
    *(src + 1) = (word >> 16) & 0xff;
    *(src + 2) = (word >> 8) & 0xff;
    *(src + 3) = word & 0xff;
}

void addMACID(Uint8 *src, Uint8 * macID) {
	*(src) = *(macID);
	*(src + 1) = *(macID + 1);
	*(src + 2) = *(macID + 2);
	*(src + 3) = *(macID + 3);
	*(src + 4) = *(macID + 4);
	*(src + 5) = *(macID + 5);
}


/**
* @internal
* @brief Add 16 bit Protocol identifier to the list of known protocols
*        The protocol specific driver is supposed to call this and it's ID to the list
*
* @param protocolType ProtocolID
*
* @retval none
*/
void addProtocolToList(uint16_t protocolType)
{
	if(MAX_NUM_PROTOCOL_IMPLEMENTED > numImplementedProtocols) {
		protocol_impl[numImplementedProtocols++] =  protocolType;
	}
}

/**
* @internal
* @brief disables Rx Interrupt
*
* @param none
*
* @retval none
*/
inline void disableRxInterrupt(void)
{
	HWREGB(PRUSS_SHAREDRAM_BASE + RX_INT_STATUS_OFFSET) = 0x00;
}
/**
* @internal
* @brief Enables Rx Interrupt
*
* @param none
*
* @retval none
*/
/*inline */void enableRxInterrupt(void)
{
	HWREGB(PRUSS_SHAREDRAM_BASE + RX_INT_STATUS_OFFSET) = 0x01;
}



