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