Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 1 | //------------------------------------------------------------------------------ |
| 2 | // <copyright file="htc_send.c" company="Atheros"> |
| 3 | // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved. |
| 4 | // |
| 5 | // |
| 6 | // Permission to use, copy, modify, and/or distribute this software for any |
| 7 | // purpose with or without fee is hereby granted, provided that the above |
| 8 | // copyright notice and this permission notice appear in all copies. |
| 9 | // |
| 10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 17 | // |
| 18 | // |
| 19 | //------------------------------------------------------------------------------ |
| 20 | //============================================================================== |
| 21 | // Author(s): ="Atheros" |
| 22 | //============================================================================== |
| 23 | #include "htc_internal.h" |
| 24 | |
| 25 | typedef enum _HTC_SEND_QUEUE_RESULT { |
| 26 | HTC_SEND_QUEUE_OK = 0, /* packet was queued */ |
| 27 | HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ |
| 28 | } HTC_SEND_QUEUE_RESULT; |
| 29 | |
| 30 | #define DO_EP_TX_COMPLETION(ep,q) DoSendCompletion(ep,q) |
| 31 | |
| 32 | /* call the distribute credits callback with the distribution */ |
| 33 | #define DO_DISTRIBUTION(t,reason,description,pList) \ |
| 34 | { \ |
| 35 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, \ |
| 36 | (" calling distribute function (%s) (dfn:0x%lX, ctxt:0x%lX, dist:0x%lX) \n", \ |
| 37 | (description), \ |
| 38 | (unsigned long)(t)->DistributeCredits, \ |
| 39 | (unsigned long)(t)->pCredDistContext, \ |
| 40 | (unsigned long)pList)); \ |
| 41 | (t)->DistributeCredits((t)->pCredDistContext, \ |
| 42 | (pList), \ |
| 43 | (reason)); \ |
| 44 | } |
| 45 | |
| 46 | static void DoSendCompletion(HTC_ENDPOINT *pEndpoint, |
| 47 | HTC_PACKET_QUEUE *pQueueToIndicate) |
| 48 | { |
| 49 | do { |
| 50 | |
| 51 | if (HTC_QUEUE_EMPTY(pQueueToIndicate)) { |
| 52 | /* nothing to indicate */ |
| 53 | break; |
| 54 | } |
| 55 | |
| 56 | if (pEndpoint->EpCallBacks.EpTxCompleteMultiple != NULL) { |
| 57 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d, send complete multiple callback (%d pkts) \n", |
| 58 | pEndpoint->Id, HTC_PACKET_QUEUE_DEPTH(pQueueToIndicate))); |
| 59 | /* a multiple send complete handler is being used, pass the queue to the handler */ |
| 60 | pEndpoint->EpCallBacks.EpTxCompleteMultiple(pEndpoint->EpCallBacks.pContext, |
| 61 | pQueueToIndicate); |
| 62 | /* all packets are now owned by the callback, reset queue to be safe */ |
| 63 | INIT_HTC_PACKET_QUEUE(pQueueToIndicate); |
| 64 | } else { |
| 65 | HTC_PACKET *pPacket; |
| 66 | /* using legacy EpTxComplete */ |
| 67 | do { |
| 68 | pPacket = HTC_PACKET_DEQUEUE(pQueueToIndicate); |
| 69 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" HTC calling ep %d send complete callback on packet 0x%lX \n", \ |
| 70 | pEndpoint->Id, (unsigned long)(pPacket))); |
| 71 | pEndpoint->EpCallBacks.EpTxComplete(pEndpoint->EpCallBacks.pContext, pPacket); |
| 72 | } while (!HTC_QUEUE_EMPTY(pQueueToIndicate)); |
| 73 | } |
| 74 | |
| 75 | } while (FALSE); |
| 76 | |
| 77 | } |
| 78 | |
| 79 | /* do final completion on sent packet */ |
| 80 | static INLINE void CompleteSentPacket(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_PACKET *pPacket) |
| 81 | { |
| 82 | pPacket->Completion = NULL; |
| 83 | |
Joe Perches | 391bb21 | 2011-01-27 20:04:21 -0800 | [diff] [blame^] | 84 | if (pPacket->Status) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 85 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
| 86 | ("CompleteSentPacket: request failed (status:%d, ep:%d, length:%d creds:%d) \n", |
| 87 | pPacket->Status, pPacket->Endpoint, pPacket->ActualLength, pPacket->PktInfo.AsTx.CreditsUsed)); |
| 88 | /* on failure to submit, reclaim credits for this packet */ |
| 89 | LOCK_HTC_TX(target); |
| 90 | pEndpoint->CreditDist.TxCreditsToDist += pPacket->PktInfo.AsTx.CreditsUsed; |
| 91 | pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); |
| 92 | DO_DISTRIBUTION(target, |
| 93 | HTC_CREDIT_DIST_SEND_COMPLETE, |
| 94 | "Send Complete", |
| 95 | target->EpCreditDistributionListHead->pNext); |
| 96 | UNLOCK_HTC_TX(target); |
| 97 | } |
| 98 | /* first, fixup the head room we allocated */ |
| 99 | pPacket->pBuffer += HTC_HDR_LENGTH; |
| 100 | } |
| 101 | |
| 102 | /* our internal send packet completion handler when packets are submited to the AR6K device |
| 103 | * layer */ |
| 104 | static void HTCSendPktCompletionHandler(void *Context, HTC_PACKET *pPacket) |
| 105 | { |
| 106 | HTC_TARGET *target = (HTC_TARGET *)Context; |
| 107 | HTC_ENDPOINT *pEndpoint = &target->EndPoint[pPacket->Endpoint]; |
| 108 | HTC_PACKET_QUEUE container; |
| 109 | |
| 110 | CompleteSentPacket(target,pEndpoint,pPacket); |
| 111 | INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket); |
| 112 | /* do completion */ |
| 113 | DO_EP_TX_COMPLETION(pEndpoint,&container); |
| 114 | } |
| 115 | |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 116 | int HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 117 | { |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 118 | int status; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 119 | A_BOOL sync = FALSE; |
| 120 | |
| 121 | if (pPacket->Completion == NULL) { |
| 122 | /* mark that this request was synchronously issued */ |
| 123 | sync = TRUE; |
| 124 | } |
| 125 | |
| 126 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, |
| 127 | ("+-HTCIssueSend: transmit length : %d (%s) \n", |
| 128 | pPacket->ActualLength + (A_UINT32)HTC_HDR_LENGTH, |
| 129 | sync ? "SYNC" : "ASYNC" )); |
| 130 | |
| 131 | /* send message to device */ |
| 132 | status = DevSendPacket(&target->Device, |
| 133 | pPacket, |
| 134 | pPacket->ActualLength + HTC_HDR_LENGTH); |
| 135 | |
| 136 | if (sync) { |
| 137 | /* use local sync variable. If this was issued asynchronously, pPacket is no longer |
| 138 | * safe to access. */ |
| 139 | pPacket->pBuffer += HTC_HDR_LENGTH; |
| 140 | } |
| 141 | |
| 142 | /* if this request was asynchronous, the packet completion routine will be invoked by |
| 143 | * the device layer when the HIF layer completes the request */ |
| 144 | |
| 145 | return status; |
| 146 | } |
| 147 | |
| 148 | /* get HTC send packets from the TX queue on an endpoint */ |
| 149 | static INLINE void GetHTCSendPackets(HTC_TARGET *target, |
| 150 | HTC_ENDPOINT *pEndpoint, |
| 151 | HTC_PACKET_QUEUE *pQueue) |
| 152 | { |
| 153 | int creditsRequired; |
| 154 | int remainder; |
| 155 | A_UINT8 sendFlags; |
| 156 | HTC_PACKET *pPacket; |
| 157 | unsigned int transferLength; |
| 158 | |
| 159 | /****** NOTE : the TX lock is held when this function is called *****************/ |
| 160 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+GetHTCSendPackets \n")); |
| 161 | |
| 162 | /* loop until we can grab as many packets out of the queue as we can */ |
| 163 | while (TRUE) { |
| 164 | |
| 165 | sendFlags = 0; |
| 166 | /* get packet at head, but don't remove it */ |
| 167 | pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue); |
| 168 | if (pPacket == NULL) { |
| 169 | break; |
| 170 | } |
| 171 | |
| 172 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%lX , Queue Depth: %d\n", |
| 173 | (unsigned long)pPacket, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); |
| 174 | |
| 175 | transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, pPacket->ActualLength + HTC_HDR_LENGTH); |
| 176 | |
| 177 | if (transferLength <= target->TargetCreditSize) { |
| 178 | creditsRequired = 1; |
| 179 | } else { |
| 180 | /* figure out how many credits this message requires */ |
| 181 | creditsRequired = transferLength / target->TargetCreditSize; |
| 182 | remainder = transferLength % target->TargetCreditSize; |
| 183 | |
| 184 | if (remainder) { |
| 185 | creditsRequired++; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Creds Required:%d Got:%d\n", |
| 190 | creditsRequired, pEndpoint->CreditDist.TxCredits)); |
| 191 | |
| 192 | if (pEndpoint->CreditDist.TxCredits < creditsRequired) { |
| 193 | |
| 194 | /* not enough credits */ |
| 195 | if (pPacket->Endpoint == ENDPOINT_0) { |
| 196 | /* leave it in the queue */ |
| 197 | break; |
| 198 | } |
| 199 | /* invoke the registered distribution function only if this is not |
| 200 | * endpoint 0, we let the driver layer provide more credits if it can. |
| 201 | * We pass the credit distribution list starting at the endpoint in question |
| 202 | * */ |
| 203 | |
| 204 | /* set how many credits we need */ |
| 205 | pEndpoint->CreditDist.TxCreditsSeek = |
| 206 | creditsRequired - pEndpoint->CreditDist.TxCredits; |
| 207 | DO_DISTRIBUTION(target, |
| 208 | HTC_CREDIT_DIST_SEEK_CREDITS, |
| 209 | "Seek Credits", |
| 210 | &pEndpoint->CreditDist); |
| 211 | pEndpoint->CreditDist.TxCreditsSeek = 0; |
| 212 | |
| 213 | if (pEndpoint->CreditDist.TxCredits < creditsRequired) { |
| 214 | /* still not enough credits to send, leave packet in the queue */ |
| 215 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, |
| 216 | (" Not enough credits for ep %d leaving packet in queue..\n", |
| 217 | pPacket->Endpoint)); |
| 218 | break; |
| 219 | } |
| 220 | |
| 221 | } |
| 222 | |
| 223 | pEndpoint->CreditDist.TxCredits -= creditsRequired; |
| 224 | INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired); |
| 225 | |
| 226 | /* check if we need credits back from the target */ |
| 227 | if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) { |
| 228 | /* we are getting low on credits, see if we can ask for more from the distribution function */ |
| 229 | pEndpoint->CreditDist.TxCreditsSeek = |
| 230 | pEndpoint->CreditDist.TxCreditsPerMaxMsg - pEndpoint->CreditDist.TxCredits; |
| 231 | |
| 232 | DO_DISTRIBUTION(target, |
| 233 | HTC_CREDIT_DIST_SEEK_CREDITS, |
| 234 | "Seek Credits", |
| 235 | &pEndpoint->CreditDist); |
| 236 | |
| 237 | pEndpoint->CreditDist.TxCreditsSeek = 0; |
| 238 | /* see if we were successful in getting more */ |
| 239 | if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) { |
| 240 | /* tell the target we need credits ASAP! */ |
| 241 | sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE; |
| 242 | INC_HTC_EP_STAT(pEndpoint, TxCreditLowIndications, 1); |
| 243 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Host Needs Credits \n")); |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | /* now we can fully dequeue */ |
| 248 | pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue); |
| 249 | /* save the number of credits this packet consumed */ |
| 250 | pPacket->PktInfo.AsTx.CreditsUsed = creditsRequired; |
| 251 | /* all TX packets are handled asynchronously */ |
| 252 | pPacket->Completion = HTCSendPktCompletionHandler; |
| 253 | pPacket->pContext = target; |
| 254 | INC_HTC_EP_STAT(pEndpoint, TxIssued, 1); |
| 255 | /* save send flags */ |
| 256 | pPacket->PktInfo.AsTx.SendFlags = sendFlags; |
| 257 | pPacket->PktInfo.AsTx.SeqNo = pEndpoint->SeqNo; |
| 258 | pEndpoint->SeqNo++; |
| 259 | /* queue this packet into the caller's queue */ |
| 260 | HTC_PACKET_ENQUEUE(pQueue,pPacket); |
| 261 | } |
| 262 | |
| 263 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-GetHTCSendPackets \n")); |
| 264 | |
| 265 | } |
| 266 | |
| 267 | static void HTCAsyncSendScatterCompletion(HIF_SCATTER_REQ *pScatterReq) |
| 268 | { |
| 269 | int i; |
| 270 | HTC_PACKET *pPacket; |
| 271 | HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *)pScatterReq->Context; |
| 272 | HTC_TARGET *target = (HTC_TARGET *)pEndpoint->target; |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 273 | int status = A_OK; |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 274 | HTC_PACKET_QUEUE sendCompletes; |
| 275 | |
| 276 | INIT_HTC_PACKET_QUEUE(&sendCompletes); |
| 277 | |
| 278 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCAsyncSendScatterCompletion TotLen: %d Entries: %d\n", |
| 279 | pScatterReq->TotalLength, pScatterReq->ValidScatterEntries)); |
| 280 | |
| 281 | DEV_FINISH_SCATTER_OPERATION(pScatterReq); |
| 282 | |
Joe Perches | 391bb21 | 2011-01-27 20:04:21 -0800 | [diff] [blame^] | 283 | if (pScatterReq->CompletionStatus) { |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 284 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("** Send Scatter Request Failed: %d \n",pScatterReq->CompletionStatus)); |
| 285 | status = A_ERROR; |
| 286 | } |
| 287 | |
| 288 | /* walk through the scatter list and process */ |
| 289 | for (i = 0; i < pScatterReq->ValidScatterEntries; i++) { |
| 290 | pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]); |
| 291 | A_ASSERT(pPacket != NULL); |
| 292 | pPacket->Status = status; |
| 293 | CompleteSentPacket(target,pEndpoint,pPacket); |
| 294 | /* add it to the completion queue */ |
| 295 | HTC_PACKET_ENQUEUE(&sendCompletes, pPacket); |
| 296 | } |
| 297 | |
| 298 | /* free scatter request */ |
| 299 | DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq); |
| 300 | /* complete all packets */ |
| 301 | DO_EP_TX_COMPLETION(pEndpoint,&sendCompletes); |
| 302 | |
| 303 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCAsyncSendScatterCompletion \n")); |
| 304 | } |
| 305 | |
| 306 | /* drain a queue and send as bundles |
| 307 | * this function may return without fully draining the queue under the following conditions : |
| 308 | * - scatter resources are exhausted |
| 309 | * - a message that will consume a partial credit will stop the bundling process early |
| 310 | * - we drop below the minimum number of messages for a bundle |
| 311 | * */ |
| 312 | static void HTCIssueSendBundle(HTC_ENDPOINT *pEndpoint, |
| 313 | HTC_PACKET_QUEUE *pQueue, |
| 314 | int *pBundlesSent, |
| 315 | int *pTotalBundlesPkts) |
| 316 | { |
| 317 | int pktsToScatter; |
| 318 | unsigned int scatterSpaceRemaining; |
| 319 | HIF_SCATTER_REQ *pScatterReq = NULL; |
| 320 | int i, packetsInScatterReq; |
| 321 | unsigned int transferLength; |
| 322 | HTC_PACKET *pPacket; |
| 323 | A_BOOL done = FALSE; |
| 324 | int bundlesSent = 0; |
| 325 | int totalPktsInBundle = 0; |
| 326 | HTC_TARGET *target = pEndpoint->target; |
| 327 | int creditRemainder = 0; |
| 328 | int creditPad; |
| 329 | |
| 330 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCIssueSendBundle \n")); |
| 331 | |
| 332 | while (!done) { |
| 333 | |
| 334 | pktsToScatter = HTC_PACKET_QUEUE_DEPTH(pQueue); |
| 335 | pktsToScatter = min(pktsToScatter, target->MaxMsgPerBundle); |
| 336 | |
| 337 | if (pktsToScatter < HTC_MIN_HTC_MSGS_TO_BUNDLE) { |
| 338 | /* not enough to bundle */ |
| 339 | break; |
| 340 | } |
| 341 | |
| 342 | pScatterReq = DEV_ALLOC_SCATTER_REQ(&target->Device); |
| 343 | |
| 344 | if (pScatterReq == NULL) { |
| 345 | /* no scatter resources */ |
| 346 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" No more scatter resources \n")); |
| 347 | break; |
| 348 | } |
| 349 | |
| 350 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" pkts to scatter: %d \n", pktsToScatter)); |
| 351 | |
| 352 | pScatterReq->TotalLength = 0; |
| 353 | pScatterReq->ValidScatterEntries = 0; |
| 354 | |
| 355 | packetsInScatterReq = 0; |
| 356 | scatterSpaceRemaining = DEV_GET_MAX_BUNDLE_SEND_LENGTH(&target->Device); |
| 357 | |
| 358 | for (i = 0; i < pktsToScatter; i++) { |
| 359 | |
| 360 | pScatterReq->ScatterList[i].pCallerContexts[0] = NULL; |
| 361 | |
| 362 | pPacket = HTC_GET_PKT_AT_HEAD(pQueue); |
| 363 | if (pPacket == NULL) { |
| 364 | A_ASSERT(FALSE); |
| 365 | break; |
| 366 | } |
| 367 | |
| 368 | creditPad = 0; |
| 369 | transferLength = DEV_CALC_SEND_PADDED_LEN(&target->Device, |
| 370 | pPacket->ActualLength + HTC_HDR_LENGTH); |
| 371 | /* see if the padded transfer length falls on a credit boundary */ |
| 372 | creditRemainder = transferLength % target->TargetCreditSize; |
| 373 | |
| 374 | if (creditRemainder != 0) { |
| 375 | /* the transfer consumes a "partial" credit, this packet cannot be bundled unless |
| 376 | * we add additional "dummy" padding (max 255 bytes) to consume the entire credit |
| 377 | *** NOTE: only allow the send padding if the endpoint is allowed to */ |
| 378 | if (pEndpoint->LocalConnectionFlags & HTC_LOCAL_CONN_FLAGS_ENABLE_SEND_BUNDLE_PADDING) { |
| 379 | if (transferLength < target->TargetCreditSize) { |
| 380 | /* special case where the transfer is less than a credit */ |
| 381 | creditPad = target->TargetCreditSize - transferLength; |
| 382 | } else { |
| 383 | creditPad = creditRemainder; |
| 384 | } |
| 385 | |
| 386 | /* now check to see if we can indicate padding in the HTC header */ |
| 387 | if ((creditPad > 0) && (creditPad <= 255)) { |
| 388 | /* adjust the transferlength of this packet with the new credit padding */ |
| 389 | transferLength += creditPad; |
| 390 | } else { |
| 391 | /* the amount to pad is too large, bail on this packet, we have to |
| 392 | * send it using the non-bundled method */ |
| 393 | pPacket = NULL; |
| 394 | } |
| 395 | } else { |
| 396 | /* bail on this packet, user does not want padding applied */ |
| 397 | pPacket = NULL; |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | if (NULL == pPacket) { |
| 402 | /* can't bundle */ |
| 403 | done = TRUE; |
| 404 | break; |
| 405 | } |
| 406 | |
| 407 | if (scatterSpaceRemaining < transferLength) { |
| 408 | /* exceeds what we can transfer */ |
| 409 | break; |
| 410 | } |
| 411 | |
| 412 | scatterSpaceRemaining -= transferLength; |
| 413 | /* now remove it from the queue */ |
| 414 | pPacket = HTC_PACKET_DEQUEUE(pQueue); |
| 415 | /* save it in the scatter list */ |
| 416 | pScatterReq->ScatterList[i].pCallerContexts[0] = pPacket; |
| 417 | /* prepare packet and flag message as part of a send bundle */ |
| 418 | HTC_PREPARE_SEND_PKT(pPacket, |
| 419 | pPacket->PktInfo.AsTx.SendFlags | HTC_FLAGS_SEND_BUNDLE, |
| 420 | creditPad, |
| 421 | pPacket->PktInfo.AsTx.SeqNo); |
| 422 | pScatterReq->ScatterList[i].pBuffer = pPacket->pBuffer; |
| 423 | pScatterReq->ScatterList[i].Length = transferLength; |
| 424 | A_ASSERT(transferLength); |
| 425 | pScatterReq->TotalLength += transferLength; |
| 426 | pScatterReq->ValidScatterEntries++; |
| 427 | packetsInScatterReq++; |
| 428 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" %d, Adding packet : 0x%lX, len:%d (remaining space:%d) \n", |
| 429 | i, (unsigned long)pPacket,transferLength,scatterSpaceRemaining)); |
| 430 | } |
| 431 | |
| 432 | if (packetsInScatterReq >= HTC_MIN_HTC_MSGS_TO_BUNDLE) { |
| 433 | /* send path is always asynchronous */ |
| 434 | pScatterReq->CompletionRoutine = HTCAsyncSendScatterCompletion; |
| 435 | pScatterReq->Context = pEndpoint; |
| 436 | bundlesSent++; |
| 437 | totalPktsInBundle += packetsInScatterReq; |
| 438 | packetsInScatterReq = 0; |
| 439 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Send Scatter total bytes: %d , entries: %d\n", |
| 440 | pScatterReq->TotalLength,pScatterReq->ValidScatterEntries)); |
| 441 | DevSubmitScatterRequest(&target->Device, pScatterReq, DEV_SCATTER_WRITE, DEV_SCATTER_ASYNC); |
| 442 | /* we don't own this anymore */ |
| 443 | pScatterReq = NULL; |
| 444 | /* try to send some more */ |
| 445 | continue; |
| 446 | } |
| 447 | |
| 448 | /* not enough packets to use the scatter request, cleanup */ |
| 449 | if (pScatterReq != NULL) { |
| 450 | if (packetsInScatterReq > 0) { |
| 451 | /* work backwards to requeue requests */ |
| 452 | for (i = (packetsInScatterReq - 1); i >= 0; i--) { |
| 453 | pPacket = (HTC_PACKET *)(pScatterReq->ScatterList[i].pCallerContexts[0]); |
| 454 | if (pPacket != NULL) { |
| 455 | /* undo any prep */ |
| 456 | HTC_UNPREPARE_SEND_PKT(pPacket); |
| 457 | /* queue back to the head */ |
| 458 | HTC_PACKET_ENQUEUE_TO_HEAD(pQueue,pPacket); |
| 459 | } |
| 460 | } |
| 461 | } |
| 462 | DEV_FREE_SCATTER_REQ(&target->Device,pScatterReq); |
| 463 | } |
| 464 | |
| 465 | /* if we get here, we sent all that we could, get out */ |
| 466 | break; |
| 467 | |
| 468 | } |
| 469 | |
| 470 | *pBundlesSent = bundlesSent; |
| 471 | *pTotalBundlesPkts = totalPktsInBundle; |
| 472 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCIssueSendBundle (sent:%d) \n",bundlesSent)); |
| 473 | |
| 474 | return; |
| 475 | } |
| 476 | |
| 477 | /* |
| 478 | * if there are no credits, the packet(s) remains in the queue. |
| 479 | * this function returns the result of the attempt to send a queue of HTC packets */ |
| 480 | static HTC_SEND_QUEUE_RESULT HTCTrySend(HTC_TARGET *target, |
| 481 | HTC_ENDPOINT *pEndpoint, |
| 482 | HTC_PACKET_QUEUE *pCallersSendQueue) |
| 483 | { |
| 484 | HTC_PACKET_QUEUE sendQueue; /* temp queue to hold packets at various stages */ |
| 485 | HTC_PACKET *pPacket; |
| 486 | int bundlesSent; |
| 487 | int pktsInBundles; |
| 488 | int overflow; |
| 489 | HTC_SEND_QUEUE_RESULT result = HTC_SEND_QUEUE_OK; |
| 490 | |
| 491 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (Queue:0x%lX Depth:%d)\n", |
| 492 | (unsigned long)pCallersSendQueue, |
| 493 | (pCallersSendQueue == NULL) ? 0 : HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue))); |
| 494 | |
| 495 | /* init the local send queue */ |
| 496 | INIT_HTC_PACKET_QUEUE(&sendQueue); |
| 497 | |
| 498 | do { |
| 499 | |
| 500 | if (NULL == pCallersSendQueue) { |
| 501 | /* caller didn't provide a queue, just wants us to check queues and send */ |
| 502 | break; |
| 503 | } |
| 504 | |
| 505 | if (HTC_QUEUE_EMPTY(pCallersSendQueue)) { |
| 506 | /* empty queue */ |
| 507 | result = HTC_SEND_QUEUE_DROP; |
| 508 | break; |
| 509 | } |
| 510 | |
| 511 | if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) >= pEndpoint->MaxTxQueueDepth) { |
| 512 | /* we've already overflowed */ |
| 513 | overflow = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue); |
| 514 | } else { |
| 515 | /* figure out how much we will overflow by */ |
| 516 | overflow = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); |
| 517 | overflow += HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue); |
| 518 | /* figure out how much we will overflow the TX queue by */ |
| 519 | overflow -= pEndpoint->MaxTxQueueDepth; |
| 520 | } |
| 521 | |
| 522 | /* if overflow is negative or zero, we are okay */ |
| 523 | if (overflow > 0) { |
| 524 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, |
| 525 | (" Endpoint %d, TX queue will overflow :%d , Tx Depth:%d, Max:%d \n", |
| 526 | pEndpoint->Id, overflow, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue), pEndpoint->MaxTxQueueDepth)); |
| 527 | } |
| 528 | if ((overflow <= 0) || (pEndpoint->EpCallBacks.EpSendFull == NULL)) { |
| 529 | /* all packets will fit or caller did not provide send full indication handler |
| 530 | * -- just move all of them to the local sendQueue object */ |
| 531 | HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&sendQueue, pCallersSendQueue); |
| 532 | } else { |
| 533 | int i; |
| 534 | int goodPkts = HTC_PACKET_QUEUE_DEPTH(pCallersSendQueue) - overflow; |
| 535 | |
| 536 | A_ASSERT(goodPkts >= 0); |
| 537 | /* we have overflowed, and a callback is provided */ |
| 538 | /* dequeue all non-overflow packets into the sendqueue */ |
| 539 | for (i = 0; i < goodPkts; i++) { |
| 540 | /* pop off caller's queue*/ |
| 541 | pPacket = HTC_PACKET_DEQUEUE(pCallersSendQueue); |
| 542 | A_ASSERT(pPacket != NULL); |
| 543 | /* insert into local queue */ |
| 544 | HTC_PACKET_ENQUEUE(&sendQueue,pPacket); |
| 545 | } |
| 546 | |
| 547 | /* the caller's queue has all the packets that won't fit*/ |
| 548 | /* walk through the caller's queue and indicate each one to the send full handler */ |
| 549 | ITERATE_OVER_LIST_ALLOW_REMOVE(&pCallersSendQueue->QueueHead, pPacket, HTC_PACKET, ListLink) { |
| 550 | |
| 551 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Indicating overflowed TX packet: 0x%lX \n", |
| 552 | (unsigned long)pPacket)); |
| 553 | if (pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext, |
| 554 | pPacket) == HTC_SEND_FULL_DROP) { |
| 555 | /* callback wants the packet dropped */ |
| 556 | INC_HTC_EP_STAT(pEndpoint, TxDropped, 1); |
| 557 | /* leave this one in the caller's queue for cleanup */ |
| 558 | } else { |
| 559 | /* callback wants to keep this packet, remove from caller's queue */ |
| 560 | HTC_PACKET_REMOVE(pCallersSendQueue, pPacket); |
| 561 | /* put it in the send queue */ |
| 562 | HTC_PACKET_ENQUEUE(&sendQueue,pPacket); |
| 563 | } |
| 564 | |
| 565 | } ITERATE_END; |
| 566 | |
| 567 | if (HTC_QUEUE_EMPTY(&sendQueue)) { |
| 568 | /* no packets made it in, caller will cleanup */ |
| 569 | result = HTC_SEND_QUEUE_DROP; |
| 570 | break; |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | } while (FALSE); |
| 575 | |
| 576 | if (result != HTC_SEND_QUEUE_OK) { |
| 577 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n")); |
| 578 | return result; |
| 579 | } |
| 580 | |
| 581 | LOCK_HTC_TX(target); |
| 582 | |
| 583 | if (!HTC_QUEUE_EMPTY(&sendQueue)) { |
| 584 | /* transfer packets */ |
| 585 | HTC_PACKET_QUEUE_TRANSFER_TO_TAIL(&pEndpoint->TxQueue,&sendQueue); |
| 586 | A_ASSERT(HTC_QUEUE_EMPTY(&sendQueue)); |
| 587 | INIT_HTC_PACKET_QUEUE(&sendQueue); |
| 588 | } |
| 589 | |
| 590 | /* increment tx processing count on entry */ |
| 591 | pEndpoint->TxProcessCount++; |
| 592 | if (pEndpoint->TxProcessCount > 1) { |
| 593 | /* another thread or task is draining the TX queues on this endpoint |
| 594 | * that thread will reset the tx processing count when the queue is drained */ |
| 595 | pEndpoint->TxProcessCount--; |
| 596 | UNLOCK_HTC_TX(target); |
| 597 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend (busy) \n")); |
| 598 | return HTC_SEND_QUEUE_OK; |
| 599 | } |
| 600 | |
| 601 | /***** beyond this point only 1 thread may enter ******/ |
| 602 | |
| 603 | /* now drain the endpoint TX queue for transmission as long as we have enough |
| 604 | * credits */ |
| 605 | while (TRUE) { |
| 606 | |
| 607 | if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) == 0) { |
| 608 | break; |
| 609 | } |
| 610 | |
| 611 | /* get all the packets for this endpoint that we can for this pass */ |
| 612 | GetHTCSendPackets(target, pEndpoint, &sendQueue); |
| 613 | |
| 614 | if (HTC_PACKET_QUEUE_DEPTH(&sendQueue) == 0) { |
| 615 | /* didn't get any packets due to a lack of credits */ |
| 616 | break; |
| 617 | } |
| 618 | |
| 619 | UNLOCK_HTC_TX(target); |
| 620 | |
| 621 | /* any packets to send are now in our local send queue */ |
| 622 | |
| 623 | bundlesSent = 0; |
| 624 | pktsInBundles = 0; |
| 625 | |
| 626 | while (TRUE) { |
| 627 | |
| 628 | /* try to send a bundle on each pass */ |
| 629 | if ((target->SendBundlingEnabled) && |
| 630 | (HTC_PACKET_QUEUE_DEPTH(&sendQueue) >= HTC_MIN_HTC_MSGS_TO_BUNDLE)) { |
| 631 | int temp1,temp2; |
| 632 | /* bundling is enabled and there is at least a minimum number of packets in the send queue |
| 633 | * send what we can in this pass */ |
| 634 | HTCIssueSendBundle(pEndpoint, &sendQueue, &temp1, &temp2); |
| 635 | bundlesSent += temp1; |
| 636 | pktsInBundles += temp2; |
| 637 | } |
| 638 | |
| 639 | /* if not bundling or there was a packet that could not be placed in a bundle, pull it out |
| 640 | * and send it the normal way */ |
| 641 | pPacket = HTC_PACKET_DEQUEUE(&sendQueue); |
| 642 | if (NULL == pPacket) { |
| 643 | /* local queue is fully drained */ |
| 644 | break; |
| 645 | } |
| 646 | HTC_PREPARE_SEND_PKT(pPacket, |
| 647 | pPacket->PktInfo.AsTx.SendFlags, |
| 648 | 0, |
| 649 | pPacket->PktInfo.AsTx.SeqNo); |
| 650 | HTCIssueSend(target, pPacket); |
| 651 | |
| 652 | /* go back and see if we can bundle some more */ |
| 653 | } |
| 654 | |
| 655 | LOCK_HTC_TX(target); |
| 656 | |
| 657 | INC_HTC_EP_STAT(pEndpoint, TxBundles, bundlesSent); |
| 658 | INC_HTC_EP_STAT(pEndpoint, TxPacketsBundled, pktsInBundles); |
| 659 | |
| 660 | } |
| 661 | |
| 662 | /* done with this endpoint, we can clear the count */ |
| 663 | pEndpoint->TxProcessCount = 0; |
| 664 | UNLOCK_HTC_TX(target); |
| 665 | |
| 666 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n")); |
| 667 | |
| 668 | return HTC_SEND_QUEUE_OK; |
| 669 | } |
| 670 | |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 671 | int HTCSendPktsMultiple(HTC_HANDLE HTCHandle, HTC_PACKET_QUEUE *pPktQueue) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 672 | { |
| 673 | HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| 674 | HTC_ENDPOINT *pEndpoint; |
| 675 | HTC_PACKET *pPacket; |
| 676 | |
| 677 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCSendPktsMultiple: Queue: 0x%lX, Pkts %d \n", |
| 678 | (unsigned long)pPktQueue, HTC_PACKET_QUEUE_DEPTH(pPktQueue))); |
| 679 | |
| 680 | /* get packet at head to figure out which endpoint these packets will go into */ |
| 681 | pPacket = HTC_GET_PKT_AT_HEAD(pPktQueue); |
| 682 | if (NULL == pPacket) { |
| 683 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n")); |
| 684 | return A_EINVAL; |
| 685 | } |
| 686 | |
| 687 | AR_DEBUG_ASSERT(pPacket->Endpoint < ENDPOINT_MAX); |
| 688 | pEndpoint = &target->EndPoint[pPacket->Endpoint]; |
| 689 | |
| 690 | HTCTrySend(target, pEndpoint, pPktQueue); |
| 691 | |
| 692 | /* do completion on any packets that couldn't get in */ |
| 693 | if (!HTC_QUEUE_EMPTY(pPktQueue)) { |
| 694 | |
| 695 | HTC_PACKET_QUEUE_ITERATE_ALLOW_REMOVE(pPktQueue,pPacket) { |
| 696 | if (HTC_STOPPING(target)) { |
| 697 | pPacket->Status = A_ECANCELED; |
| 698 | } else { |
| 699 | pPacket->Status = A_NO_RESOURCE; |
| 700 | } |
| 701 | } HTC_PACKET_QUEUE_ITERATE_END; |
| 702 | |
| 703 | DO_EP_TX_COMPLETION(pEndpoint,pPktQueue); |
| 704 | } |
| 705 | |
| 706 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPktsMultiple \n")); |
| 707 | |
| 708 | return A_OK; |
| 709 | } |
| 710 | |
| 711 | /* HTC API - HTCSendPkt */ |
Joe Perches | 1f4c34b | 2011-01-27 20:04:19 -0800 | [diff] [blame] | 712 | int HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) |
Vipin Mehta | 30295c8 | 2010-09-01 12:06:33 -0700 | [diff] [blame] | 713 | { |
| 714 | HTC_PACKET_QUEUE queue; |
| 715 | |
| 716 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, |
| 717 | ("+-HTCSendPkt: Enter endPointId: %d, buffer: 0x%lX, length: %d \n", |
| 718 | pPacket->Endpoint, (unsigned long)pPacket->pBuffer, pPacket->ActualLength)); |
| 719 | INIT_HTC_PACKET_QUEUE_AND_ADD(&queue,pPacket); |
| 720 | return HTCSendPktsMultiple(HTCHandle, &queue); |
| 721 | } |
| 722 | |
| 723 | /* check TX queues to drain because of credit distribution update */ |
| 724 | static INLINE void HTCCheckEndpointTxQueues(HTC_TARGET *target) |
| 725 | { |
| 726 | HTC_ENDPOINT *pEndpoint; |
| 727 | HTC_ENDPOINT_CREDIT_DIST *pDistItem; |
| 728 | |
| 729 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCCheckEndpointTxQueues \n")); |
| 730 | pDistItem = target->EpCreditDistributionListHead; |
| 731 | |
| 732 | /* run through the credit distribution list to see |
| 733 | * if there are packets queued |
| 734 | * NOTE: no locks need to be taken since the distribution list |
| 735 | * is not dynamic (cannot be re-ordered) and we are not modifying any state */ |
| 736 | while (pDistItem != NULL) { |
| 737 | pEndpoint = (HTC_ENDPOINT *)pDistItem->pHTCReserved; |
| 738 | |
| 739 | if (HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue) > 0) { |
| 740 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n", |
| 741 | pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue))); |
| 742 | /* try to start the stalled queue, this list is ordered by priority. |
| 743 | * Highest priority queue get's processed first, if there are credits available the |
| 744 | * highest priority queue will get a chance to reclaim credits from lower priority |
| 745 | * ones */ |
| 746 | HTCTrySend(target, pEndpoint, NULL); |
| 747 | } |
| 748 | |
| 749 | pDistItem = pDistItem->pNext; |
| 750 | } |
| 751 | |
| 752 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCCheckEndpointTxQueues \n")); |
| 753 | } |
| 754 | |
| 755 | /* process credit reports and call distribution function */ |
| 756 | void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint) |
| 757 | { |
| 758 | int i; |
| 759 | HTC_ENDPOINT *pEndpoint; |
| 760 | int totalCredits = 0; |
| 761 | A_BOOL doDist = FALSE; |
| 762 | |
| 763 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries)); |
| 764 | |
| 765 | /* lock out TX while we update credits */ |
| 766 | LOCK_HTC_TX(target); |
| 767 | |
| 768 | for (i = 0; i < NumEntries; i++, pRpt++) { |
| 769 | if (pRpt->EndpointID >= ENDPOINT_MAX) { |
| 770 | AR_DEBUG_ASSERT(FALSE); |
| 771 | break; |
| 772 | } |
| 773 | |
| 774 | pEndpoint = &target->EndPoint[pRpt->EndpointID]; |
| 775 | |
| 776 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d got %d credits \n", |
| 777 | pRpt->EndpointID, pRpt->Credits)); |
| 778 | |
| 779 | |
| 780 | #ifdef HTC_EP_STAT_PROFILING |
| 781 | |
| 782 | INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1); |
| 783 | INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, pRpt->Credits); |
| 784 | |
| 785 | if (FromEndpoint == pRpt->EndpointID) { |
| 786 | /* this credit report arrived on the same endpoint indicating it arrived in an RX |
| 787 | * packet */ |
| 788 | INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, pRpt->Credits); |
| 789 | INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1); |
| 790 | } else if (FromEndpoint == ENDPOINT_0) { |
| 791 | /* this credit arrived on endpoint 0 as a NULL message */ |
| 792 | INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, pRpt->Credits); |
| 793 | INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1); |
| 794 | } else { |
| 795 | /* arrived on another endpoint */ |
| 796 | INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, pRpt->Credits); |
| 797 | INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1); |
| 798 | } |
| 799 | |
| 800 | #endif |
| 801 | |
| 802 | if (ENDPOINT_0 == pRpt->EndpointID) { |
| 803 | /* always give endpoint 0 credits back */ |
| 804 | pEndpoint->CreditDist.TxCredits += pRpt->Credits; |
| 805 | } else { |
| 806 | /* for all other endpoints, update credits to distribute, the distribution function |
| 807 | * will handle giving out credits back to the endpoints */ |
| 808 | pEndpoint->CreditDist.TxCreditsToDist += pRpt->Credits; |
| 809 | /* flag that we have to do the distribution */ |
| 810 | doDist = TRUE; |
| 811 | } |
| 812 | |
| 813 | /* refresh tx depth for distribution function that will recover these credits |
| 814 | * NOTE: this is only valid when there are credits to recover! */ |
| 815 | pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); |
| 816 | |
| 817 | totalCredits += pRpt->Credits; |
| 818 | } |
| 819 | |
| 820 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Report indicated %d credits to distribute \n", totalCredits)); |
| 821 | |
| 822 | if (doDist) { |
| 823 | /* this was a credit return based on a completed send operations |
| 824 | * note, this is done with the lock held */ |
| 825 | DO_DISTRIBUTION(target, |
| 826 | HTC_CREDIT_DIST_SEND_COMPLETE, |
| 827 | "Send Complete", |
| 828 | target->EpCreditDistributionListHead->pNext); |
| 829 | } |
| 830 | |
| 831 | UNLOCK_HTC_TX(target); |
| 832 | |
| 833 | if (totalCredits) { |
| 834 | HTCCheckEndpointTxQueues(target); |
| 835 | } |
| 836 | |
| 837 | AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n")); |
| 838 | } |
| 839 | |
| 840 | /* flush endpoint TX queue */ |
| 841 | static void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag) |
| 842 | { |
| 843 | HTC_PACKET *pPacket; |
| 844 | HTC_PACKET_QUEUE discardQueue; |
| 845 | HTC_PACKET_QUEUE container; |
| 846 | |
| 847 | /* initialize the discard queue */ |
| 848 | INIT_HTC_PACKET_QUEUE(&discardQueue); |
| 849 | |
| 850 | LOCK_HTC_TX(target); |
| 851 | |
| 852 | /* interate from the front of the TX queue and flush out packets */ |
| 853 | ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue.QueueHead, pPacket, HTC_PACKET, ListLink) { |
| 854 | |
| 855 | /* check for removal */ |
| 856 | if ((HTC_TX_PACKET_TAG_ALL == Tag) || (Tag == pPacket->PktInfo.AsTx.Tag)) { |
| 857 | /* remove from queue */ |
| 858 | HTC_PACKET_REMOVE(&pEndpoint->TxQueue, pPacket); |
| 859 | /* add it to the discard pile */ |
| 860 | HTC_PACKET_ENQUEUE(&discardQueue, pPacket); |
| 861 | } |
| 862 | |
| 863 | } ITERATE_END; |
| 864 | |
| 865 | UNLOCK_HTC_TX(target); |
| 866 | |
| 867 | /* empty the discard queue */ |
| 868 | while (1) { |
| 869 | pPacket = HTC_PACKET_DEQUEUE(&discardQueue); |
| 870 | if (NULL == pPacket) { |
| 871 | break; |
| 872 | } |
| 873 | pPacket->Status = A_ECANCELED; |
| 874 | AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" Flushing TX packet:0x%lX, length:%d, ep:%d tag:0x%X \n", |
| 875 | (unsigned long)pPacket, pPacket->ActualLength, pPacket->Endpoint, pPacket->PktInfo.AsTx.Tag)); |
| 876 | INIT_HTC_PACKET_QUEUE_AND_ADD(&container,pPacket); |
| 877 | DO_EP_TX_COMPLETION(pEndpoint,&container); |
| 878 | } |
| 879 | |
| 880 | } |
| 881 | |
| 882 | void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist) |
| 883 | { |
| 884 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("--- EP : %d ServiceID: 0x%X --------------\n", |
| 885 | pEPDist->Endpoint, pEPDist->ServiceID)); |
| 886 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" this:0x%lX next:0x%lX prev:0x%lX\n", |
| 887 | (unsigned long)pEPDist, (unsigned long)pEPDist->pNext, (unsigned long)pEPDist->pPrev)); |
| 888 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" DistFlags : 0x%X \n", pEPDist->DistFlags)); |
| 889 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsNorm : %d \n", pEPDist->TxCreditsNorm)); |
| 890 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsMin : %d \n", pEPDist->TxCreditsMin)); |
| 891 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCredits : %d \n", pEPDist->TxCredits)); |
| 892 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsAssigned : %d \n", pEPDist->TxCreditsAssigned)); |
| 893 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsSeek : %d \n", pEPDist->TxCreditsSeek)); |
| 894 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditSize : %d \n", pEPDist->TxCreditSize)); |
| 895 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsPerMaxMsg : %d \n", pEPDist->TxCreditsPerMaxMsg)); |
| 896 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsToDist : %d \n", pEPDist->TxCreditsToDist)); |
| 897 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxQueueDepth : %d \n", |
| 898 | HTC_PACKET_QUEUE_DEPTH(&((HTC_ENDPOINT *)pEPDist->pHTCReserved)->TxQueue))); |
| 899 | AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("----------------------------------------------------\n")); |
| 900 | } |
| 901 | |
| 902 | void DumpCreditDistStates(HTC_TARGET *target) |
| 903 | { |
| 904 | HTC_ENDPOINT_CREDIT_DIST *pEPList = target->EpCreditDistributionListHead; |
| 905 | |
| 906 | while (pEPList != NULL) { |
| 907 | DumpCreditDist(pEPList); |
| 908 | pEPList = pEPList->pNext; |
| 909 | } |
| 910 | |
| 911 | if (target->DistributeCredits != NULL) { |
| 912 | DO_DISTRIBUTION(target, |
| 913 | HTC_DUMP_CREDIT_STATE, |
| 914 | "Dump State", |
| 915 | NULL); |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | /* flush all send packets from all endpoint queues */ |
| 920 | void HTCFlushSendPkts(HTC_TARGET *target) |
| 921 | { |
| 922 | HTC_ENDPOINT *pEndpoint; |
| 923 | int i; |
| 924 | |
| 925 | if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_TRC)) { |
| 926 | DumpCreditDistStates(target); |
| 927 | } |
| 928 | |
| 929 | for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { |
| 930 | pEndpoint = &target->EndPoint[i]; |
| 931 | if (pEndpoint->ServiceID == 0) { |
| 932 | /* not in use.. */ |
| 933 | continue; |
| 934 | } |
| 935 | HTCFlushEndpointTX(target,pEndpoint,HTC_TX_PACKET_TAG_ALL); |
| 936 | } |
| 937 | |
| 938 | |
| 939 | } |
| 940 | |
| 941 | /* HTC API to flush an endpoint's TX queue*/ |
| 942 | void HTCFlushEndpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, HTC_TX_TAG Tag) |
| 943 | { |
| 944 | HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| 945 | HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; |
| 946 | |
| 947 | if (pEndpoint->ServiceID == 0) { |
| 948 | AR_DEBUG_ASSERT(FALSE); |
| 949 | /* not in use.. */ |
| 950 | return; |
| 951 | } |
| 952 | |
| 953 | HTCFlushEndpointTX(target, pEndpoint, Tag); |
| 954 | } |
| 955 | |
| 956 | /* HTC API to indicate activity to the credit distribution function */ |
| 957 | void HTCIndicateActivityChange(HTC_HANDLE HTCHandle, |
| 958 | HTC_ENDPOINT_ID Endpoint, |
| 959 | A_BOOL Active) |
| 960 | { |
| 961 | HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| 962 | HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; |
| 963 | A_BOOL doDist = FALSE; |
| 964 | |
| 965 | if (pEndpoint->ServiceID == 0) { |
| 966 | AR_DEBUG_ASSERT(FALSE); |
| 967 | /* not in use.. */ |
| 968 | return; |
| 969 | } |
| 970 | |
| 971 | LOCK_HTC_TX(target); |
| 972 | |
| 973 | if (Active) { |
| 974 | if (!(pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE)) { |
| 975 | /* mark active now */ |
| 976 | pEndpoint->CreditDist.DistFlags |= HTC_EP_ACTIVE; |
| 977 | doDist = TRUE; |
| 978 | } |
| 979 | } else { |
| 980 | if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) { |
| 981 | /* mark inactive now */ |
| 982 | pEndpoint->CreditDist.DistFlags &= ~HTC_EP_ACTIVE; |
| 983 | doDist = TRUE; |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | if (doDist) { |
| 988 | /* indicate current Tx Queue depth to the credit distribution function */ |
| 989 | pEndpoint->CreditDist.TxQueueDepth = HTC_PACKET_QUEUE_DEPTH(&pEndpoint->TxQueue); |
| 990 | /* do distribution again based on activity change |
| 991 | * note, this is done with the lock held */ |
| 992 | DO_DISTRIBUTION(target, |
| 993 | HTC_CREDIT_DIST_ACTIVITY_CHANGE, |
| 994 | "Activity Change", |
| 995 | target->EpCreditDistributionListHead->pNext); |
| 996 | } |
| 997 | |
| 998 | UNLOCK_HTC_TX(target); |
| 999 | |
| 1000 | if (doDist && !Active) { |
| 1001 | /* if a stream went inactive and this resulted in a credit distribution change, |
| 1002 | * some credits may now be available for HTC packets that are stuck in |
| 1003 | * HTC queues */ |
| 1004 | HTCCheckEndpointTxQueues(target); |
| 1005 | } |
| 1006 | } |
| 1007 | |
| 1008 | A_BOOL HTCIsEndpointActive(HTC_HANDLE HTCHandle, |
| 1009 | HTC_ENDPOINT_ID Endpoint) |
| 1010 | { |
| 1011 | HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); |
| 1012 | HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; |
| 1013 | |
| 1014 | if (pEndpoint->ServiceID == 0) { |
| 1015 | return FALSE; |
| 1016 | } |
| 1017 | |
| 1018 | if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) { |
| 1019 | return TRUE; |
| 1020 | } |
| 1021 | |
| 1022 | return FALSE; |
| 1023 | } |