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

/**
 * UpdateTokenCount() - Calculates the token count for each channel
 * and updates the same in Adapter structure
 * @Adapter:	Pointer to the Adapter structure.
 *
 * Return: None
 */
static VOID UpdateTokenCount(register struct bcm_mini_adapter *Adapter)
{
	ULONG liCurrentTime;
	INT i = 0;
	struct timeval tv;
	struct bcm_packet_info *curr_pi;

	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++) {
		curr_pi = &Adapter->PackInfo[i];

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


/**
 * IsPacketAllowedForFlow() - This function checks whether the given
 * packet from the specified queue can be allowed for transmission by
 * checking the token count.
 * @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(struct bcm_mini_adapter *Adapter, struct bcm_packet_info *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(struct bcm_mini_adapter *Adapter,/**<Logical Adapter*/
			       struct bcm_packet_info *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;
}

static void get_data_packet(struct bcm_mini_adapter *ad,
			    struct bcm_packet_info *ps_sf)
{
	int packet_len;
	struct sk_buff *qpacket;

	if (!ps_sf->ucDirection)
		return;

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

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

	spin_lock_bh(&ps_sf->SFQueueLock);
	qpacket = ps_sf->FirstTxQueue;

	if (qpacket) {
		BCM_DEBUG_PRINT(ad, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL,
				"Dequeuing Data Packet");

		if (ps_sf->bEthCSSupport)
			packet_len = qpacket->len;
		else
			packet_len = qpacket->len - ETH_HLEN;

		packet_len <<= 3;
		if (packet_len <= GetSFTokenCount(ad, ps_sf)) {
			BCM_DEBUG_PRINT(ad, DBG_TYPE_TX, TX_PACKETS,
					DBG_LVL_ALL, "Allowed bytes %d",
					(packet_len >> 3));

			DEQUEUEPACKET(ps_sf->FirstTxQueue, ps_sf->LastTxQueue);
			ps_sf->uiCurrentBytesOnHost -= (qpacket->len);
			ps_sf->uiCurrentPacketsOnHost--;
				atomic_dec(&ad->TotalPacketCount);
			spin_unlock_bh(&ps_sf->SFQueueLock);

			SendPacketFromQueue(ad, ps_sf, qpacket);
			ps_sf->uiPendedLast = false;
		} else {
			BCM_DEBUG_PRINT(ad, DBG_TYPE_TX, TX_PACKETS,
					DBG_LVL_ALL, "For Queue: %zd\n",
					ps_sf - ad->PackInfo);
			BCM_DEBUG_PRINT(ad, DBG_TYPE_TX, TX_PACKETS,
					DBG_LVL_ALL,
					"\nAvailable Tokens = %d required = %d\n",
					ps_sf->uiCurrentTokenCount,
					packet_len);
			/*
			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.
			*/
			ps_sf->uiPendedLast = TRUE;
			spin_unlock_bh(&ps_sf->SFQueueLock);
		}
	} else {
		spin_unlock_bh(&ps_sf->SFQueueLock);
	}
}

static void send_control_packet(struct bcm_mini_adapter *ad,
				struct bcm_packet_info *ps_sf)
{
	char *ctrl_packet = NULL;
	INT status = 0;

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

/**
 * CheckAndSendPacketFromIndex() - This function dequeues the
 * data/control packet from the specified queue for transmission.
 * @Adapter:	Pointer to the driver control structure.
 * @iQIndex:	The queue Identifier.
 *
 * Returns: None.
 */
static VOID CheckAndSendPacketFromIndex(struct bcm_mini_adapter *Adapter,
					struct bcm_packet_info *psSF)
{
	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 */

		get_data_packet(Adapter, psSF);
	} else {
		send_control_packet(Adapter, psSF);
	}
}


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

	bool 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,
			"<======");
}