/**********************************************************************
* 			LEAKYBUCKET.C
*	This file contains the routines related to Leaky Bucket Algorithm.
***********************************************************************/
#include "headers.h"

/*********************************************************************
* Function    - UpdateTokenCount()
*
* Description - This function calculates the token count for each
*				channel and updates the same in Adapter strucuture.
*
* Parameters  - Adapter: Pointer to the Adapter structure.
*
* Returns     - None
**********************************************************************/

static VOID UpdateTokenCount(register PMINI_ADAPTER Adapter)
{
	ULONG 	liCurrentTime;
	INT 	i = 0;
	struct timeval tv;

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "=====>\n");
	if(NULL == Adapter)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "Adapter found NULL!\n");
		return;
	}

	do_gettimeofday(&tv);
	for(i = 0; i < NO_OF_QUEUES; i++)
	{
		if(TRUE == Adapter->PackInfo[i].bValid &&
			(1 == Adapter->PackInfo[i].ucDirection))
		{
			liCurrentTime = ((tv.tv_sec-
				Adapter->PackInfo[i].stLastUpdateTokenAt.tv_sec)*1000 +
				(tv.tv_usec-Adapter->PackInfo[i].stLastUpdateTokenAt.tv_usec)/
				1000);
			if(0!=liCurrentTime)
			{
				Adapter->PackInfo[i].uiCurrentTokenCount += (ULONG)
					((Adapter->PackInfo[i].uiMaxAllowedRate) *
					((ULONG)((liCurrentTime)))/1000);
				memcpy(&Adapter->PackInfo[i].stLastUpdateTokenAt,
					&tv, sizeof(struct timeval));
				Adapter->PackInfo[i].liLastUpdateTokenAt = liCurrentTime;
				if((Adapter->PackInfo[i].uiCurrentTokenCount) >=
				Adapter->PackInfo[i].uiMaxBucketSize)
				{
					Adapter->PackInfo[i].uiCurrentTokenCount =
						Adapter->PackInfo[i].uiMaxBucketSize;
				}
			}
		}
	}
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "<=====\n");
	return;

}


/*********************************************************************
* Function    - IsPacketAllowedForFlow()
*
* Description - This function checks whether the given packet from the
*				specified queue can be allowed for transmission by
*				checking the token count.
*
* Parameters  - Adapter	      :	Pointer to the Adpater structure.
* 			  - iQIndex	      :	The queue Identifier.
* 			  - ulPacketLength:	Number of bytes to be transmitted.
*
* Returns     - The number of bytes allowed for transmission.
*
***********************************************************************/
static ULONG GetSFTokenCount(PMINI_ADAPTER Adapter, PacketInfo *psSF)
{
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "IsPacketAllowedForFlow ===>");
	/* Validate the parameters */
	if(NULL == Adapter || (psSF < Adapter->PackInfo &&
		(uintptr_t)psSF > (uintptr_t) &Adapter->PackInfo[HiPriority]))
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "IPAFF: Got wrong Parameters:Adapter: %p, QIndex: %zd\n", Adapter, (psSF-Adapter->PackInfo));
		return 0;
	}

	if(FALSE != psSF->bValid && psSF->ucDirection)
	{
		if(0 != psSF->uiCurrentTokenCount)
		{
				return psSF->uiCurrentTokenCount;
		}
		else
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "Not enough tokens in queue %zd Available %u\n",
				psSF-Adapter->PackInfo, psSF->uiCurrentTokenCount);
			psSF->uiPendedLast = 1;
		}
	}
	else
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "IPAFF: Queue %zd not valid\n", psSF-Adapter->PackInfo);
	}
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TOKEN_COUNTS, DBG_LVL_ALL, "IsPacketAllowedForFlow <===");
	return 0;
}

/**
@ingroup tx_functions
This function despatches packet from the specified queue.
@return Zero(success) or Negative value(failure)
*/
static INT SendPacketFromQueue(PMINI_ADAPTER Adapter,/**<Logical Adapter*/
			       PacketInfo *psSF,		/**<Queue identifier*/
			       struct sk_buff*  Packet)	/**<Pointer to the packet to be sent*/
{
	INT  	Status=STATUS_FAILURE;
	UINT uiIndex =0,PktLen = 0;

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, SEND_QUEUE, DBG_LVL_ALL, "=====>");
	if(!Adapter || !Packet || !psSF)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, SEND_QUEUE, DBG_LVL_ALL, "Got NULL Adapter or Packet");
		return -EINVAL;
	}

	if(psSF->liDrainCalculated==0)
	{
		psSF->liDrainCalculated = jiffies;
	}
	///send the packet to the fifo..
	PktLen = Packet->len;
	Status = SetupNextSend(Adapter, Packet, psSF->usVCID_Value);
	if(Status == 0)
	{
		for(uiIndex = 0 ; uiIndex < MIBS_MAX_HIST_ENTRIES ; uiIndex++)
		{	if((PktLen <= MIBS_PKTSIZEHIST_RANGE*(uiIndex+1)) && (PktLen > MIBS_PKTSIZEHIST_RANGE*(uiIndex)))
				Adapter->aTxPktSizeHist[uiIndex]++;
		}
	}
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, SEND_QUEUE, DBG_LVL_ALL, "<=====");
	return Status;
}

/************************************************************************
* Function    - CheckAndSendPacketFromIndex()
*
* Description - This function dequeues the data/control packet from the
*				specified queue for transmission.
*
* Parameters  - Adapter : Pointer to the driver control structure.
* 			  - iQIndex : The queue Identifier.
*
* Returns     - None.
*
****************************************************************************/
static VOID CheckAndSendPacketFromIndex(PMINI_ADAPTER Adapter, PacketInfo *psSF)
{
	struct sk_buff	*QueuePacket=NULL;
	char 			*pControlPacket = NULL;
	INT				Status=0;
	int				iPacketLen=0;


	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "%zd ====>", (psSF-Adapter->PackInfo));
	if((psSF != &Adapter->PackInfo[HiPriority]) && Adapter->LinkUpStatus && atomic_read(&psSF->uiPerSFTxResourceCount))//Get data packet
  	{
		if(!psSF->ucDirection )
			return;

		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "UpdateTokenCount ");
		if(Adapter->IdleMode || Adapter->bPreparingForLowPowerMode)
			return;	/* in idle mode */

		// Check for Free Descriptors
		if(atomic_read(&Adapter->CurrNumFreeTxDesc) <= MINIMUM_PENDING_DESCRIPTORS)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, " No Free Tx Descriptor(%d) is available for Data pkt..",atomic_read(&Adapter->CurrNumFreeTxDesc));
			return ;
		}

		spin_lock_bh(&psSF->SFQueueLock);
		QueuePacket=psSF->FirstTxQueue;

		if(QueuePacket)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Dequeuing Data Packet");

			if(psSF->bEthCSSupport)
				iPacketLen = QueuePacket->len;
			else
				iPacketLen = QueuePacket->len-ETH_HLEN;

			iPacketLen<<=3;
			if(iPacketLen <= GetSFTokenCount(Adapter, psSF))
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Allowed bytes %d",
					(iPacketLen >> 3));

				DEQUEUEPACKET(psSF->FirstTxQueue,psSF->LastTxQueue);
				psSF->uiCurrentBytesOnHost -= (QueuePacket->len);
				psSF->uiCurrentPacketsOnHost--;
				atomic_dec(&Adapter->TotalPacketCount);
				spin_unlock_bh(&psSF->SFQueueLock);

			   	Status = SendPacketFromQueue(Adapter, psSF, QueuePacket);
				psSF->uiPendedLast = FALSE;
			}
			else
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "For Queue: %zd\n", psSF-Adapter->PackInfo);
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "\nAvailable Tokens = %d required = %d\n",
					psSF->uiCurrentTokenCount, iPacketLen);
				//this part indicates that because of non-availability of the tokens
				//pkt has not been send out hence setting the pending flag indicating the host to send it out
				//first next iteration  .
				psSF->uiPendedLast = TRUE;
				spin_unlock_bh(&psSF->SFQueueLock);
			}
		}
		else
		{
			spin_unlock_bh(&psSF->SFQueueLock);
		}
	}
	else
	{

		if((atomic_read(&Adapter->CurrNumFreeTxDesc) > 0 ) &&
			(atomic_read(&Adapter->index_rd_txcntrlpkt) !=
			 atomic_read(&Adapter->index_wr_txcntrlpkt))
			)
		{
			pControlPacket = Adapter->txctlpacket
			[(atomic_read(&Adapter->index_rd_txcntrlpkt)%MAX_CNTRL_PKTS)];
			if(pControlPacket)
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Sending Control packet");
				Status = SendControlPacket(Adapter, pControlPacket);
				if(STATUS_SUCCESS==Status)
				{
					spin_lock_bh(&psSF->SFQueueLock);
					psSF->NumOfPacketsSent++;
					psSF->uiSentBytes+=((PLEADER)pControlPacket)->PLength;
					psSF->uiSentPackets++;
					atomic_dec(&Adapter->TotalPacketCount);
					psSF->uiCurrentBytesOnHost -= ((PLEADER)pControlPacket)->PLength;
					psSF->uiCurrentPacketsOnHost--;
					atomic_inc(&Adapter->index_rd_txcntrlpkt);
					spin_unlock_bh(&psSF->SFQueueLock);
				}
				else
					BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "SendControlPacket Failed\n");
			}
			else
			{
					BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, " Control Pkt is not available, Indexing is wrong....");
			}
	   	}
	}
}


/*******************************************************************
* Function    - transmit_packets()
*
* Description - This function transmits the packets from different
*				queues, if free descriptors are available on target.
*
* Parameters  - Adapter:  Pointer to the Adapter structure.
*
* Returns     - None.
********************************************************************/
VOID transmit_packets(PMINI_ADAPTER Adapter)
{
	UINT 	uiPrevTotalCount = 0;
	int iIndex = 0;

	BOOLEAN exit_flag = TRUE ;

	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "=====>");

	if(NULL == Adapter)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX,TX_PACKETS, DBG_LVL_ALL, "Got NULL Adapter");
		return;
	}
	if(Adapter->device_removed == TRUE)
	{
		BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device removed");
		return;
	}

    BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "\nUpdateTokenCount ====>\n");

	UpdateTokenCount(Adapter);

    BCM_DEBUG_PRINT (Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "\nPruneQueueAllSF ====>\n");

	PruneQueueAllSF(Adapter);

	uiPrevTotalCount = atomic_read(&Adapter->TotalPacketCount);

	for(iIndex=HiPriority;iIndex>=0;iIndex--)
	{
		if(	!uiPrevTotalCount || (TRUE == Adapter->device_removed))
				break;

		if(Adapter->PackInfo[iIndex].bValid &&
			Adapter->PackInfo[iIndex].uiPendedLast &&
			Adapter->PackInfo[iIndex].uiCurrentBytesOnHost)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling CheckAndSendPacketFromIndex..");
			CheckAndSendPacketFromIndex(Adapter, &Adapter->PackInfo[iIndex]);
			uiPrevTotalCount--;
		}
	}

	while(uiPrevTotalCount > 0 && !Adapter->device_removed)
	{
		exit_flag = TRUE ;
			//second iteration to parse non-pending queues
		for(iIndex=HiPriority;iIndex>=0;iIndex--)
		{
			if( !uiPrevTotalCount || (TRUE == Adapter->device_removed))
					break;

			if(Adapter->PackInfo[iIndex].bValid &&
				Adapter->PackInfo[iIndex].uiCurrentBytesOnHost &&
				!Adapter->PackInfo[iIndex].uiPendedLast )
			{
				BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling CheckAndSendPacketFromIndex..");
				CheckAndSendPacketFromIndex(Adapter, &Adapter->PackInfo[iIndex]);
				uiPrevTotalCount--;
				exit_flag = FALSE;
			}
		}

		if(Adapter->IdleMode || Adapter->bPreparingForLowPowerMode)
		{
			BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "In Idle Mode\n");
			break;
		}
		if(exit_flag == TRUE )
		    break ;
	}/* end of inner while loop */

	update_per_cid_rx  (Adapter);
	Adapter->txtransmit_running = 0;
	BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "<======");
}