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