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