3 * Copyright (c) 2007 Atheros Communications Inc.
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;
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.
20 #include "htc_internal.h"
22 #define DO_EP_TX_COMPLETION(ep,p) \
24 (p)->Completion = NULL; \
25 (ep)->EpCallBacks.EpTxComplete((ep)->EpCallBacks.pContext,(p)); \
29 /* call the distribute credits callback with the distribution */
30 #define DO_DISTRIBUTION(t,reason,description,pList) \
32 AR_DEBUG_PRINTF(ATH_DEBUG_SEND, \
33 (" calling distribute function (%s) (dfn:0x%X, ctxt:0x%X, dist:0x%X) \n", \
35 (A_UINT32)(t)->DistributeCredits, \
36 (A_UINT32)(t)->pCredDistContext, \
38 (t)->DistributeCredits((t)->pCredDistContext, \
43 /* our internal send packet completion handler when packets are submited to the AR6K device
45 static void HTCSendPktCompletionHandler(void *Context
, HTC_PACKET
*pPacket
)
47 HTC_TARGET
*target
= (HTC_TARGET
*)Context
;
48 HTC_ENDPOINT
*pEndpoint
= &target
->EndPoint
[pPacket
->Endpoint
];
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
));
56 /* first, fixup the head room we allocated */
57 pPacket
->pBuffer
+= HTC_HDR_LENGTH
;
59 DO_EP_TX_COMPLETION(pEndpoint
,pPacket
);
62 A_STATUS
HTCIssueSend(HTC_TARGET
*target
, HTC_PACKET
*pPacket
, A_UINT8 SendFlags
)
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
);
76 if (pPacket
->Completion
== NULL
) {
77 /* mark that this request was synchronously issued */
81 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
,
82 ("+-HTCIssueSend: transmit length : %d (%s) \n",
83 pPacket
->ActualLength
+ HTC_HDR_LENGTH
,
84 sync
? "SYNC" : "ASYNC" ));
86 /* send message to device */
87 status
= DevSendPacket(&target
->Device
,
89 pPacket
->ActualLength
+ HTC_HDR_LENGTH
);
92 /* use local sync variable. If this was issued asynchronously, pPacket is no longer
94 pPacket
->pBuffer
+= HTC_HDR_LENGTH
;
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 */
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. */
105 static void HTCTrySend(HTC_TARGET
*target
,
106 HTC_PACKET
*pPacketToSend
,
110 HTC_ENDPOINT
*pEndpoint
;
114 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
,("+HTCTrySend (pPkt:0x%X)\n",(A_UINT32
)pPacketToSend
));
116 pEndpoint
= &target
->EndPoint
[ep
];
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
++;
127 /* now drain the TX queue for transmission as long as we have enough
132 if (HTC_QUEUE_EMPTY(&pEndpoint
->TxQueue
)) {
133 /* nothing in the queue */
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
));
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
;
149 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
,(" Creds Required:%d Got:%d\n",
150 creditsRequired
, pEndpoint
->CreditDist
.TxCredits
));
152 if (pEndpoint
->CreditDist
.TxCredits
< creditsRequired
) {
154 /* not enough credits */
156 if (pPacket
->Endpoint
== ENDPOINT_0
) {
157 /* leave it in the queue */
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
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
,
171 &pEndpoint
->CreditDist
);
173 pEndpoint
->CreditDist
.TxCreditsSeek
= 0;
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",
185 pEndpoint
->CreditDist
.TxCredits
-= creditsRequired
;
186 INC_HTC_EP_STAT(pEndpoint
, TxCreditsConsummed
, creditsRequired
);
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"));
195 /* now we can fully dequeue */
196 pPacket
= HTC_PACKET_DEQUEUE(&pEndpoint
->TxQueue
);
197 pEndpoint
->CurrentTxQueueDepth
--;
199 INC_HTC_EP_STAT(pEndpoint
, TxIssued
, 1);
201 UNLOCK_HTC_TX(target
);
203 HTCIssueSend(target
, pPacket
, sendFlags
);
207 /* go back and check for more messages */
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
);
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
);
226 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
,("-HTCTrySend: \n"));
229 /* HTC API - HTCSendPkt */
230 A_STATUS
HTCSendPkt(HTC_HANDLE HTCHandle
, HTC_PACKET
*pPacket
)
232 HTC_TARGET
*target
= GET_HTC_TARGET_FROM_HANDLE(HTCHandle
);
233 HTC_ENDPOINT
*pEndpoint
;
235 A_STATUS status
= A_OK
;
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
));
241 ep
= pPacket
->Endpoint
;
242 AR_DEBUG_ASSERT(ep
< ENDPOINT_MAX
);
243 pEndpoint
= &target
->EndPoint
[ep
];
247 if (HTC_STOPPING(target
)) {
248 status
= A_ECANCELED
;
249 pPacket
->Status
= status
;
250 DO_EP_TX_COMPLETION(pEndpoint
,pPacket
);
253 /* everything sent through this interface is asynchronous */
254 /* fill in HTC completion routines */
255 pPacket
->Completion
= HTCSendPktCompletionHandler
;
256 pPacket
->pContext
= target
;
258 HTCTrySend(target
, pPacket
, ep
);
262 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, ("-HTCSendPkt \n"));
268 /* check TX queues to drain because of credit distribution update */
269 static INLINE
void HTCCheckEndpointTxQueues(HTC_TARGET
*target
)
271 HTC_ENDPOINT
*pEndpoint
;
272 HTC_ENDPOINT_CREDIT_DIST
*pDistItem
;
274 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, ("+HTCCheckEndpointTxQueues \n"));
275 pDistItem
= target
->EpCreditDistributionListHead
;
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
;
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
291 HTCTrySend(target
, NULL
, pDistItem
->Endpoint
);
294 pDistItem
= pDistItem
->pNext
;
297 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, ("-HTCCheckEndpointTxQueues \n"));
300 /* process credit reports and call distribution function */
301 void HTCProcessCreditRpt(HTC_TARGET
*target
, HTC_CREDIT_REPORT
*pRpt
, int NumEntries
, HTC_ENDPOINT_ID FromEndpoint
)
304 HTC_ENDPOINT
*pEndpoint
;
305 int totalCredits
= 0;
306 A_BOOL doDist
= FALSE
;
308 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, ("+HTCProcessCreditRpt, Credit Report Entries:%d \n", NumEntries
));
310 /* lock out TX while we update credits */
313 for (i
= 0; i
< NumEntries
; i
++, pRpt
++) {
314 if (pRpt
->EndpointID
>= ENDPOINT_MAX
) {
315 AR_DEBUG_ASSERT(FALSE
);
319 pEndpoint
= &target
->EndPoint
[pRpt
->EndpointID
];
321 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, (" Endpoint %d got %d credits \n",
322 pRpt
->EndpointID
, pRpt
->Credits
));
325 #ifdef HTC_EP_STAT_PROFILING
327 INC_HTC_EP_STAT(pEndpoint
, TxCreditRpts
, 1);
328 INC_HTC_EP_STAT(pEndpoint
, TxCreditsReturned
, pRpt
->Credits
);
330 if (FromEndpoint
== pRpt
->EndpointID
) {
331 /* this credit report arrived on the same endpoint indicating it arrived in an RX
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);
340 /* arrived on another endpoint */
341 INC_HTC_EP_STAT(pEndpoint
, TxCreditsFromOther
, pRpt
->Credits
);
342 INC_HTC_EP_STAT(pEndpoint
, TxCreditRptsFromOther
, 1);
347 if (ENDPOINT_0
== pRpt
->EndpointID
) {
348 /* always give endpoint 0 credits back */
349 pEndpoint
->CreditDist
.TxCredits
+= pRpt
->Credits
;
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 */
358 totalCredits
+= pRpt
->Credits
;
361 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, (" Report indicated %d credits to distribute \n", totalCredits
));
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
,
369 target
->EpCreditDistributionListHead
->pNext
);
372 UNLOCK_HTC_TX(target
);
375 HTCCheckEndpointTxQueues(target
);
378 AR_DEBUG_PRINTF(ATH_DEBUG_SEND
, ("-HTCProcessCreditRpt \n"));
381 /* flush endpoint TX queue */
382 static void HTCFlushEndpointTX(HTC_TARGET
*target
, HTC_ENDPOINT
*pEndpoint
, HTC_TX_TAG Tag
)
385 HTC_PACKET_QUEUE discardQueue
;
387 /* initialize the discard queue */
388 INIT_HTC_PACKET_QUEUE(&discardQueue
);
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
) {
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
--;
406 UNLOCK_HTC_TX(target
);
408 /* empty the discard queue */
410 pPacket
= HTC_PACKET_DEQUEUE(&discardQueue
);
411 if (NULL
== pPacket
) {
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
);
422 void DumpCreditDist(HTC_ENDPOINT_CREDIT_DIST
*pEPDist
)
425 HTC_ENDPOINT
*pEndpoint
= (HTC_ENDPOINT
*)pEPDist
->pHTCReserved
;
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"));
445 void DumpCreditDistStates(HTC_TARGET
*target
)
447 HTC_ENDPOINT_CREDIT_DIST
*pEPList
= target
->EpCreditDistributionListHead
;
449 while (pEPList
!= NULL
) {
450 DumpCreditDist(pEPList
);
451 pEPList
= pEPList
->pNext
;
454 if (target
->DistributeCredits
!= NULL
) {
455 DO_DISTRIBUTION(target
,
456 HTC_DUMP_CREDIT_STATE
,
462 /* flush all send packets from all endpoint queues */
463 void HTCFlushSendPkts(HTC_TARGET
*target
)
465 HTC_ENDPOINT
*pEndpoint
;
468 DumpCreditDistStates(target
);
470 for (i
= ENDPOINT_0
; i
< ENDPOINT_MAX
; i
++) {
471 pEndpoint
= &target
->EndPoint
[i
];
472 if (pEndpoint
->ServiceID
== 0) {
476 HTCFlushEndpointTX(target
,pEndpoint
,HTC_TX_PACKET_TAG_ALL
);
481 /* HTC API to flush an endpoint's TX queue*/
482 void HTCFlushEndpoint(HTC_HANDLE HTCHandle
, HTC_ENDPOINT_ID Endpoint
, HTC_TX_TAG Tag
)
484 HTC_TARGET
*target
= GET_HTC_TARGET_FROM_HANDLE(HTCHandle
);
485 HTC_ENDPOINT
*pEndpoint
= &target
->EndPoint
[Endpoint
];
487 if (pEndpoint
->ServiceID
== 0) {
488 AR_DEBUG_ASSERT(FALSE
);
493 HTCFlushEndpointTX(target
, pEndpoint
, Tag
);
496 /* HTC API to indicate activity to the credit distribution function */
497 void HTCIndicateActivityChange(HTC_HANDLE HTCHandle
,
498 HTC_ENDPOINT_ID Endpoint
,
501 HTC_TARGET
*target
= GET_HTC_TARGET_FROM_HANDLE(HTCHandle
);
502 HTC_ENDPOINT
*pEndpoint
= &target
->EndPoint
[Endpoint
];
503 A_BOOL doDist
= FALSE
;
505 if (pEndpoint
->ServiceID
== 0) {
506 AR_DEBUG_ASSERT(FALSE
);
514 if (!(pEndpoint
->CreditDist
.DistFlags
& HTC_EP_ACTIVE
)) {
515 /* mark active now */
516 pEndpoint
->CreditDist
.DistFlags
|= HTC_EP_ACTIVE
;
520 if (pEndpoint
->CreditDist
.DistFlags
& HTC_EP_ACTIVE
) {
521 /* mark inactive now */
522 pEndpoint
->CreditDist
.DistFlags
&= ~HTC_EP_ACTIVE
;
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
,
533 target
->EpCreditDistributionListHead
->pNext
);
536 UNLOCK_HTC_TX(target
);