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