1054 lines
41 KiB
C
1054 lines
41 KiB
C
|
//------------------------------------------------------------------------------
|
||
|
// ISC License (ISC)
|
||
|
//
|
||
|
// Copyright (c) 2007-2012, The Linux Foundation
|
||
|
// All rights reserved.
|
||
|
// Software was previously licensed under ISC license by Qualcomm Atheros, Inc.
|
||
|
//
|
||
|
//
|
||
|
// 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 = { {0} };
|
||
|
|
||
|
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 */
|
||
|
if ((pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue))== NULL){
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("Queue is empty \n"));
|
||
|
break;
|
||
|
}
|
||
|
/* 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 = { {0} };
|
||
|
|
||
|
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);
|
||
|
if (pPacket == NULL)
|
||
|
return;
|
||
|
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 */
|
||
|
#ifdef CONFIG_ARCH_MSM9615
|
||
|
/* Even though creditPad is passed to firmware through HTC
|
||
|
* Frame header, firmware ignores the creditPad. This
|
||
|
* change is done to support packet bundling even if the
|
||
|
* packets sizes are lower. Bundling improves overall throughput */
|
||
|
if (creditPad > 0 && transferLength >= HTC_MIN_HTC_PKT_LEN_TO_BUNDLE) {
|
||
|
#else
|
||
|
if ((creditPad > 0) && (creditPad <= 255)) {
|
||
|
#endif
|
||
|
/* 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 */
|
||
|
if((pPacket = HTC_PACKET_DEQUEUE(pQueue))== NULL)
|
||
|
break;
|
||
|
|
||
|
/* 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 = { {0} }; /* 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 = { {0} };
|
||
|
|
||
|
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];
|
||
|
if (pEndpoint == NULL)
|
||
|
return;
|
||
|
|
||
|
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 = { {0} };
|
||
|
HTC_PACKET_QUEUE container = { {0} };
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
if(HTC_TX_PACKET_TAG_ALL == Tag)
|
||
|
pPacket->Status = A_ECANCELED;
|
||
|
else
|
||
|
pPacket->Status = A_ECOMM;
|
||
|
|
||
|
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;
|
||
|
|
||
|
if (Endpoint < 0 || Endpoint >= ENDPOINT_MAX)
|
||
|
return;
|
||
|
|
||
|
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;
|
||
|
}
|