1/* 2 * 3 * Copyright (c) 2007 Atheros Communications Inc. 4 * All rights reserved. 5 * 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation; 10 * 11 * Software distributed under the License is distributed on an "AS 12 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 13 * implied. See the License for the specific language governing 14 * rights and limitations under the License. 15 * 16 * 17 * 18 */ 19 20#include "htc_internal.h" 21 22#define DO_EP_TX_COMPLETION(ep,p) \ 23{ \ 24 (p)->Completion = NULL; \ 25 (ep)->EpCallBacks.EpTxComplete((ep)->EpCallBacks.pContext,(p)); \ 26} 27 28 29/* call the distribute credits callback with the distribution */ 30#define DO_DISTRIBUTION(t,reason,description,pList) \ 31{ \ 32 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, \ 33 (" calling distribute function (%s) (dfn:0x%X, ctxt:0x%X, dist:0x%X) \n", \ 34 (description), \ 35 (A_UINT32)(t)->DistributeCredits, \ 36 (A_UINT32)(t)->pCredDistContext, \ 37 (A_UINT32)pList)); \ 38 (t)->DistributeCredits((t)->pCredDistContext, \ 39 (pList), \ 40 (reason)); \ 41} 42 43/* our internal send packet completion handler when packets are submited to the AR6K device 44 * layer */ 45static void HTCSendPktCompletionHandler(void *Context, HTC_PACKET *pPacket) 46{ 47 HTC_TARGET *target = (HTC_TARGET *)Context; 48 HTC_ENDPOINT *pEndpoint = &target->EndPoint[pPacket->Endpoint]; 49 50 51 if (A_FAILED(pPacket->Status)) { 52 AR_DEBUG_PRINTF(ATH_DEBUG_ERR, 53 ("HTCSendPktCompletionHandler: request failed (status:%d, ep:%d) \n", 54 pPacket->Status, pPacket->Endpoint)); 55 } 56 /* first, fixup the head room we allocated */ 57 pPacket->pBuffer += HTC_HDR_LENGTH; 58 /* do completion */ 59 DO_EP_TX_COMPLETION(pEndpoint,pPacket); 60} 61 62A_STATUS HTCIssueSend(HTC_TARGET *target, HTC_PACKET *pPacket, A_UINT8 SendFlags) 63{ 64 A_STATUS status; 65 A_UINT8 *pHdrBuf; 66 A_BOOL sync = FALSE; 67 68 /* caller always provides headrooom */ 69 pPacket->pBuffer -= HTC_HDR_LENGTH; 70 pHdrBuf = pPacket->pBuffer; 71 /* setup frame header */ 72 A_SET_UINT16_FIELD(pHdrBuf,HTC_FRAME_HDR,PayloadLen,(A_UINT16)pPacket->ActualLength); 73 A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,Flags,SendFlags); 74 A_SET_UINT8_FIELD(pHdrBuf,HTC_FRAME_HDR,EndpointID, (A_UINT8)pPacket->Endpoint); 75 76 if (pPacket->Completion == NULL) { 77 /* mark that this request was synchronously issued */ 78 sync = TRUE; 79 } 80 81 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, 82 ("+-HTCIssueSend: transmit length : %d (%s) \n", 83 pPacket->ActualLength + HTC_HDR_LENGTH, 84 sync ? "SYNC" : "ASYNC" )); 85 86 /* send message to device */ 87 status = DevSendPacket(&target->Device, 88 pPacket, 89 pPacket->ActualLength + HTC_HDR_LENGTH); 90 91 if (sync) { 92 /* use local sync variable. If this was issued asynchronously, pPacket is no longer 93 * safe to access. */ 94 pPacket->pBuffer += HTC_HDR_LENGTH; 95 } 96 97 /* if this request was asynchronous, the packet completion routine will be invoked by 98 * the device layer when the HIF layer completes the request */ 99 100 return status; 101} 102 103/* try to send the current packet or a packet at the head of the TX queue, 104 * if there are no credits, the packet remains in the queue. */ 105static void HTCTrySend(HTC_TARGET *target, 106 HTC_PACKET *pPacketToSend, 107 HTC_ENDPOINT_ID ep) 108{ 109 HTC_PACKET *pPacket; 110 HTC_ENDPOINT *pEndpoint; 111 int creditsRequired; 112 A_UINT8 sendFlags; 113 114 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("+HTCTrySend (pPkt:0x%X)\n",(A_UINT32)pPacketToSend)); 115 116 pEndpoint = &target->EndPoint[ep]; 117 118 LOCK_HTC_TX(target); 119 120 if (pPacketToSend != NULL) { 121 /* caller supplied us a packet to queue to the tail of the HTC TX queue before 122 * we check the tx queue */ 123 HTC_PACKET_ENQUEUE(&pEndpoint->TxQueue,pPacketToSend); 124 pEndpoint->CurrentTxQueueDepth++; 125 } 126 127 /* now drain the TX queue for transmission as long as we have enough 128 * credits */ 129 130 while (1) { 131 132 if (HTC_QUEUE_EMPTY(&pEndpoint->TxQueue)) { 133 /* nothing in the queue */ 134 break; 135 } 136 137 sendFlags = 0; 138 139 /* get packet at head, but don't remove it */ 140 pPacket = HTC_GET_PKT_AT_HEAD(&pEndpoint->TxQueue); 141 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Got head packet:0x%X , Queue Depth: %d\n", 142 (A_UINT32)pPacket, pEndpoint->CurrentTxQueueDepth)); 143 144 /* figure out how many credits this message requires */ 145 creditsRequired = pPacket->ActualLength + HTC_HDR_LENGTH; 146 creditsRequired += target->TargetCreditSize - 1; 147 creditsRequired /= target->TargetCreditSize; 148 149 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Creds Required:%d Got:%d\n", 150 creditsRequired, pEndpoint->CreditDist.TxCredits)); 151 152 if (pEndpoint->CreditDist.TxCredits < creditsRequired) { 153 154 /* not enough credits */ 155 156 if (pPacket->Endpoint == ENDPOINT_0) { 157 /* leave it in the queue */ 158 break; 159 } 160 /* invoke the registered distribution function only if this is not 161 * endpoint 0, we let the driver layer provide more credits if it can. 162 * We pass the credit distribution list starting at the endpoint in question 163 * */ 164 165 /* set how many credits we need */ 166 pEndpoint->CreditDist.TxCreditsSeek = 167 creditsRequired - pEndpoint->CreditDist.TxCredits; 168 DO_DISTRIBUTION(target, 169 HTC_CREDIT_DIST_SEEK_CREDITS, 170 "Seek Credits", 171 &pEndpoint->CreditDist); 172 173 pEndpoint->CreditDist.TxCreditsSeek = 0; 174 175 if (pEndpoint->CreditDist.TxCredits < creditsRequired) { 176 /* still not enough credits to send, leave packet in the queue */ 177 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, 178 (" Not enough credits for ep %d leaving packet in queue..\n", 179 pPacket->Endpoint)); 180 break; 181 } 182 183 } 184 185 pEndpoint->CreditDist.TxCredits -= creditsRequired; 186 INC_HTC_EP_STAT(pEndpoint, TxCreditsConsummed, creditsRequired); 187 188 /* check if we need credits */ 189 if (pEndpoint->CreditDist.TxCredits < pEndpoint->CreditDist.TxCreditsPerMaxMsg) { 190 sendFlags |= HTC_FLAGS_NEED_CREDIT_UPDATE; 191 INC_HTC_EP_STAT(pEndpoint, TxCreditLowIndications, 1); 192 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,(" Host Needs Credits \n")); 193 } 194 195 /* now we can fully dequeue */ 196 pPacket = HTC_PACKET_DEQUEUE(&pEndpoint->TxQueue); 197 pEndpoint->CurrentTxQueueDepth--; 198 199 INC_HTC_EP_STAT(pEndpoint, TxIssued, 1); 200 201 UNLOCK_HTC_TX(target); 202 203 HTCIssueSend(target, pPacket, sendFlags); 204 205 LOCK_HTC_TX(target); 206 207 /* go back and check for more messages */ 208 } 209 210 if (pEndpoint->CurrentTxQueueDepth >= pEndpoint->MaxTxQueueDepth) { 211 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d, TX queue is full, Depth:%d, Max:%d \n", 212 ep, pEndpoint->CurrentTxQueueDepth, pEndpoint->MaxTxQueueDepth)); 213 UNLOCK_HTC_TX(target); 214 /* queue is now full, let caller know */ 215 if (pEndpoint->EpCallBacks.EpSendFull != NULL) { 216 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Calling driver's send full callback.... \n")); 217 pEndpoint->EpCallBacks.EpSendFull(pEndpoint->EpCallBacks.pContext, ep); 218 } 219 } else { 220 UNLOCK_HTC_TX(target); 221 /* queue is now available for new packet, let caller know */ 222 if (pEndpoint->EpCallBacks.EpSendAvail) 223 pEndpoint->EpCallBacks.EpSendAvail(pEndpoint->EpCallBacks.pContext, ep); 224 } 225 226 AR_DEBUG_PRINTF(ATH_DEBUG_SEND,("-HTCTrySend: \n")); 227} 228 229/* HTC API - HTCSendPkt */ 230A_STATUS HTCSendPkt(HTC_HANDLE HTCHandle, HTC_PACKET *pPacket) 231{ 232 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 233 HTC_ENDPOINT *pEndpoint; 234 HTC_ENDPOINT_ID ep; 235 A_STATUS status = A_OK; 236 237 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, 238 ("+HTCSendPkt: Enter endPointId: %d, buffer: 0x%X, length: %d \n", 239 pPacket->Endpoint, (A_UINT32)pPacket->pBuffer, pPacket->ActualLength)); 240 241 ep = pPacket->Endpoint; 242 AR_DEBUG_ASSERT(ep < ENDPOINT_MAX); 243 pEndpoint = &target->EndPoint[ep]; 244 245 do { 246 247 if (HTC_STOPPING(target)) { 248 status = A_ECANCELED; 249 pPacket->Status = status; 250 DO_EP_TX_COMPLETION(pEndpoint,pPacket); 251 break; 252 } 253 /* everything sent through this interface is asynchronous */ 254 /* fill in HTC completion routines */ 255 pPacket->Completion = HTCSendPktCompletionHandler; 256 pPacket->pContext = target; 257 258 HTCTrySend(target, pPacket, ep); 259 260 } while (FALSE); 261 262 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCSendPkt \n")); 263 264 return status; 265} 266 267 268/* check TX queues to drain because of credit distribution update */ 269static INLINE void HTCCheckEndpointTxQueues(HTC_TARGET *target) 270{ 271 HTC_ENDPOINT *pEndpoint; 272 HTC_ENDPOINT_CREDIT_DIST *pDistItem; 273 274 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCCheckEndpointTxQueues \n")); 275 pDistItem = target->EpCreditDistributionListHead; 276 277 /* run through the credit distribution list to see 278 * if there are packets queued 279 * NOTE: no locks need to be taken since the distribution list 280 * is not dynamic (cannot be re-ordered) and we are not modifying any state */ 281 while (pDistItem != NULL) { 282 pEndpoint = (HTC_ENDPOINT *)pDistItem->pHTCReserved; 283 284 if (pEndpoint->CurrentTxQueueDepth > 0) { 285 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Ep %d has %d credits and %d Packets in TX Queue \n", 286 pDistItem->Endpoint, pEndpoint->CreditDist.TxCredits, pEndpoint->CurrentTxQueueDepth)); 287 /* try to start the stalled queue, this list is ordered by priority. 288 * Highest priority queue get's processed first, if there are credits available the 289 * highest priority queue will get a chance to reclaim credits from lower priority 290 * ones */ 291 HTCTrySend(target, NULL, pDistItem->Endpoint); 292 } 293 294 pDistItem = pDistItem->pNext; 295 } 296 297 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCCheckEndpointTxQueues \n")); 298} 299 300/* process credit reports and call distribution function */ 301void HTCProcessCreditRpt(HTC_TARGET *target, HTC_CREDIT_REPORT *pRpt, int NumEntries, HTC_ENDPOINT_ID FromEndpoint) 302{ 303 int i; 304 HTC_ENDPOINT *pEndpoint; 305 int totalCredits = 0; 306 A_BOOL doDist = FALSE; 307 308 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries)); 309 310 /* lock out TX while we update credits */ 311 LOCK_HTC_TX(target); 312 313 for (i = 0; i < NumEntries; i++, pRpt++) { 314 if (pRpt->EndpointID >= ENDPOINT_MAX) { 315 AR_DEBUG_ASSERT(FALSE); 316 break; 317 } 318 319 pEndpoint = &target->EndPoint[pRpt->EndpointID]; 320 321 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Endpoint %d got %d credits \n", 322 pRpt->EndpointID, pRpt->Credits)); 323 324 325#ifdef HTC_EP_STAT_PROFILING 326 327 INC_HTC_EP_STAT(pEndpoint, TxCreditRpts, 1); 328 INC_HTC_EP_STAT(pEndpoint, TxCreditsReturned, pRpt->Credits); 329 330 if (FromEndpoint == pRpt->EndpointID) { 331 /* this credit report arrived on the same endpoint indicating it arrived in an RX 332 * packet */ 333 INC_HTC_EP_STAT(pEndpoint, TxCreditsFromRx, pRpt->Credits); 334 INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromRx, 1); 335 } else if (FromEndpoint == ENDPOINT_0) { 336 /* this credit arrived on endpoint 0 as a NULL message */ 337 INC_HTC_EP_STAT(pEndpoint, TxCreditsFromEp0, pRpt->Credits); 338 INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromEp0, 1); 339 } else { 340 /* arrived on another endpoint */ 341 INC_HTC_EP_STAT(pEndpoint, TxCreditsFromOther, pRpt->Credits); 342 INC_HTC_EP_STAT(pEndpoint, TxCreditRptsFromOther, 1); 343 } 344 345#endif 346 347 if (ENDPOINT_0 == pRpt->EndpointID) { 348 /* always give endpoint 0 credits back */ 349 pEndpoint->CreditDist.TxCredits += pRpt->Credits; 350 } else { 351 /* for all other endpoints, update credits to distribute, the distribution function 352 * will handle giving out credits back to the endpoints */ 353 pEndpoint->CreditDist.TxCreditsToDist += pRpt->Credits; 354 /* flag that we have to do the distribution */ 355 doDist = TRUE; 356 } 357 358 totalCredits += pRpt->Credits; 359 } 360 361 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, (" Report indicated %d credits to distribute \n", totalCredits)); 362 363 if (doDist) { 364 /* this was a credit return based on a completed send operations 365 * note, this is done with the lock held */ 366 DO_DISTRIBUTION(target, 367 HTC_CREDIT_DIST_SEND_COMPLETE, 368 "Send Complete", 369 target->EpCreditDistributionListHead->pNext); 370 } 371 372 UNLOCK_HTC_TX(target); 373 374 if (totalCredits) { 375 HTCCheckEndpointTxQueues(target); 376 } 377 378 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, ("-HTCProcessCreditRpt \n")); 379} 380 381/* flush endpoint TX queue */ 382static void HTCFlushEndpointTX(HTC_TARGET *target, HTC_ENDPOINT *pEndpoint, HTC_TX_TAG Tag) 383{ 384 HTC_PACKET *pPacket; 385 HTC_PACKET_QUEUE discardQueue; 386 387 /* initialize the discard queue */ 388 INIT_HTC_PACKET_QUEUE(&discardQueue); 389 390 LOCK_HTC_TX(target); 391 392 /* interate from the front of the TX queue and flush out packets */ 393 ITERATE_OVER_LIST_ALLOW_REMOVE(&pEndpoint->TxQueue, pPacket, HTC_PACKET, ListLink) { 394 395 /* check for removal */ 396 if ((HTC_TX_PACKET_TAG_ALL == Tag) || (Tag == pPacket->PktInfo.AsTx.Tag)) { 397 /* remove from queue */ 398 HTC_PACKET_REMOVE(pPacket); 399 /* add it to the discard pile */ 400 HTC_PACKET_ENQUEUE(&discardQueue, pPacket); 401 pEndpoint->CurrentTxQueueDepth--; 402 } 403 404 } ITERATE_END; 405 406 UNLOCK_HTC_TX(target); 407 408 /* empty the discard queue */ 409 while (1) { 410 pPacket = HTC_PACKET_DEQUEUE(&discardQueue); 411 if (NULL == pPacket) { 412 break; 413 } 414 pPacket->Status = A_ECANCELED; 415 AR_DEBUG_PRINTF(ATH_DEBUG_TRC, (" Flushing TX packet:0x%X, length:%d, ep:%d tag:0x%X \n", 416 (A_UINT32)pPacket, pPacket->ActualLength, pPacket->Endpoint, pPacket->PktInfo.AsTx.Tag)); 417 DO_EP_TX_COMPLETION(pEndpoint,pPacket); 418 } 419 420} 421 422void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST *pEPDist) 423{ 424#ifdef DEBUG 425 HTC_ENDPOINT *pEndpoint = (HTC_ENDPOINT *)pEPDist->pHTCReserved; 426#endif 427 428 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("--- EP : %d ServiceID: 0x%X --------------\n", 429 pEPDist->Endpoint, pEPDist->ServiceID)); 430 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" this:0x%X next:0x%X prev:0x%X\n", 431 (A_UINT32)pEPDist, (A_UINT32)pEPDist->pNext, (A_UINT32)pEPDist->pPrev)); 432 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" DistFlags : 0x%X \n", pEPDist->DistFlags)); 433 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsNorm : %d \n", pEPDist->TxCreditsNorm)); 434 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsMin : %d \n", pEPDist->TxCreditsMin)); 435 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCredits : %d \n", pEPDist->TxCredits)); 436 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsAssigned : %d \n", pEPDist->TxCreditsAssigned)); 437 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsSeek : %d \n", pEPDist->TxCreditsSeek)); 438 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditSize : %d \n", pEPDist->TxCreditSize)); 439 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsPerMaxMsg : %d \n", pEPDist->TxCreditsPerMaxMsg)); 440 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxCreditsToDist : %d \n", pEPDist->TxCreditsToDist)); 441 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, (" TxQueueDepth : %d \n", pEndpoint->CurrentTxQueueDepth)); 442 AR_DEBUG_PRINTF(ATH_DEBUG_ANY, ("----------------------------------------------------\n")); 443} 444 445void DumpCreditDistStates(HTC_TARGET *target) 446{ 447 HTC_ENDPOINT_CREDIT_DIST *pEPList = target->EpCreditDistributionListHead; 448 449 while (pEPList != NULL) { 450 DumpCreditDist(pEPList); 451 pEPList = pEPList->pNext; 452 } 453 454 if (target->DistributeCredits != NULL) { 455 DO_DISTRIBUTION(target, 456 HTC_DUMP_CREDIT_STATE, 457 "Dump State", 458 NULL); 459 } 460} 461 462/* flush all send packets from all endpoint queues */ 463void HTCFlushSendPkts(HTC_TARGET *target) 464{ 465 HTC_ENDPOINT *pEndpoint; 466 int i; 467 468 DumpCreditDistStates(target); 469 470 for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { 471 pEndpoint = &target->EndPoint[i]; 472 if (pEndpoint->ServiceID == 0) { 473 /* not in use.. */ 474 continue; 475 } 476 HTCFlushEndpointTX(target,pEndpoint,HTC_TX_PACKET_TAG_ALL); 477 } 478 479} 480 481/* HTC API to flush an endpoint's TX queue*/ 482void HTCFlushEndpoint(HTC_HANDLE HTCHandle, HTC_ENDPOINT_ID Endpoint, HTC_TX_TAG Tag) 483{ 484 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 485 HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; 486 487 if (pEndpoint->ServiceID == 0) { 488 AR_DEBUG_ASSERT(FALSE); 489 /* not in use.. */ 490 return; 491 } 492 493 HTCFlushEndpointTX(target, pEndpoint, Tag); 494} 495 496/* HTC API to indicate activity to the credit distribution function */ 497void HTCIndicateActivityChange(HTC_HANDLE HTCHandle, 498 HTC_ENDPOINT_ID Endpoint, 499 A_BOOL Active) 500{ 501 HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle); 502 HTC_ENDPOINT *pEndpoint = &target->EndPoint[Endpoint]; 503 A_BOOL doDist = FALSE; 504 505 if (pEndpoint->ServiceID == 0) { 506 AR_DEBUG_ASSERT(FALSE); 507 /* not in use.. */ 508 return; 509 } 510 511 LOCK_HTC_TX(target); 512 513 if (Active) { 514 if (!(pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE)) { 515 /* mark active now */ 516 pEndpoint->CreditDist.DistFlags |= HTC_EP_ACTIVE; 517 doDist = TRUE; 518 } 519 } else { 520 if (pEndpoint->CreditDist.DistFlags & HTC_EP_ACTIVE) { 521 /* mark inactive now */ 522 pEndpoint->CreditDist.DistFlags &= ~HTC_EP_ACTIVE; 523 doDist = TRUE; 524 } 525 } 526 527 if (doDist) { 528 /* do distribution again based on activity change 529 * note, this is done with the lock held */ 530 DO_DISTRIBUTION(target, 531 HTC_CREDIT_DIST_ACTIVITY_CHANGE, 532 "Activity Change", 533 target->EpCreditDistributionListHead->pNext); 534 } 535 536 UNLOCK_HTC_TX(target); 537 538} 539