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