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