| Index: chromeos/drivers/ath6kl/htc2/htc_send.c
|
| diff --git a/chromeos/drivers/ath6kl/htc2/htc_send.c b/chromeos/drivers/ath6kl/htc2/htc_send.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2837db1554059c60070a5d72006daf15a858954e
|
| --- /dev/null
|
| +++ b/chromeos/drivers/ath6kl/htc2/htc_send.c
|
| @@ -0,0 +1,1019 @@
|
| +//------------------------------------------------------------------------------
|
| +// <copyright file="htc_send.c" company="Atheros">
|
| +// Copyright (c) 2007-2008 Atheros Corporation. All rights reserved.
|
| +//
|
| +// This program is free software; you can redistribute it and/or modify
|
| +// it under the terms of the GNU General Public License version 2 as
|
| +// published by the Free Software Foundation;
|
| +//
|
| +// Software distributed under the License is distributed on an "AS
|
| +// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
| +// implied. See the License for the specific language governing
|
| +// rights and limitations under the License.
|
| +//
|
| +//
|
| +//------------------------------------------------------------------------------
|
| +//==============================================================================
|
| +// Author(s): ="Atheros"
|
| +//==============================================================================
|
| +#include "htc_internal.h"
|
| +
|
| +typedef enum _HTC_SEND_QUEUE_RESULT {
|
| + HTC_SEND_QUEUE_OK = 0, /* packet was queued */
|
| + HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */
|
| +} HTC_SEND_QUEUE_RESULT;
|
| +
|
| +#define DO_EP_TX_COMPLETION(ep,q) DoSendCompletion(ep,q)
|
| +
|
| +/* call the distribute credits callback with the distribution */
|
| +#define DO_DISTRIBUTION(t,reason,description,pList) \
|
| +{ \
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, \
|
| + (" calling distribute function (%s) (dfn:0x%X, ctxt:0x%X, dist:0x%X) \n", \
|
| + (description), \
|
| + (A_UINT32)(t)->DistributeCredits, \
|
| + (A_UINT32)(t)->pCredDistContext, \
|
| + (A_UINT32)pList)); \
|
| + (t)->DistributeCredits((t)->pCredDistContext, \
|
| + (pList), \
|
| + (reason)); \
|
| +}
|
| +
|
| +static void DoSendCompletion(HTC_ENDPOINT *pEndpoint,
|
| + HTC_PACKET_QUEUE *pQueueToIndicate)
|
| +{
|
| + do {
|
| +
|
| + if (HTC_QUEUE_EMPTY(pQueueToIndicate)) {
|
| + /* nothing to indicate */
|
| + break;
|
| + }
|
| +
|
| + if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d, send complete multiple callback (%d pkts) \n",
|
| + pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate)));
|
| + /* a multiple send complete handler is being used, pass the queue to the handler */
|
| + pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint->EpCallBacks.pContext,
|
| + pQueueToIndicate);
|
| + /* all packets are now owned by the callback, reset queue to be safe */
|
| + INIT_HTC_PACKET_QUEUE(pQueueToIndicate);
|
| + } else {
|
| + HTC_PACKET *pPacket;
|
| + /* using legacy EpTxComplete */
|
| + do {
|
| + pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d send complete callback on packet 0x%X \n", \
|
| + pEndpoint->Id, (A_UINT32)(pPacket)));
|
| + pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket);
|
| + } while (!HTC_QUEUE_EMPTY(pQueueToIndicate));
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| +}
|
| +
|
| +/* do final completion on sent packet */
|
| +static INLINE void CompleteSentPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket)
|
| +{
|
| + pPacket->Completion = NULL;
|
| +
|
| + if (A_FAILED(pPacket->Status)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,
|
| + ("CompleteSentPacket: request failed (status:%d, ep:%d, length:%d creds:%d) \n",
|
| + pPacket->Status, pPacket->Endpoint, pPacket->ActualLength, pPacket->PktInfo.AsTx.CreditsUsed));
|
| + /* on failure to submit, reclaim credits for this packet */
|
| + LOCK_HTC_TX(target);
|
| + pEndpoint->CreditDist.TxCreditsToDist += pPacket->PktInfo.AsTx.CreditsUsed;
|
| + pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
|
| + DO_DISTRIBUTION(target,
|
| + HTC_CREDIT_DIST_SEND_COMPLETE,
|
| + "Send Complete",
|
| + target->EpCreditDistributionListHead->pNext);
|
| + UNLOCK_HTC_TX(target);
|
| + }
|
| + /* first, fixup the head room we allocated */
|
| + pPacket->pBuffer += HTC_HDR_LENGTH;
|
| +}
|
| +
|
| +/* our internal send packet completion handler when packets are submited to the AR6K device
|
| + * layer */
|
| +static void HTCSendPktCompletionHandler(void *Context, HTC_PACKET *pPacket)
|
| +{
|
| + HTC_TARGET *target = (HTC_TARGET *)Context;
|
| + HTC_ENDPOINT *pEndpoint = &target->EndPoint[pPacket->Endpoint];
|
| + HTC_PACKET_QUEUE container;
|
| +
|
| + CompleteSentPacket(target,pEndpoint,pPacket);
|
| + INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
|
| + /* do completion */
|
| + DO_EP_TX_COMPLETION(pEndpoint,&container);
|
| +}
|
| +
|
| +A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket)
|
| +{
|
| + A_STATUS status;
|
| + A_BOOL sync = FALSE;
|
| +
|
| + if (pPacket->Completion == NULL) {
|
| + /* mark that this request was synchronously issued */
|
| + sync = TRUE;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + ("+-HTCIssueSend: transmit length : %d (%s) \n",
|
| + pPacket->ActualLength + HTC_HDR_LENGTH,
|
| + sync ? "SYNC" : "ASYNC" ));
|
| +
|
| + /* send message to device */
|
| + status = DevSendPacket(&target->Device,
|
| + pPacket,
|
| + pPacket->ActualLength + HTC_HDR_LENGTH);
|
| +
|
| + if (sync) {
|
| + /* use local sync variable. If this was issued asynchronously, pPacket is no longer
|
| + * safe to access. */
|
| + pPacket->pBuffer += HTC_HDR_LENGTH;
|
| + }
|
| +
|
| + /* if this request was asynchronous, the packet completion routine will be invoked by
|
| + * the device layer when the HIF layer completes the request */
|
| +
|
| + return status;
|
| +}
|
| +
|
| + /* get HTC send packets from the TX queue on an endpoint */
|
| +static INLINE void GetHTCSendPackets(HTC_TARGET *target,
|
| + HTC_ENDPOINT *pEndpoint,
|
| + HTC_PACKET_QUEUE *pQueue)
|
| +{
|
| + int creditsRequired;
|
| + int remainder;
|
| + A_UINT8 sendFlags;
|
| + HTC_PACKET *pPacket;
|
| + int transferLength;
|
| +
|
| + /****** NOTE : the TX lock is held when this function is called *****************/
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets \n"));
|
| +
|
| + /* loop until we can grab as many packets out of the queue as we can */
|
| + while (TRUE) {
|
| +
|
| + sendFlags = 0;
|
| + /* get packet at head, but don't remove it */
|
| + pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue);
|
| + if (pPacket == NULL) {
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%X , Queue Depth: %d\n",
|
| + (A_UINT32)pPacket, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
|
| +
|
| + transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, pPacket->ActualLength + HTC_HDR_LENGTH);
|
| +
|
| + if (transferLength <= target->TargetCreditSize) {
|
| + creditsRequired = 1;
|
| + } else {
|
| + /* figure out how many credits this message requires */
|
| + creditsRequired = transferLength / target->TargetCreditSize;
|
| + remainder = transferLength % target->TargetCreditSize;
|
| +
|
| + if (remainder) {
|
| + creditsRequired++;
|
| + }
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Creds Required:%d Got:%d\n",
|
| + creditsRequired, pEndpoint->CreditDist.TxCredits));
|
| +
|
| + if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
|
| +
|
| + /* not enough credits */
|
| + if (pPacket->Endpoint == ENDPOINT_0) {
|
| + /* leave it in the queue */
|
| + break;
|
| + }
|
| + /* invoke the registered distribution function only if this is not
|
| + * endpoint 0, we let the driver layer provide more credits if it can.
|
| + * We pass the credit distribution list starting at the endpoint in question
|
| + * */
|
| +
|
| + /* set how many credits we need */
|
| + pEndpoint->CreditDist.TxCreditsSeek =
|
| + creditsRequired - pEndpoint->CreditDist.TxCredits;
|
| + DO_DISTRIBUTION(target,
|
| + HTC_CREDIT_DIST_SEEK_CREDITS,
|
| + "Seek Credits",
|
| + &pEndpoint->CreditDist);
|
| + pEndpoint->CreditDist.TxCreditsSeek = 0;
|
| +
|
| + if (pEndpoint->CreditDist.TxCredits < creditsRequired) {
|
| + /* still not enough credits to send, leave packet in the queue */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + (" Not enough credits for ep %d leaving packet in queue..\n",
|
| + pPacket->Endpoint));
|
| + break;
|
| + }
|
| +
|
| + }
|
| +
|
| + pEndpoint->CreditDist.TxCredits -= creditsRequired;
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired);
|
| +
|
| + /* check if we need credits back from the target */
|
| + if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
|
| + /* we are getting low on credits, see if we can ask for more from the distribution function */
|
| + pEndpoint->CreditDist.TxCreditsSeek =
|
| + pEndpoint->CreditDist.TxCreditsPerMaxMsg - pEndpoint->CreditDist.TxCredits;
|
| +
|
| + DO_DISTRIBUTION(target,
|
| + HTC_CREDIT_DIST_SEEK_CREDITS,
|
| + "Seek Credits",
|
| + &pEndpoint->CreditDist);
|
| +
|
| + pEndpoint->CreditDist.TxCreditsSeek = 0;
|
| + /* see if we were successful in getting more */
|
| + if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) {
|
| + /* tell the target we need credits ASAP! */
|
| + sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE;
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditLowIndications, 1);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Host Needs Credits \n"));
|
| + }
|
| + }
|
| +
|
| + /* now we can fully dequeue */
|
| + pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue);
|
| + /* save the number of credits this packet consumed */
|
| + pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired;
|
| + /* all TX packets are handled asynchronously */
|
| + pPacket->Completion = HTCSendPktCompletionHandler;
|
| + pPacket->pContext = target;
|
| + INC_HTC_EP_STAT(pEndpoint, TxIssued, 1);
|
| + /* save send flags */
|
| + pPacket->PktInfo.AsTx.SendFlags = sendFlags;
|
| + pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo;
|
| + pEndpoint->SeqNo++;
|
| + /* queue this packet into the caller's queue */
|
| + HTC_PACKET_ENQUEUE(pQueue,pPacket);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n"));
|
| +
|
| +}
|
| +
|
| +static void HTCAsyncSendScatterCompletion(HIF_SCATTER_REQ *pScatterReq)
|
| +{
|
| + int i;
|
| + HTC_PACKET *pPacket;
|
| + HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *)pScatterReq->Context;
|
| + HTC_TARGET *target = (HTC_TARGET *)pEndpoint->target;
|
| + A_STATUS status = A_OK;
|
| + HTC_PACKET_QUEUE sendCompletes;
|
| +
|
| + INIT_HTC_PACKET_QUEUE(&sendCompletes);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCAsyncSendScatterCompletion TotLen: %d Entries: %d\n",
|
| + pScatterReq->TotalLength, pScatterReq->ValidScatterEntries));
|
| +
|
| + DEV_FINISH_SCATTER_OPERATION(pScatterReq);
|
| +
|
| + if (A_FAILED(pScatterReq->CompletionStatus)) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Send Scatter Request Failed: %d \n",pScatterReq->CompletionStatus));
|
| + status = A_ERROR;
|
| + }
|
| +
|
| + /* walk through the scatter list and process */
|
| + for (i = 0; i < pScatterReq->ValidScatterEntries; i++) {
|
| + pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
|
| + A_ASSERT(pPacket != NULL);
|
| + pPacket->Status = status;
|
| + CompleteSentPacket(target,pEndpoint,pPacket);
|
| + /* add it to the completion queue */
|
| + HTC_PACKET_ENQUEUE(&sendCompletes, pPacket);
|
| + }
|
| +
|
| + /* free scatter request */
|
| + DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
|
| + /* complete all packets */
|
| + DO_EP_TX_COMPLETION(pEndpoint,&sendCompletes);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCAsyncSendScatterCompletion \n"));
|
| +}
|
| +
|
| + /* drain a queue and send as bundles
|
| + * this function may return without fully draining the queue under the following conditions :
|
| + * - scatter resources are exhausted
|
| + * - a message that will consume a partial credit will stop the bundling process early
|
| + * - we drop below the minimum number of messages for a bundle
|
| + * */
|
| +static void HTCIssueSendBundle(HTC_ENDPOINT *pEndpoint,
|
| + HTC_PACKET_QUEUE *pQueue,
|
| + int *pBundlesSent,
|
| + int *pTotalBundlesPkts)
|
| +{
|
| + int pktsToScatter;
|
| + int scatterSpaceRemaining;
|
| + HIF_SCATTER_REQ *pScatterReq = NULL;
|
| + int i, packetsInScatterReq;
|
| + int transferLength;
|
| + HTC_PACKET *pPacket;
|
| + A_BOOL done = FALSE;
|
| + int bundlesSent = 0;
|
| + int totalPktsInBundle = 0;
|
| + HTC_TARGET *target = pEndpoint->target;
|
| + int creditRemainder = 0;
|
| + int creditPad;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCIssueSendBundle \n"));
|
| +
|
| + while (!done) {
|
| +
|
| + pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pQueue);
|
| + pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle);
|
| +
|
| + if (pktsToScatter < HTC_MIN_HTC_MSGS_TO_BUNDLE) {
|
| + /* not enough to bundle */
|
| + break;
|
| + }
|
| +
|
| + pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device);
|
| +
|
| + if (pScatterReq == NULL) {
|
| + /* no scatter resources */
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" No more scatter resources \n"));
|
| + break;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" pkts to scatter: %d \n", pktsToScatter));
|
| +
|
| + pScatterReq->TotalLength = 0;
|
| + pScatterReq->ValidScatterEntries = 0;
|
| +
|
| + packetsInScatterReq = 0;
|
| + scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_SEND_LENGTH(&target->Device);
|
| +
|
| + for (i = 0; i < pktsToScatter; i++) {
|
| +
|
| + pScatterReq->ScatterList[i].pCallerContexts[0] = NULL;
|
| +
|
| + pPacket = HTC_GET_PKT_AT_HEAD(pQueue);
|
| + if (pPacket == NULL) {
|
| + A_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + creditPad = 0;
|
| + transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device,
|
| + pPacket->ActualLength + HTC_HDR_LENGTH);
|
| + /* see if the padded transfer length falls on a credit boundary */
|
| + creditRemainder = transferLength % target->TargetCreditSize;
|
| +
|
| + if (creditRemainder != 0) {
|
| + /* the transfer consumes a "partial" credit, this packet cannot be bundled unless
|
| + * we add additional "dummy" padding (max 255 bytes) to consume the entire credit
|
| + *** NOTE: only allow the send padding if the endpoint is allowed to */
|
| + if (pEndpoint->LocalConnectionFlags & HTC_LOCAL_CONN_FLAGS_ENABLE_SEND_BUNDLE_PADDING) {
|
| + if (transferLength < target->TargetCreditSize) {
|
| + /* special case where the transfer is less than a credit */
|
| + creditPad = target->TargetCreditSize - transferLength;
|
| + } else {
|
| + creditPad = creditRemainder;
|
| + }
|
| +
|
| + /* now check to see if we can indicate padding in the HTC header */
|
| + if ((creditPad > 0) && (creditPad <= 255)) {
|
| + /* adjust the transferlength of this packet with the new credit padding */
|
| + transferLength += creditPad;
|
| + } else {
|
| + /* the amount to pad is too large, bail on this packet, we have to
|
| + * send it using the non-bundled method */
|
| + pPacket = NULL;
|
| + }
|
| + } else {
|
| + /* bail on this packet, user does not want padding applied */
|
| + pPacket = NULL;
|
| + }
|
| + }
|
| +
|
| + if (NULL == pPacket) {
|
| + /* can't bundle */
|
| + done = TRUE;
|
| + break;
|
| + }
|
| +
|
| + if ((scatterSpaceRemaining - transferLength) < 0) {
|
| + /* exceeds what we can transfer */
|
| + break;
|
| + }
|
| +
|
| + scatterSpaceRemaining -= transferLength;
|
| + /* now remove it from the queue */
|
| + pPacket = HTC_PACKET_DEQUEUE(pQueue);
|
| + /* save it in the scatter list */
|
| + pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket;
|
| + /* prepare packet and flag message as part of a send bundle */
|
| + HTC_PREPARE_SEND_PKT(pPacket,
|
| + pPacket->PktInfo.AsTx.SendFlags | HTC_FLAGS_SEND_BUNDLE,
|
| + creditPad,
|
| + pPacket->PktInfo.AsTx.SeqNo);
|
| + pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer;
|
| + pScatterReq->ScatterList[i].Length = transferLength;
|
| + A_ASSERT(transferLength);
|
| + pScatterReq->TotalLength += transferLength;
|
| + pScatterReq->ValidScatterEntries++;
|
| + packetsInScatterReq++;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" %d, Adding packet : 0x%X, len:%d (remaining space:%d) \n",
|
| + i, (A_UINT32)pPacket,transferLength,scatterSpaceRemaining));
|
| + }
|
| +
|
| + if (packetsInScatterReq >= HTC_MIN_HTC_MSGS_TO_BUNDLE) {
|
| + /* send path is always asynchronous */
|
| + pScatterReq->CompletionRoutine = HTCAsyncSendScatterCompletion;
|
| + pScatterReq->Context = pEndpoint;
|
| + bundlesSent++;
|
| + totalPktsInBundle += packetsInScatterReq;
|
| + packetsInScatterReq = 0;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Send Scatter total bytes: %d , entries: %d\n",
|
| + pScatterReq->TotalLength,pScatterReq->ValidScatterEntries));
|
| + DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_WRITE, DEV_SCATTER_ASYNC);
|
| + /* we don't own this anymore */
|
| + pScatterReq = NULL;
|
| + /* try to send some more */
|
| + continue;
|
| + }
|
| +
|
| + /* not enough packets to use the scatter request, cleanup */
|
| + if (pScatterReq != NULL) {
|
| + if (packetsInScatterReq > 0) {
|
| + /* work backwards to requeue requests */
|
| + for (i = (packetsInScatterReq - 1); i >= 0; i--) {
|
| + pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]);
|
| + if (pPacket != NULL) {
|
| + /* undo any prep */
|
| + HTC_UNPREPARE_SEND_PKT(pPacket);
|
| + /* queue back to the head */
|
| + HTC_PACKET_ENQUEUE_TO_HEAD(pQueue,pPacket);
|
| + }
|
| + }
|
| + }
|
| + DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq);
|
| + }
|
| +
|
| + /* if we get here, we sent all that we could, get out */
|
| + break;
|
| +
|
| + }
|
| +
|
| + *pBundlesSent = bundlesSent;
|
| + *pTotalBundlesPkts = totalPktsInBundle;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCIssueSendBundle (sent:%d) \n",bundlesSent));
|
| +
|
| + return;
|
| +}
|
| +
|
| +/*
|
| + * if there are no credits, the packet(s) remains in the queue.
|
| + * this function returns the result of the attempt to send a queue of HTC packets */
|
| +static HTC_SEND_QUEUE_RESULT HTCTrySend(HTC_TARGET *target,
|
| + HTC_ENDPOINT *pEndpoint,
|
| + HTC_PACKET_QUEUE *pCallersSendQueue)
|
| +{
|
| + HTC_PACKET_QUEUE sendQueue; /* temp queue to hold packets at various stages */
|
| + HTC_PACKET *pPacket;
|
| + int bundlesSent;
|
| + int pktsInBundles;
|
| + int overflow;
|
| + HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (Queue:0x%X Depth:%d)\n",
|
| + (A_UINT32)pCallersSendQueue,
|
| + (pCallersSendQueue == NULL) ? 0 : HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue)));
|
| +
|
| + /* init the local send queue */
|
| + INIT_HTC_PACKET_QUEUE(&sendQueue);
|
| +
|
| + do {
|
| +
|
| + if (NULL == pCallersSendQueue) {
|
| + /* caller didn't provide a queue, just wants us to check queues and send */
|
| + break;
|
| + }
|
| +
|
| + if (HTC_QUEUE_EMPTY(pCallersSendQueue)) {
|
| + /* empty queue */
|
| + result = HTC_SEND_QUEUE_DROP;
|
| + break;
|
| + }
|
| +
|
| + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >= pEndpoint->MaxTxQueueDepth) {
|
| + /* we've already overflowed */
|
| + overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
|
| + } else {
|
| + /* figure out how much we will overflow by */
|
| + overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
|
| + overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue);
|
| + /* figure out how much we will overflow the TX queue by */
|
| + overflow -= pEndpoint->MaxTxQueueDepth;
|
| + }
|
| +
|
| + /* if overflow is negative or zero, we are okay */
|
| + if (overflow > 0) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + (" Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d \n",
|
| + pEndpoint->Id, overflow, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue), pEndpoint->MaxTxQueueDepth));
|
| + }
|
| + if ((overflow <= 0) || (pEndpoint->EpCallBacks.EpSendFull == NULL)) {
|
| + /* all packets will fit or caller did not provide send full indication handler
|
| + * -- just move all of them to the local sendQueue object */
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue, pCallersSendQueue);
|
| + } else {
|
| + int i;
|
| + int goodPkts = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) - overflow;
|
| +
|
| + A_ASSERT(goodPkts >= 0);
|
| + /* we have overflowed, and a callback is provided */
|
| + /* dequeue all non-overflow packets into the sendqueue */
|
| + for (i = 0; i < goodPkts; i++) {
|
| + /* pop off caller's queue*/
|
| + pPacket = HTC_PACKET_DEQUEUE(pCallersSendQueue);
|
| + A_ASSERT(pPacket != NULL);
|
| + /* insert into local queue */
|
| + HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
|
| + }
|
| +
|
| + /* the caller's queue has all the packets that won't fit*/
|
| + /* walk through the caller's queue and indicate each one to the send full handler */
|
| + ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue->QueueHead, pPacket, HTC_PACKET, ListLink) {
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Indicating overflowed TX packet: 0x%X \n",
|
| + (A_UINT32)pPacket));
|
| + if (pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext,
|
| + pPacket) == HTC_SEND_FULL_DROP) {
|
| + /* callback wants the packet dropped */
|
| + INC_HTC_EP_STAT(pEndpoint, TxDropped, 1);
|
| + /* leave this one in the caller's queue for cleanup */
|
| + } else {
|
| + /* callback wants to keep this packet, remove from caller's queue */
|
| + HTC_PACKET_REMOVE(pCallersSendQueue, pPacket);
|
| + /* put it in the send queue */
|
| + HTC_PACKET_ENQUEUE(&sendQueue,pPacket);
|
| + }
|
| +
|
| + } ITERATE_END;
|
| +
|
| + if (HTC_QUEUE_EMPTY(&sendQueue)) {
|
| + /* no packets made it in, caller will cleanup */
|
| + result = HTC_SEND_QUEUE_DROP;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + } while (FALSE);
|
| +
|
| + if (result != HTC_SEND_QUEUE_OK) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
|
| + return result;
|
| + }
|
| +
|
| + LOCK_HTC_TX(target);
|
| +
|
| + if (!HTC_QUEUE_EMPTY(&sendQueue)) {
|
| + /* transfer packets */
|
| + HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue,&sendQueue);
|
| + A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue));
|
| + INIT_HTC_PACKET_QUEUE(&sendQueue);
|
| + }
|
| +
|
| + /* increment tx processing count on entry */
|
| + pEndpoint->TxProcessCount++;
|
| + if (pEndpoint->TxProcessCount > 1) {
|
| + /* another thread or task is draining the TX queues on this endpoint
|
| + * that thread will reset the tx processing count when the queue is drained */
|
| + pEndpoint->TxProcessCount--;
|
| + UNLOCK_HTC_TX(target);
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (busy) \n"));
|
| + return HTC_SEND_QUEUE_OK;
|
| + }
|
| +
|
| + /***** beyond this point only 1 thread may enter ******/
|
| +
|
| + /* now drain the endpoint TX queue for transmission as long as we have enough
|
| + * credits */
|
| + while (TRUE) {
|
| +
|
| + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) {
|
| + break;
|
| + }
|
| +
|
| + /* get all the packets for this endpoint that we can for this pass */
|
| + GetHTCSendPackets(target, pEndpoint, &sendQueue);
|
| +
|
| + if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) {
|
| + /* didn't get any packets due to a lack of credits */
|
| + break;
|
| + }
|
| +
|
| + UNLOCK_HTC_TX(target);
|
| +
|
| + /* any packets to send are now in our local send queue */
|
| +
|
| + bundlesSent = 0;
|
| + pktsInBundles = 0;
|
| +
|
| + while (TRUE) {
|
| +
|
| + /* try to send a bundle on each pass */
|
| + if ((target->SendBundlingEnabled) &&
|
| + (HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) {
|
| + int temp1,temp2;
|
| + /* bundling is enabled and there is at least a minimum number of packets in the send queue
|
| + * send what we can in this pass */
|
| + HTCIssueSendBundle(pEndpoint, &sendQueue, &temp1, &temp2);
|
| + bundlesSent += temp1;
|
| + pktsInBundles += temp2;
|
| + }
|
| +
|
| + /* if not bundling or there was a packet that could not be placed in a bundle, pull it out
|
| + * and send it the normal way */
|
| + pPacket = HTC_PACKET_DEQUEUE(&sendQueue);
|
| + if (NULL == pPacket) {
|
| + /* local queue is fully drained */
|
| + break;
|
| + }
|
| + HTC_PREPARE_SEND_PKT(pPacket,
|
| + pPacket->PktInfo.AsTx.SendFlags,
|
| + 0,
|
| + pPacket->PktInfo.AsTx.SeqNo);
|
| + HTCIssueSend(target, pPacket);
|
| +
|
| + /* go back and see if we can bundle some more */
|
| + }
|
| +
|
| + LOCK_HTC_TX(target);
|
| +
|
| + INC_HTC_EP_STAT(pEndpoint, TxBundles, bundlesSent);
|
| + INC_HTC_EP_STAT(pEndpoint, TxPacketsBundled, pktsInBundles);
|
| +
|
| + }
|
| +
|
| + /* done with this endpoint, we can clear the count */
|
| + pEndpoint->TxProcessCount = 0;
|
| + UNLOCK_HTC_TX(target);
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n"));
|
| +
|
| + return HTC_SEND_QUEUE_OK;
|
| +}
|
| +
|
| +A_STATUS HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue)
|
| +{
|
| + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
|
| + HTC_ENDPOINT *pEndpoint;
|
| + HTC_PACKET *pPacket;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: 0x%X, Pkts %d \n",
|
| + (A_UINT32)pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue)));
|
| +
|
| + /* get packet at head to figure out which endpoint these packets will go into */
|
| + pPacket = HTC_GET_PKT_AT_HEAD(pPktQueue);
|
| + if (NULL == pPacket) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
|
| + return A_EINVAL;
|
| + }
|
| +
|
| + AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX);
|
| + pEndpoint = &target->EndPoint[pPacket->Endpoint];
|
| +
|
| + HTCTrySend(target, pEndpoint, pPktQueue);
|
| +
|
| + /* do completion on any packets that couldn't get in */
|
| + if (!HTC_QUEUE_EMPTY(pPktQueue)) {
|
| +
|
| + HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) {
|
| + if (HTC_STOPPING(target)) {
|
| + pPacket->Status = A_ECANCELED;
|
| + } else {
|
| + pPacket->Status = A_NO_RESOURCE;
|
| + }
|
| + } HTC_PACKET_QUEUE_ITERATE_END;
|
| +
|
| + DO_EP_TX_COMPLETION(pEndpoint,pPktQueue);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n"));
|
| +
|
| + return A_OK;
|
| +}
|
| +
|
| +/* HTC API - HTCSendPkt */
|
| +A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket)
|
| +{
|
| + HTC_PACKET_QUEUE queue;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND,
|
| + ("+-HTCSendPkt: Enter endPointId: %d, buffer: 0x%X, length: %d \n",
|
| + pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->ActualLength));
|
| + INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket);
|
| + return HTCSendPktsMultiple(HTCHandle, &queue);
|
| +}
|
| +
|
| +/* check TX queues to drain because of credit distribution update */
|
| +static INLINE void HTCCheckEndpointTxQueues(HTC_TARGET *target)
|
| +{
|
| + HTC_ENDPOINT *pEndpoint;
|
| + HTC_ENDPOINT_CREDIT_DIST *pDistItem;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCCheckEndpointTxQueues \n"));
|
| + pDistItem = target->EpCreditDistributionListHead;
|
| +
|
| + /* run through the credit distribution list to see
|
| + * if there are packets queued
|
| + * NOTE: no locks need to be taken since the distribution list
|
| + * is not dynamic (cannot be re-ordered) and we are not modifying any state */
|
| + while (pDistItem != NULL) {
|
| + pEndpoint = (HTC_ENDPOINT *)pDistItem->pHTCReserved;
|
| +
|
| + if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) > 0) {
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n",
|
| + pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue)));
|
| + /* try to start the stalled queue, this list is ordered by priority.
|
| + * Highest priority queue get's processed first, if there are credits available the
|
| + * highest priority queue will get a chance to reclaim credits from lower priority
|
| + * ones */
|
| + HTCTrySend(target, pEndpoint, NULL);
|
| + }
|
| +
|
| + pDistItem = pDistItem->pNext;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCCheckEndpointTxQueues \n"));
|
| +}
|
| +
|
| +/* process credit reports and call distribution function */
|
| +void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint)
|
| +{
|
| + int i;
|
| + HTC_ENDPOINT *pEndpoint;
|
| + int totalCredits = 0;
|
| + A_BOOL doDist = FALSE;
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries));
|
| +
|
| + /* lock out TX while we update credits */
|
| + LOCK_HTC_TX(target);
|
| +
|
| + for (i = 0; i < NumEntries; i++, pRpt++) {
|
| + if (pRpt->EndpointID >= ENDPOINT_MAX) {
|
| + AR_DEBUG_ASSERT(FALSE);
|
| + break;
|
| + }
|
| +
|
| + pEndpoint = &target->EndPoint[pRpt->EndpointID];
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d got %d credits \n",
|
| + pRpt->EndpointID, pRpt->Credits));
|
| +
|
| +
|
| +#ifdef HTC_EP_STAT_PROFILING
|
| +
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1);
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, pRpt->Credits);
|
| +
|
| + if (FromEndpoint == pRpt->EndpointID) {
|
| + /* this credit report arrived on the same endpoint indicating it arrived in an RX
|
| + * packet */
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, pRpt->Credits);
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1);
|
| + } else if (FromEndpoint == ENDPOINT_0) {
|
| + /* this credit arrived on endpoint 0 as a NULL message */
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, pRpt->Credits);
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1);
|
| + } else {
|
| + /* arrived on another endpoint */
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, pRpt->Credits);
|
| + INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1);
|
| + }
|
| +
|
| +#endif
|
| +
|
| + if (ENDPOINT_0 == pRpt->EndpointID) {
|
| + /* always give endpoint 0 credits back */
|
| + pEndpoint->CreditDist.TxCredits += pRpt->Credits;
|
| + } else {
|
| + /* for all other endpoints, update credits to distribute, the distribution function
|
| + * will handle giving out credits back to the endpoints */
|
| + pEndpoint->CreditDist.TxCreditsToDist += pRpt->Credits;
|
| + /* flag that we have to do the distribution */
|
| + doDist = TRUE;
|
| + }
|
| +
|
| + /* refresh tx depth for distribution function that will recover these credits
|
| + * NOTE: this is only valid when there are credits to recover! */
|
| + pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
|
| +
|
| + totalCredits += pRpt->Credits;
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Report indicated %d credits to distribute \n", totalCredits));
|
| +
|
| + if (doDist) {
|
| + /* this was a credit return based on a completed send operations
|
| + * note, this is done with the lock held */
|
| + DO_DISTRIBUTION(target,
|
| + HTC_CREDIT_DIST_SEND_COMPLETE,
|
| + "Send Complete",
|
| + target->EpCreditDistributionListHead->pNext);
|
| + }
|
| +
|
| + UNLOCK_HTC_TX(target);
|
| +
|
| + if (totalCredits) {
|
| + HTCCheckEndpointTxQueues(target);
|
| + }
|
| +
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n"));
|
| +}
|
| +
|
| +/* flush endpoint TX queue */
|
| +static void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag)
|
| +{
|
| + HTC_PACKET *pPacket;
|
| + HTC_PACKET_QUEUE discardQueue;
|
| + HTC_PACKET_QUEUE container;
|
| +
|
| + /* initialize the discard queue */
|
| + INIT_HTC_PACKET_QUEUE(&discardQueue);
|
| +
|
| + LOCK_HTC_TX(target);
|
| +
|
| + /* interate from the front of the TX queue and flush out packets */
|
| + ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket, HTC_PACKET, ListLink) {
|
| +
|
| + /* check for removal */
|
| + if ((HTC_TX_PACKET_TAG_ALL == Tag) || (Tag == pPacket->PktInfo.AsTx.Tag)) {
|
| + /* remove from queue */
|
| + HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket);
|
| + /* add it to the discard pile */
|
| + HTC_PACKET_ENQUEUE(&discardQueue, pPacket);
|
| + }
|
| +
|
| + } ITERATE_END;
|
| +
|
| + UNLOCK_HTC_TX(target);
|
| +
|
| + /* empty the discard queue */
|
| + while (1) {
|
| + pPacket = HTC_PACKET_DEQUEUE(&discardQueue);
|
| + if (NULL == pPacket) {
|
| + break;
|
| + }
|
| + pPacket->Status = A_ECANCELED;
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" Flushing TX packet:0x%X, length:%d, ep:%d tag:0x%X \n",
|
| + (A_UINT32)pPacket, pPacket->ActualLength, pPacket->Endpoint, pPacket->PktInfo.AsTx.Tag));
|
| + INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket);
|
| + DO_EP_TX_COMPLETION(pEndpoint,&container);
|
| + }
|
| +
|
| +}
|
| +
|
| +void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist)
|
| +{
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("--- EP : %d ServiceID: 0x%X --------------\n",
|
| + pEPDist->Endpoint, pEPDist->ServiceID));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" this:0x%X next:0x%X prev:0x%X\n",
|
| + (A_UINT32)pEPDist, (A_UINT32)pEPDist->pNext, (A_UINT32)pEPDist->pPrev));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" DistFlags : 0x%X \n", pEPDist->DistFlags));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsNorm : %d \n", pEPDist->TxCreditsNorm));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsMin : %d \n", pEPDist->TxCreditsMin));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCredits : %d \n", pEPDist->TxCredits));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsAssigned : %d \n", pEPDist->TxCreditsAssigned));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsSeek : %d \n", pEPDist->TxCreditsSeek));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditSize : %d \n", pEPDist->TxCreditSize));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsPerMaxMsg : %d \n", pEPDist->TxCreditsPerMaxMsg));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsToDist : %d \n", pEPDist->TxCreditsToDist));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxQueueDepth : %d \n",
|
| + HTC_PACKET_QUEUE_DEPTH(&((HTC_ENDPOINT *)pEPDist->pHTCReserved)->TxQueue)));
|
| + AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("----------------------------------------------------\n"));
|
| +}
|
| +
|
| +void DumpCreditDistStates(HTC_TARGET *target)
|
| +{
|
| + HTC_ENDPOINT_CREDIT_DIST *pEPList = target->EpCreditDistributionListHead;
|
| +
|
| + while (pEPList != NULL) {
|
| + DumpCreditDist(pEPList);
|
| + pEPList = pEPList->pNext;
|
| + }
|
| +
|
| + if (target->DistributeCredits != NULL) {
|
| + DO_DISTRIBUTION(target,
|
| + HTC_DUMP_CREDIT_STATE,
|
| + "Dump State",
|
| + NULL);
|
| + }
|
| +}
|
| +
|
| +/* flush all send packets from all endpoint queues */
|
| +void HTCFlushSendPkts(HTC_TARGET *target)
|
| +{
|
| + HTC_ENDPOINT *pEndpoint;
|
| + int i;
|
| +
|
| + if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) {
|
| + DumpCreditDistStates(target);
|
| + }
|
| +
|
| + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) {
|
| + pEndpoint = &target->EndPoint[i];
|
| + if (pEndpoint->ServiceID == 0) {
|
| + /* not in use.. */
|
| + continue;
|
| + }
|
| + HTCFlushEndpointTX(target,pEndpoint,HTC_TX_PACKET_TAG_ALL);
|
| + }
|
| +
|
| +
|
| +}
|
| +
|
| +/* HTC API to flush an endpoint's TX queue*/
|
| +void HTCFlushEndpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, HTC_TX_TAG Tag)
|
| +{
|
| + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
|
| + HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint];
|
| +
|
| + if (pEndpoint->ServiceID == 0) {
|
| + AR_DEBUG_ASSERT(FALSE);
|
| + /* not in use.. */
|
| + return;
|
| + }
|
| +
|
| + HTCFlushEndpointTX(target, pEndpoint, Tag);
|
| +}
|
| +
|
| +/* HTC API to indicate activity to the credit distribution function */
|
| +void HTCIndicateActivityChange(HTC_HANDLE HTCHandle,
|
| + HTC_ENDPOINT_ID Endpoint,
|
| + A_BOOL Active)
|
| +{
|
| + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
|
| + HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint];
|
| + A_BOOL doDist = FALSE;
|
| +
|
| + if (pEndpoint->ServiceID == 0) {
|
| + AR_DEBUG_ASSERT(FALSE);
|
| + /* not in use.. */
|
| + return;
|
| + }
|
| +
|
| + LOCK_HTC_TX(target);
|
| +
|
| + if (Active) {
|
| + if (!(pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE)) {
|
| + /* mark active now */
|
| + pEndpoint->CreditDist.DistFlags |= HTC_EP_ACTIVE;
|
| + doDist = TRUE;
|
| + }
|
| + } else {
|
| + if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
|
| + /* mark inactive now */
|
| + pEndpoint->CreditDist.DistFlags &= ~HTC_EP_ACTIVE;
|
| + doDist = TRUE;
|
| + }
|
| + }
|
| +
|
| + if (doDist) {
|
| + /* indicate current Tx Queue depth to the credit distribution function */
|
| + pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue);
|
| + /* do distribution again based on activity change
|
| + * note, this is done with the lock held */
|
| + DO_DISTRIBUTION(target,
|
| + HTC_CREDIT_DIST_ACTIVITY_CHANGE,
|
| + "Activity Change",
|
| + target->EpCreditDistributionListHead->pNext);
|
| + }
|
| +
|
| + UNLOCK_HTC_TX(target);
|
| +
|
| + if (doDist && !Active) {
|
| + /* if a stream went inactive and this resulted in a credit distribution change,
|
| + * some credits may now be available for HTC packets that are stuck in
|
| + * HTC queues */
|
| + HTCCheckEndpointTxQueues(target);
|
| + }
|
| +}
|
| +
|
| +A_BOOL HTCIsEndpointActive(HTC_HANDLE HTCHandle,
|
| + HTC_ENDPOINT_ID Endpoint)
|
| +{
|
| + HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
|
| + HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint];
|
| +
|
| + if (pEndpoint->ServiceID == 0) {
|
| + return FALSE;
|
| + }
|
| +
|
| + if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) {
|
| + return TRUE;
|
| + }
|
| +
|
| + return FALSE;
|
| +}
|
|
|