]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/ath6kl/htc2/htc_send.c
Merge tag 'v2.6.37' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / staging / ath6kl / htc2 / htc_send.c
diff --git a/drivers/staging/ath6kl/htc2/htc_send.c b/drivers/staging/ath6kl/htc2/htc_send.c
new file mode 100644 (file)
index 0000000..bc7ee78
--- /dev/null
@@ -0,0 +1,1023 @@
+//------------------------------------------------------------------------------
+// <copyright file="htc_send.c" company="Atheros">
+//    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;
+}