• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * DHD PROP_TXSTATUS Module.
3  *
4  * Copyright (C) 1999-2019, Broadcom.
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions
16  * of the license of that module.  An independent module is a module which is
17  * not derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  *
25  * <<Broadcom-WL-IPTag/Open:>>
26  *
27  * $Id: dhd_wlfc.c 743239 2018-01-25 08:33:18Z $
28  *
29  */
30 
31 #include <typedefs.h>
32 #include <osl.h>
33 
34 #include <bcmutils.h>
35 #include <bcmendian.h>
36 
37 #include <dngl_stats.h>
38 #include <dhd.h>
39 
40 #include <dhd_bus.h>
41 
42 #include <dhd_dbg.h>
43 #include <dhd_config.h>
44 #include <wl_android.h>
45 
46 #ifdef PROP_TXSTATUS /* a form of flow control between host and dongle */
47 #include <wlfc_proto.h>
48 #include <dhd_wlfc.h>
49 #endif // endif
50 
51 #ifdef DHDTCPACK_SUPPRESS
52 #include <dhd_ip.h>
53 #endif /* DHDTCPACK_SUPPRESS */
54 
55 /*
56  * wlfc naming and lock rules:
57  *
58  * 1. Private functions name like _dhd_wlfc_XXX, declared as static and avoid
59  * wlfc lock operation.
60  * 2. Public functions name like dhd_wlfc_XXX, use wlfc lock if needed.
61  * 3. Non-Proptxstatus module call public functions only and avoid wlfc lock
62  * operation.
63  *
64  */
65 
66 #if defined(DHD_WLFC_THREAD)
67 #define WLFC_THREAD_QUICK_RETRY_WAIT_MS 10 /* 10 msec */
68 #define WLFC_THREAD_RETRY_WAIT_MS 10000    /* 10 sec */
69 #endif                                     /* defined (DHD_WLFC_THREAD) */
70 
71 #ifdef PROP_TXSTATUS
72 
73 #define DHD_WLFC_QMON_COMPLETE(entry)
74 
75 /** reordering related */
76 
77 #if defined(DHD_WLFC_THREAD)
_dhd_wlfc_thread_wakeup(dhd_pub_t * dhdp)78 static void _dhd_wlfc_thread_wakeup(dhd_pub_t *dhdp)
79 {
80     dhdp->wlfc_thread_go = TRUE;
81     wake_up_interruptible(&dhdp->wlfc_wqhead);
82 }
83 #endif /* DHD_WLFC_THREAD */
84 
_dhd_wlfc_adjusted_seq(void * p,uint8 current_seq)85 static uint16 _dhd_wlfc_adjusted_seq(void *p, uint8 current_seq)
86 {
87     uint16 seq;
88 
89     if (!p) {
90         return 0xffff;
91     }
92 
93     seq = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
94     if (seq < current_seq) {
95         /* wrap around */
96         seq += 0x100;
97     }
98 
99     return seq;
100 }
101 
102 /**
103  * Enqueue a caller supplied packet on a caller supplied precedence queue,
104  * optionally reorder suppressed packets.
105  *    @param[in] pq       caller supplied packet queue to enqueue the packet on
106  *    @param[in] prec     precedence of the to-be-queued packet
107  *    @param[in] p        transmit packet to enqueue
108  *    @param[in] qHead    if TRUE, enqueue to head instead of tail. Used to
109  * maintain d11 seq order.
110  *    @param[in] current_seq
111  *    @param[in] reOrder  reOrder on odd precedence (=suppress queue)
112  */
_dhd_wlfc_prec_enque(struct pktq * pq,int prec,void * p,bool qHead,uint8 current_seq,bool reOrder)113 static void _dhd_wlfc_prec_enque(struct pktq *pq, int prec, void *p, bool qHead,
114                                  uint8 current_seq, bool reOrder)
115 {
116     struct pktq_prec *q;
117     uint16 seq, seq2;
118     void *p2, *p2_prev;
119 
120     if (!p) {
121         return;
122     }
123 
124     ASSERT(prec >= 0 && prec < pq->num_prec);
125     ASSERT(PKTLINK(p) == NULL); /* queueing chains not allowed */
126 
127     ASSERT(!pktq_full(pq));
128     ASSERT(!pktqprec_full(pq, prec));
129 
130     q = &pq->q[prec];
131 
132     if (q->head == NULL) {
133         /* empty queue */
134         q->head = p;
135         q->tail = p;
136     } else {
137         if (reOrder && (prec & 1)) {
138             seq = _dhd_wlfc_adjusted_seq(p, current_seq);
139             p2 = qHead ? q->head : q->tail;
140             seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
141             if ((qHead && ((seq + 1) > seq2)) ||
142                 (!qHead && ((seq2 + 1) > seq))) {
143                 /* need reorder */
144                 p2 = q->head;
145                 p2_prev = NULL;
146                 seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
147 
148                 while (seq > seq2) {
149                     p2_prev = p2;
150                     p2 = PKTLINK(p2);
151                     if (!p2) {
152                         break;
153                     }
154                     seq2 = _dhd_wlfc_adjusted_seq(p2, current_seq);
155                 }
156 
157                 if (p2_prev == NULL) {
158                     /* insert head */
159                     PKTSETLINK(p, q->head);
160                     q->head = p;
161                 } else if (p2 == NULL) {
162                     /* insert tail */
163                     PKTSETLINK(p2_prev, p);
164                     q->tail = p;
165                 } else {
166                     /* insert after p2_prev */
167                     PKTSETLINK(p, PKTLINK(p2_prev));
168                     PKTSETLINK(p2_prev, p);
169                 }
170                 goto exit;
171             }
172         }
173 
174         if (qHead) {
175             PKTSETLINK(p, q->head);
176             q->head = p;
177         } else {
178             PKTSETLINK(q->tail, p);
179             q->tail = p;
180         }
181     }
182 
183 exit:
184 
185     q->n_pkts++;
186     pq->n_pkts_tot++;
187 
188     if (pq->hi_prec < prec) {
189         pq->hi_prec = (uint8)prec;
190     }
191 } /* _dhd_wlfc_prec_enque */
192 
193 /**
194  * Create a place to store all packet pointers submitted to the firmware until a
195  * status comes back, suppress or otherwise.
196  *
197  * hang-er: noun, a contrivance on which things are hung, as a hook.
198  */
199 /** @deprecated soon */
_dhd_wlfc_hanger_create(dhd_pub_t * dhd,int max_items)200 static void *_dhd_wlfc_hanger_create(dhd_pub_t *dhd, int max_items)
201 {
202     int i;
203     wlfc_hanger_t *hanger;
204 
205     /* allow only up to a specific size for now */
206     ASSERT(max_items == WLFC_HANGER_MAXITEMS);
207 
208     if ((hanger = (wlfc_hanger_t *)DHD_OS_PREALLOC(
209              dhd, DHD_PREALLOC_DHD_WLFC_HANGER, WLFC_HANGER_SIZE(max_items))) ==
210         NULL) {
211         return NULL;
212     }
213     memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
214     hanger->max_items = max_items;
215 
216     for (i = 0; i < hanger->max_items; i++) {
217         hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
218     }
219     return hanger;
220 }
221 
222 /** @deprecated soon */
_dhd_wlfc_hanger_delete(dhd_pub_t * dhd,void * hanger)223 static int _dhd_wlfc_hanger_delete(dhd_pub_t *dhd, void *hanger)
224 {
225     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
226 
227     if (h) {
228         DHD_OS_PREFREE(dhd, h, WLFC_HANGER_SIZE(h->max_items));
229         return BCME_OK;
230     }
231     return BCME_BADARG;
232 }
233 
234 /** @deprecated soon */
_dhd_wlfc_hanger_get_free_slot(void * hanger)235 static uint16 _dhd_wlfc_hanger_get_free_slot(void *hanger)
236 {
237     uint32 i;
238     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
239 
240     if (h) {
241         i = h->slot_pos + 1;
242         if (i == h->max_items) {
243             i = 0;
244         }
245         while (i != h->slot_pos) {
246             if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
247                 h->slot_pos = i;
248                 return (uint16)i;
249             }
250             i++;
251             if (i == h->max_items) {
252                 i = 0;
253             }
254         }
255         h->failed_slotfind++;
256     }
257     return WLFC_HANGER_MAXITEMS;
258 }
259 
260 /** @deprecated soon */
_dhd_wlfc_hanger_get_genbit(void * hanger,void * pkt,uint32 slot_id,int * gen)261 static int _dhd_wlfc_hanger_get_genbit(void *hanger, void *pkt, uint32 slot_id,
262                                        int *gen)
263 {
264     int rc = BCME_OK;
265     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
266 
267     *gen = 0xff;
268 
269     /* this packet was not pushed at the time it went to the firmware */
270     if (slot_id == WLFC_HANGER_MAXITEMS) {
271         return BCME_NOTFOUND;
272     }
273 
274     if (h) {
275         if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
276             *gen = h->items[slot_id].gen;
277         } else {
278             DHD_ERROR(
279                 ("Error: %s():%d item not used\n", __FUNCTION__, __LINE__));
280             rc = BCME_NOTFOUND;
281         }
282     } else {
283         rc = BCME_BADARG;
284     }
285 
286     return rc;
287 }
288 
289 /** @deprecated soon */
_dhd_wlfc_hanger_pushpkt(void * hanger,void * pkt,uint32 slot_id)290 static int _dhd_wlfc_hanger_pushpkt(void *hanger, void *pkt, uint32 slot_id)
291 {
292     int rc = BCME_OK;
293     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
294 
295     if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
296         if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
297             h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
298             h->items[slot_id].pkt = pkt;
299             h->items[slot_id].pkt_state = 0;
300             h->items[slot_id].pkt_txstatus = 0;
301             h->pushed++;
302         } else {
303             h->failed_to_push++;
304             rc = BCME_NOTFOUND;
305         }
306     } else {
307         rc = BCME_BADARG;
308     }
309 
310     return rc;
311 }
312 
313 /** @deprecated soon */
_dhd_wlfc_hanger_poppkt(void * hanger,uint32 slot_id,void ** pktout,bool remove_from_hanger)314 static int _dhd_wlfc_hanger_poppkt(void *hanger, uint32 slot_id, void **pktout,
315                                    bool remove_from_hanger)
316 {
317     int rc = BCME_OK;
318     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
319 
320     *pktout = NULL;
321 
322     /* this packet was not pushed at the time it went to the firmware */
323     if (slot_id == WLFC_HANGER_MAXITEMS) {
324         return BCME_NOTFOUND;
325     }
326 
327     if (h) {
328         if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
329             *pktout = h->items[slot_id].pkt;
330             if (remove_from_hanger) {
331                 h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_FREE;
332                 h->items[slot_id].pkt = NULL;
333                 h->items[slot_id].gen = 0xff;
334                 h->items[slot_id].identifier = 0;
335                 h->popped++;
336             }
337         } else {
338             h->failed_to_pop++;
339             rc = BCME_NOTFOUND;
340         }
341     } else {
342         rc = BCME_BADARG;
343     }
344 
345     return rc;
346 }
347 
348 /** @deprecated soon */
_dhd_wlfc_hanger_mark_suppressed(void * hanger,uint32 slot_id,uint8 gen)349 static int _dhd_wlfc_hanger_mark_suppressed(void *hanger, uint32 slot_id,
350                                             uint8 gen)
351 {
352     int rc = BCME_OK;
353     wlfc_hanger_t *h = (wlfc_hanger_t *)hanger;
354 
355     /* this packet was not pushed at the time it went to the firmware */
356     if (slot_id == WLFC_HANGER_MAXITEMS) {
357         return BCME_NOTFOUND;
358     }
359     if (h) {
360         h->items[slot_id].gen = gen;
361         if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
362             h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
363         } else {
364             rc = BCME_BADARG;
365         }
366     } else {
367         rc = BCME_BADARG;
368     }
369 
370     return rc;
371 }
372 
373 /** remove reference of specific packet in hanger */
374 /** @deprecated soon */
_dhd_wlfc_hanger_remove_reference(wlfc_hanger_t * h,void * pkt)375 static bool _dhd_wlfc_hanger_remove_reference(wlfc_hanger_t *h, void *pkt)
376 {
377     int i;
378 
379     if (!h || !pkt) {
380         return FALSE;
381     }
382 
383     i = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
384     if ((i < h->max_items) && (pkt == h->items[i].pkt)) {
385         if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
386             h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
387             h->items[i].pkt = NULL;
388             h->items[i].gen = 0xff;
389             h->items[i].identifier = 0;
390             return TRUE;
391         } else {
392             DHD_ERROR(("Error: %s():%d item not suppressed\n", __FUNCTION__,
393                        __LINE__));
394         }
395     }
396 
397     return FALSE;
398 }
399 
400 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
_dhd_wlfc_enque_afq(athost_wl_status_info_t * ctx,void * p)401 static int _dhd_wlfc_enque_afq(athost_wl_status_info_t *ctx, void *p)
402 {
403     wlfc_mac_descriptor_t *entry;
404     uint16 entry_idx = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
405     uint8 prec = DHD_PKTTAG_FIFO(PKTTAG(p));
406 
407     if (entry_idx < WLFC_MAC_DESC_TABLE_SIZE) {
408         entry = &ctx->destination_entries.nodes[entry_idx];
409     } else if (entry_idx < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM)) {
410         entry = &ctx->destination_entries
411                      .interfaces[entry_idx - WLFC_MAC_DESC_TABLE_SIZE];
412     } else {
413         entry = &ctx->destination_entries.other;
414     }
415 
416     pktq_penq(&entry->afq, prec, p);
417 
418     return BCME_OK;
419 }
420 
421 /** afq = At Firmware Queue, queue containing packets pending in the dongle */
_dhd_wlfc_deque_afq(athost_wl_status_info_t * ctx,uint16 hslot,uint8 hcnt,uint8 prec,void ** pktout)422 static int _dhd_wlfc_deque_afq(athost_wl_status_info_t *ctx, uint16 hslot,
423                                uint8 hcnt, uint8 prec, void **pktout)
424 {
425     wlfc_mac_descriptor_t *entry;
426     struct pktq *pq;
427     struct pktq_prec *q;
428     void *p, *b;
429 
430     if (!ctx) {
431         DHD_ERROR(("%s: ctx(%p), pktout(%p)\n", __FUNCTION__, ctx, pktout));
432         return BCME_BADARG;
433     }
434 
435     if (pktout) {
436         *pktout = NULL;
437     }
438 
439     ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
440 
441     if (hslot < WLFC_MAC_DESC_TABLE_SIZE) {
442         entry = &ctx->destination_entries.nodes[hslot];
443     } else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM)) {
444         entry = &ctx->destination_entries
445                      .interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
446     } else {
447         entry = &ctx->destination_entries.other;
448     }
449 
450     pq = &entry->afq;
451 
452     ASSERT(prec < pq->num_prec);
453 
454     q = &pq->q[prec];
455 
456     b = NULL;
457     p = q->head;
458 
459     while (p &&
460            (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
461         b = p;
462         p = PKTLINK(p);
463     }
464 
465     if (p == NULL) {
466         /* none is matched */
467         if (b) {
468             DHD_ERROR(
469                 ("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
470         } else {
471             DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
472         }
473 
474         return BCME_ERROR;
475     }
476 
477     bcm_pkt_validate_chk(p);
478 
479     if (!b) {
480         /* head packet is matched */
481         if ((q->head = PKTLINK(p)) == NULL) {
482             q->tail = NULL;
483         }
484     } else {
485         /* middle packet is matched */
486         DHD_INFO(
487             ("%s: out of order, seq(%d), head_seq(%d)\n", __FUNCTION__, hcnt,
488              WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(q->head)))));
489         ctx->stats.ooo_pkts[prec]++;
490         PKTSETLINK(b, PKTLINK(p));
491         if (PKTLINK(p) == NULL) {
492             q->tail = b;
493         }
494     }
495 
496     q->n_pkts--;
497     pq->n_pkts_tot--;
498 
499 #ifdef WL_TXQ_STALL
500     q->dequeue_count++;
501 #endif // endif
502 
503     PKTSETLINK(p, NULL);
504 
505     if (pktout) {
506         *pktout = p;
507     }
508 
509     return BCME_OK;
510 } /* _dhd_wlfc_deque_afq */
511 
512 /**
513  * Flow control information piggy backs on packets, in the form of one or more
514  * TLVs. This function pushes one or more TLVs onto a packet that is going to be
515  * sent towards the dongle.
516  *
517  *     @param[in]     ctx
518  *     @param[in/out] packet
519  *     @param[in]     tim_signal TRUE if parameter 'tim_bmp' is valid
520  *     @param[in]     tim_bmp
521  *     @param[in]     mac_handle
522  *     @param[in]     htodtag
523  *     @param[in]     htodseq d11 seqno for seqno reuse, only used if 'seq
524  * reuse' was agreed upon earlier between host and firmware.
525  *     @param[in]     skip_wlfc_hdr
526  */
_dhd_wlfc_pushheader(athost_wl_status_info_t * ctx,void ** packet,bool tim_signal,uint8 tim_bmp,uint8 mac_handle,uint32 htodtag,uint16 htodseq,bool skip_wlfc_hdr)527 static int _dhd_wlfc_pushheader(athost_wl_status_info_t *ctx, void **packet,
528                                 bool tim_signal, uint8 tim_bmp,
529                                 uint8 mac_handle, uint32 htodtag,
530                                 uint16 htodseq, bool skip_wlfc_hdr)
531 {
532     uint32 wl_pktinfo = 0;
533     uint8 *wlh;
534     uint8 dataOffset = 0;
535     uint8 fillers;
536     uint8 tim_signal_len = 0;
537     dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
538 
539     struct bdc_header *h;
540     void *p = *packet;
541 
542     if (skip_wlfc_hdr) {
543         goto push_bdc_hdr;
544     }
545 
546     if (tim_signal) {
547         tim_signal_len = TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
548     }
549 
550     /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
551     dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + TLV_HDR_LEN + tim_signal_len;
552     if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
553         dataOffset += WLFC_CTL_VALUE_LEN_SEQ;
554     }
555 
556     fillers = ROUNDUP(dataOffset, 0x4) - dataOffset;
557     dataOffset += fillers;
558 
559     PKTPUSH(ctx->osh, p, dataOffset);
560     wlh = (uint8 *)PKTDATA(ctx->osh, p);
561 
562     wl_pktinfo = htol32(htodtag);
563 
564     wlh[TLV_TAG_OFF] = WLFC_CTL_TYPE_PKTTAG;
565     wlh[TLV_LEN_OFF] = WLFC_CTL_VALUE_LEN_PKTTAG;
566     memcpy(&wlh[TLV_HDR_LEN] /* dst */, &wl_pktinfo, sizeof(uint32));
567 
568     if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
569         uint16 wl_seqinfo = htol16(htodseq);
570         wlh[TLV_LEN_OFF] += WLFC_CTL_VALUE_LEN_SEQ;
571         memcpy(&wlh[TLV_HDR_LEN + WLFC_CTL_VALUE_LEN_PKTTAG], &wl_seqinfo,
572                WLFC_CTL_VALUE_LEN_SEQ);
573     }
574 
575     if (tim_signal_len) {
576         wlh[dataOffset - fillers - tim_signal_len] =
577             WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
578         wlh[dataOffset - fillers - tim_signal_len + 1] =
579             WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
580         wlh[dataOffset - fillers - tim_signal_len + 0x2] = mac_handle;
581         wlh[dataOffset - fillers - tim_signal_len + 0x3] = tim_bmp;
582     }
583     if (fillers) {
584         memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
585     }
586 
587 push_bdc_hdr:
588     PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
589     h = (struct bdc_header *)PKTDATA(ctx->osh, p);
590     h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
591     if (PKTSUMNEEDED(p)) {
592         h->flags |= BDC_FLAG_SUM_NEEDED;
593     }
594 
595     h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
596     h->flags2 = 0;
597     h->dataOffset = dataOffset >> 0x2;
598     BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
599     *packet = p;
600     return BCME_OK;
601 } /* _dhd_wlfc_pushheader */
602 
603 /**
604  * Removes (PULLs) flow control related headers from the caller supplied packet,
605  * is invoked eg when a packet is about to be freed.
606  */
_dhd_wlfc_pullheader(athost_wl_status_info_t * ctx,void * pktbuf)607 static int _dhd_wlfc_pullheader(athost_wl_status_info_t *ctx, void *pktbuf)
608 {
609     struct bdc_header *h;
610 
611     if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
612         DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
613                    PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
614         return BCME_ERROR;
615     }
616     h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
617 
618     /* pull BDC header */
619     PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
620 
621     if (PKTLEN(ctx->osh, pktbuf) < (uint)(h->dataOffset << 0x2)) {
622         DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
623                    PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 0x2)));
624         return BCME_ERROR;
625     }
626 
627     /* pull wl-header */
628     PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 0x2));
629     return BCME_OK;
630 }
631 
632 /**
633  * @param[in/out] p packet
634  */
635 static wlfc_mac_descriptor_t *
_dhd_wlfc_find_table_entry(athost_wl_status_info_t * ctx,void * p)636 _dhd_wlfc_find_table_entry(athost_wl_status_info_t *ctx, void *p)
637 {
638     int i;
639     wlfc_mac_descriptor_t *table = ctx->destination_entries.nodes;
640     uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
641     uint8 *dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
642     wlfc_mac_descriptor_t *entry = DHD_PKTTAG_ENTRY(PKTTAG(p));
643     int iftype = ctx->destination_entries.interfaces[ifid].iftype;
644 
645     /* saved one exists, return it */
646     if (entry) {
647         return entry;
648     }
649 
650     /* Multicast destination, STA and P2P clients get the interface entry.
651      * STA/GC gets the Mac Entry for TDLS destinations, TDLS destinations
652      * have their own entry.
653      */
654     if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
655          iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
656         (ctx->destination_entries.interfaces[ifid].occupied)) {
657         entry = &ctx->destination_entries.interfaces[ifid];
658     }
659 
660     if (entry && ETHER_ISMULTI(dstn)) {
661         DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
662         return entry;
663     }
664 
665     for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
666         if (table[i].occupied) {
667             if (table[i].interface_id == ifid) {
668                 if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
669                     entry = &table[i];
670                     break;
671                 }
672             }
673         }
674     }
675 
676     if (entry == NULL) {
677         entry = &ctx->destination_entries.other;
678     }
679 
680     DHD_PKTTAG_SET_ENTRY(PKTTAG(p), entry);
681 
682     return entry;
683 } /* _dhd_wlfc_find_table_entry */
684 
685 /**
686  * In case a packet must be dropped (because eg the queues are full), various
687  * tallies have to be be updated. Called from several other functions.
688  *     @param[in] dhdp pointer to public DHD structure
689  *     @param[in] prec precedence of the packet
690  *     @param[in] p    the packet to be dropped
691  *     @param[in] bPktInQ TRUE if packet is part of a queue
692  */
_dhd_wlfc_prec_drop(dhd_pub_t * dhdp,int prec,void * p,bool bPktInQ)693 static int _dhd_wlfc_prec_drop(dhd_pub_t *dhdp, int prec, void *p, bool bPktInQ)
694 {
695     athost_wl_status_info_t *ctx;
696     void *pout = NULL;
697 
698     ASSERT(dhdp && p);
699     if (prec < 0 || prec >= WLFC_PSQ_PREC_COUNT) {
700         ASSERT(0);
701         return BCME_BADARG;
702     }
703 
704     ctx = (athost_wl_status_info_t *)dhdp->wlfc_state;
705 
706     if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
707         /* suppressed queue, need pop from hanger */
708         _dhd_wlfc_hanger_poppkt(
709             ctx->hanger, WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p))),
710             &pout, TRUE);
711         ASSERT(p == pout);
712     }
713 
714     if (!(prec & 1)) {
715 #ifdef DHDTCPACK_SUPPRESS
716         /* pkt in delayed q, so fake push BDC header for
717          * dhd_tcpack_check_xmit() and dhd_txcomplete().
718          */
719         _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE);
720 
721         /* This packet is about to be freed, so remove it from tcp_ack_info_tbl
722          * This must be one of...
723          * 1. A pkt already in delayQ is evicted by another pkt with higher
724          * precedence in _dhd_wlfc_prec_enq_with_drop()
725          * 2. A pkt could not be enqueued to delayQ because it is full,
726          * in _dhd_wlfc_enque_delayq().
727          * 3. A pkt could not be enqueued to delayQ because it is full,
728          * in _dhd_wlfc_rollback_packet_toq().
729          */
730         if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
731             DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
732                        " Stop using it\n",
733                        __FUNCTION__, __LINE__));
734             dhd_tcpack_suppress_set(dhdp, TCPACK_SUP_OFF);
735         }
736 #endif /* DHDTCPACK_SUPPRESS */
737     }
738 
739     if (bPktInQ) {
740         ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec >> 1]--;
741         ctx->pkt_cnt_per_ac[prec >> 1]--;
742         ctx->pkt_cnt_in_psq--;
743     }
744 
745     ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][DHD_PKTTAG_FIFO(PKTTAG(p))]--;
746     ctx->stats.pktout++;
747     ctx->stats.drop_pkts[prec]++;
748 
749     dhd_txcomplete(dhdp, p, FALSE);
750     PKTFREE(ctx->osh, p, TRUE);
751 
752     return 0;
753 } /* _dhd_wlfc_prec_drop */
754 
755 /**
756  * Called when eg the host handed a new packet over to the driver, or when the
757  * dongle reported that a packet could currently not be transmitted
758  * (=suppressed). This function enqueues a transmit packet in the host driver to
759  * be (re)transmitted at a later opportunity.
760  *     @param[in] dhdp pointer to public DHD structure
761  *     @param[in] qHead When TRUE, queue packet at head instead of tail, to
762  * preserve d11 sequence
763  */
_dhd_wlfc_prec_enq_with_drop(dhd_pub_t * dhdp,struct pktq * pq,void * pkt,int prec,bool qHead,uint8 current_seq)764 static bool _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq,
765                                          void *pkt, int prec, bool qHead,
766                                          uint8 current_seq)
767 {
768     void *p = NULL;
769     int eprec = -1; /* precedence to evict from */
770     athost_wl_status_info_t *ctx;
771 
772     ASSERT(dhdp && pq && pkt);
773     ASSERT(prec >= 0 && prec < pq->num_prec);
774 
775     ctx = (athost_wl_status_info_t *)dhdp->wlfc_state;
776 
777     /* Fast case, precedence queue is not full and we are also not
778      * exceeding total queue length
779      */
780     if (!pktqprec_full(pq, prec) && !pktq_full(pq)) {
781         goto exit;
782     }
783 
784     /* Determine precedence from which to evict packet, if any */
785     if (pktqprec_full(pq, prec)) {
786         eprec = prec;
787     } else if (pktq_full(pq)) {
788         p = pktq_peek_tail(pq, &eprec);
789         if (!p) {
790             DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
791             return FALSE;
792         }
793         if ((eprec > prec) || (eprec < 0)) {
794             if (!pktqprec_empty(pq, prec)) {
795                 eprec = prec;
796             } else {
797                 return FALSE;
798             }
799         }
800     }
801 
802     /* Evict if needed */
803     if (eprec >= 0) {
804         /* Detect queueing to unconfigured precedence */
805         ASSERT(!pktqprec_empty(pq, eprec));
806         /* Evict all fragmented frames */
807         dhd_prec_drop_pkts(dhdp, pq, eprec, _dhd_wlfc_prec_drop);
808     }
809 
810 exit:
811     /* Enqueue */
812     _dhd_wlfc_prec_enque(pq, prec, pkt, qHead, current_seq,
813                          WLFC_GET_REORDERSUPP(dhdp->wlfc_mode));
814     ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(pkt))][prec >> 1]++;
815     ctx->pkt_cnt_per_ac[prec >> 1]++;
816     ctx->pkt_cnt_in_psq++;
817 
818     return TRUE;
819 } /* _dhd_wlfc_prec_enq_with_drop */
820 
821 /**
822  * Called during eg the 'committing' of a transmit packet from the OS layer to a
823  * lower layer, in the event that this 'commit' failed.
824  */
_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t * ctx,void * p,ewlfc_packet_state_t pkt_type,uint32 hslot)825 static int _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t *ctx, void *p,
826                                          ewlfc_packet_state_t pkt_type,
827                                          uint32 hslot)
828 {
829     /*
830      * put the packet back to the head of queue
831      * - suppressed packet goes back to suppress sub-queue
832      * - pull out the header, if new or delayed packet
833      *
834      * Note: hslot is used only when header removal is done.
835      */
836     wlfc_mac_descriptor_t *entry;
837     int rc = BCME_OK;
838     int prec, fifo_id;
839 
840     entry = _dhd_wlfc_find_table_entry(ctx, p);
841     prec = DHD_PKTTAG_FIFO(PKTTAG(p));
842     fifo_id = prec << 1;
843     if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) {
844         fifo_id += 1;
845     }
846     if (entry != NULL) {
847         /*
848         if this packet did not count against FIFO credit, it must have
849         taken a requested_credit from the firmware (for pspoll etc.)
850         */
851         if ((prec != AC_COUNT) && !DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
852             entry->requested_credit++;
853         }
854 
855         if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
856             /* decrement sequence count */
857             WLFC_DECR_SEQCOUNT(entry, prec);
858             /* remove header first */
859             rc = _dhd_wlfc_pullheader(ctx, p);
860             if (rc != BCME_OK) {
861                 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
862                 goto exit;
863             }
864         }
865 
866         if (_dhd_wlfc_prec_enq_with_drop(
867                 ctx->dhdp, &entry->psq, p, fifo_id, TRUE,
868                 WLFC_SEQCOUNT(entry, fifo_id >> 1)) == FALSE) {
869             /* enque failed */
870             DHD_ERROR(("Error: %s():%d, fifo_id(%d)\n", __FUNCTION__, __LINE__,
871                        fifo_id));
872             rc = BCME_ERROR;
873         }
874     } else {
875         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
876         rc = BCME_ERROR;
877     }
878 
879 exit:
880     if (rc != BCME_OK) {
881         ctx->stats.rollback_failed++;
882         _dhd_wlfc_prec_drop(ctx->dhdp, fifo_id, p, FALSE);
883     } else {
884         ctx->stats.rollback++;
885     }
886 
887     return rc;
888 } /* _dhd_wlfc_rollback_packet_toq */
889 
890 /** Returns TRUE if host OS -> DHD flow control is allowed on the caller
891  * supplied interface */
_dhd_wlfc_allow_fc(athost_wl_status_info_t * ctx,uint8 ifid)892 static bool _dhd_wlfc_allow_fc(athost_wl_status_info_t *ctx, uint8 ifid)
893 {
894     int prec, ac_traffic = WLFC_NO_TRAFFIC;
895 
896     for (prec = 0; prec < AC_COUNT; prec++) {
897         if (ctx->pkt_cnt_in_drv[ifid][prec] > 0) {
898             if (ac_traffic == WLFC_NO_TRAFFIC) {
899                 ac_traffic = prec + 1;
900             } else if (ac_traffic != (prec + 1)) {
901                 ac_traffic = WLFC_MULTI_TRAFFIC;
902             }
903         }
904     }
905 
906     if (ac_traffic >= 1 && ac_traffic <= AC_COUNT) {
907         /* single AC (BE/BK/VI/VO) in queue */
908         if (ctx->allow_fc) {
909             return TRUE;
910         } else {
911             uint32 delta;
912             uint32 curr_t = OSL_SYSUPTIME();
913 
914             if (ctx->fc_defer_timestamp == 0) {
915                 /* first single ac scenario */
916                 ctx->fc_defer_timestamp = curr_t;
917                 return FALSE;
918             }
919 
920             /* single AC duration, this handles wrap around, e.g. 1 - ~0 = 2. */
921             delta = curr_t - ctx->fc_defer_timestamp;
922             if (delta >= WLFC_FC_DEFER_PERIOD_MS) {
923                 ctx->allow_fc = TRUE;
924             }
925         }
926     } else {
927         /* multiple ACs or BCMC in queue */
928         ctx->allow_fc = FALSE;
929         ctx->fc_defer_timestamp = 0;
930     }
931 
932     return ctx->allow_fc;
933 } /* _dhd_wlfc_allow_fc */
934 
935 /**
936  * Starts or stops the flow of transmit packets from the host OS towards the
937  * DHD, depending on low/high watermarks.
938  */
_dhd_wlfc_flow_control_check(athost_wl_status_info_t * ctx,struct pktq * pq,uint8 if_id)939 static void _dhd_wlfc_flow_control_check(athost_wl_status_info_t *ctx,
940                                          struct pktq *pq, uint8 if_id)
941 {
942     dhd_pub_t *dhdp;
943 
944     ASSERT(ctx);
945 
946     dhdp = (dhd_pub_t *)ctx->dhdp;
947     ASSERT(dhdp);
948 
949     if (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id)) {
950         return;
951     }
952 
953     if ((ctx->hostif_flow_state[if_id] == OFF) &&
954         !_dhd_wlfc_allow_fc(ctx, if_id)) {
955         return;
956     }
957 
958     if ((pq->n_pkts_tot <= WLFC_FLOWCONTROL_LOWATER) &&
959         (ctx->hostif_flow_state[if_id] == ON)) {
960         /* start traffic */
961         ctx->hostif_flow_state[if_id] = OFF;
962         WLFC_DBGMESG(("F"));
963 
964         dhd_txflowcontrol(dhdp, if_id, OFF);
965 
966         ctx->toggle_host_if = 0;
967     }
968 
969     if (pq->n_pkts_tot >= WLFC_FLOWCONTROL_HIWATER &&
970         ctx->hostif_flow_state[if_id] == OFF) {
971         /* stop traffic */
972         ctx->hostif_flow_state[if_id] = ON;
973         WLFC_DBGMESG(("N"));
974 
975         dhd_txflowcontrol(dhdp, if_id, ON);
976 
977         ctx->host_ifidx = if_id;
978         ctx->toggle_host_if = 1;
979     }
980 
981     return;
982 } /* _dhd_wlfc_flow_control_check */
983 
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)984 static int _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t *ctx,
985                                             wlfc_mac_descriptor_t *entry,
986                                             uint8 ta_bmp)
987 {
988     int rc = BCME_OK;
989     void *p = NULL;
990     int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen + 16;
991     dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
992 
993     if (dhdp->proptxstatus_txoff) {
994         rc = BCME_NORESOURCE;
995         return rc;
996     }
997 
998     /* allocate a dummy packet */
999     p = PKTGET(ctx->osh, dummylen, TRUE);
1000     if (p) {
1001         PKTPULL(ctx->osh, p, dummylen);
1002         DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
1003         _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0,
1004                              FALSE);
1005         DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
1006         DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
1007 #ifdef PROP_TXSTATUS_DEBUG
1008         ctx->stats.signal_only_pkts_sent++;
1009 #endif // endif
1010 
1011 #if defined(BCMPCIE)
1012         rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
1013 #else
1014         rc = dhd_bus_txdata(dhdp->bus, p);
1015 #endif // endif
1016         if (rc != BCME_OK) {
1017             _dhd_wlfc_pullheader(ctx, p);
1018             PKTFREE(ctx->osh, p, TRUE);
1019         }
1020     } else {
1021         DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n", __FUNCTION__,
1022                    dummylen));
1023         rc = BCME_NOMEM;
1024         dhdp->tx_pktgetfail++;
1025     }
1026 
1027     return rc;
1028 } /* _dhd_wlfc_send_signalonly_packet */
1029 
1030 /**
1031  * Called on eg receiving 'mac close' indication from dongle. Updates the
1032  * per-MAC administration maintained in caller supplied parameter 'entry'.
1033  *
1034  *    @param[in/out] entry  administration about a remote MAC entity
1035  *    @param[in]     prec   precedence queue for this remote MAC entitity
1036  *
1037  * Return value: TRUE if traffic availability changed
1038  */
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1039 static bool _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t *ctx,
1040                                             wlfc_mac_descriptor_t *entry,
1041                                             int prec)
1042 {
1043     bool rc = FALSE;
1044 
1045     if (entry->state == WLFC_STATE_CLOSE) {
1046         if ((pktqprec_n_pkts(&entry->psq, (prec << 1)) == 0) &&
1047             (pktqprec_n_pkts(&entry->psq, ((prec << 1) + 1)) == 0)) {
1048             /* no packets in both 'normal' and 'suspended' queues */
1049             if (entry->traffic_pending_bmp & NBITVAL(prec)) {
1050                 rc = TRUE;
1051                 entry->traffic_pending_bmp =
1052                     entry->traffic_pending_bmp & ~NBITVAL(prec);
1053             }
1054         } else {
1055             /* packets are queued in host for transmission to dongle */
1056             if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
1057                 rc = TRUE;
1058                 entry->traffic_pending_bmp =
1059                     entry->traffic_pending_bmp | NBITVAL(prec);
1060             }
1061         }
1062     }
1063 
1064     if (rc) {
1065         /* request a TIM update to firmware at the next piggyback opportunity */
1066         if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
1067             entry->send_tim_signal = 1;
1068             _dhd_wlfc_send_signalonly_packet(ctx, entry,
1069                                              entry->traffic_pending_bmp);
1070             entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1071             entry->send_tim_signal = 0;
1072         } else {
1073             rc = FALSE;
1074         }
1075     }
1076 
1077     return rc;
1078 } /* _dhd_wlfc_traffic_pending_check */
1079 
1080 /**
1081  * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the
1082  * firmware. Enqueues the packet to transmit to firmware again at a later
1083  * opportunity.
1084  */
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)1085 static int _dhd_wlfc_enque_suppressed(athost_wl_status_info_t *ctx, int prec,
1086                                       void *p)
1087 {
1088     wlfc_mac_descriptor_t *entry;
1089 
1090     entry = _dhd_wlfc_find_table_entry(ctx, p);
1091     if (entry == NULL) {
1092         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1093         return BCME_NOTFOUND;
1094     }
1095     /*
1096     - suppressed packets go to sub_queue[2*prec + 1] AND
1097     - delayed packets go to sub_queue[2*prec + 0] to ensure
1098     order of delivery.
1099     */
1100     if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p,
1101                                      ((prec << 1) + 1), FALSE,
1102                                      WLFC_SEQCOUNT(entry, prec)) == FALSE) {
1103         ctx->stats.delayq_full_error++;
1104         WLFC_DBGMESG(("s"));
1105         return BCME_ERROR;
1106     }
1107 
1108     /* A packet has been pushed, update traffic availability bitmap, if
1109      * applicable */
1110     _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1111     _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1112     return BCME_OK;
1113 }
1114 
1115 /**
1116  * Called when a transmit packet is about to be 'committed' from the OS layer to
1117  * a lower layer towards the dongle (eg the DBUS layer). Updates wlfc
1118  * administration. May modify packet.
1119  *
1120  *     @param[in/out] ctx    driver specific flow control administration
1121  *     @param[in/out] entry  The remote MAC entity for which the packet is
1122  * destined.
1123  *     @param[in/out] packet Packet to send. This function optionally adds TLVs
1124  * to the packet.
1125  *     @param[in] header_needed True if packet is 'new' to flow control
1126  *     @param[out] slot Handle to container in which the packet was 'parked'
1127  */
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void ** packet,int header_needed,uint32 * slot)1128 static int _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t *ctx,
1129                                       wlfc_mac_descriptor_t *entry,
1130                                       void **packet, int header_needed,
1131                                       uint32 *slot)
1132 {
1133     int rc = BCME_OK;
1134     int hslot = WLFC_HANGER_MAXITEMS;
1135     bool send_tim_update = FALSE;
1136     uint32 htod = 0;
1137     uint16 htodseq = 0;
1138     uint8 free_ctr;
1139     int gen = 0xff;
1140     dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1141     void *p = *packet;
1142 
1143     *slot = hslot;
1144 
1145     if (entry == NULL) {
1146         entry = _dhd_wlfc_find_table_entry(ctx, p);
1147     }
1148 
1149     if (entry == NULL) {
1150         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1151         return BCME_ERROR;
1152     }
1153 
1154     if (entry->send_tim_signal) {
1155         /* sends a traffic indication bitmap to the dongle */
1156         send_tim_update = TRUE;
1157         entry->send_tim_signal = 0;
1158         entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1159     }
1160 
1161     if (header_needed) {
1162         if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1163             hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
1164         } else {
1165             hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1166         }
1167         gen = entry->generation;
1168         free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1169     } else {
1170         if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1171             htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
1172         }
1173 
1174         hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1175 
1176         if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
1177             gen = entry->generation;
1178         } else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1179             gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1180         } else {
1181             _dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
1182         }
1183 
1184         free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1185         /* remove old header */
1186         _dhd_wlfc_pullheader(ctx, p);
1187     }
1188 
1189     if (hslot >= WLFC_HANGER_MAXITEMS) {
1190         DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
1191         return BCME_ERROR;
1192     }
1193 
1194     WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
1195     WL_TXSTATUS_SET_HSLOT(htod, hslot);
1196     WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1197     WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1198     WL_TXSTATUS_SET_GENERATION(htod, gen);
1199     DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1200 
1201     if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1202         /*
1203         Indicate that this packet is being sent in response to an
1204         explicit request from the firmware side.
1205         */
1206         WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1207     } else {
1208         WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1209     }
1210 
1211     rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
1212                               entry->traffic_lastreported_bmp,
1213                               entry->mac_handle, htod, htodseq, FALSE);
1214     if (rc == BCME_OK) {
1215         DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1216 
1217         if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1218             wlfc_hanger_t *h = (wlfc_hanger_t *)(ctx->hanger);
1219             if (header_needed) {
1220                 /*
1221                 a new header was created for this packet.
1222                 push to hanger slot and scrub q. Since bus
1223                 send succeeded, increment seq number as well.
1224                 */
1225                 rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1226                 if (rc == BCME_OK) {
1227 #ifdef PROP_TXSTATUS_DEBUG
1228                     h->items[hslot].push_time = OSL_SYSUPTIME();
1229 #endif // endif
1230                 } else {
1231                     DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
1232                                __FUNCTION__, rc));
1233                 }
1234             } else {
1235                 /* clear hanger state */
1236                 if (((wlfc_hanger_t *)(ctx->hanger))->items[hslot].pkt != p) {
1237                     DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
1238                                __FUNCTION__, p, h->items[hslot].pkt));
1239                 }
1240                 ASSERT(h->items[hslot].pkt == p);
1241                 bcm_object_feature_set(h->items[hslot].pkt,
1242                                        BCM_OBJECT_FEATURE_PKT_STATE, 0);
1243                 h->items[hslot].pkt_state = 0;
1244                 h->items[hslot].pkt_txstatus = 0;
1245                 h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
1246             }
1247         }
1248 
1249         if ((rc == BCME_OK) && header_needed) {
1250             /* increment free running sequence count */
1251             WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1252         }
1253     }
1254     *slot = hslot;
1255     *packet = p;
1256     return rc;
1257 } /* _dhd_wlfc_pretx_pktprocess */
1258 
1259 /**
1260  * A remote wireless mac may be temporarily 'closed' due to power management.
1261  * Returns '1' if remote mac is in the 'open' state, otherwise '0'.
1262  */
_dhd_wlfc_is_destination_open(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1263 static int _dhd_wlfc_is_destination_open(athost_wl_status_info_t *ctx,
1264                                          wlfc_mac_descriptor_t *entry, int prec)
1265 {
1266     wlfc_mac_descriptor_t *interfaces = ctx->destination_entries.interfaces;
1267 
1268     if (entry->interface_id >= WLFC_MAX_IFNUM) {
1269         ASSERT(&ctx->destination_entries.other == entry);
1270         return 1;
1271     }
1272 
1273     if (interfaces[entry->interface_id].iftype == WLC_E_IF_ROLE_P2P_GO) {
1274         /* - destination interface is of type p2p GO.
1275         For a p2pGO interface, if the destination is OPEN but the interface is
1276         CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1277         destination-specific-credit left send packets. This is because the
1278         firmware storing the destination-specific-requested packet in queue.
1279         */
1280         if ((entry->state == WLFC_STATE_CLOSE) &&
1281             (entry->requested_credit == 0) && (entry->requested_packet == 0)) {
1282             return 0;
1283         }
1284     }
1285 
1286     /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1287     if ((((entry->state == WLFC_STATE_CLOSE) ||
1288           (interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
1289          (entry->requested_credit == 0) && (entry->requested_packet == 0)) ||
1290         (!(entry->ac_bitmap & (1 << prec)))) {
1291         return 0;
1292     }
1293 
1294     return 1;
1295 } /* _dhd_wlfc_is_destination_open */
1296 
1297 /**
1298  * Dequeues a suppressed or delayed packet from a queue
1299  *    @param[in/out] ctx          Driver specific flow control administration
1300  *    @param[in]  prec            Precedence of queue to dequeue from
1301  *    @param[out] ac_credit_spent Boolean, returns 0 or 1
1302  *    @param[out] needs_hdr       Boolean, returns 0 or 1
1303  *    @param[out] entry_out       The remote MAC for which the packet is
1304  * destined
1305  *    @param[in]  only_no_credit  If TRUE, searches all entries instead of just
1306  * the active ones
1307  *
1308  * Return value: the dequeued packet
1309  */
_dhd_wlfc_deque_delayedq(athost_wl_status_info_t * ctx,int prec,uint8 * ac_credit_spent,uint8 * needs_hdr,wlfc_mac_descriptor_t ** entry_out,bool only_no_credit)1310 static void *_dhd_wlfc_deque_delayedq(athost_wl_status_info_t *ctx, int prec,
1311                                       uint8 *ac_credit_spent, uint8 *needs_hdr,
1312                                       wlfc_mac_descriptor_t **entry_out,
1313                                       bool only_no_credit)
1314 {
1315     wlfc_mac_descriptor_t *entry;
1316     int total_entries;
1317     void *p = NULL;
1318     int i;
1319     uint8 credit_spent =
1320         ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
1321 
1322     *entry_out = NULL;
1323     /* most cases a packet will count against FIFO credit */
1324     *ac_credit_spent = credit_spent;
1325 
1326     /* search all entries, include nodes as well as interfaces */
1327     if (only_no_credit) {
1328         total_entries = ctx->requested_entry_count;
1329     } else {
1330         total_entries = ctx->active_entry_count;
1331     }
1332 
1333     for (i = 0; i < total_entries; i++) {
1334         if (only_no_credit) {
1335             entry = ctx->requested_entry[i];
1336         } else {
1337             entry = ctx->active_entry_head;
1338             /* move head to ensure fair round-robin */
1339             ctx->active_entry_head = ctx->active_entry_head->next;
1340         }
1341         ASSERT(entry);
1342 
1343         if (entry->occupied &&
1344             _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
1345 #ifdef PROPTX_MAXCOUNT
1346             (entry->transit_count < entry->transit_maxcount) &&
1347 #endif /* PROPTX_MAXCOUNT */
1348             (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
1349             (!entry->suppressed)) {
1350             *ac_credit_spent = credit_spent;
1351             if (entry->state == WLFC_STATE_CLOSE) {
1352                 *ac_credit_spent = 0;
1353             }
1354 
1355             /* higher precedence will be picked up first,
1356              * i.e. suppressed packets before delayed ones
1357              */
1358             p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
1359             *needs_hdr = 0;
1360             if (p == NULL) {
1361                 /* De-Q from delay Q */
1362                 p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
1363                 *needs_hdr = 1;
1364             }
1365 
1366             if (p != NULL) {
1367                 bcm_pkt_validate_chk(p);
1368                 /* did the packet come from suppress sub-queue? */
1369                 if (entry->requested_credit > 0) {
1370                     entry->requested_credit--;
1371 #ifdef PROP_TXSTATUS_DEBUG
1372                     entry->dstncredit_sent_packets++;
1373 #endif // endif
1374                 } else if (entry->requested_packet > 0) {
1375                     entry->requested_packet--;
1376                     DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1377                 }
1378 
1379                 *entry_out = entry;
1380                 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
1381                 ctx->pkt_cnt_per_ac[prec]--;
1382                 ctx->pkt_cnt_in_psq--;
1383                 _dhd_wlfc_flow_control_check(ctx, &entry->psq,
1384                                              DHD_PKTTAG_IF(PKTTAG(p)));
1385                 /*
1386                  * A packet has been picked up, update traffic availability
1387                  * bitmap, if applicable.
1388                  */
1389                 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1390                 return p;
1391             }
1392         }
1393     }
1394     return NULL;
1395 } /* _dhd_wlfc_deque_delayedq */
1396 
1397 /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue
1398  */
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1399 static int _dhd_wlfc_enque_delayq(athost_wl_status_info_t *ctx, void *pktbuf,
1400                                   int prec)
1401 {
1402     wlfc_mac_descriptor_t *entry;
1403 
1404     if (pktbuf != NULL) {
1405         entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1406         if (entry == NULL) {
1407             DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1408             return BCME_ERROR;
1409         }
1410 
1411         /*
1412         - suppressed packets go to sub_queue[2*prec + 1] AND
1413         - delayed packets go to sub_queue[2*prec + 0] to ensure
1414         order of delivery.
1415         */
1416         if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf,
1417                                          (prec << 1), FALSE,
1418                                          WLFC_SEQCOUNT(entry, prec)) == FALSE) {
1419             WLFC_DBGMESG(("D"));
1420             ctx->stats.delayq_full_error++;
1421             return BCME_ERROR;
1422         }
1423 
1424         /* A packet has been pushed, update traffic availability bitmap, if
1425          * applicable */
1426         _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1427     }
1428 
1429     return BCME_OK;
1430 } /* _dhd_wlfc_enque_delayq */
1431 
1432 /** Returns TRUE if caller supplied packet is destined for caller supplied
1433  * interface */
_dhd_wlfc_ifpkt_fn(void * p,void * p_ifid)1434 static bool _dhd_wlfc_ifpkt_fn(void *p, void *p_ifid)
1435 {
1436     if (!p || !p_ifid) {
1437         return FALSE;
1438     }
1439 
1440     return (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) &&
1441             (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
1442 }
1443 
1444 /** Returns TRUE if caller supplied packet is destined for caller supplied
1445  * remote MAC */
_dhd_wlfc_entrypkt_fn(void * p,void * entry)1446 static bool _dhd_wlfc_entrypkt_fn(void *p, void *entry)
1447 {
1448     if (!p || !entry) {
1449         return FALSE;
1450     }
1451 
1452     return (DHD_PKTTAG_WLFCPKT(PKTTAG(p)) &&
1453             (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
1454 }
1455 
_dhd_wlfc_return_implied_credit(athost_wl_status_info_t * wlfc,void * pkt)1456 static void _dhd_wlfc_return_implied_credit(athost_wl_status_info_t *wlfc,
1457                                             void *pkt)
1458 {
1459     dhd_pub_t *dhdp;
1460     bool credit_return = FALSE;
1461 
1462     if (!wlfc || !pkt) {
1463         return;
1464     }
1465 
1466     dhdp = (dhd_pub_t *)(wlfc->dhdp);
1467     if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
1468         DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
1469         int lender, credit_returned = 0;
1470         uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
1471 
1472         credit_return = TRUE;
1473 
1474         /* Note that borrower is fifo_id */
1475         /* Return credits to highest priority lender first */
1476         for (lender = AC_COUNT; lender >= 0; lender--) {
1477             if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1478                 wlfc->FIFO_credit[lender]++;
1479                 wlfc->credits_borrowed[fifo_id][lender]--;
1480                 credit_returned = 1;
1481                 break;
1482             }
1483         }
1484 
1485         if (!credit_returned) {
1486             wlfc->FIFO_credit[fifo_id]++;
1487         }
1488     }
1489 
1490     BCM_REFERENCE(credit_return);
1491 #if defined(DHD_WLFC_THREAD)
1492     if (credit_return) {
1493         _dhd_wlfc_thread_wakeup(dhdp);
1494     }
1495 #endif /* defined(DHD_WLFC_THREAD) */
1496 }
1497 
1498 /** Removes and frees a packet from the hanger. Called during eg tx complete. */
_dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t * wlfc,uint32 slot_id,uint8 pkt_state,int pkt_txstatus)1499 static void _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t *wlfc,
1500                                       uint32 slot_id, uint8 pkt_state,
1501                                       int pkt_txstatus)
1502 {
1503     wlfc_hanger_t *hanger;
1504     wlfc_hanger_item_t *item;
1505 
1506     if (!wlfc) {
1507         return;
1508     }
1509 
1510     hanger = (wlfc_hanger_t *)wlfc->hanger;
1511     if (!hanger) {
1512         return;
1513     }
1514 
1515     if (slot_id == WLFC_HANGER_MAXITEMS) {
1516         return;
1517     }
1518 
1519     item = &hanger->items[slot_id];
1520 
1521     if (item->pkt) {
1522         item->pkt_state |= pkt_state;
1523         if (pkt_txstatus != -1) {
1524             item->pkt_txstatus = (uint8)pkt_txstatus;
1525         }
1526         bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE,
1527                                item->pkt_state);
1528         if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
1529             void *p = NULL;
1530             void *pkt = item->pkt;
1531             uint8 old_state = item->state;
1532             int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
1533             BCM_REFERENCE(ret);
1534             BCM_REFERENCE(pkt);
1535             ASSERT((ret == BCME_OK) && p && (pkt == p));
1536             if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
1537                 printf(
1538                     "ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
1539                     pkt, old_state, item->pkt_state);
1540             }
1541             ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
1542 
1543             /* free packet */
1544             wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
1545                                 [DHD_PKTTAG_FIFO(PKTTAG(p))]--;
1546             wlfc->stats.pktout++;
1547             dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
1548             PKTFREE(wlfc->osh, p, TRUE);
1549         }
1550     } else {
1551         /* free slot */
1552         if (item->state == WLFC_HANGER_ITEM_STATE_FREE) {
1553             DHD_ERROR(
1554                 ("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
1555                  __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
1556         }
1557         item->state = WLFC_HANGER_ITEM_STATE_FREE;
1558     }
1559 } /* _dhd_wlfc_hanger_free_pkt */
1560 
1561 /** Called during eg detach() */
_dhd_wlfc_pktq_flush(athost_wl_status_info_t * ctx,struct pktq * pq,bool dir,f_processpkt_t fn,void * arg,q_type_t q_type)1562 static void _dhd_wlfc_pktq_flush(athost_wl_status_info_t *ctx, struct pktq *pq,
1563                                  bool dir, f_processpkt_t fn, void *arg,
1564                                  q_type_t q_type)
1565 {
1566     int prec;
1567     dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1568 
1569     ASSERT(dhdp);
1570 
1571     /* Optimize flush, if pktq len = 0, just return.
1572      * pktq len of 0 means pktq's prec q's are all empty.
1573      */
1574     if (pq->n_pkts_tot == 0) {
1575         return;
1576     }
1577 
1578     for (prec = 0; prec < pq->num_prec; prec++) {
1579         struct pktq_prec *q;
1580         void *p, *prev = NULL;
1581 
1582         q = &pq->q[prec];
1583         p = q->head;
1584         while (p) {
1585             bcm_pkt_validate_chk(p);
1586             if (fn == NULL || (*fn)(p, arg)) {
1587                 bool head = (p == q->head);
1588                 if (head) {
1589                     q->head = PKTLINK(p);
1590                 } else {
1591                     PKTSETLINK(prev, PKTLINK(p));
1592                 }
1593                 if (q_type == Q_TYPE_PSQ) {
1594                     if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
1595                         _dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
1596                     }
1597                     ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec >> 1]--;
1598                     ctx->pkt_cnt_per_ac[prec >> 1]--;
1599                     ctx->pkt_cnt_in_psq--;
1600                     ctx->stats.cleanup_psq_cnt++;
1601                     if (!(prec & 1)) {
1602                         /* pkt in delayed q, so fake push BDC header for
1603                          * dhd_tcpack_check_xmit() and dhd_txcomplete().
1604                          */
1605                         _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0, 0, 0, TRUE);
1606 #ifdef DHDTCPACK_SUPPRESS
1607                         if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
1608                             DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
1609                                        " Stop using it\n",
1610                                        __FUNCTION__, __LINE__));
1611                             dhd_tcpack_suppress_set(dhdp, TCPACK_SUP_OFF);
1612                         }
1613 #endif /* DHDTCPACK_SUPPRESS */
1614                     }
1615                 } else if (q_type == Q_TYPE_AFQ) {
1616                     wlfc_mac_descriptor_t *entry =
1617                         _dhd_wlfc_find_table_entry(ctx, p);
1618                     if (entry->transit_count) {
1619                         entry->transit_count--;
1620                     }
1621                     if (entry->suppr_transit_count) {
1622                         entry->suppr_transit_count--;
1623                         if (entry->suppressed && (!entry->onbus_pkts_count) &&
1624                             (!entry->suppr_transit_count)) {
1625                             entry->suppressed = FALSE;
1626                         }
1627                     }
1628                     _dhd_wlfc_return_implied_credit(ctx, p);
1629                     ctx->stats.cleanup_fw_cnt++;
1630                 }
1631                 PKTSETLINK(p, NULL);
1632                 if (dir) {
1633                     ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec >> 1]--;
1634                     ctx->stats.pktout++;
1635                     dhd_txcomplete(dhdp, p, FALSE);
1636                 }
1637                 PKTFREE(ctx->osh, p, dir);
1638 
1639                 q->n_pkts--;
1640                 pq->n_pkts_tot--;
1641 #ifdef WL_TXQ_STALL
1642                 q->dequeue_count++;
1643 #endif // endif
1644 
1645                 p = (head ? q->head : PKTLINK(prev));
1646             } else {
1647                 prev = p;
1648                 p = PKTLINK(p);
1649             }
1650         }
1651 
1652         if (q->head == NULL) {
1653             ASSERT(q->n_pkts == 0);
1654             q->tail = NULL;
1655         }
1656     }
1657 
1658     if (fn == NULL) {
1659         ASSERT(pq->n_pkts_tot == 0);
1660     }
1661 } /* _dhd_wlfc_pktq_flush */
1662 
1663 #ifndef BCMDBUS
1664 /** !BCMDBUS specific function. Dequeues a packet from the caller supplied
1665  * queue. */
_dhd_wlfc_pktq_pdeq_with_fn(struct pktq * pq,int prec,f_processpkt_t fn,void * arg)1666 static void *_dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec,
1667                                          f_processpkt_t fn, void *arg)
1668 {
1669     struct pktq_prec *q;
1670     void *p, *prev = NULL;
1671 
1672     ASSERT(prec >= 0 && prec < pq->num_prec);
1673 
1674     q = &pq->q[prec];
1675     p = q->head;
1676 
1677     while (p) {
1678         if (fn == NULL || (*fn)(p, arg)) {
1679             break;
1680         } else {
1681             prev = p;
1682             p = PKTLINK(p);
1683         }
1684     }
1685     if (p == NULL) {
1686         return NULL;
1687     }
1688 
1689     bcm_pkt_validate_chk(p);
1690 
1691     if (prev == NULL) {
1692         if ((q->head = PKTLINK(p)) == NULL) {
1693             q->tail = NULL;
1694         }
1695     } else {
1696         PKTSETLINK(prev, PKTLINK(p));
1697         if (q->tail == p) {
1698             q->tail = prev;
1699         }
1700     }
1701 
1702     q->n_pkts--;
1703 
1704     pq->n_pkts_tot--;
1705 
1706 #ifdef WL_TXQ_STALL
1707     q->dequeue_count++;
1708 #endif // endif
1709 
1710     PKTSETLINK(p, NULL);
1711 
1712     return p;
1713 }
1714 
1715 /** !BCMDBUS specific function */
_dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1716 static void _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1717 {
1718     int prec;
1719     void *pkt = NULL, *head = NULL, *tail = NULL;
1720     struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
1721     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
1722     wlfc_hanger_t *h = (wlfc_hanger_t *)wlfc->hanger;
1723     wlfc_mac_descriptor_t *entry;
1724 
1725     dhd_os_sdlock_txq(dhd);
1726     for (prec = 0; prec < txq->num_prec; prec++) {
1727         while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
1728 #ifdef DHDTCPACK_SUPPRESS
1729             if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
1730                 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
1731                            __FUNCTION__, __LINE__));
1732                 dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
1733             }
1734 #endif /* DHDTCPACK_SUPPRESS */
1735             if (!head) {
1736                 head = pkt;
1737             }
1738             if (tail) {
1739                 PKTSETLINK(tail, pkt);
1740             }
1741             tail = pkt;
1742         }
1743     }
1744     dhd_os_sdunlock_txq(dhd);
1745 
1746     while ((pkt = head)) {
1747         head = PKTLINK(pkt);
1748         PKTSETLINK(pkt, NULL);
1749         entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
1750 
1751         if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
1752             !_dhd_wlfc_hanger_remove_reference(h, pkt)) {
1753             DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
1754                        __FUNCTION__, pkt));
1755         }
1756         if (entry->transit_count) {
1757             entry->transit_count--;
1758         }
1759         if (entry->suppr_transit_count) {
1760             entry->suppr_transit_count--;
1761             if (entry->suppressed && (!entry->onbus_pkts_count) &&
1762                 (!entry->suppr_transit_count)) {
1763                 entry->suppressed = FALSE;
1764             }
1765         }
1766         _dhd_wlfc_return_implied_credit(wlfc, pkt);
1767         wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))]
1768                             [DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
1769         wlfc->stats.pktout++;
1770         wlfc->stats.cleanup_txq_cnt++;
1771         dhd_txcomplete(dhd, pkt, FALSE);
1772         PKTFREE(wlfc->osh, pkt, TRUE);
1773     }
1774 } /* _dhd_wlfc_cleanup_txq */
1775 #endif /* !BCMDBUS */
1776 
1777 /** called during eg detach */
_dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1778 void _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1779 {
1780     int i;
1781     int total_entries;
1782     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
1783     wlfc_mac_descriptor_t *table;
1784     wlfc_hanger_t *h = (wlfc_hanger_t *)wlfc->hanger;
1785 
1786     wlfc->stats.cleanup_txq_cnt = 0;
1787     wlfc->stats.cleanup_psq_cnt = 0;
1788     wlfc->stats.cleanup_fw_cnt = 0;
1789 
1790     /*
1791      *  flush sequence should be txq -> psq -> hanger/afq, hanger has to be last
1792      * one
1793      */
1794 #ifndef BCMDBUS
1795     /* flush bus->txq */
1796     _dhd_wlfc_cleanup_txq(dhd, fn, arg);
1797 #endif /* !BCMDBUS */
1798 
1799     /* flush psq, search all entries, include nodes as well as interfaces */
1800     total_entries =
1801         sizeof(wlfc->destination_entries) / sizeof(wlfc_mac_descriptor_t);
1802     table = (wlfc_mac_descriptor_t *)&wlfc->destination_entries;
1803 
1804     for (i = 0; i < total_entries; i++) {
1805         if (table[i].occupied) {
1806             /* release packets held in PSQ (both delayed and suppressed) */
1807             if (table[i].psq.n_pkts_tot) {
1808                 WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n", __FUNCTION__, i,
1809                               table[i].psq.n_pkts_tot));
1810                 _dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE, fn, arg,
1811                                      Q_TYPE_PSQ);
1812             }
1813 
1814             /* free packets held in AFQ */
1815             if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.n_pkts_tot)) {
1816                 _dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE, fn, arg,
1817                                      Q_TYPE_AFQ);
1818             }
1819 
1820             if ((fn == NULL) &&
1821                 (&table[i] != &wlfc->destination_entries.other)) {
1822                 table[i].occupied = 0;
1823                 if (table[i].transit_count || table[i].suppr_transit_count) {
1824                     DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
1825                                __FUNCTION__, i, table[i].transit_count,
1826                                table[i].suppr_transit_count));
1827                 }
1828             }
1829         }
1830     }
1831 
1832     /*
1833         . flush remained pkt in hanger queue, not in bus->txq nor psq.
1834         . the remained pkt was successfully downloaded to dongle already.
1835         . hanger slot state cannot be set to free until receive txstatus update.
1836     */
1837     if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
1838         for (i = 0; i < h->max_items; i++) {
1839             if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
1840                 (h->items[i].state ==
1841                  WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
1842                 if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
1843                     h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
1844                 }
1845             }
1846         }
1847     }
1848 
1849     return;
1850 } /* _dhd_wlfc_cleanup */
1851 
1852 /** Called after eg the dongle signalled a new remote MAC that it connected with
1853  * to the DHD */
_dhd_wlfc_mac_entry_update(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea,f_processpkt_t fn,void * arg)1854 static int _dhd_wlfc_mac_entry_update(athost_wl_status_info_t *ctx,
1855                                       wlfc_mac_descriptor_t *entry,
1856                                       uint8 action, uint8 ifid, uint8 iftype,
1857                                       uint8 *ea, f_processpkt_t fn, void *arg)
1858 {
1859     int rc = BCME_OK;
1860 
1861     if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) ||
1862         (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
1863         entry->occupied = 1;
1864         entry->state = WLFC_STATE_OPEN;
1865         entry->requested_credit = 0;
1866         entry->interface_id = ifid;
1867         entry->iftype = iftype;
1868         entry->ac_bitmap = 0xff; /* update this when handling APSD */
1869 
1870         /* for an interface entry we may not care about the MAC address */
1871         if (ea != NULL) {
1872             memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1873         }
1874 
1875         if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1876             entry->suppressed = FALSE;
1877             entry->transit_count = 0;
1878 #if defined(WL_EXT_IAPSTA) && defined(PROPTX_MAXCOUNT)
1879             entry->transit_maxcount = wl_ext_get_wlfc_maxcount(ctx->dhdp, ifid);
1880 #endif /* PROPTX_MAXCOUNT */
1881             entry->suppr_transit_count = 0;
1882             entry->onbus_pkts_count = 0;
1883         }
1884 
1885         if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1886             dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
1887 
1888             pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1889             _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1890 
1891             if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1892                 pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
1893             }
1894 
1895             if (entry->next == NULL) {
1896                 /* not linked to anywhere, add to tail */
1897                 if (ctx->active_entry_head) {
1898                     entry->prev = ctx->active_entry_head->prev;
1899                     ctx->active_entry_head->prev->next = entry;
1900                     ctx->active_entry_head->prev = entry;
1901                     entry->next = ctx->active_entry_head;
1902                 } else {
1903                     ASSERT(ctx->active_entry_count == 0);
1904                     entry->prev = entry->next = entry;
1905                     ctx->active_entry_head = entry;
1906                 }
1907                 ctx->active_entry_count++;
1908             } else {
1909                 DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
1910                            (int)(entry - &ctx->destination_entries.nodes[0])));
1911             }
1912         }
1913     } else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1914         /* When the entry is deleted, the packets that are queued in the entry
1915            must be cleanup. The cleanup action should be before the occupied is
1916            set as 0.
1917         */
1918         _dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
1919         _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1920 
1921         entry->occupied = 0;
1922         entry->state = WLFC_STATE_CLOSE;
1923         memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
1924 
1925         if (entry->next) {
1926             /* not floating, remove from Q */
1927             if (ctx->active_entry_count <= 1) {
1928                 /* last item */
1929                 ctx->active_entry_head = NULL;
1930                 ctx->active_entry_count = 0;
1931             } else {
1932                 entry->prev->next = entry->next;
1933                 entry->next->prev = entry->prev;
1934                 if (entry == ctx->active_entry_head) {
1935                     ctx->active_entry_head = entry->next;
1936                 }
1937                 ctx->active_entry_count--;
1938             }
1939             entry->next = entry->prev = NULL;
1940         } else {
1941             DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1942         }
1943     }
1944     return rc;
1945 } /* _dhd_wlfc_mac_entry_update */
1946 
1947 #ifdef LIMIT_BORROW
1948 
1949 /** LIMIT_BORROW specific function */
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,int highest_lender_ac,int borrower_ac,bool bBorrowAll)1950 static int _dhd_wlfc_borrow_credit(athost_wl_status_info_t *ctx,
1951                                    int highest_lender_ac, int borrower_ac,
1952                                    bool bBorrowAll)
1953 {
1954     int lender_ac, borrow_limit = 0;
1955     int rc = -1;
1956 
1957     if (ctx == NULL) {
1958         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1959         return -1;
1960     }
1961 
1962     /* Borrow from lowest priority available AC (including BC/MC credits) */
1963     for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
1964         if (!bBorrowAll) {
1965             borrow_limit =
1966                 ctx->Init_FIFO_credit[lender_ac] / WLFC_BORROW_LIMIT_RATIO;
1967         } else {
1968             borrow_limit = 0;
1969         }
1970 
1971         if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
1972             ctx->credits_borrowed[borrower_ac][lender_ac]++;
1973             ctx->FIFO_credit[lender_ac]--;
1974             rc = lender_ac;
1975             break;
1976         }
1977     }
1978 
1979     return rc;
1980 }
1981 
1982 /** LIMIT_BORROW specific function */
_dhd_wlfc_return_credit(athost_wl_status_info_t * ctx,int lender_ac,int borrower_ac)1983 static int _dhd_wlfc_return_credit(athost_wl_status_info_t *ctx, int lender_ac,
1984                                    int borrower_ac)
1985 {
1986     if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
1987         (borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
1988         DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
1989                    __FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
1990 
1991         return BCME_BADARG;
1992     }
1993 
1994     ctx->credits_borrowed[borrower_ac][lender_ac]--;
1995     ctx->FIFO_credit[lender_ac]++;
1996 
1997     return BCME_OK;
1998 }
1999 
2000 #endif /* LIMIT_BORROW */
2001 
2002 /**
2003  * Called on an interface event (WLC_E_IF) indicated by firmware.
2004  *     @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or
2005  * eWLFC_MAC_ENTRY_ACTION_ADD
2006  */
_dhd_wlfc_interface_entry_update(void * state,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)2007 static int _dhd_wlfc_interface_entry_update(void *state, uint8 action,
2008                                             uint8 ifid, uint8 iftype, uint8 *ea)
2009 {
2010     athost_wl_status_info_t *ctx = (athost_wl_status_info_t *)state;
2011     wlfc_mac_descriptor_t *entry;
2012 
2013     if (ifid >= WLFC_MAX_IFNUM) {
2014         return BCME_BADARG;
2015     }
2016 
2017     entry = &ctx->destination_entries.interfaces[ifid];
2018 
2019     return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
2020                                       _dhd_wlfc_ifpkt_fn, &ifid);
2021 }
2022 
2023 /**
2024  * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle
2025  * (broadcast/multicast specific)
2026  */
_dhd_wlfc_BCMCCredit_support_update(void * state)2027 static int _dhd_wlfc_BCMCCredit_support_update(void *state)
2028 {
2029     athost_wl_status_info_t *ctx = (athost_wl_status_info_t *)state;
2030 
2031     ctx->bcmc_credit_supported = TRUE;
2032     return BCME_OK;
2033 }
2034 
2035 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
_dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)2036 static int _dhd_wlfc_FIFOcreditmap_update(void *state, uint8 *credits)
2037 {
2038     athost_wl_status_info_t *ctx = (athost_wl_status_info_t *)state;
2039     int i;
2040 
2041     for (i = 0; i <= 0x4; i++) {
2042         if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
2043             DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n", __FUNCTION__,
2044                        ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
2045         }
2046     }
2047 
2048     /* update the AC FIFO credit map */
2049     ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
2050     ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
2051     ctx->FIFO_credit[0x2] += (credits[0x2] - ctx->Init_FIFO_credit[0x2]);
2052     ctx->FIFO_credit[0x3] += (credits[0x3] - ctx->Init_FIFO_credit[0x3]);
2053     ctx->FIFO_credit[0x4] += (credits[0x4] - ctx->Init_FIFO_credit[0x4]);
2054 
2055     ctx->Init_FIFO_credit[0] = credits[0];
2056     ctx->Init_FIFO_credit[1] = credits[1];
2057     ctx->Init_FIFO_credit[0x2] = credits[0x2];
2058     ctx->Init_FIFO_credit[0x3] = credits[0x3];
2059     ctx->Init_FIFO_credit[0x4] = credits[0x4];
2060 
2061     /* credit for ATIM FIFO is not used yet. */
2062     ctx->Init_FIFO_credit[0x5] = ctx->FIFO_credit[0x5] = 0;
2063 
2064     return BCME_OK;
2065 }
2066 
2067 /**
2068  * Called during committing of a transmit packet from the OS DHD layer to the
2069  * next layer towards the dongle (eg the DBUS layer). All transmit packets flow
2070  * via this function to the next layer.
2071  *
2072  *     @param[in/out] ctx      Driver specific flow control administration
2073  *     @param[in] ac           Access Category (QoS) of called supplied packet
2074  *     @param[in] commit_info  Contains eg the packet to send
2075  *     @param[in] fcommit      Function pointer to transmit function of next
2076  * software layer
2077  *     @param[in] commit_ctx   Opaque context used when calling next layer
2078  */
_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t * ctx,int ac,dhd_wlfc_commit_info_t * commit_info,f_commitpkt_t fcommit,void * commit_ctx)2079 static int _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t *ctx, int ac,
2080                                           dhd_wlfc_commit_info_t *commit_info,
2081                                           f_commitpkt_t fcommit,
2082                                           void *commit_ctx)
2083 {
2084     uint32 hslot;
2085     int rc;
2086     dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2087 
2088     /*
2089         if ac_fifo_credit_spent = 0
2090 
2091         This packet will not count against the FIFO credit.
2092         To ensure the txstatus corresponding to this packet
2093         does not provide an implied credit (default behavior)
2094         mark the packet accordingly.
2095 
2096         if ac_fifo_credit_spent = 1
2097 
2098         This is a normal packet and it counts against the FIFO
2099         credit count.
2100     */
2101     DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p),
2102                               commit_info->ac_fifo_credit_spent);
2103     rc =
2104         _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
2105                                    commit_info->needs_hdr, &hslot);
2106     if (rc == BCME_OK) {
2107         rc = fcommit(commit_ctx, commit_info->p);
2108         if (rc == BCME_OK) {
2109             uint8 gen = WL_TXSTATUS_GET_GENERATION(
2110                 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
2111             ctx->stats.pkt2bus++;
2112             if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
2113                 ctx->stats.send_pkts[ac]++;
2114                 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
2115             }
2116 
2117             if (gen != commit_info->mac_entry->generation) {
2118                 /* will be suppressed back by design */
2119                 if (!commit_info->mac_entry->suppressed) {
2120                     commit_info->mac_entry->suppressed = TRUE;
2121                 }
2122                 commit_info->mac_entry->suppr_transit_count++;
2123             }
2124             commit_info->mac_entry->transit_count++;
2125             commit_info->mac_entry->onbus_pkts_count++;
2126         } else if (commit_info->needs_hdr) {
2127             if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2128                 void *pout = NULL;
2129                 /* pop hanger for delayed packet */
2130                 _dhd_wlfc_hanger_poppkt(ctx->hanger,
2131                                         WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(
2132                                             PKTTAG(commit_info->p))),
2133                                         &pout, TRUE);
2134                 ASSERT(commit_info->p == pout);
2135             }
2136         }
2137     } else {
2138         ctx->stats.generic_error++;
2139     }
2140 
2141     if (rc != BCME_OK) {
2142         /*
2143            pretx pkt process or bus commit has failed, rollback.
2144            - remove wl-header for a delayed packet
2145            - save wl-header header for suppressed packets
2146            - reset credit check flag
2147         */
2148         _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p,
2149                                       commit_info->pkt_type, hslot);
2150         DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
2151     }
2152 
2153     return rc;
2154 } /* _dhd_wlfc_handle_packet_commit */
2155 
2156 /** Returns remote MAC descriptor for caller supplied MAC address */
_dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)2157 static uint8 _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
2158 {
2159     wlfc_mac_descriptor_t *table = ((athost_wl_status_info_t *)dhdp->wlfc_state)
2160                                        ->destination_entries.nodes;
2161     uint8 table_index;
2162 
2163     if (ea != NULL) {
2164         for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE;
2165              table_index++) {
2166             if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
2167                 table[table_index].occupied) {
2168                 return table_index;
2169             }
2170         }
2171     }
2172     return WLFC_MAC_DESC_ID_INVALID;
2173 }
2174 
2175 /**
2176  * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle,
2177  * indicating the status of a frame that the dongle attempted to transmit over
2178  * the wireless medium.
2179  */
dhd_wlfc_suppressed_acked_update(dhd_pub_t * dhd,uint16 hslot,uint8 prec,uint8 hcnt)2180 static int dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot,
2181                                             uint8 prec, uint8 hcnt)
2182 {
2183     athost_wl_status_info_t *ctx;
2184     wlfc_mac_descriptor_t *entry = NULL;
2185     struct pktq *pq;
2186     struct pktq_prec *q;
2187     void *p, *b;
2188 
2189     if (!dhd) {
2190         DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
2191         return BCME_BADARG;
2192     }
2193     ctx = (athost_wl_status_info_t *)dhd->wlfc_state;
2194     if (!ctx) {
2195         DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
2196         return BCME_ERROR;
2197     }
2198 
2199     ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
2200 
2201     if (hslot < WLFC_MAC_DESC_TABLE_SIZE) {
2202         entry = &ctx->destination_entries.nodes[hslot];
2203     } else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM)) {
2204         entry = &ctx->destination_entries
2205                      .interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
2206     } else {
2207         entry = &ctx->destination_entries.other;
2208     }
2209 
2210     pq = &entry->psq;
2211 
2212     ASSERT(((prec << 1) + 1) < pq->num_prec);
2213 
2214     q = &pq->q[((prec << 1) + 1)];
2215 
2216     b = NULL;
2217     p = q->head;
2218 
2219     while (p &&
2220            (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
2221         b = p;
2222         p = PKTLINK(p);
2223     }
2224 
2225     if (p == NULL) {
2226         /* none is matched */
2227         if (b) {
2228             DHD_ERROR(
2229                 ("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
2230         } else {
2231             DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
2232         }
2233 
2234         return BCME_ERROR;
2235     }
2236 
2237     if (!b) {
2238         /* head packet is matched */
2239         if ((q->head = PKTLINK(p)) == NULL) {
2240             q->tail = NULL;
2241         }
2242     } else {
2243         /* middle packet is matched */
2244         PKTSETLINK(b, PKTLINK(p));
2245         if (PKTLINK(p) == NULL) {
2246             q->tail = b;
2247         }
2248     }
2249 
2250     q->n_pkts--;
2251     pq->n_pkts_tot--;
2252 
2253 #ifdef WL_TXQ_STALL
2254     q->dequeue_count++;
2255 #endif // endif
2256 
2257     ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
2258     ctx->pkt_cnt_per_ac[prec]--;
2259 
2260     PKTSETLINK(p, NULL);
2261 
2262     if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2263         _dhd_wlfc_enque_afq(ctx, p);
2264     } else {
2265         _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
2266     }
2267 
2268     entry->transit_count++;
2269 
2270     return BCME_OK;
2271 }
2272 
_dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len,void ** p_mac)2273 static int _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8 *pkt_info,
2274                                                 uint8 len, void **p_mac)
2275 {
2276     uint8 status_flag_ori, status_flag;
2277     uint32 status;
2278     int ret = BCME_OK;
2279     int remove_from_hanger_ori, remove_from_hanger = 1;
2280     void *pktbuf = NULL;
2281     uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
2282     uint16 hslot;
2283     wlfc_mac_descriptor_t *entry = NULL;
2284     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2285     uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
2286 
2287     memcpy(&status, pkt_info, sizeof(uint32));
2288     status = ltoh32(status);
2289     status_flag = WL_TXSTATUS_GET_FLAGS(status);
2290     hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
2291     hslot = WL_TXSTATUS_GET_HSLOT(status);
2292     fifo_id = WL_TXSTATUS_GET_FIFO(status);
2293     gen = WL_TXSTATUS_GET_GENERATION(status);
2294 
2295     if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2296         memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS,
2297                WLFC_CTL_VALUE_LEN_SEQ);
2298         seq = ltoh16(seq);
2299         seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
2300         seq_num = WL_SEQ_GET_NUM(seq);
2301     }
2302 
2303     wlfc->stats.txstatus_in += len;
2304 
2305     if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
2306         wlfc->stats.pkt_freed += len;
2307     } else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
2308         wlfc->stats.pkt_freed += len;
2309     } else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
2310         wlfc->stats.d11_suppress += len;
2311         remove_from_hanger = 0;
2312     } else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
2313         wlfc->stats.wl_suppress += len;
2314         remove_from_hanger = 0;
2315     } else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
2316         wlfc->stats.wlc_tossed_pkts += len;
2317     } else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2318         wlfc->stats.pkt_freed += len;
2319     } else if (status_flag == WLFC_CTL_PKTFLAG_EXPIRED) {
2320         wlfc->stats.pkt_exptime += len;
2321     } else if (status_flag == WLFC_CTL_PKTFLAG_DROPPED) {
2322         wlfc->stats.pkt_dropped += len;
2323     }
2324 
2325     if (dhd->proptxstatus_txstatus_ignore) {
2326         if (!remove_from_hanger) {
2327             DHD_ERROR(("suppress txstatus: %d\n", status_flag));
2328         }
2329         return BCME_OK;
2330     }
2331 
2332     status_flag_ori = status_flag;
2333     remove_from_hanger_ori = remove_from_hanger;
2334 
2335     while (count < len) {
2336         if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2337             dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
2338         }
2339         if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2340             ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
2341         } else {
2342             status_flag = status_flag_ori;
2343             remove_from_hanger = remove_from_hanger_ori;
2344             ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
2345             if (!pktbuf) {
2346                 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2347                                           WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
2348                 goto cont;
2349             } else {
2350                 wlfc_hanger_t *h = (wlfc_hanger_t *)wlfc->hanger;
2351                 if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
2352                     status_flag = WLFC_CTL_PKTFLAG_DISCARD;
2353                     remove_from_hanger = 1;
2354                 }
2355             }
2356         }
2357 
2358         if ((ret != BCME_OK) || !pktbuf) {
2359             goto cont;
2360         }
2361 
2362         bcm_pkt_validate_chk(pktbuf);
2363 
2364         /* set fifo_id to correct value because not all FW does that */
2365         fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
2366 
2367         entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
2368 
2369         if (!remove_from_hanger) {
2370             /* this packet was suppressed */
2371             if (!entry->suppressed || (entry->generation != gen)) {
2372                 if (!entry->suppressed) {
2373                     entry->suppr_transit_count = entry->transit_count;
2374                     if (p_mac) {
2375                         *p_mac = entry;
2376                     }
2377                 } else {
2378                     DHD_ERROR(("gen(%d), entry->generation(%d)\n", gen,
2379                                entry->generation));
2380                 }
2381                 entry->suppressed = TRUE;
2382             }
2383             entry->generation = gen;
2384         }
2385 
2386 #ifdef PROP_TXSTATUS_DEBUG
2387         if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2388             uint32 new_t = OSL_SYSUPTIME();
2389             uint32 old_t;
2390             uint32 delta;
2391             old_t = ((wlfc_hanger_t *)(wlfc->hanger))->items[hslot].push_time;
2392 
2393             wlfc->stats.latency_sample_count++;
2394             if (new_t > old_t) {
2395                 delta = new_t - old_t;
2396             } else {
2397                 delta = 0xffffffff + new_t - old_t;
2398             }
2399             wlfc->stats.total_status_latency += delta;
2400             wlfc->stats.latency_most_recent = delta;
2401 
2402             wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
2403             if (wlfc->stats.idx_delta ==
2404                 sizeof(wlfc->stats.deltas) / sizeof(uint32)) {
2405                 wlfc->stats.idx_delta = 0;
2406             }
2407         }
2408 #endif /* PROP_TXSTATUS_DEBUG */
2409 
2410         /* pick up the implicit credit from this packet */
2411         if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
2412             _dhd_wlfc_return_implied_credit(wlfc, pktbuf);
2413         } else {
2414             /*
2415             if this packet did not count against FIFO credit, it must have
2416             taken a requested_credit from the destination entry (for pspoll
2417             etc.)
2418             */
2419             if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
2420                 entry->requested_credit++;
2421 #if defined(DHD_WLFC_THREAD)
2422                 _dhd_wlfc_thread_wakeup(dhd);
2423 #endif /* DHD_WLFC_THREAD */
2424             }
2425 #ifdef PROP_TXSTATUS_DEBUG
2426             entry->dstncredit_acks++;
2427 #endif // endif
2428         }
2429 
2430         if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
2431             (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
2432             /* save generation bit inside packet */
2433             WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
2434 
2435             if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2436                 WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
2437                 WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
2438             }
2439 
2440             ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
2441             if (ret != BCME_OK) {
2442                 /* delay q is full, drop this packet */
2443                 DHD_WLFC_QMON_COMPLETE(entry);
2444                 _dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
2445             } else {
2446                 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2447                     /* Mark suppressed to avoid a double free
2448                     during wlfc cleanup
2449                     */
2450                     _dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
2451                 }
2452             }
2453         } else {
2454             DHD_WLFC_QMON_COMPLETE(entry);
2455 
2456             if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2457                 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2458                                           WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
2459             } else {
2460                 dhd_txcomplete(dhd, pktbuf, TRUE);
2461                 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
2462                                     [DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
2463                 wlfc->stats.pktout++;
2464                 /* free the packet */
2465                 PKTFREE(wlfc->osh, pktbuf, TRUE);
2466             }
2467         }
2468         /* pkt back from firmware side */
2469         if (entry->transit_count) {
2470             entry->transit_count--;
2471         }
2472         if (entry->suppr_transit_count) {
2473             entry->suppr_transit_count--;
2474             if (entry->suppressed && (!entry->onbus_pkts_count) &&
2475                 (!entry->suppr_transit_count)) {
2476                 entry->suppressed = FALSE;
2477             }
2478         }
2479 
2480     cont:
2481         hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
2482         if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2483             hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
2484         }
2485 
2486         if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
2487             seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
2488         }
2489 
2490         count++;
2491     }
2492 
2493     return BCME_OK;
2494 } /* _dhd_wlfc_compressed_txstatus_update */
2495 
2496 /**
2497  * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the
2498  * dongle.
2499  *    @param[in] credits caller supplied credit that will be added to the host
2500  * credit.
2501  */
_dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)2502 static int _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8 *credits)
2503 {
2504     int i;
2505     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2506     for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
2507 #ifdef PROP_TXSTATUS_DEBUG
2508         wlfc->stats.fifo_credits_back[i] += credits[i];
2509 #endif // endif
2510 
2511         /* update FIFO credits */
2512         if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT) {
2513             int lender; /* Note that borrower is i */
2514 
2515             /* Return credits to highest priority lender first */
2516             for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0);
2517                  lender--) {
2518                 if (wlfc->credits_borrowed[i][lender] > 0) {
2519                     if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
2520                         credits[i] -= (uint8)wlfc->credits_borrowed[i][lender];
2521                         wlfc->FIFO_credit[lender] +=
2522                             wlfc->credits_borrowed[i][lender];
2523                         wlfc->credits_borrowed[i][lender] = 0;
2524                     } else {
2525                         wlfc->credits_borrowed[i][lender] -= credits[i];
2526                         wlfc->FIFO_credit[lender] += credits[i];
2527                         credits[i] = 0;
2528                     }
2529                 }
2530             }
2531 
2532             /* If we have more credits left over, these must belong to the AC */
2533             if (credits[i] > 0) {
2534                 wlfc->FIFO_credit[i] += credits[i];
2535             }
2536 
2537             if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
2538                 wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
2539             }
2540         }
2541     }
2542 
2543 #if defined(DHD_WLFC_THREAD)
2544     _dhd_wlfc_thread_wakeup(dhd);
2545 #endif /* defined(DHD_WLFC_THREAD) */
2546 
2547     return BCME_OK;
2548 } /* _dhd_wlfc_fifocreditback_indicate */
2549 
2550 #ifndef BCMDBUS
2551 /** !BCMDBUS specific function */
_dhd_wlfc_suppress_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)2552 static void _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
2553 {
2554     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2555     wlfc_mac_descriptor_t *entry;
2556     int prec;
2557     void *pkt = NULL, *head = NULL, *tail = NULL;
2558     struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
2559     uint8 results[WLFC_CTL_VALUE_LEN_TXSTATUS + WLFC_CTL_VALUE_LEN_SEQ];
2560     uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
2561     uint32 htod = 0;
2562     uint16 htodseq = 0;
2563     bool bCreditUpdate = FALSE;
2564 
2565     dhd_os_sdlock_txq(dhd);
2566     for (prec = 0; prec < txq->num_prec; prec++) {
2567         while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
2568             if (!head) {
2569                 head = pkt;
2570             }
2571             if (tail) {
2572                 PKTSETLINK(tail, pkt);
2573             }
2574             tail = pkt;
2575         }
2576     }
2577     dhd_os_sdunlock_txq(dhd);
2578 
2579     while ((pkt = head)) {
2580         head = PKTLINK(pkt);
2581         PKTSETLINK(pkt, NULL);
2582 
2583         entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
2584         if (!entry) {
2585             PKTFREE(dhd->osh, pkt, TRUE);
2586             continue;
2587         }
2588         if (entry->onbus_pkts_count > 0) {
2589             entry->onbus_pkts_count--;
2590         }
2591         if (entry->suppressed && (!entry->onbus_pkts_count) &&
2592             (!entry->suppr_transit_count)) {
2593             entry->suppressed = FALSE;
2594         }
2595         /* fake a suppression txstatus */
2596         htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
2597         WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
2598         WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
2599         htod = htol32(htod);
2600         memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
2601         if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2602             htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
2603             if (IS_WL_TO_REUSE_SEQ(htodseq)) {
2604                 SET_WL_HAS_ASSIGNED_SEQ(htodseq);
2605                 RESET_WL_TO_REUSE_SEQ(htodseq);
2606             }
2607             htodseq = htol16(htodseq);
2608             memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
2609                    WLFC_CTL_VALUE_LEN_SEQ);
2610         }
2611         if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2612             _dhd_wlfc_enque_afq(wlfc, pkt);
2613         }
2614         _dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
2615 
2616         /* fake a fifo credit back */
2617         if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
2618             credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
2619             bCreditUpdate = TRUE;
2620         }
2621     }
2622 
2623     if (bCreditUpdate) {
2624         _dhd_wlfc_fifocreditback_indicate(dhd, credits);
2625     }
2626 } /* _dhd_wlfc_suppress_txq */
2627 #endif /* !BCMDBUS */
2628 
_dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)2629 static int _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
2630 {
2631     uint32 timestamp;
2632 
2633     (void)dhd;
2634 
2635     bcopy(&value[0x2], &timestamp, sizeof(uint32));
2636     timestamp = ltoh32(timestamp);
2637     DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
2638     return BCME_OK;
2639 }
2640 
_dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)2641 static int _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8 *rssi)
2642 {
2643     (void)dhd;
2644     (void)rssi;
2645     return BCME_OK;
2646 }
2647 
_dhd_wlfc_add_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2648 static void _dhd_wlfc_add_requested_entry(athost_wl_status_info_t *wlfc,
2649                                           wlfc_mac_descriptor_t *entry)
2650 {
2651     int i;
2652 
2653     if (!wlfc || !entry) {
2654         return;
2655     }
2656 
2657     for (i = 0; i < wlfc->requested_entry_count; i++) {
2658         if (entry == wlfc->requested_entry[i]) {
2659             break;
2660         }
2661     }
2662 
2663     if (i == wlfc->requested_entry_count) {
2664         /* no match entry found */
2665         ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE - 1));
2666         wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
2667     }
2668 }
2669 
2670 /** called on eg receiving 'mac open' event from the dongle. */
_dhd_wlfc_remove_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2671 static void _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t *wlfc,
2672                                              wlfc_mac_descriptor_t *entry)
2673 {
2674     int i;
2675 
2676     if (!wlfc || !entry) {
2677         return;
2678     }
2679 
2680     for (i = 0; i < wlfc->requested_entry_count; i++) {
2681         if (entry == wlfc->requested_entry[i]) {
2682             break;
2683         }
2684     }
2685 
2686     if (i < wlfc->requested_entry_count) {
2687         /* found */
2688         ASSERT(wlfc->requested_entry_count > 0);
2689         wlfc->requested_entry_count--;
2690         if (i != wlfc->requested_entry_count) {
2691             wlfc->requested_entry[i] =
2692                 wlfc->requested_entry[wlfc->requested_entry_count];
2693         }
2694         wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
2695     }
2696 }
2697 
2698 /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
_dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2699 static int _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8 *value, uint8 type)
2700 {
2701     int rc;
2702     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2703     wlfc_mac_descriptor_t *table;
2704     uint8 existing_index;
2705     uint8 table_index;
2706     uint8 ifid;
2707     uint8 *ea;
2708 
2709     WLFC_DBGMESG(("%s(), mac [" MACDBG "],%s,idx:%d,id:0x%02x\n", __FUNCTION__,
2710                   MAC2STRDBG(&value[0x2]),
2711                   ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD" : "DEL"),
2712                   WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
2713 
2714     table = wlfc->destination_entries.nodes;
2715     table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
2716     ifid = value[1];
2717     ea = &value[0x2];
2718 
2719     _dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
2720     if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
2721         existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[0x2]);
2722         if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
2723             (existing_index != table_index) && table[existing_index].occupied) {
2724             /*
2725             there is an existing different entry, free the old one
2726             and move it to new index if necessary.
2727             */
2728             rc = _dhd_wlfc_mac_entry_update(
2729                 wlfc, &table[existing_index], eWLFC_MAC_ENTRY_ACTION_DEL,
2730                 table[existing_index].interface_id,
2731                 table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
2732                 &table[existing_index]);
2733         }
2734 
2735         if (!table[table_index].occupied) {
2736             /* this new MAC entry does not exist, create one */
2737             table[table_index].mac_handle = value[0];
2738             rc = _dhd_wlfc_mac_entry_update(
2739                 wlfc, &table[table_index], eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2740                 wlfc->destination_entries.interfaces[ifid].iftype, ea, NULL,
2741                 NULL);
2742         } else {
2743             /* the space should have been empty, but it's not */
2744             wlfc->stats.mac_update_failed++;
2745         }
2746     }
2747 
2748     if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2749         if (table[table_index].occupied) {
2750             rc = _dhd_wlfc_mac_entry_update(
2751                 wlfc, &table[table_index], eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2752                 wlfc->destination_entries.interfaces[ifid].iftype, ea,
2753                 _dhd_wlfc_entrypkt_fn, &table[table_index]);
2754         } else {
2755             /* the space should have been occupied, but it's not */
2756             wlfc->stats.mac_update_failed++;
2757         }
2758     }
2759     BCM_REFERENCE(rc);
2760     return BCME_OK;
2761 } /* _dhd_wlfc_mac_table_update */
2762 
2763 /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
_dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2764 static int _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8 *value, uint8 type)
2765 {
2766     /* Handle PS on/off indication */
2767     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2768     wlfc_mac_descriptor_t *table;
2769     wlfc_mac_descriptor_t
2770         *desc; /* a table maps from mac handle to mac descriptor */
2771     uint8 mac_handle = value[0];
2772     int i;
2773 
2774     table = wlfc->destination_entries.nodes;
2775     desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2776     if (desc->occupied) {
2777         if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2778             desc->state = WLFC_STATE_OPEN;
2779             desc->ac_bitmap = 0xff;
2780             DHD_WLFC_CTRINC_MAC_OPEN(desc);
2781             desc->requested_credit = 0;
2782             desc->requested_packet = 0;
2783             _dhd_wlfc_remove_requested_entry(wlfc, desc);
2784         } else {
2785             desc->state = WLFC_STATE_CLOSE;
2786             DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2787             /* Indicate to firmware if there is any traffic pending. */
2788             for (i = 0; i < AC_COUNT; i++) {
2789                 _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2790             }
2791         }
2792     } else {
2793         wlfc->stats.psmode_update_failed++;
2794     }
2795 
2796     return BCME_OK;
2797 } /* _dhd_wlfc_psmode_update */
2798 
2799 /** called upon receiving 'interface open' or 'interface close' event from the
2800  * dongle */
_dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2801 static int _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8 *value, uint8 type)
2802 {
2803     /* Handle PS on/off indication */
2804     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2805     wlfc_mac_descriptor_t *table;
2806     uint8 if_id = value[0];
2807 
2808     if (if_id < WLFC_MAX_IFNUM) {
2809         table = wlfc->destination_entries.interfaces;
2810         if (table[if_id].occupied) {
2811             if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2812                 table[if_id].state = WLFC_STATE_OPEN;
2813             } else {
2814                 table[if_id].state = WLFC_STATE_CLOSE;
2815             }
2816             return BCME_OK;
2817         }
2818     }
2819     wlfc->stats.interface_update_failed++;
2820 
2821     return BCME_OK;
2822 }
2823 
2824 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle
2825  */
_dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)2826 static int _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8 *value)
2827 {
2828     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2829     wlfc_mac_descriptor_t *table;
2830     wlfc_mac_descriptor_t *desc;
2831     uint8 mac_handle;
2832     uint8 credit;
2833 
2834     table = wlfc->destination_entries.nodes;
2835     mac_handle = value[1];
2836     credit = value[0];
2837 
2838     desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2839     if (desc->occupied) {
2840         desc->requested_credit = credit;
2841 
2842         desc->ac_bitmap = value[0x2] & (~(1 << AC_COUNT));
2843         _dhd_wlfc_add_requested_entry(wlfc, desc);
2844 #if defined(DHD_WLFC_THREAD)
2845         if (credit) {
2846             _dhd_wlfc_thread_wakeup(dhd);
2847         }
2848 #endif /* DHD_WLFC_THREAD */
2849     } else {
2850         wlfc->stats.credit_request_failed++;
2851     }
2852 
2853     return BCME_OK;
2854 }
2855 
2856 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle
2857  */
_dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)2858 static int _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8 *value)
2859 {
2860     athost_wl_status_info_t *wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2861     wlfc_mac_descriptor_t *table;
2862     wlfc_mac_descriptor_t *desc;
2863     uint8 mac_handle;
2864     uint8 packet_count;
2865 
2866     table = wlfc->destination_entries.nodes;
2867     mac_handle = value[1];
2868     packet_count = value[0];
2869 
2870     desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2871     if (desc->occupied) {
2872         desc->requested_packet = packet_count;
2873 
2874         desc->ac_bitmap = value[0x2] & (~(1 << AC_COUNT));
2875         _dhd_wlfc_add_requested_entry(wlfc, desc);
2876 #if defined(DHD_WLFC_THREAD)
2877         if (packet_count) {
2878             _dhd_wlfc_thread_wakeup(dhd);
2879         }
2880 #endif /* DHD_WLFC_THREAD */
2881     } else {
2882         wlfc->stats.packet_request_failed++;
2883     }
2884 
2885     return BCME_OK;
2886 }
2887 
2888 /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the
2889  * dongle */
_dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)2890 static void _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len,
2891                                            uchar *info_buf, uint *info_len)
2892 {
2893     if (info_len) {
2894         /* Check copy length to avoid buffer overrun. In case of length
2895          * exceeding WLHOST_REORDERDATA_TOTLEN, return failure instead sending
2896          * incomplete result of length WLHOST_REORDERDATA_TOTLEN
2897          */
2898         if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
2899             bcopy(val, info_buf, len);
2900             *info_len = len;
2901         } else {
2902             *info_len = 0;
2903         }
2904     }
2905 }
2906 
2907 /*
2908  * public functions
2909  */
2910 
dhd_wlfc_is_supported(dhd_pub_t * dhd)2911 bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
2912 {
2913     bool rc = TRUE;
2914 
2915     if (dhd == NULL) {
2916         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2917         return FALSE;
2918     }
2919 
2920     dhd_os_wlfc_block(dhd);
2921 
2922     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
2923         rc = FALSE;
2924     }
2925 
2926     dhd_os_wlfc_unblock(dhd);
2927 
2928     return rc;
2929 }
2930 
dhd_wlfc_enable(dhd_pub_t * dhd)2931 int dhd_wlfc_enable(dhd_pub_t *dhd)
2932 {
2933     int i, rc = BCME_OK;
2934     athost_wl_status_info_t *wlfc;
2935 
2936     if (dhd == NULL) {
2937         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2938         return BCME_BADARG;
2939     }
2940 
2941     dhd_os_wlfc_block(dhd);
2942 
2943     if (!dhd->wlfc_enabled || dhd->wlfc_state) {
2944         rc = BCME_OK;
2945         goto exit;
2946     }
2947 
2948     /* allocate space to track txstatus propagated from firmware */
2949     dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
2950                                       sizeof(athost_wl_status_info_t));
2951     if (dhd->wlfc_state == NULL) {
2952         rc = BCME_NOMEM;
2953         goto exit;
2954     }
2955 
2956     /* initialize state space */
2957     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
2958     memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2959 
2960     /* remember osh & dhdp */
2961     wlfc->osh = dhd->osh;
2962     wlfc->dhdp = dhd;
2963 
2964     if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2965         wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
2966         if (wlfc->hanger == NULL) {
2967             DHD_OS_PREFREE(dhd, dhd->wlfc_state,
2968                            sizeof(athost_wl_status_info_t));
2969             dhd->wlfc_state = NULL;
2970             rc = BCME_NOMEM;
2971             goto exit;
2972         }
2973     }
2974 
2975     dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2976     /* default to check rx pkt */
2977     dhd->wlfc_rxpkt_chk = TRUE;
2978     if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
2979         dhd->wlfc_rxpkt_chk = FALSE;
2980     }
2981 
2982     /* initialize all interfaces to accept traffic */
2983     for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2984         wlfc->hostif_flow_state[i] = OFF;
2985     }
2986 
2987     _dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
2988                                eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL,
2989                                NULL);
2990 
2991     wlfc->allow_credit_borrow = 0;
2992     wlfc->single_ac = 0;
2993     wlfc->single_ac_timestamp = 0;
2994 
2995 exit:
2996     DHD_ERROR(("%s: ret=%d\n", __FUNCTION__, rc));
2997     dhd_os_wlfc_unblock(dhd);
2998 
2999     return rc;
3000 } /* dhd_wlfc_enable */
3001 
3002 #ifdef SUPPORT_P2P_GO_PS
3003 
3004 /**
3005  * Called when the host platform enters a lower power mode, eg right before a
3006  * system hibernate. SUPPORT_P2P_GO_PS specific function.
3007  */
dhd_wlfc_suspend(dhd_pub_t * dhd)3008 int dhd_wlfc_suspend(dhd_pub_t *dhd)
3009 {
3010     uint32 tlv = 0;
3011 
3012     DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
3013     if (!dhd->wlfc_enabled) {
3014         return -1;
3015     }
3016 
3017     if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0)) {
3018         return -1;
3019     }
3020     if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0) {
3021         return 0;
3022     }
3023     tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3024     if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3025         return -1;
3026     }
3027 
3028     return 0;
3029 }
3030 
3031 /**
3032  * Called when the host platform resumes from a power management operation, eg
3033  * resume after a system hibernate. SUPPORT_P2P_GO_PS specific function.
3034  */
dhd_wlfc_resume(dhd_pub_t * dhd)3035 int dhd_wlfc_resume(dhd_pub_t *dhd)
3036 {
3037     uint32 tlv = 0;
3038 
3039     DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
3040     if (!dhd->wlfc_enabled) {
3041         return -1;
3042     }
3043 
3044     if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0)) {
3045         return -1;
3046     }
3047     if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
3048         (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) {
3049         return 0;
3050     }
3051     tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3052     if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3053         return -1;
3054     }
3055 
3056     return 0;
3057 }
3058 
3059 #endif /* SUPPORT_P2P_GO_PS */
3060 
3061 /** A flow control header was received from firmware, containing one or more
3062  * TLVs */
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)3063 int dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void *pktbuf, int tlv_hdr_len,
3064                                uchar *reorder_info_buf, uint *reorder_info_len)
3065 {
3066     uint8 type, len;
3067     uint8 *value;
3068     uint8 *tmpbuf;
3069     uint16 remainder = (uint16)tlv_hdr_len;
3070     uint16 processed = 0;
3071     athost_wl_status_info_t *wlfc = NULL;
3072     void *entry;
3073 
3074     if ((dhd == NULL) || (pktbuf == NULL)) {
3075         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3076         return BCME_BADARG;
3077     }
3078 
3079     dhd_os_wlfc_block(dhd);
3080 
3081     if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
3082         if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3083             dhd_os_wlfc_unblock(dhd);
3084             return WLFC_UNSUPPORTED;
3085         }
3086         wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
3087     }
3088 
3089     tmpbuf = (uint8 *)PKTDATA(dhd->osh, pktbuf);
3090 
3091     if (remainder) {
3092         while ((processed < (WLFC_MAX_PENDING_DATALEN * 0x2)) &&
3093                (remainder > 0)) {
3094             type = tmpbuf[processed];
3095             if (type == WLFC_CTL_TYPE_FILLER) {
3096                 remainder -= 1;
3097                 processed += 1;
3098                 continue;
3099             }
3100 
3101             len = tmpbuf[processed + 1];
3102             value = &tmpbuf[processed + 0x2];
3103 
3104             if (remainder < (0x2 + len)) {
3105                 break;
3106             }
3107 
3108             remainder -= 0x2 + len;
3109             processed += 0x2 + len;
3110             entry = NULL;
3111 
3112             DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
3113                       __FUNCTION__, __LINE__, type, remainder, processed));
3114 
3115             if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS) {
3116                 _dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
3117                                                reorder_info_len);
3118             }
3119 
3120             if (wlfc == NULL) {
3121                 ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
3122 
3123                 if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
3124                     type != WLFC_CTL_TYPE_TRANS_ID) {
3125                     DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
3126                               " type %d remainder %d processed %d\n",
3127                               __FUNCTION__, __LINE__, type, remainder,
3128                               processed));
3129                 }
3130                 continue;
3131             }
3132 
3133             if (type == WLFC_CTL_TYPE_TXSTATUS) {
3134                 _dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
3135             } else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
3136                 uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
3137 
3138                 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
3139                     compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
3140                 }
3141                 _dhd_wlfc_compressed_txstatus_update(
3142                     dhd, value, value[compcnt_offset], &entry);
3143             } else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
3144                 _dhd_wlfc_fifocreditback_indicate(dhd, value);
3145             } else if (type == WLFC_CTL_TYPE_RSSI) {
3146                 _dhd_wlfc_rssi_indicate(dhd, value);
3147             } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
3148                 _dhd_wlfc_credit_request(dhd, value);
3149             } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
3150                 _dhd_wlfc_packet_request(dhd, value);
3151             } else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
3152                        (type == WLFC_CTL_TYPE_MAC_CLOSE)) {
3153                 _dhd_wlfc_psmode_update(dhd, value, type);
3154             } else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
3155                        (type == WLFC_CTL_TYPE_MACDESC_DEL)) {
3156                 _dhd_wlfc_mac_table_update(dhd, value, type);
3157             } else if (type == WLFC_CTL_TYPE_TRANS_ID) {
3158                 _dhd_wlfc_dbg_senum_check(dhd, value);
3159             } else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
3160                        (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
3161                 _dhd_wlfc_interface_update(dhd, value, type);
3162             }
3163 
3164 #ifndef BCMDBUS
3165             if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
3166                 /* suppress all packets for this mac entry from bus->txq */
3167                 _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
3168             }
3169 #endif    /* !BCMDBUS */
3170         } /* while */
3171 
3172         if (remainder != 0 && wlfc) {
3173             /* trouble..., something is not right */
3174             wlfc->stats.tlv_parse_failed++;
3175         }
3176     } /* if */
3177 
3178     if (wlfc) {
3179         wlfc->stats.dhd_hdrpulls++;
3180     }
3181 
3182     dhd_os_wlfc_unblock(dhd);
3183     return BCME_OK;
3184 }
3185 
3186 KERNEL_THREAD_RETURN_TYPE
dhd_wlfc_transfer_packets(void * data)3187 dhd_wlfc_transfer_packets(void *data)
3188 {
3189     dhd_pub_t *dhdp = (dhd_pub_t *)data;
3190     int ac, single_ac = 0, rc = BCME_OK;
3191     dhd_wlfc_commit_info_t commit_info;
3192     athost_wl_status_info_t *ctx;
3193     int bus_retry_count = 0;
3194     int pkt_send = 0;
3195     int pkt_send_per_ac = 0;
3196 
3197     uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
3198     uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
3199     uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
3200     bool no_credit = FALSE;
3201 
3202     int lender;
3203 
3204 #if defined(DHD_WLFC_THREAD)
3205     /* wait till someone wakeup me up, will change it at running time */
3206     int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
3207 #endif /* defined(DHD_WLFC_THREAD) */
3208 
3209 #if defined(DHD_WLFC_THREAD)
3210     while (1) {
3211         bus_retry_count = 0;
3212         pkt_send = 0;
3213         tx_map = 0;
3214         rx_map = 0;
3215         packets_map = 0;
3216         wait_msec = wait_event_interruptible_timeout(
3217             dhdp->wlfc_wqhead, dhdp->wlfc_thread_go, wait_msec);
3218         if (kthread_should_stop()) {
3219             break;
3220         }
3221         dhdp->wlfc_thread_go = FALSE;
3222 
3223         dhd_os_wlfc_block(dhdp);
3224 #endif /* defined(DHD_WLFC_THREAD) */
3225         ctx = (athost_wl_status_info_t *)dhdp->wlfc_state;
3226 #if defined(DHD_WLFC_THREAD)
3227         if (!ctx) {
3228             goto exit;
3229         }
3230 #endif /* defined(DHD_WLFC_THREAD) */
3231 
3232         memset(&commit_info, 0, sizeof(commit_info));
3233 
3234         /*
3235         Commit packets for regular AC traffic. Higher priority first.
3236         First, use up FIFO credits available to each AC. Based on distribution
3237         and credits left, borrow from other ACs as applicable
3238 
3239         -NOTE:
3240         If the bus between the host and firmware is overwhelmed by the
3241         traffic from host, it is possible that higher priority traffic
3242         starves the lower priority queue. If that occurs often, we may
3243         have to employ weighted round-robin or ucode scheme to avoid
3244         low priority packet starvation.
3245         */
3246 
3247         for (ac = AC_COUNT; ac >= 0; ac--) {
3248             if (dhdp->wlfc_rxpkt_chk) {
3249                 /* check rx packet */
3250                 uint32 curr_t = OSL_SYSUPTIME(), delta;
3251 
3252                 delta = curr_t - ctx->rx_timestamp[ac];
3253                 if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
3254                     rx_map |= (1 << ac);
3255                 }
3256             }
3257 
3258             if (ctx->pkt_cnt_per_ac[ac] == 0) {
3259                 continue;
3260             }
3261 
3262             tx_map |= (1 << ac);
3263             single_ac = ac + 1;
3264             pkt_send_per_ac = 0;
3265             while ((FALSE == dhdp->proptxstatus_txoff) &&
3266                    (pkt_send_per_ac < WLFC_PACKET_BOUND)) {
3267                 /* packets from delayQ with less priority are fresh and
3268                  * they'd need header and have no MAC entry
3269                  */
3270                 no_credit = (ctx->FIFO_credit[ac] < 1);
3271                 if (dhdp->proptxstatus_credit_ignore ||
3272                     ((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
3273                     no_credit = FALSE;
3274                 }
3275 
3276                 lender = -1;
3277 #ifdef LIMIT_BORROW
3278                 if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
3279                     dhdp->wlfc_borrow_allowed) {
3280                     /* try borrow from lower priority */
3281                     lender = _dhd_wlfc_borrow_credit(ctx, ac - 1, ac, FALSE);
3282                     if (lender != -1) {
3283                         no_credit = FALSE;
3284                     }
3285                 }
3286 #endif // endif
3287                 commit_info.needs_hdr = 1;
3288                 commit_info.mac_entry = NULL;
3289                 commit_info.p = _dhd_wlfc_deque_delayedq(
3290                     ctx, ac, &(commit_info.ac_fifo_credit_spent),
3291                     &(commit_info.needs_hdr), &(commit_info.mac_entry),
3292                     no_credit);
3293                 commit_info.pkt_type = (commit_info.needs_hdr)
3294                                            ? eWLFC_PKTTYPE_DELAYED
3295                                            : eWLFC_PKTTYPE_SUPPRESSED;
3296 
3297                 if (commit_info.p == NULL) {
3298 #ifdef LIMIT_BORROW
3299                     if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3300                         _dhd_wlfc_return_credit(ctx, lender, ac);
3301                     }
3302 #endif // endif
3303                     break;
3304                 }
3305 
3306                 if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
3307                     ASSERT(ctx->FIFO_credit[ac] >=
3308                            commit_info.ac_fifo_credit_spent);
3309                 }
3310                 /* here we can ensure have credit or no credit needed */
3311                 rc = _dhd_wlfc_handle_packet_commit(
3312                     ctx, ac, &commit_info, ctx->fcommit, ctx->commit_ctx);
3313                 /* Bus commits may fail (e.g. flow control); abort after retries
3314                  */
3315                 if (rc == BCME_OK) {
3316                     pkt_send++;
3317                     pkt_send_per_ac++;
3318                     if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
3319                         ctx->FIFO_credit[ac]--;
3320                     }
3321 #ifdef LIMIT_BORROW
3322                     else if (!commit_info.ac_fifo_credit_spent &&
3323                              (lender != -1) && dhdp->wlfc_borrow_allowed) {
3324                         _dhd_wlfc_return_credit(ctx, lender, ac);
3325                     }
3326 #endif // endif
3327                 } else {
3328 #ifdef LIMIT_BORROW
3329                     if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3330                         _dhd_wlfc_return_credit(ctx, lender, ac);
3331                     }
3332 #endif // endif
3333                     bus_retry_count++;
3334                     if (bus_retry_count >= BUS_RETRIES) {
3335                         DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3336                         goto exit;
3337                     }
3338                 }
3339             }
3340 
3341             if (ctx->pkt_cnt_per_ac[ac]) {
3342                 packets_map |= (1 << ac);
3343             }
3344         }
3345 
3346         if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
3347             /* nothing send out or remain in queue */
3348             rc = BCME_OK;
3349             goto exit;
3350         }
3351 
3352         if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
3353             /* only one tx ac exist and no higher rx ac */
3354             if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
3355                 ac = single_ac - 1;
3356             } else {
3357                 uint32 delta;
3358                 uint32 curr_t = OSL_SYSUPTIME();
3359 
3360                 if (single_ac != ctx->single_ac) {
3361                     /* new single ac traffic (first single ac or different
3362                      * single ac) */
3363                     ctx->allow_credit_borrow = 0;
3364                     ctx->single_ac_timestamp = curr_t;
3365                     ctx->single_ac = (uint8)single_ac;
3366                     rc = BCME_OK;
3367                     goto exit;
3368                 }
3369                 /* same ac traffic, check if it lasts enough time */
3370                 delta = curr_t - ctx->single_ac_timestamp;
3371 
3372                 if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
3373                     /* wait enough time, can borrow now */
3374                     ctx->allow_credit_borrow = 1;
3375                     ac = single_ac - 1;
3376                 } else {
3377                     rc = BCME_OK;
3378                     goto exit;
3379                 }
3380             }
3381         } else {
3382             /* If we have multiple AC traffic, turn off borrowing, mark time and
3383              * bail out */
3384             ctx->allow_credit_borrow = 0;
3385             ctx->single_ac_timestamp = 0;
3386             ctx->single_ac = 0;
3387             rc = BCME_OK;
3388             goto exit;
3389         }
3390 
3391         if (packets_map == 0) {
3392             /* nothing to send, skip borrow */
3393             rc = BCME_OK;
3394             goto exit;
3395         }
3396 
3397         /* At this point, borrow all credits only for ac */
3398         while (FALSE == dhdp->proptxstatus_txoff) {
3399 #ifdef LIMIT_BORROW
3400             if (dhdp->wlfc_borrow_allowed) {
3401                 if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac,
3402                                                       TRUE)) == -1) {
3403                     break;
3404                 }
3405             } else {
3406                 break;
3407             }
3408 #endif // endif
3409             commit_info.p = _dhd_wlfc_deque_delayedq(
3410                 ctx, ac, &(commit_info.ac_fifo_credit_spent),
3411                 &(commit_info.needs_hdr), &(commit_info.mac_entry), FALSE);
3412             if (commit_info.p == NULL) {
3413                 /* before borrow only one ac exists and now this only ac is
3414                  * empty */
3415 #ifdef LIMIT_BORROW
3416                 _dhd_wlfc_return_credit(ctx, lender, ac);
3417 #endif // endif
3418                 break;
3419             }
3420 
3421             commit_info.pkt_type = (commit_info.needs_hdr)
3422                                        ? eWLFC_PKTTYPE_DELAYED
3423                                        : eWLFC_PKTTYPE_SUPPRESSED;
3424 
3425             rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3426                                                 ctx->fcommit, ctx->commit_ctx);
3427             /* Bus commits may fail (e.g. flow control); abort after retries */
3428             if (rc == BCME_OK) {
3429                 pkt_send++;
3430                 if (commit_info.ac_fifo_credit_spent) {
3431 #ifndef LIMIT_BORROW
3432                     ctx->FIFO_credit[ac]--;
3433 #endif // endif
3434                 } else {
3435 #ifdef LIMIT_BORROW
3436                     _dhd_wlfc_return_credit(ctx, lender, ac);
3437 #endif // endif
3438                 }
3439             } else {
3440 #ifdef LIMIT_BORROW
3441                 _dhd_wlfc_return_credit(ctx, lender, ac);
3442 #endif // endif
3443                 bus_retry_count++;
3444                 if (bus_retry_count >= BUS_RETRIES) {
3445                     DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3446                     goto exit;
3447                 }
3448             }
3449         }
3450 
3451         BCM_REFERENCE(pkt_send);
3452 
3453     exit:
3454 #if defined(DHD_WLFC_THREAD)
3455         dhd_os_wlfc_unblock(dhdp);
3456         if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
3457             wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
3458         } else {
3459             wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
3460         }
3461     }
3462     return 0;
3463 #else
3464     return rc;
3465 #endif /* defined(DHD_WLFC_THREAD) */
3466 }
3467 
3468 /**
3469  * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS
3470  * layer. Called by eg dhd_sendpkt().
3471  *     @param[in] dhdp                  Pointer to public DHD structure
3472  *     @param[in] fcommit               Pointer to transmit function of next
3473  * layer
3474  *     @param[in] commit_ctx            Opaque context used when calling next
3475  * layer
3476  *     @param[in] pktbuf                Packet to send
3477  *     @param[in] need_toggle_host_if   If TRUE, resets flag ctx->toggle_host_if
3478  */
dhd_wlfc_commit_packets(dhd_pub_t * dhdp,f_commitpkt_t fcommit,void * commit_ctx,void * pktbuf,bool need_toggle_host_if)3479 int dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit,
3480                             void *commit_ctx, void *pktbuf,
3481                             bool need_toggle_host_if)
3482 {
3483     int rc = BCME_OK;
3484     athost_wl_status_info_t *ctx;
3485 
3486 #if defined(DHD_WLFC_THREAD)
3487     if (!pktbuf) {
3488         return BCME_OK;
3489     }
3490 #endif /* defined(DHD_WLFC_THREAD) */
3491 
3492     if ((dhdp == NULL) || (fcommit == NULL)) {
3493         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3494         return BCME_BADARG;
3495     }
3496 
3497     dhd_os_wlfc_block(dhdp);
3498 
3499     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3500         if (pktbuf) {
3501             DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
3502         }
3503         rc = WLFC_UNSUPPORTED;
3504         goto exit;
3505     }
3506 
3507     ctx = (athost_wl_status_info_t *)dhdp->wlfc_state;
3508 
3509 #ifdef BCMDBUS
3510     if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
3511         if (pktbuf) {
3512             PKTFREE(ctx->osh, pktbuf, TRUE);
3513             rc = BCME_OK;
3514         }
3515         goto exit;
3516     }
3517 #endif /* BCMDBUS */
3518 
3519     if (dhdp->proptxstatus_module_ignore) {
3520         if (pktbuf) {
3521             uint32 htod = 0;
3522             WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
3523             _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
3524             if (fcommit(commit_ctx, pktbuf)) {
3525                 /* free it if failed, otherwise do it in tx complete cb */
3526                 PKTFREE(ctx->osh, pktbuf, TRUE);
3527             }
3528             rc = BCME_OK;
3529         }
3530         goto exit;
3531     }
3532 
3533     if (pktbuf) {
3534         int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
3535         ASSERT(ac <= AC_COUNT);
3536         DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
3537         /* en-queue the packets to respective queue. */
3538         rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
3539         if (rc) {
3540             _dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
3541         } else {
3542             ctx->stats.pktin++;
3543             ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
3544         }
3545     }
3546 
3547     if (!ctx->fcommit) {
3548         ctx->fcommit = fcommit;
3549     } else {
3550         ASSERT(ctx->fcommit == fcommit);
3551     }
3552     if (!ctx->commit_ctx) {
3553         ctx->commit_ctx = commit_ctx;
3554     } else {
3555         ASSERT(ctx->commit_ctx == commit_ctx);
3556     }
3557 
3558 #if defined(DHD_WLFC_THREAD)
3559     _dhd_wlfc_thread_wakeup(dhdp);
3560 #else
3561     dhd_wlfc_transfer_packets(dhdp);
3562 #endif /* defined(DHD_WLFC_THREAD) */
3563 
3564 exit:
3565     dhd_os_wlfc_unblock(dhdp);
3566     return rc;
3567 } /* dhd_wlfc_commit_packets */
3568 
3569 /**
3570  * Called when the (lower) DBUS layer indicates completion (succesfull or not)
3571  * of a transmit packet
3572  */
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)3573 int dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
3574 {
3575     athost_wl_status_info_t *wlfc;
3576     wlfc_mac_descriptor_t *entry;
3577     void *pout = NULL;
3578     int rtn = BCME_OK;
3579     if ((dhd == NULL) || (txp == NULL)) {
3580         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3581         return BCME_BADARG;
3582     }
3583 
3584     bcm_pkt_validate_chk(txp);
3585 
3586     dhd_os_wlfc_block(dhd);
3587 
3588     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3589         rtn = WLFC_UNSUPPORTED;
3590         goto EXIT;
3591     }
3592 
3593     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
3594     if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
3595 #ifdef PROP_TXSTATUS_DEBUG
3596         wlfc->stats.signal_only_pkts_freed++;
3597 #endif // endif
3598         /* is this a signal-only packet? */
3599         _dhd_wlfc_pullheader(wlfc, txp);
3600         PKTFREE(wlfc->osh, txp, TRUE);
3601         goto EXIT;
3602     }
3603 
3604     entry = _dhd_wlfc_find_table_entry(wlfc, txp);
3605     ASSERT(entry);
3606 
3607     if (!success || dhd->proptxstatus_txstatus_ignore) {
3608         WLFC_DBGMESG(
3609             ("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
3610              __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
3611         if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3612             _dhd_wlfc_hanger_poppkt(
3613                 wlfc->hanger,
3614                 WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout,
3615                 TRUE);
3616             ASSERT(txp == pout);
3617         }
3618 
3619         /* indicate failure and free the packet */
3620         dhd_txcomplete(dhd, txp, success);
3621 
3622         /* return the credit, if necessary */
3623         _dhd_wlfc_return_implied_credit(wlfc, txp);
3624 
3625         if (entry->transit_count) {
3626             entry->transit_count--;
3627         }
3628         if (entry->suppr_transit_count) {
3629             entry->suppr_transit_count--;
3630         }
3631         wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))]
3632                             [DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
3633         wlfc->stats.pktout++;
3634         PKTFREE(wlfc->osh, txp, TRUE);
3635     } else {
3636         /* bus confirmed pkt went to firmware side */
3637         if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
3638             _dhd_wlfc_enque_afq(wlfc, txp);
3639         } else {
3640             int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
3641             _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
3642                                       WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
3643         }
3644     }
3645 
3646     ASSERT(entry->onbus_pkts_count > 0);
3647     if (entry->onbus_pkts_count > 0) {
3648         entry->onbus_pkts_count--;
3649     }
3650     if (entry->suppressed && (!entry->onbus_pkts_count) &&
3651         (!entry->suppr_transit_count)) {
3652         entry->suppressed = FALSE;
3653     }
3654 EXIT:
3655     dhd_os_wlfc_unblock(dhd);
3656     return rtn;
3657 } /* dhd_wlfc_txcomplete */
3658 
dhd_wlfc_init(dhd_pub_t * dhd)3659 int dhd_wlfc_init(dhd_pub_t *dhd)
3660 {
3661     /* enable all signals & indicate host proptxstatus logic is active */
3662     uint32 tlv, mode, fw_caps;
3663     int ret = 0;
3664 
3665     if (dhd == NULL) {
3666         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3667         return BCME_BADARG;
3668     }
3669 
3670     dhd_os_wlfc_block(dhd);
3671     if (dhd->wlfc_enabled) {
3672         DHD_INFO(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
3673         dhd_os_wlfc_unblock(dhd);
3674         return BCME_OK;
3675     }
3676     dhd->wlfc_enabled = TRUE;
3677     dhd_os_wlfc_unblock(dhd);
3678 
3679     tlv = WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS |
3680           WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
3681           WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
3682           WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3683 
3684     /*
3685     try to enable/disable signaling by sending "tlv" iovar. if that fails,
3686     fallback to no flow control? Print a message for now.
3687     */
3688 
3689     /* enable proptxtstatus signaling by default */
3690     if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3691         /*
3692         Leaving the message for now, it should be removed after a while; once
3693         the tlv situation is stable.
3694         */
3695         DHD_INFO(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
3696                   dhd->wlfc_enabled ? "enabled" : "disabled", tlv));
3697     }
3698 
3699     mode = 0;
3700 
3701     /* query caps */
3702     ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR,
3703                                     FALSE, 0);
3704     if (!ret) {
3705         DHD_INFO(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__,
3706                   fw_caps));
3707 
3708         if (WLFC_IS_OLD_DEF(fw_caps)) {
3709 #ifdef BCMDBUS
3710             mode = WLFC_MODE_HANGER;
3711 #else
3712             /* enable proptxtstatus v2 by default */
3713             mode = WLFC_MODE_AFQ;
3714 #endif /* BCMDBUS */
3715         } else {
3716             WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
3717 #ifdef BCMDBUS
3718             WLFC_SET_AFQ(mode, 0);
3719 #endif /* BCMDBUS */
3720             WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps));
3721             WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
3722         }
3723         ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR,
3724                                         TRUE, 0);
3725     }
3726 
3727     dhd_os_wlfc_block(dhd);
3728 
3729     dhd->wlfc_mode = 0;
3730     if (ret >= 0) {
3731         if (WLFC_IS_OLD_DEF(mode)) {
3732             WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
3733         } else {
3734             dhd->wlfc_mode = mode;
3735         }
3736     }
3737 
3738     DHD_INFO(
3739         ("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
3740 #ifdef LIMIT_BORROW
3741     dhd->wlfc_borrow_allowed = TRUE;
3742 #endif // endif
3743     dhd_os_wlfc_unblock(dhd);
3744 
3745     if (dhd->plat_init) {
3746         dhd->plat_init((void *)dhd);
3747     }
3748 
3749     return BCME_OK;
3750 } /* dhd_wlfc_init */
3751 
3752 /** AMPDU host reorder specific function */
dhd_wlfc_hostreorder_init(dhd_pub_t * dhd)3753 int dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
3754 {
3755     /* enable only ampdu hostreorder here */
3756     uint32 tlv;
3757 
3758     if (dhd == NULL) {
3759         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3760         return BCME_BADARG;
3761     }
3762 
3763     DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
3764 
3765     tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3766 
3767     /* enable proptxtstatus signaling by default */
3768     if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3769         DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
3770                    __FUNCTION__));
3771     } else {
3772         /*
3773         Leaving the message for now, it should be removed after a while; once
3774         the tlv situation is stable.
3775         */
3776         DHD_ERROR(
3777             ("%s(): successful bdcv2 tlv signaling, %d\n", __FUNCTION__, tlv));
3778     }
3779 
3780     dhd_os_wlfc_block(dhd);
3781     dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
3782     dhd_os_wlfc_unblock(dhd);
3783     /* terence 20161229: enable ampdu_hostreorder if tlv enable hostreorder */
3784     dhd_conf_set_intiovar(dhd, 0, WLC_SET_VAR, "ampdu_hostreorder", 1, 0, TRUE);
3785 
3786     return BCME_OK;
3787 }
3788 
dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)3789 int dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
3790 {
3791     if (dhd == NULL) {
3792         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3793         return BCME_BADARG;
3794     }
3795 
3796     dhd_os_wlfc_block(dhd);
3797 
3798     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3799         dhd_os_wlfc_unblock(dhd);
3800         return WLFC_UNSUPPORTED;
3801     }
3802 
3803 #ifndef BCMDBUS
3804     _dhd_wlfc_cleanup_txq(dhd, fn, arg);
3805 #endif /* !BCMDBUS */
3806 
3807     dhd_os_wlfc_unblock(dhd);
3808 
3809     return BCME_OK;
3810 }
3811 
3812 /** release all packet resources */
dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)3813 int dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
3814 {
3815     if (dhd == NULL) {
3816         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3817         return BCME_BADARG;
3818     }
3819 
3820     dhd_os_wlfc_block(dhd);
3821 
3822     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3823         dhd_os_wlfc_unblock(dhd);
3824         return WLFC_UNSUPPORTED;
3825     }
3826 
3827     _dhd_wlfc_cleanup(dhd, fn, arg);
3828 
3829     dhd_os_wlfc_unblock(dhd);
3830 
3831     return BCME_OK;
3832 }
3833 
dhd_wlfc_deinit(dhd_pub_t * dhd)3834 int dhd_wlfc_deinit(dhd_pub_t *dhd)
3835 {
3836     /* cleanup all psq related resources */
3837     athost_wl_status_info_t *wlfc;
3838     uint32 tlv = 0;
3839     uint32 hostreorder = 0;
3840 
3841     if (dhd == NULL) {
3842         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3843         return BCME_BADARG;
3844     }
3845 
3846     dhd_os_wlfc_block(dhd);
3847     if (!dhd->wlfc_enabled) {
3848         DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
3849         dhd_os_wlfc_unblock(dhd);
3850         return BCME_OK;
3851     }
3852 
3853     dhd->wlfc_enabled = FALSE;
3854     dhd_os_wlfc_unblock(dhd);
3855 
3856     /* query ampdu hostreorder */
3857     (void)dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder", &hostreorder,
3858                                     WLC_GET_VAR, FALSE, 0);
3859 
3860     if (hostreorder) {
3861         tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3862         DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
3863                    __FUNCTION__, __LINE__));
3864     }
3865 
3866     /* Disable proptxtstatus signaling for deinit */
3867     (void)dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
3868 
3869     dhd_os_wlfc_block(dhd);
3870 
3871     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3872         dhd_os_wlfc_unblock(dhd);
3873         return WLFC_UNSUPPORTED;
3874     }
3875 
3876     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
3877 
3878     _dhd_wlfc_cleanup(dhd, NULL, NULL);
3879 
3880     if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3881         int i;
3882         wlfc_hanger_t *h = (wlfc_hanger_t *)wlfc->hanger;
3883         for (i = 0; i < h->max_items; i++) {
3884             if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
3885                 _dhd_wlfc_hanger_free_pkt(wlfc, i,
3886                                           WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
3887             }
3888         }
3889 
3890         /* delete hanger */
3891         _dhd_wlfc_hanger_delete(dhd, h);
3892     }
3893 
3894     /* free top structure */
3895     DHD_OS_PREFREE(dhd, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
3896     dhd->wlfc_state = NULL;
3897     dhd->proptxstatus_mode =
3898         hostreorder ? WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
3899 
3900     dhd_os_wlfc_unblock(dhd);
3901 
3902     if (dhd->plat_deinit) {
3903         dhd->plat_deinit((void *)dhd);
3904     }
3905     return BCME_OK;
3906 } /* dhd_wlfc_init */
3907 
3908 /**
3909  * Called on an interface event (WLC_E_IF) indicated by firmware
3910  *     @param[in] dhdp   Pointer to public DHD structure
3911  *     @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or
3912  * eWLFC_MAC_ENTRY_ACTION_ADD
3913  */
dhd_wlfc_interface_event(dhd_pub_t * dhdp,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)3914 int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid,
3915                              uint8 iftype, uint8 *ea)
3916 {
3917     int rc;
3918 
3919     if (dhdp == NULL) {
3920         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3921         return BCME_BADARG;
3922     }
3923 
3924     dhd_os_wlfc_block(dhdp);
3925 
3926     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3927         dhd_os_wlfc_unblock(dhdp);
3928         return WLFC_UNSUPPORTED;
3929     }
3930 
3931     rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid,
3932                                           iftype, ea);
3933 
3934     dhd_os_wlfc_unblock(dhdp);
3935     return rc;
3936 }
3937 
3938 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
dhd_wlfc_FIFOcreditmap_event(dhd_pub_t * dhdp,uint8 * event_data)3939 int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8 *event_data)
3940 {
3941     int rc;
3942 
3943     if (dhdp == NULL) {
3944         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3945         return BCME_BADARG;
3946     }
3947 
3948     dhd_os_wlfc_block(dhdp);
3949 
3950     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3951         dhd_os_wlfc_unblock(dhdp);
3952         return WLFC_UNSUPPORTED;
3953     }
3954 
3955     rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
3956 
3957     dhd_os_wlfc_unblock(dhdp);
3958 
3959     return rc;
3960 }
3961 #ifdef LIMIT_BORROW
dhd_wlfc_disable_credit_borrow_event(dhd_pub_t * dhdp,uint8 * event_data)3962 int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8 *event_data)
3963 {
3964     if (dhdp == NULL || event_data == NULL) {
3965         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3966         return BCME_BADARG;
3967     }
3968     dhd_os_wlfc_block(dhdp);
3969     dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
3970     dhd_os_wlfc_unblock(dhdp);
3971 
3972     return BCME_OK;
3973 }
3974 #endif /* LIMIT_BORROW */
3975 
3976 /**
3977  * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle
3978  * (broadcast/multicast specific)
3979  */
dhd_wlfc_BCMCCredit_support_event(dhd_pub_t * dhdp)3980 int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
3981 {
3982     int rc;
3983 
3984     if (dhdp == NULL) {
3985         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3986         return BCME_BADARG;
3987     }
3988 
3989     dhd_os_wlfc_block(dhdp);
3990 
3991     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3992         dhd_os_wlfc_unblock(dhdp);
3993         return WLFC_UNSUPPORTED;
3994     }
3995 
3996     rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
3997 
3998     dhd_os_wlfc_unblock(dhdp);
3999     return rc;
4000 }
4001 
4002 /** debug specific function */
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)4003 int dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
4004 {
4005     int i;
4006     uint8 *ea;
4007     athost_wl_status_info_t *wlfc;
4008     wlfc_hanger_t *h;
4009     wlfc_mac_descriptor_t *mac_table;
4010     wlfc_mac_descriptor_t *interfaces;
4011     char *iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
4012 
4013     if (!dhdp || !strbuf) {
4014         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4015         return BCME_BADARG;
4016     }
4017 
4018     dhd_os_wlfc_block(dhdp);
4019 
4020     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4021         dhd_os_wlfc_unblock(dhdp);
4022         return WLFC_UNSUPPORTED;
4023     }
4024 
4025     wlfc = (athost_wl_status_info_t *)dhdp->wlfc_state;
4026 
4027     h = (wlfc_hanger_t *)wlfc->hanger;
4028     if (h == NULL) {
4029         bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4030     }
4031 
4032     mac_table = wlfc->destination_entries.nodes;
4033     interfaces = wlfc->destination_entries.interfaces;
4034     bcm_bprintf(strbuf, "---- wlfc stats ----\n");
4035 
4036     if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
4037         h = (wlfc_hanger_t *)wlfc->hanger;
4038         if (h == NULL) {
4039             bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4040         } else {
4041             bcm_bprintf(strbuf,
4042                         "wlfc hanger (pushed,popped,f_push,"
4043                         "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
4044                         h->pushed, h->popped, h->failed_to_push,
4045                         h->failed_to_pop, h->failed_slotfind,
4046                         (h->pushed - h->popped));
4047         }
4048     }
4049 
4050     bcm_bprintf(strbuf,
4051                 "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
4052                 "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
4053                 wlfc->stats.tlv_parse_failed, wlfc->stats.credit_request_failed,
4054                 wlfc->stats.mac_update_failed, wlfc->stats.psmode_update_failed,
4055                 wlfc->stats.delayq_full_error, wlfc->stats.rollback_failed);
4056 
4057     bcm_bprintf(
4058         strbuf,
4059         "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
4060         "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
4061         "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
4062         wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0],
4063         wlfc->stats.send_pkts[0], wlfc->stats.drop_pkts[0],
4064         wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
4065         wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1],
4066         wlfc->stats.send_pkts[1], wlfc->stats.drop_pkts[0x2],
4067         wlfc->stats.drop_pkts[0x3], wlfc->stats.ooo_pkts[1],
4068         wlfc->Init_FIFO_credit[0x2], wlfc->FIFO_credit[0x2],
4069         wlfc->stats.send_pkts[0x2], wlfc->stats.drop_pkts[0x4],
4070         wlfc->stats.drop_pkts[0x5], wlfc->stats.ooo_pkts[0x2],
4071         wlfc->Init_FIFO_credit[0x3], wlfc->FIFO_credit[0x3],
4072         wlfc->stats.send_pkts[0x3], wlfc->stats.drop_pkts[0x6],
4073         wlfc->stats.drop_pkts[0x7], wlfc->stats.ooo_pkts[0x3],
4074         wlfc->Init_FIFO_credit[0x4], wlfc->FIFO_credit[0x4],
4075         wlfc->stats.send_pkts[0x4], wlfc->stats.drop_pkts[0x8],
4076         wlfc->stats.drop_pkts[0x9], wlfc->stats.ooo_pkts[0x4]);
4077 
4078     bcm_bprintf(strbuf, "\n");
4079     for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4080         if (interfaces[i].occupied) {
4081             char *iftype_desc;
4082 
4083             if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT) {
4084                 iftype_desc = "<Unknown";
4085             } else {
4086                 iftype_desc = iftypes[interfaces[i].iftype];
4087             }
4088 
4089             ea = interfaces[i].ea;
4090             bcm_bprintf(strbuf,
4091                         "INTERFACE[%d].ea = "
4092                         "[" MACDBG "], if:%d, type: %s "
4093                         "netif_flow_control:%s\n",
4094                         i, MAC2STRDBG(ea), interfaces[i].interface_id,
4095                         iftype_desc,
4096                         ((wlfc->hostif_flow_state[i] == OFF) ? " OFF" : " ON"));
4097 
4098             bcm_bprintf(
4099                 strbuf,
4100                 "INTERFACE[%d].PSQ(len,state,credit),"
4101                 "(trans,supp_trans,onbus)"
4102                 "= (%d,%s,%d),(%d,%d,%d)\n",
4103                 i, interfaces[i].psq.n_pkts_tot,
4104                 ((interfaces[i].state == WLFC_STATE_OPEN) ? "OPEN" : "CLOSE"),
4105                 interfaces[i].requested_credit, interfaces[i].transit_count,
4106                 interfaces[i].suppr_transit_count,
4107                 interfaces[i].onbus_pkts_count);
4108 
4109             bcm_bprintf(
4110                 strbuf,
4111                 "INTERFACE[%d].PSQ"
4112                 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4113                 "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
4114                 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4115                 i, interfaces[i].psq.q[0].n_pkts, interfaces[i].psq.q[1].n_pkts,
4116                 interfaces[i].afq.q[0].n_pkts, interfaces[i].psq.q[0x2].n_pkts,
4117                 interfaces[i].psq.q[0x3].n_pkts, interfaces[i].afq.q[1].n_pkts,
4118                 interfaces[i].psq.q[0x4].n_pkts, interfaces[i].psq.q[0x5].n_pkts,
4119                 interfaces[i].afq.q[0x2].n_pkts, interfaces[i].psq.q[0x6].n_pkts,
4120                 interfaces[i].psq.q[0x7].n_pkts, interfaces[i].afq.q[0x3].n_pkts,
4121                 interfaces[i].psq.q[0x8].n_pkts, interfaces[i].psq.q[0x9].n_pkts,
4122                 interfaces[i].afq.q[0x4].n_pkts);
4123         }
4124     }
4125 
4126     bcm_bprintf(strbuf, "\n");
4127     for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4128         if (mac_table[i].occupied) {
4129             ea = mac_table[i].ea;
4130             bcm_bprintf(strbuf,
4131                         "MAC_table[%d].ea = "
4132                         "[" MACDBG "], if:%d \n",
4133                         i, MAC2STRDBG(ea), mac_table[i].interface_id);
4134 
4135             bcm_bprintf(
4136                 strbuf,
4137                 "MAC_table[%d].PSQ(len,state,credit),"
4138                 "(trans,supp_trans,onbus)"
4139                 "= (%d,%s,%d),(%d,%d,%d)\n",
4140                 i, mac_table[i].psq.n_pkts_tot,
4141                 ((mac_table[i].state == WLFC_STATE_OPEN) ? " OPEN" : "CLOSE"),
4142                 mac_table[i].requested_credit, mac_table[i].transit_count,
4143                 mac_table[i].suppr_transit_count,
4144                 mac_table[i].onbus_pkts_count);
4145 #ifdef PROP_TXSTATUS_DEBUG
4146             bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
4147                         i, mac_table[i].opened_ct, mac_table[i].closed_ct);
4148 #endif // endif
4149             bcm_bprintf(
4150                 strbuf,
4151                 "MAC_table[%d].PSQ"
4152                 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4153                 "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
4154                 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4155                 i, mac_table[i].psq.q[0].n_pkts, mac_table[i].psq.q[1].n_pkts,
4156                 mac_table[i].afq.q[0].n_pkts, mac_table[i].psq.q[0x2].n_pkts,
4157                 mac_table[i].psq.q[0x3].n_pkts, mac_table[i].afq.q[1].n_pkts,
4158                 mac_table[i].psq.q[0x4].n_pkts, mac_table[i].psq.q[0x5].n_pkts,
4159                 mac_table[i].afq.q[0x2].n_pkts, mac_table[i].psq.q[0x6].n_pkts,
4160                 mac_table[i].psq.q[0x7].n_pkts, mac_table[i].afq.q[0x3].n_pkts,
4161                 mac_table[i].psq.q[0x8].n_pkts, mac_table[i].psq.q[0x9].n_pkts,
4162                 mac_table[i].afq.q[0x4].n_pkts);
4163         }
4164     }
4165 
4166 #ifdef PROP_TXSTATUS_DEBUG
4167     {
4168         int avg;
4169         int moving_avg = 0;
4170         int moving_samples;
4171 
4172         if (wlfc->stats.latency_sample_count) {
4173             moving_samples = sizeof(wlfc->stats.deltas) / sizeof(uint32);
4174 
4175             for (i = 0; i < moving_samples; i++) {
4176                 moving_avg += wlfc->stats.deltas[i];
4177             }
4178             moving_avg /= moving_samples;
4179 
4180             avg = (0x64 * wlfc->stats.total_status_latency) /
4181                   wlfc->stats.latency_sample_count;
4182             bcm_bprintf(strbuf,
4183                         "txstatus latency (average, last, moving[%d]) = "
4184                         "(%d.%d, %03d, %03d)\n",
4185                         moving_samples, avg / 0x64, (avg - (avg / 0x64) * 0x64),
4186                         wlfc->stats.latency_most_recent, moving_avg);
4187         }
4188     }
4189 
4190     bcm_bprintf(
4191         strbuf,
4192         "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
4193         "back = (%d,%d,%d,%d,%d,%d)\n",
4194         wlfc->stats.fifo_credits_sent[0], wlfc->stats.fifo_credits_sent[1],
4195         wlfc->stats.fifo_credits_sent[0x2], wlfc->stats.fifo_credits_sent[0x3],
4196         wlfc->stats.fifo_credits_sent[0x4], wlfc->stats.fifo_credits_sent[0x5],
4197 
4198         wlfc->stats.fifo_credits_back[0], wlfc->stats.fifo_credits_back[1],
4199         wlfc->stats.fifo_credits_back[0x2], wlfc->stats.fifo_credits_back[0x3],
4200         wlfc->stats.fifo_credits_back[0x4], wlfc->stats.fifo_credits_back[0x5]);
4201     {
4202         uint32 fifo_cr_sent = 0;
4203         uint32 fifo_cr_acked = 0;
4204         uint32 request_cr_sent = 0;
4205         uint32 request_cr_ack = 0;
4206         uint32 bc_mc_cr_ack = 0;
4207 
4208         for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent) / sizeof(uint32);
4209              i++) {
4210             fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
4211         }
4212 
4213         for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back) / sizeof(uint32);
4214              i++) {
4215             fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
4216         }
4217 
4218         for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4219             if (wlfc->destination_entries.nodes[i].occupied) {
4220                 request_cr_sent +=
4221                     wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
4222             }
4223         }
4224         for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4225             if (wlfc->destination_entries.interfaces[i].occupied) {
4226                 request_cr_sent += wlfc->destination_entries.interfaces[i]
4227                                        .dstncredit_sent_packets;
4228             }
4229         }
4230         for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4231             if (wlfc->destination_entries.nodes[i].occupied) {
4232                 request_cr_ack +=
4233                     wlfc->destination_entries.nodes[i].dstncredit_acks;
4234             }
4235         }
4236         for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4237             if (wlfc->destination_entries.interfaces[i].occupied) {
4238                 request_cr_ack +=
4239                     wlfc->destination_entries.interfaces[i].dstncredit_acks;
4240             }
4241         }
4242         bcm_bprintf(strbuf,
4243                     "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
4244                     "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
4245                     fifo_cr_sent, fifo_cr_acked, request_cr_sent,
4246                     request_cr_ack,
4247                     wlfc->destination_entries.other.dstncredit_acks,
4248                     bc_mc_cr_ack, wlfc->stats.signal_only_pkts_sent,
4249                     wlfc->stats.signal_only_pkts_freed);
4250     }
4251 #endif /* PROP_TXSTATUS_DEBUG */
4252     bcm_bprintf(strbuf, "\n");
4253     bcm_bprintf(strbuf,
4254                 "wlfc- pkt((in,2bus,txstats,hdrpull,out),"
4255                 "(dropped,hdr_only,wlc_tossed,wlc_dropped,wlc_exptime)"
4256                 "(freed,free_err,rollback)) = "
4257                 "((%d,%d,%d,%d,%d),(%d,%d,%d,%d,%d),(%d,%d,%d))\n",
4258                 wlfc->stats.pktin, wlfc->stats.pkt2bus, wlfc->stats.txstatus_in,
4259                 wlfc->stats.dhd_hdrpulls, wlfc->stats.pktout,
4260 
4261                 wlfc->stats.pktdropped, wlfc->stats.wlfc_header_only_pkt,
4262                 wlfc->stats.wlc_tossed_pkts, wlfc->stats.pkt_dropped,
4263                 wlfc->stats.pkt_exptime,
4264 
4265                 wlfc->stats.pkt_freed, wlfc->stats.pkt_free_err,
4266                 wlfc->stats.rollback);
4267 
4268     bcm_bprintf(
4269         strbuf,
4270         "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
4271         "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
4272         wlfc->stats.d11_suppress, wlfc->stats.wl_suppress,
4273         wlfc->stats.bad_suppress,
4274 
4275         wlfc->stats.psq_d11sup_enq, wlfc->stats.psq_wlsup_enq,
4276         wlfc->stats.psq_hostq_enq, wlfc->stats.mac_handle_notfound,
4277 
4278         wlfc->stats.psq_d11sup_retx, wlfc->stats.psq_wlsup_retx,
4279         wlfc->stats.psq_hostq_retx);
4280 
4281     bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
4282                 wlfc->stats.cleanup_txq_cnt, wlfc->stats.cleanup_psq_cnt,
4283                 wlfc->stats.cleanup_fw_cnt);
4284 
4285     bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
4286 
4287     for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4288         bcm_bprintf(strbuf,
4289                     "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n",
4290                     i, wlfc->pkt_cnt_in_q[i][0], wlfc->pkt_cnt_in_q[i][1],
4291                     wlfc->pkt_cnt_in_q[i][0x2], wlfc->pkt_cnt_in_q[i][0x3],
4292                     wlfc->pkt_cnt_in_q[i][0x4]);
4293     }
4294     bcm_bprintf(strbuf, "\n");
4295 
4296     dhd_os_wlfc_unblock(dhdp);
4297     return BCME_OK;
4298 } /* dhd_wlfc_dump */
4299 
dhd_wlfc_clear_counts(dhd_pub_t * dhd)4300 int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
4301 {
4302     athost_wl_status_info_t *wlfc;
4303     wlfc_hanger_t *hanger;
4304 
4305     if (dhd == NULL) {
4306         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4307         return BCME_BADARG;
4308     }
4309 
4310     dhd_os_wlfc_block(dhd);
4311 
4312     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4313         dhd_os_wlfc_unblock(dhd);
4314         return WLFC_UNSUPPORTED;
4315     }
4316 
4317     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
4318 
4319     memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
4320 
4321     if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4322         hanger = (wlfc_hanger_t *)wlfc->hanger;
4323 
4324         hanger->pushed = 0;
4325         hanger->popped = 0;
4326         hanger->failed_slotfind = 0;
4327         hanger->failed_to_pop = 0;
4328         hanger->failed_to_push = 0;
4329     }
4330 
4331     dhd_os_wlfc_unblock(dhd);
4332 
4333     return BCME_OK;
4334 }
4335 
4336 /** returns TRUE if flow control is enabled */
dhd_wlfc_get_enable(dhd_pub_t * dhd,bool * val)4337 int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
4338 {
4339     if (!dhd || !val) {
4340         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4341         return BCME_BADARG;
4342     }
4343 
4344     dhd_os_wlfc_block(dhd);
4345 
4346     *val = dhd->wlfc_enabled;
4347 
4348     dhd_os_wlfc_unblock(dhd);
4349 
4350     return BCME_OK;
4351 }
4352 
4353 /** Called via an IOVAR */
dhd_wlfc_get_mode(dhd_pub_t * dhd,int * val)4354 int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
4355 {
4356     if (!dhd || !val) {
4357         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4358         return BCME_BADARG;
4359     }
4360 
4361     dhd_os_wlfc_block(dhd);
4362 
4363     *val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
4364 
4365     dhd_os_wlfc_unblock(dhd);
4366 
4367     return BCME_OK;
4368 }
4369 
4370 /** Called via an IOVAR */
dhd_wlfc_set_mode(dhd_pub_t * dhd,int val)4371 int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
4372 {
4373     if (!dhd) {
4374         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4375         return BCME_BADARG;
4376     }
4377 
4378     dhd_os_wlfc_block(dhd);
4379 
4380     if (dhd->wlfc_state) {
4381         dhd->proptxstatus_mode = val & 0xff;
4382     }
4383 
4384     dhd_os_wlfc_unblock(dhd);
4385 
4386     return BCME_OK;
4387 }
4388 
4389 /** Called when rx frame is received from the dongle */
dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd,void * pktbuf)4390 bool dhd_wlfc_is_header_only_pkt(dhd_pub_t *dhd, void *pktbuf)
4391 {
4392     athost_wl_status_info_t *wlfc;
4393     bool rc = FALSE;
4394 
4395     if (dhd == NULL) {
4396         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4397         return FALSE;
4398     }
4399 
4400     dhd_os_wlfc_block(dhd);
4401 
4402     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4403         dhd_os_wlfc_unblock(dhd);
4404         return FALSE;
4405     }
4406 
4407     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
4408 
4409     if (PKTLEN(wlfc->osh, pktbuf) == 0) {
4410         wlfc->stats.wlfc_header_only_pkt++;
4411         rc = TRUE;
4412     }
4413 
4414     dhd_os_wlfc_unblock(dhd);
4415 
4416     return rc;
4417 }
4418 
dhd_wlfc_flowcontrol(dhd_pub_t * dhdp,bool state,bool bAcquireLock)4419 int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
4420 {
4421     if (dhdp == NULL) {
4422         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4423         return BCME_BADARG;
4424     }
4425 
4426     if (bAcquireLock) {
4427         dhd_os_wlfc_block(dhdp);
4428     }
4429 
4430     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
4431         dhdp->proptxstatus_module_ignore) {
4432         if (bAcquireLock) {
4433             dhd_os_wlfc_unblock(dhdp);
4434         }
4435         return WLFC_UNSUPPORTED;
4436     }
4437 
4438     if (state != dhdp->proptxstatus_txoff) {
4439         dhdp->proptxstatus_txoff = state;
4440     }
4441 
4442     if (bAcquireLock) {
4443         dhd_os_wlfc_unblock(dhdp);
4444     }
4445 
4446     return BCME_OK;
4447 }
4448 
4449 /** Called when eg an rx frame is received from the dongle */
dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd,uint8 prio)4450 int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t *dhd, uint8 prio)
4451 {
4452     athost_wl_status_info_t *wlfc;
4453     int rx_path_ac = -1;
4454 
4455     if ((dhd == NULL) || (prio >= NUMPRIO)) {
4456         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4457         return BCME_BADARG;
4458     }
4459 
4460     dhd_os_wlfc_block(dhd);
4461 
4462     if (!dhd->wlfc_rxpkt_chk) {
4463         dhd_os_wlfc_unblock(dhd);
4464         return BCME_OK;
4465     }
4466 
4467     if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4468         dhd_os_wlfc_unblock(dhd);
4469         return WLFC_UNSUPPORTED;
4470     }
4471 
4472     wlfc = (athost_wl_status_info_t *)dhd->wlfc_state;
4473 
4474     rx_path_ac = prio2fifo[prio];
4475     wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
4476 
4477     dhd_os_wlfc_unblock(dhd);
4478 
4479     return BCME_OK;
4480 }
4481 
4482 /** called via an IOVAR */
dhd_wlfc_get_module_ignore(dhd_pub_t * dhd,int * val)4483 int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
4484 {
4485     if (!dhd || !val) {
4486         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4487         return BCME_BADARG;
4488     }
4489 
4490     dhd_os_wlfc_block(dhd);
4491 
4492     *val = dhd->proptxstatus_module_ignore;
4493 
4494     dhd_os_wlfc_unblock(dhd);
4495 
4496     return BCME_OK;
4497 }
4498 
4499 /** called via an IOVAR */
dhd_wlfc_set_module_ignore(dhd_pub_t * dhd,int val)4500 int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
4501 {
4502     uint32 tlv = 0;
4503     bool bChanged = FALSE;
4504 
4505     if (!dhd) {
4506         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4507         return BCME_BADARG;
4508     }
4509 
4510     dhd_os_wlfc_block(dhd);
4511 
4512     if ((bool)val != dhd->proptxstatus_module_ignore) {
4513         dhd->proptxstatus_module_ignore = (val != 0);
4514         /* force txstatus_ignore sync with proptxstatus_module_ignore */
4515         dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
4516         if (FALSE == dhd->proptxstatus_module_ignore) {
4517             tlv = WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS |
4518                   WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
4519                   WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
4520         }
4521         /* always enable host reorder */
4522         tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4523         bChanged = TRUE;
4524     }
4525 
4526     dhd_os_wlfc_unblock(dhd);
4527 
4528     if (bChanged) {
4529         /* select enable proptxtstatus signaling */
4530         if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4531             DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
4532                        __FUNCTION__, tlv));
4533         } else {
4534             DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
4535                        __FUNCTION__, tlv));
4536         }
4537     }
4538 
4539 #if defined(DHD_WLFC_THREAD)
4540     _dhd_wlfc_thread_wakeup(dhd);
4541 #endif /* defined(DHD_WLFC_THREAD) */
4542 
4543     return BCME_OK;
4544 }
4545 
4546 /** called via an IOVAR */
dhd_wlfc_get_credit_ignore(dhd_pub_t * dhd,int * val)4547 int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
4548 {
4549     if (!dhd || !val) {
4550         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4551         return BCME_BADARG;
4552     }
4553 
4554     dhd_os_wlfc_block(dhd);
4555 
4556     *val = dhd->proptxstatus_credit_ignore;
4557 
4558     dhd_os_wlfc_unblock(dhd);
4559 
4560     return BCME_OK;
4561 }
4562 
4563 /** called via an IOVAR */
dhd_wlfc_set_credit_ignore(dhd_pub_t * dhd,int val)4564 int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
4565 {
4566     if (!dhd) {
4567         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4568         return BCME_BADARG;
4569     }
4570 
4571     dhd_os_wlfc_block(dhd);
4572 
4573     dhd->proptxstatus_credit_ignore = (val != 0);
4574 
4575     dhd_os_wlfc_unblock(dhd);
4576 
4577     return BCME_OK;
4578 }
4579 
4580 /** called via an IOVAR */
dhd_wlfc_get_txstatus_ignore(dhd_pub_t * dhd,int * val)4581 int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
4582 {
4583     if (!dhd || !val) {
4584         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4585         return BCME_BADARG;
4586     }
4587 
4588     dhd_os_wlfc_block(dhd);
4589 
4590     *val = dhd->proptxstatus_txstatus_ignore;
4591 
4592     dhd_os_wlfc_unblock(dhd);
4593 
4594     return BCME_OK;
4595 }
4596 
4597 /** called via an IOVAR */
dhd_wlfc_set_txstatus_ignore(dhd_pub_t * dhd,int val)4598 int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
4599 {
4600     if (!dhd) {
4601         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4602         return BCME_BADARG;
4603     }
4604 
4605     dhd_os_wlfc_block(dhd);
4606 
4607     dhd->proptxstatus_txstatus_ignore = (val != 0);
4608 
4609     dhd_os_wlfc_unblock(dhd);
4610 
4611     return BCME_OK;
4612 }
4613 
4614 /** called via an IOVAR */
dhd_wlfc_get_rxpkt_chk(dhd_pub_t * dhd,int * val)4615 int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
4616 {
4617     if (!dhd || !val) {
4618         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4619         return BCME_BADARG;
4620     }
4621 
4622     dhd_os_wlfc_block(dhd);
4623 
4624     *val = dhd->wlfc_rxpkt_chk;
4625 
4626     dhd_os_wlfc_unblock(dhd);
4627 
4628     return BCME_OK;
4629 }
4630 
4631 /** called via an IOVAR */
dhd_wlfc_set_rxpkt_chk(dhd_pub_t * dhd,int val)4632 int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
4633 {
4634     if (!dhd) {
4635         DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4636         return BCME_BADARG;
4637     }
4638 
4639     dhd_os_wlfc_block(dhd);
4640 
4641     dhd->wlfc_rxpkt_chk = (val != 0);
4642 
4643     dhd_os_wlfc_unblock(dhd);
4644 
4645     return BCME_OK;
4646 }
4647 
4648 #ifdef PROPTX_MAXCOUNT
dhd_wlfc_update_maxcount(dhd_pub_t * dhdp,uint8 ifid,int maxcount)4649 int dhd_wlfc_update_maxcount(dhd_pub_t *dhdp, uint8 ifid, int maxcount)
4650 {
4651     athost_wl_status_info_t *ctx;
4652     int rc = 0;
4653 
4654     if (dhdp == NULL) {
4655         DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
4656         return BCME_BADARG;
4657     }
4658 
4659     dhd_os_wlfc_block(dhdp);
4660 
4661     if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4662         rc = WLFC_UNSUPPORTED;
4663         goto exit;
4664     }
4665 
4666     if (ifid >= WLFC_MAX_IFNUM) {
4667         DHD_ERROR(("%s: bad ifid\n", __FUNCTION__));
4668         rc = BCME_BADARG;
4669         goto exit;
4670     }
4671 
4672     ctx = (athost_wl_status_info_t *)dhdp->wlfc_state;
4673     ctx->destination_entries.interfaces[ifid].transit_maxcount = maxcount;
4674 exit:
4675     dhd_os_wlfc_unblock(dhdp);
4676     return rc;
4677 }
4678 #endif /* PROPTX_MAXCOUNT */
4679 
4680 #endif /* PROP_TXSTATUS */
4681