//------------------------------------------------------------------------------ // // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved. // // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // //------------------------------------------------------------------------------ //============================================================================== // 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%lX, ctxt:0x%lX, dist:0x%lX) \n", \ (description), \ (unsigned long)(t)->DistributeCredits, \ (unsigned long)(t)->pCredDistContext, \ (unsigned long)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%lX \n", \ pEndpoint->Id, (unsigned long)(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 + (A_UINT32)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; unsigned 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%lX , Queue Depth: %d\n", (unsigned long)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; unsigned int scatterSpaceRemaining; HIF_SCATTER_REQ *pScatterReq = NULL; int i, packetsInScatterReq; unsigned 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) { /* 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%lX, len:%d (remaining space:%d) \n", i, (unsigned long)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%lX Depth:%d)\n", (unsigned long)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%lX \n", (unsigned long)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%lX, Pkts %d \n", (unsigned long)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%lX, length: %d \n", pPacket->Endpoint, (unsigned long)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%lX, length:%d, ep:%d tag:0x%X \n", (unsigned long)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%lX next:0x%lX prev:0x%lX\n", (unsigned long)pEPDist, (unsigned long)pEPDist->pNext, (unsigned long)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; }