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; |
+} |