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 (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id))
1063 return;
1064
1065 if ((ctx->hostif_flow_state[if_id] == OFF) && !_dhd_wlfc_allow_fc(ctx, if_id))
1066 return;
1067
1068 if ((pq->n_pkts_tot <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
1069 /* start traffic */
1070 ctx->hostif_flow_state[if_id] = OFF;
1071 /*
1072 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
1073 pq->n_pkts_tot, if_id, __FUNCTION__));
1074 */
1075 WLFC_DBGMESG(("F"));
1076
1077 dhd_txflowcontrol(dhdp, if_id, OFF);
1078
1079 ctx->toggle_host_if = 0;
1080 }
1081
1082 if (pq->n_pkts_tot >= WLFC_FLOWCONTROL_HIWATER && ctx->hostif_flow_state[if_id] == OFF) {
1083 /* stop traffic */
1084 ctx->hostif_flow_state[if_id] = ON;
1085 /*
1086 WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic %s()\n",
1087 pq->n_pkts_tot, if_id, __FUNCTION__));
1088 */
1089 WLFC_DBGMESG(("N"));
1090
1091 dhd_txflowcontrol(dhdp, if_id, ON);
1092
1093 ctx->host_ifidx = if_id;
1094 ctx->toggle_host_if = 1;
1095 }
1096
1097 return;
1098 } /* _dhd_wlfc_flow_control_check */
1099
1100 /** XXX: Warning: this function directly accesses bus-transmit function */
1101 static int
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)1102 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1103 uint8 ta_bmp)
1104 {
1105 int rc = BCME_OK;
1106 void* p = NULL;
1107 int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 16;
1108 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1109
1110 if (dhdp->proptxstatus_txoff) {
1111 rc = BCME_NORESOURCE;
1112 return rc;
1113 }
1114
1115 /* allocate a dummy packet */
1116 p = PKTGET(ctx->osh, dummylen, TRUE);
1117 if (p) {
1118 PKTPULL(ctx->osh, p, dummylen);
1119 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
1120 _dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE);
1121 DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
1122 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
1123 #ifdef PROP_TXSTATUS_DEBUG
1124 ctx->stats.signal_only_pkts_sent++;
1125 #endif
1126
1127 #if defined(BCMPCIE)
1128 /* XXX : RAHUL : Verify the ifidx */
1129 rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
1130 #else
1131 rc = dhd_bus_txdata(dhdp->bus, p);
1132 #endif
1133 if (rc != BCME_OK) {
1134 _dhd_wlfc_pullheader(ctx, p);
1135 PKTFREE(ctx->osh, p, TRUE);
1136 }
1137 } else {
1138 DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
1139 __FUNCTION__, dummylen));
1140 rc = BCME_NOMEM;
1141 dhdp->tx_pktgetfail++;
1142 }
1143
1144 return rc;
1145 } /* _dhd_wlfc_send_signalonly_packet */
1146
1147 /**
1148 * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
1149 * maintained in caller supplied parameter 'entry'.
1150 *
1151 * @param[in/out] entry administration about a remote MAC entity
1152 * @param[in] prec precedence queue for this remote MAC entitity
1153 *
1154 * Return value: TRUE if traffic availability changed
1155 */
1156 static bool
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1157 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1158 int prec)
1159 {
1160 bool rc = FALSE;
1161
1162 if (entry->state == WLFC_STATE_CLOSE) {
1163 if ((pktqprec_n_pkts(&entry->psq, (prec << 1)) == 0) &&
1164 (pktqprec_n_pkts(&entry->psq, ((prec << 1) + 1)) == 0)) {
1165 /* no packets in both 'normal' and 'suspended' queues */
1166 if (entry->traffic_pending_bmp & NBITVAL(prec)) {
1167 rc = TRUE;
1168 entry->traffic_pending_bmp =
1169 entry->traffic_pending_bmp & ~ NBITVAL(prec);
1170 }
1171 } else {
1172 /* packets are queued in host for transmission to dongle */
1173 if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
1174 rc = TRUE;
1175 entry->traffic_pending_bmp =
1176 entry->traffic_pending_bmp | NBITVAL(prec);
1177 }
1178 }
1179 }
1180
1181 if (rc) {
1182 /* request a TIM update to firmware at the next piggyback opportunity */
1183 if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
1184 entry->send_tim_signal = 1;
1185 /*
1186 XXX: send a header only packet from the same context.
1187 --this should change to sending from a timeout or similar.
1188 */
1189 _dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
1190 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1191 entry->send_tim_signal = 0;
1192 } else {
1193 rc = FALSE;
1194 }
1195 }
1196
1197 return rc;
1198 } /* _dhd_wlfc_traffic_pending_check */
1199
1200 /**
1201 * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
1202 * the packet to transmit to firmware again at a later opportunity.
1203 */
1204 static int
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)1205 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
1206 {
1207 wlfc_mac_descriptor_t* entry;
1208
1209 entry = _dhd_wlfc_find_table_entry(ctx, p);
1210 if (entry == NULL) {
1211 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1212 return BCME_NOTFOUND;
1213 }
1214 /*
1215 - suppressed packets go to sub_queue[2*prec + 1] AND
1216 - delayed packets go to sub_queue[2*prec + 0] to ensure
1217 order of delivery.
1218 */
1219 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, ((prec << 1) + 1), FALSE,
1220 WLFC_SEQCOUNT(entry, prec))
1221 == FALSE) {
1222 ctx->stats.delayq_full_error++;
1223 /* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
1224 WLFC_DBGMESG(("s"));
1225 return BCME_ERROR;
1226 }
1227
1228 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1229 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1230 _dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1231 return BCME_OK;
1232 }
1233
1234 /**
1235 * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
1236 * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
1237 *
1238 * @param[in/out] ctx driver specific flow control administration
1239 * @param[in/out] entry The remote MAC entity for which the packet is destined.
1240 * @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
1241 * @param[in] header_needed True if packet is 'new' to flow control
1242 * @param[out] slot Handle to container in which the packet was 'parked'
1243 */
1244 static int
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void ** packet,int header_needed,uint32 * slot)1245 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
1246 wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot)
1247 {
1248 int rc = BCME_OK;
1249 int hslot = WLFC_HANGER_MAXITEMS;
1250 bool send_tim_update = FALSE;
1251 uint32 htod = 0;
1252 uint16 htodseq = 0;
1253 uint8 free_ctr;
1254 int gen = 0xff;
1255 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1256 void * p = *packet;
1257
1258 *slot = hslot;
1259
1260 if (entry == NULL) {
1261 entry = _dhd_wlfc_find_table_entry(ctx, p);
1262 }
1263
1264 if (entry == NULL) {
1265 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1266 return BCME_ERROR;
1267 }
1268
1269 if (entry->send_tim_signal) {
1270 /* sends a traffic indication bitmap to the dongle */
1271 send_tim_update = TRUE;
1272 entry->send_tim_signal = 0;
1273 entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1274 }
1275
1276 if (header_needed) {
1277 if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1278 hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
1279 } else {
1280 hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1281 }
1282 gen = entry->generation;
1283 free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1284 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1285 _dhd_wlfc_bprint(ctx, "d%u.%u.%u-",
1286 (uint8)(entry - &ctx->destination_entries.nodes[0]), gen, free_ctr);
1287 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1288 } else {
1289 if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1290 htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
1291 }
1292
1293 hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1294
1295 if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
1296 gen = entry->generation;
1297 } else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1298 gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1299 } else {
1300 _dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
1301 }
1302
1303 free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1304 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1305 _dhd_wlfc_bprint(ctx, "s%u.%u.%u-",
1306 (uint8)(entry - &ctx->destination_entries.nodes[0]), gen, free_ctr);
1307 if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1308 _dhd_wlfc_bprint(ctx, "%u.%u-",
1309 IS_WL_TO_REUSE_SEQ(DHD_PKTTAG_H2DSEQ(PKTTAG(p))),
1310 WL_SEQ_GET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(p))));
1311 }
1312 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1313 /* remove old header */
1314 _dhd_wlfc_pullheader(ctx, p);
1315 }
1316
1317 if (hslot >= WLFC_HANGER_MAXITEMS) {
1318 DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
1319 return BCME_ERROR;
1320 }
1321
1322 WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
1323 WL_TXSTATUS_SET_HSLOT(htod, hslot);
1324 WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1325 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1326 WL_TXSTATUS_SET_GENERATION(htod, gen);
1327 DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1328
1329 if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1330 /*
1331 Indicate that this packet is being sent in response to an
1332 explicit request from the firmware side.
1333 */
1334 WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1335 } else {
1336 WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1337 }
1338
1339 rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
1340 entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE);
1341 if (rc == BCME_OK) {
1342 DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1343
1344 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1345 wlfc_hanger_t *h = (wlfc_hanger_t*)(ctx->hanger);
1346 if (header_needed) {
1347 /*
1348 a new header was created for this packet.
1349 push to hanger slot and scrub q. Since bus
1350 send succeeded, increment seq number as well.
1351 */
1352 rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1353 if (rc == BCME_OK) {
1354 #ifdef PROP_TXSTATUS_DEBUG
1355 h->items[hslot].push_time =
1356 OSL_SYSUPTIME();
1357 #endif
1358 } else {
1359 DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
1360 __FUNCTION__, rc));
1361 }
1362 } else {
1363 /* clear hanger state */
1364 if (((wlfc_hanger_t*)(ctx->hanger))->items[hslot].pkt != p)
1365 DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
1366 __FUNCTION__, p, h->items[hslot].pkt));
1367 ASSERT(h->items[hslot].pkt == p);
1368 bcm_object_feature_set(h->items[hslot].pkt,
1369 BCM_OBJECT_FEATURE_PKT_STATE, 0);
1370 h->items[hslot].pkt_state = 0;
1371 h->items[hslot].pkt_txstatus = 0;
1372 h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
1373 }
1374 }
1375
1376 if ((rc == BCME_OK) && header_needed) {
1377 /* increment free running sequence count */
1378 WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1379 }
1380 }
1381 *slot = hslot;
1382 *packet = p;
1383 return rc;
1384 } /* _dhd_wlfc_pretx_pktprocess */
1385
1386 /**
1387 * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
1388 * mac is in the 'open' state, otherwise '0'.
1389 */
1390 static int
_dhd_wlfc_is_destination_open(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1391 _dhd_wlfc_is_destination_open(athost_wl_status_info_t* ctx,
1392 wlfc_mac_descriptor_t* entry, int prec)
1393 {
1394 wlfc_mac_descriptor_t* interfaces = ctx->destination_entries.interfaces;
1395
1396 if (entry->interface_id >= WLFC_MAX_IFNUM) {
1397 ASSERT(&ctx->destination_entries.other == entry);
1398 return 1;
1399 }
1400
1401 if (interfaces[entry->interface_id].iftype ==
1402 WLC_E_IF_ROLE_P2P_GO) {
1403 /* - destination interface is of type p2p GO.
1404 For a p2pGO interface, if the destination is OPEN but the interface is
1405 CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1406 destination-specific-credit left send packets. This is because the
1407 firmware storing the destination-specific-requested packet in queue.
1408 */
1409 /* XXX: This behavior will change one PM1 protocol mod is complete */
1410 if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1411 (entry->requested_packet == 0)) {
1412 return 0;
1413 }
1414 }
1415
1416 /* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1417 if ((((entry->state == WLFC_STATE_CLOSE) ||
1418 (interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
1419 (entry->requested_credit == 0) &&
1420 (entry->requested_packet == 0)) ||
1421 (!(entry->ac_bitmap & (1 << prec)))) {
1422 return 0;
1423 }
1424
1425 return 1;
1426 } /* _dhd_wlfc_is_destination_open */
1427
1428 /**
1429 * Dequeues a suppressed or delayed packet from a queue
1430 * @param[in/out] ctx Driver specific flow control administration
1431 * @param[in] prec Precedence of queue to dequeue from
1432 * @param[out] ac_credit_spent Boolean, returns 0 or 1
1433 * @param[out] needs_hdr Boolean, returns 0 or 1
1434 * @param[out] entry_out The remote MAC for which the packet is destined
1435 * @param[in] only_no_credit If TRUE, searches all entries instead of just the active ones
1436 *
1437 * Return value: the dequeued packet
1438 */
1439
1440 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)1441 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, int prec,
1442 uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out,
1443 bool only_no_credit)
1444 {
1445 wlfc_mac_descriptor_t* entry;
1446 int total_entries;
1447 void* p = NULL;
1448 int i;
1449 uint8 credit_spent = ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
1450 uint16 qlen;
1451 bool change_entry = FALSE;
1452
1453 BCM_REFERENCE(qlen);
1454 BCM_REFERENCE(change_entry);
1455
1456 *entry_out = NULL;
1457 /* most cases a packet will count against FIFO credit */
1458 *ac_credit_spent = credit_spent;
1459
1460 /* search all entries, include nodes as well as interfaces */
1461 if (only_no_credit) {
1462 total_entries = ctx->requested_entry_count;
1463 } else {
1464 total_entries = ctx->active_entry_count;
1465 }
1466
1467 for (i = 0; i < total_entries; i++) {
1468 if (only_no_credit) {
1469 entry = ctx->requested_entry[i];
1470 } else {
1471 entry = ctx->active_entry_head;
1472 }
1473 ASSERT(entry);
1474
1475 if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
1476 #ifdef PROPTX_MAXCOUNT
1477 (entry->transit_count < entry->transit_maxcount) &&
1478 #endif /* PROPTX_MAXCOUNT */
1479 (entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
1480 (!entry->suppressed)) {
1481 *ac_credit_spent = credit_spent;
1482 if (entry->state == WLFC_STATE_CLOSE) {
1483 *ac_credit_spent = 0;
1484 }
1485
1486 /* higher precedence will be picked up first,
1487 * i.e. suppressed packets before delayed ones
1488 */
1489 p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
1490 *needs_hdr = 0;
1491 if (p == NULL) {
1492 /* De-Q from delay Q */
1493 p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
1494 *needs_hdr = 1;
1495 }
1496
1497 if (p != NULL) {
1498 bcm_pkt_validate_chk(p, "_dhd_wlfc_deque_afq");
1499 /* did the packet come from suppress sub-queue? */
1500 if (entry->requested_credit > 0) {
1501 entry->requested_credit--;
1502 #ifdef PROP_TXSTATUS_DEBUG
1503 entry->dstncredit_sent_packets++;
1504 #endif
1505 } else if (entry->requested_packet > 0) {
1506 entry->requested_packet--;
1507 DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1508 }
1509
1510 *entry_out = entry;
1511 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
1512 ctx->pkt_cnt_per_ac[prec]--;
1513 ctx->pkt_cnt_in_psq--;
1514 #ifdef BULK_DEQUEUE
1515 /* Check pkts in delayq */
1516 if (entry->state == WLFC_STATE_OPEN) {
1517 entry->release_count[prec]++;
1518 qlen = pktq_mlen(&entry->psq,
1519 (1 << PSQ_SUP_IDX(prec) | 1 << PSQ_DLY_IDX(prec)));
1520
1521 if (entry->release_count[prec] == ctx->max_release_count ||
1522 qlen == 0) {
1523 change_entry = TRUE;
1524 entry->release_count[prec] = 0;
1525 }
1526
1527 if (change_entry) {
1528 /* move head */
1529 ctx->active_entry_head =
1530 ctx->active_entry_head->next;
1531 }
1532 }
1533 #endif /* BULK_DEQUEUE */
1534 _dhd_wlfc_flow_control_check(ctx, &entry->psq,
1535 DHD_PKTTAG_IF(PKTTAG(p)));
1536 /*
1537 * A packet has been picked up, update traffic availability bitmap,
1538 * if applicable.
1539 */
1540 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1541 return p;
1542 }
1543 }
1544 if (!only_no_credit) {
1545 /* move head */
1546 ctx->active_entry_head = ctx->active_entry_head->next;
1547 }
1548 }
1549 return NULL;
1550 } /* _dhd_wlfc_deque_delayedq */
1551
1552 /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
1553 static int
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1554 _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
1555 {
1556 wlfc_mac_descriptor_t* entry;
1557
1558 if (pktbuf != NULL) {
1559 entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1560 if (entry == NULL) {
1561 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1562 return BCME_ERROR;
1563 }
1564
1565 /*
1566 - suppressed packets go to sub_queue[2*prec + 1] AND
1567 - delayed packets go to sub_queue[2*prec + 0] to ensure
1568 order of delivery.
1569 */
1570 if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1),
1571 FALSE, WLFC_SEQCOUNT(entry, prec))
1572 == FALSE) {
1573 WLFC_DBGMESG(("D"));
1574 ctx->stats.delayq_full_error++;
1575 return BCME_ERROR;
1576 }
1577
1578 #ifdef QMONITOR
1579 dhd_qmon_tx(&entry->qmon);
1580 #endif
1581
1582 /* A packet has been pushed, update traffic availability bitmap, if applicable */
1583 _dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1584 }
1585
1586 return BCME_OK;
1587 } /* _dhd_wlfc_enque_delayq */
1588
1589 /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
_dhd_wlfc_ifpkt_fn(void * p,void * p_ifid)1590 static bool _dhd_wlfc_ifpkt_fn(void* p, void *p_ifid)
1591 {
1592 if (!p || !p_ifid)
1593 return FALSE;
1594
1595 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
1596 }
1597
1598 /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
_dhd_wlfc_entrypkt_fn(void * p,void * entry)1599 static bool _dhd_wlfc_entrypkt_fn(void* p, void *entry)
1600 {
1601 if (!p || !entry)
1602 return FALSE;
1603
1604 return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
1605 }
1606
1607 static void
_dhd_wlfc_return_implied_credit(athost_wl_status_info_t * wlfc,void * pkt)1608 _dhd_wlfc_return_implied_credit(athost_wl_status_info_t* wlfc, void* pkt)
1609 {
1610 dhd_pub_t *dhdp;
1611 bool credit_return = FALSE;
1612
1613 if (!wlfc || !pkt) {
1614 return;
1615 }
1616
1617 dhdp = (dhd_pub_t *)(wlfc->dhdp);
1618 if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
1619 DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
1620 int lender, credit_returned = 0;
1621 uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
1622
1623 credit_return = TRUE;
1624
1625 /* Note that borrower is fifo_id */
1626 /* Return credits to highest priority lender first */
1627 for (lender = AC_COUNT; lender >= 0; lender--) {
1628 if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1629 wlfc->FIFO_credit[lender]++;
1630 wlfc->credits_borrowed[fifo_id][lender]--;
1631 credit_returned = 1;
1632 break;
1633 }
1634 }
1635
1636 if (!credit_returned) {
1637 wlfc->FIFO_credit[fifo_id]++;
1638 }
1639 }
1640
1641 BCM_REFERENCE(credit_return);
1642 #if defined(DHD_WLFC_THREAD)
1643 if (credit_return) {
1644 _dhd_wlfc_thread_wakeup(dhdp);
1645 }
1646 #endif /* defined(DHD_WLFC_THREAD) */
1647 }
1648
1649 /** Removes and frees a packet from the hanger. Called during eg tx complete. */
1650 static void
_dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t * wlfc,uint32 slot_id,uint8 pkt_state,int pkt_txstatus)1651 _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t* wlfc, uint32 slot_id, uint8 pkt_state,
1652 int pkt_txstatus)
1653 {
1654 wlfc_hanger_t* hanger;
1655 wlfc_hanger_item_t* item;
1656
1657 if (!wlfc)
1658 return;
1659
1660 hanger = (wlfc_hanger_t*)wlfc->hanger;
1661 if (!hanger)
1662 return;
1663
1664 if (slot_id == WLFC_HANGER_MAXITEMS)
1665 return;
1666
1667 item = &hanger->items[slot_id];
1668
1669 if (item->pkt) {
1670 item->pkt_state |= pkt_state;
1671 if (pkt_txstatus != -1)
1672 item->pkt_txstatus = (uint8)pkt_txstatus;
1673 bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE, item->pkt_state);
1674 if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
1675 void *p = NULL;
1676 void *pkt = item->pkt;
1677 uint8 old_state = item->state;
1678 int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
1679 BCM_REFERENCE(ret);
1680 BCM_REFERENCE(pkt);
1681 ASSERT((ret == BCME_OK) && p && (pkt == p));
1682 if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
1683 printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
1684 pkt, old_state, item->pkt_state);
1685 }
1686 ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
1687
1688 /* free packet */
1689 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
1690 [DHD_PKTTAG_FIFO(PKTTAG(p))]--;
1691 wlfc->stats.pktout++;
1692 dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
1693 PKTFREE(wlfc->osh, p, TRUE);
1694 }
1695 } else {
1696 /* free slot */
1697 if (item->state == WLFC_HANGER_ITEM_STATE_FREE)
1698 DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
1699 __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
1700 item->state = WLFC_HANGER_ITEM_STATE_FREE;
1701 }
1702 } /* _dhd_wlfc_hanger_free_pkt */
1703
1704 /** Called during eg detach() */
1705 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)1706 _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
1707 bool dir, f_processpkt_t fn, void *arg, q_type_t q_type)
1708 {
1709 int prec;
1710 dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1711
1712 ASSERT(dhdp);
1713
1714 /* Optimize flush, if pktq len = 0, just return.
1715 * pktq len of 0 means pktq's prec q's are all empty.
1716 */
1717 if (pq->n_pkts_tot == 0) {
1718 return;
1719 }
1720
1721 for (prec = 0; prec < pq->num_prec; prec++) {
1722 struct pktq_prec *q;
1723 void *p, *prev = NULL;
1724
1725 q = &pq->q[prec];
1726 p = q->head;
1727 while (p) {
1728 bcm_pkt_validate_chk(p, "_dhd_wlfc_pktq_flush");
1729 if (fn == NULL || (*fn)(p, arg)) {
1730 bool head = (p == q->head);
1731 if (head)
1732 q->head = PKTLINK(p);
1733 else
1734 PKTSETLINK(prev, PKTLINK(p));
1735 if (q_type == Q_TYPE_PSQ) {
1736 if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
1737 _dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
1738 }
1739 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1740 ctx->pkt_cnt_per_ac[prec>>1]--;
1741 ctx->pkt_cnt_in_psq--;
1742 ctx->stats.cleanup_psq_cnt++;
1743 if (!(prec & 1)) {
1744 /* pkt in delayed q, so fake push BDC header for
1745 * dhd_tcpack_check_xmit() and dhd_txcomplete().
1746 */
1747 _dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0,
1748 0, 0, TRUE);
1749 #ifdef DHDTCPACK_SUPPRESS
1750 if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
1751 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
1752 " Stop using it\n",
1753 __FUNCTION__, __LINE__));
1754 dhd_tcpack_suppress_set(dhdp,
1755 TCPACK_SUP_OFF);
1756 }
1757 #endif /* DHDTCPACK_SUPPRESS */
1758 }
1759 } else if (q_type == Q_TYPE_AFQ) {
1760 wlfc_mac_descriptor_t* entry =
1761 _dhd_wlfc_find_table_entry(ctx, p);
1762 if (entry->transit_count)
1763 entry->transit_count--;
1764 if (entry->suppr_transit_count) {
1765 entry->suppr_transit_count--;
1766 if (entry->suppressed &&
1767 (!entry->onbus_pkts_count) &&
1768 (!entry->suppr_transit_count))
1769 entry->suppressed = FALSE;
1770 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1771 _dhd_wlfc_bprint(ctx, "[sc]-");
1772 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1773 }
1774 _dhd_wlfc_return_implied_credit(ctx, p);
1775 ctx->stats.cleanup_fw_cnt++;
1776 }
1777 PKTSETLINK(p, NULL);
1778 if (dir) {
1779 ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1780 ctx->stats.pktout++;
1781 dhd_txcomplete(dhdp, p, FALSE);
1782 }
1783 PKTFREE(ctx->osh, p, dir);
1784
1785 q->n_pkts--;
1786 pq->n_pkts_tot--;
1787 #ifdef WL_TXQ_STALL
1788 q->dequeue_count++;
1789 #endif
1790
1791 p = (head ? q->head : PKTLINK(prev));
1792 } else {
1793 prev = p;
1794 p = PKTLINK(p);
1795 }
1796 }
1797
1798 if (q->head == NULL) {
1799 ASSERT(q->n_pkts == 0);
1800 q->tail = NULL;
1801 }
1802
1803 }
1804
1805 if (fn == NULL)
1806 ASSERT(pq->n_pkts_tot == 0);
1807 } /* _dhd_wlfc_pktq_flush */
1808
1809 #ifndef BCMDBUS
1810 /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
1811 static void*
_dhd_wlfc_pktq_pdeq_with_fn(struct pktq * pq,int prec,f_processpkt_t fn,void * arg)1812 _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
1813 {
1814 struct pktq_prec *q;
1815 void *p, *prev = NULL;
1816
1817 ASSERT(prec >= 0 && prec < pq->num_prec);
1818
1819 q = &pq->q[prec];
1820 p = q->head;
1821
1822 while (p) {
1823 if (fn == NULL || (*fn)(p, arg)) {
1824 break;
1825 } else {
1826 prev = p;
1827 p = PKTLINK(p);
1828 }
1829 }
1830 if (p == NULL)
1831 return NULL;
1832
1833 bcm_pkt_validate_chk(p, "_dhd_wlfc_pktq_flush");
1834
1835 if (prev == NULL) {
1836 if ((q->head = PKTLINK(p)) == NULL) {
1837 q->tail = NULL;
1838 }
1839 } else {
1840 PKTSETLINK(prev, PKTLINK(p));
1841 if (q->tail == p) {
1842 q->tail = prev;
1843 }
1844 }
1845
1846 q->n_pkts--;
1847
1848 pq->n_pkts_tot--;
1849
1850 #ifdef WL_TXQ_STALL
1851 q->dequeue_count++;
1852 #endif
1853
1854 PKTSETLINK(p, NULL);
1855
1856 return p;
1857 }
1858
1859 /** !BCMDBUS specific function */
1860 static void
_dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1861 _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1862 {
1863 int prec;
1864 void *pkt = NULL, *head = NULL, *tail = NULL;
1865 struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
1866 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1867 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1868 wlfc_mac_descriptor_t* entry;
1869
1870 dhd_os_sdlock_txq(dhd);
1871 for (prec = 0; prec < txq->num_prec; prec++) {
1872 while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
1873 #ifdef DHDTCPACK_SUPPRESS
1874 if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
1875 DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
1876 __FUNCTION__, __LINE__));
1877 dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
1878 }
1879 #endif /* DHDTCPACK_SUPPRESS */
1880 if (!head) {
1881 head = pkt;
1882 }
1883 if (tail) {
1884 PKTSETLINK(tail, pkt);
1885 }
1886 tail = pkt;
1887 }
1888 }
1889 dhd_os_sdunlock_txq(dhd);
1890
1891 while ((pkt = head)) {
1892 head = PKTLINK(pkt);
1893 PKTSETLINK(pkt, NULL);
1894 entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
1895
1896 if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
1897 !_dhd_wlfc_hanger_remove_reference(h, pkt)) {
1898 DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
1899 __FUNCTION__, pkt));
1900 }
1901 if (entry->transit_count)
1902 entry->transit_count--;
1903 if (entry->suppr_transit_count) {
1904 entry->suppr_transit_count--;
1905 if (entry->suppressed &&
1906 (!entry->onbus_pkts_count) &&
1907 (!entry->suppr_transit_count))
1908 entry->suppressed = FALSE;
1909 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1910 _dhd_wlfc_bprint(wlfc, "[sc]-");
1911 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1912 }
1913 _dhd_wlfc_return_implied_credit(wlfc, pkt);
1914 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))][DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
1915 wlfc->stats.pktout++;
1916 wlfc->stats.cleanup_txq_cnt++;
1917 dhd_txcomplete(dhd, pkt, FALSE);
1918 PKTFREE(wlfc->osh, pkt, TRUE);
1919 }
1920 } /* _dhd_wlfc_cleanup_txq */
1921
1922 #endif /* !BCMDBUS */
1923
1924 /** called during eg detach */
1925 void
_dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1926 _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1927 {
1928 int i;
1929 int total_entries;
1930 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1931 wlfc_mac_descriptor_t* table;
1932 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1933
1934 wlfc->stats.cleanup_txq_cnt = 0;
1935 wlfc->stats.cleanup_psq_cnt = 0;
1936 wlfc->stats.cleanup_fw_cnt = 0;
1937
1938 /*
1939 * flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
1940 */
1941 #ifndef BCMDBUS
1942 /* flush bus->txq */
1943 _dhd_wlfc_cleanup_txq(dhd, fn, arg);
1944 #endif /* BCMDBUS */
1945
1946 /* flush psq, search all entries, include nodes as well as interfaces */
1947 total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
1948 table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
1949
1950 for (i = 0; i < total_entries; i++) {
1951 if (table[i].occupied) {
1952 /* release packets held in PSQ (both delayed and suppressed) */
1953 if (table[i].psq.n_pkts_tot) {
1954 WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
1955 __FUNCTION__, i, table[i].psq.n_pkts_tot));
1956 _dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE,
1957 fn, arg, Q_TYPE_PSQ);
1958 }
1959
1960 /* free packets held in AFQ */
1961 if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.n_pkts_tot)) {
1962 _dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE,
1963 fn, arg, Q_TYPE_AFQ);
1964 }
1965
1966 if ((fn == NULL) && (&table[i] != &wlfc->destination_entries.other)) {
1967 table[i].occupied = 0;
1968 if (table[i].transit_count || table[i].suppr_transit_count) {
1969 DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
1970 __FUNCTION__, i,
1971 table[i].transit_count,
1972 table[i].suppr_transit_count));
1973 }
1974 }
1975 }
1976 }
1977
1978 /*
1979 . flush remained pkt in hanger queue, not in bus->txq nor psq.
1980 . the remained pkt was successfully downloaded to dongle already.
1981 . hanger slot state cannot be set to free until receive txstatus update.
1982 */
1983 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
1984 for (i = 0; i < h->max_items; i++) {
1985 if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
1986 (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
1987 if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
1988 h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
1989 }
1990 }
1991 }
1992 }
1993
1994 return;
1995 } /* _dhd_wlfc_cleanup */
1996
1997 /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
1998 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)1999 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
2000 uint8 action, uint8 ifid, uint8 iftype, uint8* ea,
2001 f_processpkt_t fn, void *arg)
2002 {
2003 int rc = BCME_OK;
2004 uint8 i;
2005
2006 BCM_REFERENCE(i);
2007
2008 #ifdef QMONITOR
2009 dhd_qmon_reset(&entry->qmon);
2010 #endif
2011
2012 if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
2013 entry->occupied = 1;
2014 entry->state = WLFC_STATE_OPEN;
2015 entry->requested_credit = 0;
2016 entry->interface_id = ifid;
2017 entry->iftype = iftype;
2018 entry->ac_bitmap = 0xff; /* update this when handling APSD */
2019 #ifdef BULK_DEQUEUE
2020 for (i = 0; i < AC_COUNT + 1; i++) {
2021 entry->release_count[i] = 0;
2022 }
2023 #endif /* BULK_DEQUEUE */
2024 /* for an interface entry we may not care about the MAC address */
2025 if (ea != NULL)
2026 memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
2027
2028 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
2029 entry->suppressed = FALSE;
2030 entry->transit_count = 0;
2031 #if defined(WL_EXT_IAPSTA) && defined(PROPTX_MAXCOUNT)
2032 entry->transit_maxcount = wl_ext_get_wlfc_maxcount(ctx->dhdp, ifid);
2033 #endif /* PROPTX_MAXCOUNT */
2034 entry->suppr_transit_count = 0;
2035 entry->onbus_pkts_count = 0;
2036 }
2037
2038 if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
2039 dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2040
2041 pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
2042 _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
2043
2044 if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2045 pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
2046 }
2047
2048 if (entry->next == NULL) {
2049 /* not linked to anywhere, add to tail */
2050 if (ctx->active_entry_head) {
2051 entry->prev = ctx->active_entry_head->prev;
2052 ctx->active_entry_head->prev->next = entry;
2053 ctx->active_entry_head->prev = entry;
2054 entry->next = ctx->active_entry_head;
2055 } else {
2056 ASSERT(ctx->active_entry_count == 0);
2057 entry->prev = entry->next = entry;
2058 ctx->active_entry_head = entry;
2059 }
2060 ctx->active_entry_count++;
2061 } else {
2062 DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
2063 (int)(entry - &ctx->destination_entries.nodes[0])));
2064 }
2065 }
2066 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2067 for (i = 0; i < (AC_COUNT + 1); i++) {
2068 entry->last_send_seq[i] = 255;
2069 entry->last_complete_seq[i] = 255;
2070 }
2071 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2072 } else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
2073 /* When the entry is deleted, the packets that are queued in the entry must be
2074 cleanup. The cleanup action should be before the occupied is set as 0.
2075 */
2076 _dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
2077 _dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
2078
2079 entry->occupied = 0;
2080 entry->state = WLFC_STATE_CLOSE;
2081 memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
2082
2083 if (entry->next) {
2084 /* not floating, remove from Q */
2085 if (ctx->active_entry_count <= 1) {
2086 /* last item */
2087 ctx->active_entry_head = NULL;
2088 ctx->active_entry_count = 0;
2089 } else {
2090 entry->prev->next = entry->next;
2091 entry->next->prev = entry->prev;
2092 if (entry == ctx->active_entry_head) {
2093 ctx->active_entry_head = entry->next;
2094 }
2095 ctx->active_entry_count--;
2096 }
2097 entry->next = entry->prev = NULL;
2098 } else {
2099 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2100 }
2101 }
2102 return rc;
2103 } /* _dhd_wlfc_mac_entry_update */
2104
2105 #ifdef LIMIT_BORROW
2106
2107 /** LIMIT_BORROW specific function */
2108 static int
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,int highest_lender_ac,int borrower_ac,bool bBorrowAll)2109 _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, int highest_lender_ac, int borrower_ac,
2110 bool bBorrowAll)
2111 {
2112 int lender_ac, borrow_limit = 0;
2113 int rc = -1;
2114
2115 if (ctx == NULL) {
2116 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2117 return -1;
2118 }
2119
2120 /* Borrow from lowest priority available AC (including BC/MC credits) */
2121 for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
2122 if (!bBorrowAll) {
2123 borrow_limit = ctx->Init_FIFO_credit[lender_ac]/WLFC_BORROW_LIMIT_RATIO;
2124 } else {
2125 borrow_limit = 0;
2126 }
2127
2128 if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
2129 ctx->credits_borrowed[borrower_ac][lender_ac]++;
2130 ctx->FIFO_credit[lender_ac]--;
2131 rc = lender_ac;
2132 break;
2133 }
2134 }
2135
2136 return rc;
2137 }
2138
2139 /** LIMIT_BORROW specific function */
_dhd_wlfc_return_credit(athost_wl_status_info_t * ctx,int lender_ac,int borrower_ac)2140 static int _dhd_wlfc_return_credit(athost_wl_status_info_t* ctx, int lender_ac, int borrower_ac)
2141 {
2142 if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
2143 (borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
2144 DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
2145 __FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
2146
2147 return BCME_BADARG;
2148 }
2149
2150 ctx->credits_borrowed[borrower_ac][lender_ac]--;
2151 ctx->FIFO_credit[lender_ac]++;
2152
2153 return BCME_OK;
2154 }
2155
2156 #endif /* LIMIT_BORROW */
2157
2158 /**
2159 * Called on an interface event (WLC_E_IF) indicated by firmware.
2160 * @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
2161 */
2162 static int
_dhd_wlfc_interface_entry_update(void * state,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)2163 _dhd_wlfc_interface_entry_update(void* state,
2164 uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
2165 {
2166 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2167 wlfc_mac_descriptor_t* entry;
2168
2169 if (ifid >= WLFC_MAX_IFNUM)
2170 return BCME_BADARG;
2171
2172 entry = &ctx->destination_entries.interfaces[ifid];
2173
2174 return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
2175 _dhd_wlfc_ifpkt_fn, &ifid);
2176 }
2177
2178 /**
2179 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
2180 * specific)
2181 */
2182 static int
_dhd_wlfc_BCMCCredit_support_update(void * state)2183 _dhd_wlfc_BCMCCredit_support_update(void* state)
2184 {
2185 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2186
2187 ctx->bcmc_credit_supported = TRUE;
2188 return BCME_OK;
2189 }
2190
2191 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
2192 static int
_dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)2193 _dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
2194 {
2195 athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2196 int i;
2197
2198 for (i = 0; i <= 4; i++) {
2199 if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
2200 DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
2201 __FUNCTION__, ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
2202 }
2203 }
2204
2205 /* update the AC FIFO credit map */
2206 ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
2207 ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
2208 ctx->FIFO_credit[2] += (credits[2] - ctx->Init_FIFO_credit[2]);
2209 ctx->FIFO_credit[3] += (credits[3] - ctx->Init_FIFO_credit[3]);
2210 ctx->FIFO_credit[4] += (credits[4] - ctx->Init_FIFO_credit[4]);
2211
2212 ctx->Init_FIFO_credit[0] = credits[0];
2213 ctx->Init_FIFO_credit[1] = credits[1];
2214 ctx->Init_FIFO_credit[2] = credits[2];
2215 ctx->Init_FIFO_credit[3] = credits[3];
2216 ctx->Init_FIFO_credit[4] = credits[4];
2217
2218 /* credit for ATIM FIFO is not used yet. */
2219 ctx->Init_FIFO_credit[5] = ctx->FIFO_credit[5] = 0;
2220
2221 return BCME_OK;
2222 }
2223
2224 /**
2225 * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
2226 * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
2227 *
2228 * @param[in/out] ctx Driver specific flow control administration
2229 * @param[in] ac Access Category (QoS) of called supplied packet
2230 * @param[in] commit_info Contains eg the packet to send
2231 * @param[in] fcommit Function pointer to transmit function of next software layer
2232 * @param[in] commit_ctx Opaque context used when calling next layer
2233 */
2234 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)2235 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
2236 dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
2237 {
2238 uint32 hslot;
2239 int rc;
2240 dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2241
2242 /*
2243 if ac_fifo_credit_spent = 0
2244
2245 This packet will not count against the FIFO credit.
2246 To ensure the txstatus corresponding to this packet
2247 does not provide an implied credit (default behavior)
2248 mark the packet accordingly.
2249
2250 if ac_fifo_credit_spent = 1
2251
2252 This is a normal packet and it counts against the FIFO
2253 credit count.
2254 */
2255 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
2256 rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
2257 commit_info->needs_hdr, &hslot);
2258
2259 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2260 _dhd_wlfc_check_send_order(ctx, commit_info->mac_entry, commit_info->p);
2261 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2262 if (rc == BCME_OK) {
2263 rc = fcommit(commit_ctx, commit_info->p);
2264 if (rc == BCME_OK) {
2265 uint8 gen = WL_TXSTATUS_GET_GENERATION(
2266 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
2267 dhd_txpkt_log_and_dump(dhdp, commit_info->p, NULL);
2268 ctx->stats.pkt2bus++;
2269 if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
2270 ctx->stats.send_pkts[ac]++;
2271 WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
2272 }
2273
2274 if (gen != commit_info->mac_entry->generation) {
2275 /* will be suppressed back by design */
2276 if (!commit_info->mac_entry->suppressed) {
2277 commit_info->mac_entry->suppressed = TRUE;
2278 }
2279 commit_info->mac_entry->suppr_transit_count++;
2280 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2281 _dhd_wlfc_bprint(ctx, "[si%u]-",
2282 commit_info->mac_entry->suppr_transit_count);
2283 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2284 }
2285 commit_info->mac_entry->transit_count++;
2286 commit_info->mac_entry->onbus_pkts_count++;
2287 } else if (commit_info->needs_hdr) {
2288 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2289 void *pout = NULL;
2290 /* pop hanger for delayed packet */
2291 _dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(
2292 DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p))), &pout, TRUE);
2293 ASSERT(commit_info->p == pout);
2294 }
2295 }
2296 } else {
2297 ctx->stats.generic_error++;
2298 }
2299
2300 if (rc != BCME_OK) {
2301 /*
2302 pretx pkt process or bus commit has failed, rollback.
2303 - remove wl-header for a delayed packet
2304 - save wl-header header for suppressed packets
2305 - reset credit check flag
2306 */
2307 _dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, commit_info->pkt_type, hslot);
2308 DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
2309 }
2310
2311 return rc;
2312 } /* _dhd_wlfc_handle_packet_commit */
2313
2314 /** Returns remote MAC descriptor for caller supplied MAC address */
2315 static uint8
_dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)2316 _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
2317 {
2318 wlfc_mac_descriptor_t* table =
2319 ((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
2320 uint8 table_index;
2321
2322 if (ea != NULL) {
2323 for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
2324 if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
2325 table[table_index].occupied)
2326 return table_index;
2327 }
2328 }
2329 return WLFC_MAC_DESC_ID_INVALID;
2330 }
2331
2332 /**
2333 * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
2334 * status of a frame that the dongle attempted to transmit over the wireless medium.
2335 */
2336 static int
dhd_wlfc_suppressed_acked_update(dhd_pub_t * dhd,uint16 hslot,uint8 prec,uint8 hcnt)2337 dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot, uint8 prec, uint8 hcnt)
2338 {
2339 athost_wl_status_info_t* ctx;
2340 wlfc_mac_descriptor_t* entry = NULL;
2341 struct pktq *pq;
2342 struct pktq_prec *q;
2343 void *p, *b;
2344
2345 if (!dhd) {
2346 DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
2347 return BCME_BADARG;
2348 }
2349 ctx = (athost_wl_status_info_t*)dhd->wlfc_state;
2350 if (!ctx) {
2351 DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
2352 return BCME_ERROR;
2353 }
2354
2355 ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
2356
2357 if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
2358 entry = &ctx->destination_entries.nodes[hslot];
2359 else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
2360 entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
2361 else
2362 entry = &ctx->destination_entries.other;
2363
2364 pq = &entry->psq;
2365
2366 ASSERT(((prec << 1) + 1) < pq->num_prec);
2367
2368 q = &pq->q[((prec << 1) + 1)];
2369
2370 b = NULL;
2371 p = q->head;
2372
2373 while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
2374 b = p;
2375 p = PKTLINK(p);
2376 }
2377
2378 if (p == NULL) {
2379 /* none is matched */
2380 if (b) {
2381 DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
2382 } else {
2383 DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
2384 }
2385
2386 return BCME_ERROR;
2387 }
2388
2389 if (!b) {
2390 /* head packet is matched */
2391 if ((q->head = PKTLINK(p)) == NULL) {
2392 q->tail = NULL;
2393 }
2394 } else {
2395 /* middle packet is matched */
2396 PKTSETLINK(b, PKTLINK(p));
2397 if (PKTLINK(p) == NULL) {
2398 q->tail = b;
2399 }
2400 }
2401
2402 q->n_pkts--;
2403 pq->n_pkts_tot--;
2404
2405 #ifdef WL_TXQ_STALL
2406 q->dequeue_count++;
2407 #endif
2408
2409 ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
2410 ctx->pkt_cnt_per_ac[prec]--;
2411
2412 PKTSETLINK(p, NULL);
2413
2414 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2415 _dhd_wlfc_enque_afq(ctx, p);
2416 } else {
2417 _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
2418 }
2419
2420 entry->transit_count++;
2421
2422 return BCME_OK;
2423 }
2424
2425 static int
_dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len,void ** p_mac)2426 _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len, void** p_mac)
2427 {
2428 uint8 status_flag_ori, status_flag;
2429 uint32 status;
2430 int ret = BCME_OK;
2431 int remove_from_hanger_ori, remove_from_hanger = 1;
2432 void* pktbuf = NULL;
2433 uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
2434 uint16 hslot;
2435 wlfc_mac_descriptor_t* entry = NULL;
2436 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2437 uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
2438 uint16 pktfate_status;
2439
2440 memcpy(&status, pkt_info, sizeof(uint32));
2441 status = ltoh32(status);
2442 status_flag = WL_TXSTATUS_GET_FLAGS(status);
2443 hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
2444 hslot = WL_TXSTATUS_GET_HSLOT(status);
2445 fifo_id = WL_TXSTATUS_GET_FIFO(status);
2446 gen = WL_TXSTATUS_GET_GENERATION(status);
2447
2448 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2449 memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS, WLFC_CTL_VALUE_LEN_SEQ);
2450 seq = ltoh16(seq);
2451 seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
2452 seq_num = WL_SEQ_GET_NUM(seq);
2453 }
2454
2455 wlfc->stats.txstatus_in += len;
2456
2457 if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
2458 wlfc->stats.pkt_freed += len;
2459 } else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
2460 wlfc->stats.pkt_freed += len;
2461 } else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
2462 wlfc->stats.d11_suppress += len;
2463 remove_from_hanger = 0;
2464 } else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
2465 wlfc->stats.wl_suppress += len;
2466 remove_from_hanger = 0;
2467 } else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
2468 wlfc->stats.wlc_tossed_pkts += len;
2469 } else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2470 wlfc->stats.pkt_freed += len;
2471 } else if (status_flag == WLFC_CTL_PKTFLAG_EXPIRED) {
2472 wlfc->stats.pkt_exptime += len;
2473 } else if (status_flag == WLFC_CTL_PKTFLAG_DROPPED) {
2474 wlfc->stats.pkt_dropped += len;
2475 }
2476
2477 if (dhd->proptxstatus_txstatus_ignore) {
2478 if (!remove_from_hanger) {
2479 DHD_ERROR(("suppress txstatus: %d\n", status_flag));
2480 }
2481 return BCME_OK;
2482 }
2483
2484 status_flag_ori = status_flag;
2485 remove_from_hanger_ori = remove_from_hanger;
2486
2487 while (count < len) {
2488 if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2489 dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
2490 }
2491 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2492 ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
2493 } else {
2494 status_flag = status_flag_ori;
2495 remove_from_hanger = remove_from_hanger_ori;
2496 ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
2497 if (!pktbuf) {
2498 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2499 WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
2500 goto cont;
2501 } else {
2502 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2503 if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
2504 status_flag = WLFC_CTL_PKTFLAG_DISCARD;
2505 remove_from_hanger = 1;
2506 }
2507 }
2508 }
2509
2510 if ((ret != BCME_OK) || !pktbuf) {
2511 goto cont;
2512 }
2513
2514 bcm_pkt_validate_chk(pktbuf, "_dhd_wlfc_compressed_txstatus_update");
2515
2516 pktfate_status = ltoh16(status_flag_ori) & WLFC_CTL_PKTFLAG_MASK;
2517 dhd_txpkt_log_and_dump(dhd, pktbuf, &pktfate_status);
2518
2519 /* set fifo_id to correct value because not all FW does that */
2520 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
2521
2522 entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
2523
2524 if (!remove_from_hanger) {
2525 /* this packet was suppressed */
2526 if (!entry->suppressed || (entry->generation != gen)) {
2527 if (!entry->suppressed) {
2528 entry->suppr_transit_count = entry->transit_count;
2529 if (p_mac) {
2530 *p_mac = entry;
2531 }
2532 } else {
2533 DHD_ERROR(("gen(%d), entry->generation(%d)\n",
2534 gen, entry->generation));
2535 }
2536 entry->suppressed = TRUE;
2537
2538 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2539 _dhd_wlfc_bprint(wlfc, "[ss%u.%u.%u]-",
2540 (uint8)(entry - &wlfc->destination_entries.nodes[0]),
2541 entry->generation,
2542 entry->suppr_transit_count);
2543 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2544 }
2545 entry->generation = gen;
2546 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2547 if (gen == WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)))) {
2548 printf("==%d.%d==\n", gen, hcnt);
2549 }
2550 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2551 }
2552
2553 #ifdef PROP_TXSTATUS_DEBUG
2554 if (!WLFC_GET_AFQ(dhd->wlfc_mode))
2555 {
2556 uint32 new_t = OSL_SYSUPTIME();
2557 uint32 old_t;
2558 uint32 delta;
2559 old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[hslot].push_time;
2560
2561 wlfc->stats.latency_sample_count++;
2562 if (new_t > old_t)
2563 delta = new_t - old_t;
2564 else
2565 delta = 0xffffffff + new_t - old_t;
2566 wlfc->stats.total_status_latency += delta;
2567 wlfc->stats.latency_most_recent = delta;
2568
2569 wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
2570 if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
2571 wlfc->stats.idx_delta = 0;
2572 }
2573 #endif /* PROP_TXSTATUS_DEBUG */
2574
2575 /* pick up the implicit credit from this packet */
2576 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
2577 _dhd_wlfc_return_implied_credit(wlfc, pktbuf);
2578 } else {
2579 /*
2580 if this packet did not count against FIFO credit, it must have
2581 taken a requested_credit from the destination entry (for pspoll etc.)
2582 */
2583 if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
2584 entry->requested_credit++;
2585 #if defined(DHD_WLFC_THREAD)
2586 _dhd_wlfc_thread_wakeup(dhd);
2587 #endif /* DHD_WLFC_THREAD */
2588 }
2589 #ifdef PROP_TXSTATUS_DEBUG
2590 entry->dstncredit_acks++;
2591 #endif
2592 }
2593
2594 if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
2595 (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
2596 /* save generation bit inside packet */
2597 WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
2598
2599 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2600 WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
2601 WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
2602 }
2603
2604 ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
2605 if (ret != BCME_OK) {
2606 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2607 _dhd_wlfc_bprint(wlfc, "f%u.%u.%u-",
2608 (uint8)(entry - &wlfc->destination_entries.nodes[0]),
2609 gen,
2610 hcnt);
2611 _dhd_wlfc_check_complete_order(wlfc, entry, pktbuf);
2612 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2613 /* delay q is full, drop this packet */
2614 DHD_WLFC_QMON_COMPLETE(entry);
2615 _dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
2616 } else {
2617 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2618 /* Mark suppressed to avoid a double free
2619 during wlfc cleanup
2620 */
2621 _dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
2622 }
2623 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2624 _dhd_wlfc_bprint(wlfc, "r%u.%u.%u.%u-",
2625 status_flag,
2626 (uint8)(entry - &wlfc->destination_entries.nodes[0]),
2627 gen,
2628 hcnt);
2629 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2630 _dhd_wlfc_bprint(wlfc, "%u.%u-", seq_fromfw, seq_num);
2631 }
2632
2633 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2634 }
2635 } else {
2636 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2637 _dhd_wlfc_bprint(wlfc, "c%u.%u.%u.%u-",
2638 status_flag,
2639 (uint8)(entry - &wlfc->destination_entries.nodes[0]),
2640 gen,
2641 hcnt);
2642 _dhd_wlfc_check_complete_order(wlfc, entry, pktbuf);
2643 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2644
2645 DHD_WLFC_QMON_COMPLETE(entry);
2646
2647 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2648 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2649 WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
2650 } else {
2651 dhd_txcomplete(dhd, pktbuf, TRUE);
2652 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
2653 [DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
2654 wlfc->stats.pktout++;
2655 /* free the packet */
2656 PKTFREE(wlfc->osh, pktbuf, TRUE);
2657 }
2658 }
2659 /* pkt back from firmware side */
2660 if (entry->transit_count)
2661 entry->transit_count--;
2662 if (entry->suppr_transit_count) {
2663 entry->suppr_transit_count--;
2664 if (entry->suppressed &&
2665 (!entry->onbus_pkts_count) &&
2666 (!entry->suppr_transit_count))
2667 entry->suppressed = FALSE;
2668 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2669 _dhd_wlfc_bprint(wlfc, "[sc]-");
2670 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2671 }
2672
2673 cont:
2674 hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
2675 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2676 hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
2677 }
2678
2679 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
2680 seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
2681 }
2682
2683 count++;
2684 }
2685
2686 return BCME_OK;
2687 } /* _dhd_wlfc_compressed_txstatus_update */
2688
2689 /**
2690 * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
2691 * @param[in] credits caller supplied credit that will be added to the host credit.
2692 */
2693 static int
_dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)2694 _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
2695 {
2696 int i;
2697 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2698 for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
2699 #ifdef PROP_TXSTATUS_DEBUG
2700 wlfc->stats.fifo_credits_back[i] += credits[i];
2701 #endif
2702
2703 /* update FIFO credits */
2704 if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
2705 {
2706 int lender; /* Note that borrower is i */
2707
2708 /* Return credits to highest priority lender first */
2709 for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
2710 if (wlfc->credits_borrowed[i][lender] > 0) {
2711 if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
2712 credits[i] -=
2713 (uint8)wlfc->credits_borrowed[i][lender];
2714 wlfc->FIFO_credit[lender] +=
2715 wlfc->credits_borrowed[i][lender];
2716 wlfc->credits_borrowed[i][lender] = 0;
2717 } else {
2718 wlfc->credits_borrowed[i][lender] -= credits[i];
2719 wlfc->FIFO_credit[lender] += credits[i];
2720 credits[i] = 0;
2721 }
2722 }
2723 }
2724
2725 /* If we have more credits left over, these must belong to the AC */
2726 if (credits[i] > 0) {
2727 wlfc->FIFO_credit[i] += credits[i];
2728 }
2729
2730 if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
2731 wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
2732 }
2733 }
2734 }
2735
2736 #if defined(DHD_WLFC_THREAD)
2737 _dhd_wlfc_thread_wakeup(dhd);
2738 #endif /* defined(DHD_WLFC_THREAD) */
2739
2740 return BCME_OK;
2741 } /* _dhd_wlfc_fifocreditback_indicate */
2742
2743 #ifndef BCMDBUS
2744 /** !BCMDBUS specific function */
2745 static void
_dhd_wlfc_suppress_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)2746 _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
2747 {
2748 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2749 wlfc_mac_descriptor_t* entry;
2750 int prec;
2751 void *pkt = NULL, *head = NULL, *tail = NULL;
2752 struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
2753 uint8 results[WLFC_CTL_VALUE_LEN_TXSTATUS+WLFC_CTL_VALUE_LEN_SEQ];
2754 uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
2755 uint32 htod = 0;
2756 uint16 htodseq = 0;
2757 bool bCreditUpdate = FALSE;
2758
2759 dhd_os_sdlock_txq(dhd);
2760 for (prec = 0; prec < txq->num_prec; prec++) {
2761 while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
2762 if (!head) {
2763 head = pkt;
2764 }
2765 if (tail) {
2766 PKTSETLINK(tail, pkt);
2767 }
2768 tail = pkt;
2769 }
2770 }
2771 dhd_os_sdunlock_txq(dhd);
2772
2773 while ((pkt = head)) {
2774 head = PKTLINK(pkt);
2775 PKTSETLINK(pkt, NULL);
2776
2777 entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
2778 if (!entry) {
2779 PKTFREE(dhd->osh, pkt, TRUE);
2780 continue;
2781 }
2782 if (entry->onbus_pkts_count > 0) {
2783 entry->onbus_pkts_count--;
2784 }
2785 if (entry->suppressed &&
2786 (!entry->onbus_pkts_count) &&
2787 (!entry->suppr_transit_count)) {
2788 entry->suppressed = FALSE;
2789 }
2790 /* fake a suppression txstatus */
2791 htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
2792 WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
2793 WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
2794 htod = htol32(htod);
2795 memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
2796 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2797 htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
2798 if (IS_WL_TO_REUSE_SEQ(htodseq)) {
2799 SET_WL_HAS_ASSIGNED_SEQ(htodseq);
2800 RESET_WL_TO_REUSE_SEQ(htodseq);
2801 }
2802 htodseq = htol16(htodseq);
2803 memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
2804 WLFC_CTL_VALUE_LEN_SEQ);
2805 }
2806 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2807 _dhd_wlfc_enque_afq(wlfc, pkt);
2808 }
2809 _dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
2810
2811 /* fake a fifo credit back */
2812 if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
2813 credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
2814 bCreditUpdate = TRUE;
2815 }
2816 }
2817
2818 if (bCreditUpdate) {
2819 _dhd_wlfc_fifocreditback_indicate(dhd, credits);
2820 }
2821 } /* _dhd_wlfc_suppress_txq */
2822
2823 #endif /* !BCMDBUS */
2824
2825 static int
_dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)2826 _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
2827 {
2828 uint32 timestamp;
2829
2830 (void)dhd;
2831
2832 bcopy(&value[2], ×tamp, sizeof(uint32));
2833 timestamp = ltoh32(timestamp);
2834 DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
2835 return BCME_OK;
2836 }
2837
2838 static int
_dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)2839 _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
2840 {
2841 (void)dhd;
2842 (void)rssi;
2843 return BCME_OK;
2844 }
2845
2846 static void
_dhd_wlfc_add_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2847 _dhd_wlfc_add_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2848 {
2849 int i;
2850
2851 if (!wlfc || !entry) {
2852 return;
2853 }
2854
2855 for (i = 0; i < wlfc->requested_entry_count; i++) {
2856 if (entry == wlfc->requested_entry[i]) {
2857 break;
2858 }
2859 }
2860
2861 if (i == wlfc->requested_entry_count) {
2862 /* no match entry found */
2863 ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE-1));
2864 wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
2865 }
2866 }
2867
2868 /** called on eg receiving 'mac open' event from the dongle. */
2869 static void
_dhd_wlfc_remove_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2870 _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2871 {
2872 int i;
2873
2874 if (!wlfc || !entry) {
2875 return;
2876 }
2877
2878 for (i = 0; i < wlfc->requested_entry_count; i++) {
2879 if (entry == wlfc->requested_entry[i]) {
2880 break;
2881 }
2882 }
2883
2884 if (i < wlfc->requested_entry_count) {
2885 /* found */
2886 ASSERT(wlfc->requested_entry_count > 0);
2887 wlfc->requested_entry_count--;
2888 if (i != wlfc->requested_entry_count) {
2889 wlfc->requested_entry[i] =
2890 wlfc->requested_entry[wlfc->requested_entry_count];
2891 }
2892 wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
2893 }
2894 }
2895
2896 /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
2897 static int
_dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2898 _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2899 {
2900 int rc;
2901 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2902 wlfc_mac_descriptor_t* table;
2903 uint8 existing_index;
2904 uint8 table_index;
2905 uint8 ifid;
2906 uint8* ea;
2907
2908 WLFC_DBGMESG(("%s(), mac ["MACDBG"],%s,idx:%d,id:0x%02x\n",
2909 __FUNCTION__, MAC2STRDBG(&value[2]),
2910 ((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
2911 WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
2912
2913 table = wlfc->destination_entries.nodes;
2914 table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
2915 ifid = value[1];
2916 ea = &value[2];
2917
2918 _dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
2919 if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
2920 existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2921 if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
2922 (existing_index != table_index) && table[existing_index].occupied) {
2923 /*
2924 there is an existing different entry, free the old one
2925 and move it to new index if necessary.
2926 */
2927 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[existing_index],
2928 eWLFC_MAC_ENTRY_ACTION_DEL, table[existing_index].interface_id,
2929 table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
2930 &table[existing_index]);
2931 }
2932
2933 if (!table[table_index].occupied) {
2934 /* this new MAC entry does not exist, create one */
2935 table[table_index].mac_handle = value[0];
2936 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2937 eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2938 wlfc->destination_entries.interfaces[ifid].iftype,
2939 ea, NULL, NULL);
2940 } else {
2941 /* the space should have been empty, but it's not */
2942 wlfc->stats.mac_update_failed++;
2943 }
2944 }
2945
2946 if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2947 if (table[table_index].occupied) {
2948 rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2949 eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2950 wlfc->destination_entries.interfaces[ifid].iftype,
2951 ea, _dhd_wlfc_entrypkt_fn, &table[table_index]);
2952 } else {
2953 /* the space should have been occupied, but it's not */
2954 wlfc->stats.mac_update_failed++;
2955 }
2956 }
2957 BCM_REFERENCE(rc);
2958 return BCME_OK;
2959 } /* _dhd_wlfc_mac_table_update */
2960
2961 /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
2962 static int
_dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2963 _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2964 {
2965 /* Handle PS on/off indication */
2966 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2967 wlfc_mac_descriptor_t* table;
2968 wlfc_mac_descriptor_t* desc; /* a table maps from mac handle to mac descriptor */
2969 uint8 mac_handle = value[0];
2970 int i;
2971
2972 table = wlfc->destination_entries.nodes;
2973 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2974 if (desc->occupied) {
2975 #ifdef BULK_DEQUEUE
2976 for (i = 0; i < AC_COUNT + 1; i++) {
2977 desc->release_count[i] = 0;
2978 }
2979 #endif /* BULK_DEQUEUE */
2980 if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2981 desc->state = WLFC_STATE_OPEN;
2982 desc->ac_bitmap = 0xff;
2983 DHD_WLFC_CTRINC_MAC_OPEN(desc);
2984 desc->requested_credit = 0;
2985 desc->requested_packet = 0;
2986 _dhd_wlfc_remove_requested_entry(wlfc, desc);
2987 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2988 _dhd_wlfc_bprint(wlfc, "[op%u.%u]-",
2989 (uint8)(table - &wlfc->destination_entries.nodes[0]),
2990 OSL_SYSUPTIME());
2991 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2992 } else {
2993 desc->state = WLFC_STATE_CLOSE;
2994 DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2995 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2996 _dhd_wlfc_bprint(wlfc, "[cl%u.%u]-",
2997 (uint8)(table - &wlfc->destination_entries.nodes[0]),
2998 OSL_SYSUPTIME());
2999 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3000 /* Indicate to firmware if there is any traffic pending. */
3001 for (i = 0; i < AC_COUNT; i++) {
3002 _dhd_wlfc_traffic_pending_check(wlfc, desc, i);
3003 }
3004 }
3005 } else {
3006 wlfc->stats.psmode_update_failed++;
3007 }
3008
3009 return BCME_OK;
3010 } /* _dhd_wlfc_psmode_update */
3011
3012 /** called upon receiving 'interface open' or 'interface close' event from the dongle */
3013 static int
_dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)3014 _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
3015 {
3016 /* Handle PS on/off indication */
3017 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3018 wlfc_mac_descriptor_t* table;
3019 uint8 if_id = value[0];
3020 uint8 i;
3021
3022 BCM_REFERENCE(i);
3023
3024 if (if_id < WLFC_MAX_IFNUM) {
3025 table = wlfc->destination_entries.interfaces;
3026 if (table[if_id].occupied) {
3027 #ifdef BULK_DEQUEUE
3028 for (i = 0; i < AC_COUNT + 1; i++) {
3029 table->release_count[i] = 0;
3030 }
3031 #endif /* BULK_DEQUEUE */
3032 if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
3033 table[if_id].state = WLFC_STATE_OPEN;
3034 /* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
3035 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3036 _dhd_wlfc_bprint(wlfc, "[op%u.%u]-",
3037 (uint8)(table - &wlfc->destination_entries.nodes[0]),
3038 OSL_SYSUPTIME());
3039 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3040 } else {
3041 table[if_id].state = WLFC_STATE_CLOSE;
3042 /* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
3043 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3044 _dhd_wlfc_bprint(wlfc, "[cl%u.%u]-",
3045 (uint8)(table - &wlfc->destination_entries.nodes[0]),
3046 OSL_SYSUPTIME());
3047 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3048 }
3049 return BCME_OK;
3050 }
3051 }
3052 wlfc->stats.interface_update_failed++;
3053
3054 /* XXX: what is an appropriate error? */
3055 return BCME_OK;
3056 }
3057
3058 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
3059 static int
_dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)3060 _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
3061 {
3062 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3063 wlfc_mac_descriptor_t* table;
3064 wlfc_mac_descriptor_t* desc;
3065 uint8 mac_handle;
3066 uint8 credit;
3067
3068 table = wlfc->destination_entries.nodes;
3069 mac_handle = value[1];
3070 credit = value[0];
3071
3072 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
3073 if (desc->occupied) {
3074 desc->requested_credit = credit;
3075
3076 /* XXX: toggle AC prec bitmap based on received bmp, exclude ac/bc pkt */
3077 desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
3078 _dhd_wlfc_add_requested_entry(wlfc, desc);
3079 #if defined(DHD_WLFC_THREAD)
3080 if (credit) {
3081 _dhd_wlfc_thread_wakeup(dhd);
3082 }
3083 #endif /* DHD_WLFC_THREAD */
3084 } else {
3085 wlfc->stats.credit_request_failed++;
3086 }
3087
3088 return BCME_OK;
3089 }
3090
3091 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
3092 static int
_dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)3093 _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
3094 {
3095 athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3096 wlfc_mac_descriptor_t* table;
3097 wlfc_mac_descriptor_t* desc;
3098 uint8 mac_handle;
3099 uint8 packet_count;
3100
3101 table = wlfc->destination_entries.nodes;
3102 mac_handle = value[1];
3103 packet_count = value[0];
3104
3105 desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
3106 if (desc->occupied) {
3107 desc->requested_packet = packet_count;
3108
3109 /* XXX: toggle AC prec bitmap based on received bmp, exclude ac/bc pkt */
3110 desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
3111 _dhd_wlfc_add_requested_entry(wlfc, desc);
3112 #if defined(DHD_WLFC_THREAD)
3113 if (packet_count) {
3114 _dhd_wlfc_thread_wakeup(dhd);
3115 }
3116 #endif /* DHD_WLFC_THREAD */
3117 } else {
3118 wlfc->stats.packet_request_failed++;
3119 }
3120
3121 return BCME_OK;
3122 }
3123
3124 /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
3125 static void
_dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)3126 _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
3127 {
3128 if (info_len) {
3129 /* Check copy length to avoid buffer overrun. In case of length exceeding
3130 * WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
3131 * of length WLHOST_REORDERDATA_TOTLEN
3132 */
3133 if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
3134 bcopy(val, info_buf, len);
3135 *info_len = len;
3136 } else {
3137 *info_len = 0;
3138 }
3139 }
3140 }
3141
3142 /*
3143 * public functions
3144 */
3145
dhd_wlfc_is_supported(dhd_pub_t * dhd)3146 bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
3147 {
3148 bool rc = TRUE;
3149
3150 if (dhd == NULL) {
3151 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3152 return FALSE;
3153 }
3154
3155 dhd_os_wlfc_block(dhd);
3156
3157 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3158 rc = FALSE;
3159 }
3160
3161 dhd_os_wlfc_unblock(dhd);
3162
3163 return rc;
3164 }
3165
3166 #ifdef BULK_DEQUEUE
3167 #ifndef WLFC_MAX_RELEASE_CNT
3168 #ifdef CUSTOM_AMPDU_MPDU
3169 #define WLFC_MAX_RELEASE_CNT CUSTOM_AMPDU_MPDU
3170 #else
3171 #define WLFC_MAX_RELEASE_CNT 16
3172 #endif /* CUSTOM_AMPDU_MPDU */
3173 #endif /* WLFC_MAX_RELEASE_CNT */
3174 #endif /* BULK_DEQUEUE */
3175
dhd_wlfc_enable(dhd_pub_t * dhd)3176 int dhd_wlfc_enable(dhd_pub_t *dhd)
3177 {
3178 int i, rc = BCME_OK;
3179 athost_wl_status_info_t* wlfc;
3180
3181 if (dhd == NULL) {
3182 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3183 return BCME_BADARG;
3184 }
3185
3186 dhd_os_wlfc_block(dhd);
3187
3188 if (!dhd->wlfc_enabled || dhd->wlfc_state) {
3189 rc = BCME_OK;
3190 goto exit;
3191 }
3192
3193 /* allocate space to track txstatus propagated from firmware */
3194 dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
3195 sizeof(athost_wl_status_info_t));
3196 if (dhd->wlfc_state == NULL) {
3197 rc = BCME_NOMEM;
3198 goto exit;
3199 }
3200
3201 /* initialize state space */
3202 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3203 memset(wlfc, 0, sizeof(athost_wl_status_info_t));
3204
3205 /* remember osh & dhdp */
3206 wlfc->osh = dhd->osh;
3207 wlfc->dhdp = dhd;
3208 #ifdef BULK_DEQUEUE
3209 wlfc->max_release_count = WLFC_MAX_RELEASE_CNT;
3210 #endif /* BULK_DEQUEUE */
3211 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3212 wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
3213 if (wlfc->hanger == NULL) {
3214 DHD_OS_PREFREE(dhd, dhd->wlfc_state,
3215 sizeof(athost_wl_status_info_t));
3216 dhd->wlfc_state = NULL;
3217 rc = BCME_NOMEM;
3218 goto exit;
3219 }
3220 }
3221
3222 dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
3223 /* default to check rx pkt */
3224 dhd->wlfc_rxpkt_chk = TRUE;
3225 #if defined (LINUX) || defined(linux)
3226 if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
3227 dhd->wlfc_rxpkt_chk = FALSE;
3228 }
3229 #endif /* defined (LINUX) || defined(linux) */
3230
3231 /* initialize all interfaces to accept traffic */
3232 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
3233 wlfc->hostif_flow_state[i] = OFF;
3234 }
3235
3236 _dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
3237 eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL, NULL);
3238
3239 wlfc->allow_credit_borrow = 0;
3240 wlfc->single_ac = 0;
3241 wlfc->single_ac_timestamp = 0;
3242
3243 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3244 wlfc->log_buf = MALLOC(dhd->osh, WLFC_LOG_BUF_SIZE);
3245 wlfc->log_buf[WLFC_LOG_BUF_SIZE - 1] = 0;
3246 wlfc->log_buf_offset = 0;
3247 wlfc->log_buf_full = FALSE;
3248 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3249
3250 exit:
3251 DHD_ERROR(("%s: ret=%d\n", __FUNCTION__, rc));
3252 dhd_os_wlfc_unblock(dhd);
3253
3254 return rc;
3255 } /* dhd_wlfc_enable */
3256
3257 #ifdef SUPPORT_P2P_GO_PS
3258
3259 /**
3260 * Called when the host platform enters a lower power mode, eg right before a system hibernate.
3261 * SUPPORT_P2P_GO_PS specific function.
3262 */
3263 int
dhd_wlfc_suspend(dhd_pub_t * dhd)3264 dhd_wlfc_suspend(dhd_pub_t *dhd)
3265 {
3266 uint32 tlv = 0;
3267
3268 DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
3269 if (!dhd->wlfc_enabled)
3270 return -1;
3271
3272 if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
3273 return -1;
3274 if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0)
3275 return 0;
3276 tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3277 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
3278 return -1;
3279
3280 return 0;
3281 }
3282
3283 /**
3284 * Called when the host platform resumes from a power management operation, eg resume after a
3285 * system hibernate. SUPPORT_P2P_GO_PS specific function.
3286 */
3287 int
dhd_wlfc_resume(dhd_pub_t * dhd)3288 dhd_wlfc_resume(dhd_pub_t *dhd)
3289 {
3290 uint32 tlv = 0;
3291
3292 DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
3293 if (!dhd->wlfc_enabled)
3294 return -1;
3295
3296 if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
3297 return -1;
3298 if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
3299 (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS))
3300 return 0;
3301 tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3302 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
3303 return -1;
3304
3305 return 0;
3306 }
3307
3308 #endif /* SUPPORT_P2P_GO_PS */
3309
3310 /** A flow control header was received from firmware, containing one or more TLVs */
3311 int
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)3312 dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
3313 uint *reorder_info_len)
3314 {
3315 uint8 type, len;
3316 uint8* value;
3317 uint8* tmpbuf;
3318 uint16 remainder = (uint16)tlv_hdr_len;
3319 uint16 processed = 0;
3320 athost_wl_status_info_t* wlfc = NULL;
3321 void* entry;
3322
3323 if ((dhd == NULL) || (pktbuf == NULL)) {
3324 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3325 return BCME_BADARG;
3326 }
3327
3328 dhd_os_wlfc_block(dhd);
3329
3330 if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
3331 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3332 dhd_os_wlfc_unblock(dhd);
3333 return WLFC_UNSUPPORTED;
3334 }
3335 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3336 }
3337
3338 tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
3339
3340 if (remainder) {
3341 while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
3342 type = tmpbuf[processed];
3343 if (type == WLFC_CTL_TYPE_FILLER) {
3344 remainder -= 1;
3345 processed += 1;
3346 continue;
3347 }
3348
3349 len = tmpbuf[processed + 1];
3350 value = &tmpbuf[processed + 2];
3351
3352 if (remainder < (2 + len))
3353 break;
3354
3355 remainder -= 2 + len;
3356 processed += 2 + len;
3357 entry = NULL;
3358
3359 DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
3360 __FUNCTION__, __LINE__, type, remainder, processed));
3361
3362 if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
3363 _dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
3364 reorder_info_len);
3365
3366 if (wlfc == NULL) {
3367 ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
3368
3369 if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
3370 type != WLFC_CTL_TYPE_TRANS_ID)
3371 DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
3372 " type %d remainder %d processed %d\n",
3373 __FUNCTION__, __LINE__, type, remainder, processed));
3374 continue;
3375 }
3376
3377 if (type == WLFC_CTL_TYPE_TXSTATUS) {
3378 _dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
3379 } else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
3380 uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
3381
3382 if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
3383 compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
3384 }
3385 _dhd_wlfc_compressed_txstatus_update(dhd, value,
3386 value[compcnt_offset], &entry);
3387 } else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
3388 _dhd_wlfc_fifocreditback_indicate(dhd, value);
3389 } else if (type == WLFC_CTL_TYPE_RSSI) {
3390 _dhd_wlfc_rssi_indicate(dhd, value);
3391 } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
3392 _dhd_wlfc_credit_request(dhd, value);
3393 } else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
3394 _dhd_wlfc_packet_request(dhd, value);
3395 } else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
3396 (type == WLFC_CTL_TYPE_MAC_CLOSE)) {
3397 _dhd_wlfc_psmode_update(dhd, value, type);
3398 } else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
3399 (type == WLFC_CTL_TYPE_MACDESC_DEL)) {
3400 _dhd_wlfc_mac_table_update(dhd, value, type);
3401 } else if (type == WLFC_CTL_TYPE_TRANS_ID) {
3402 _dhd_wlfc_dbg_senum_check(dhd, value);
3403 } else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
3404 (type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
3405 _dhd_wlfc_interface_update(dhd, value, type);
3406 }
3407
3408 #ifndef BCMDBUS
3409 if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
3410 /* suppress all packets for this mac entry from bus->txq */
3411 _dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
3412 }
3413 #endif /* !BCMDBUS */
3414 } /* while */
3415
3416 if (remainder != 0 && wlfc) {
3417 /* trouble..., something is not right */
3418 wlfc->stats.tlv_parse_failed++;
3419 }
3420 } /* if */
3421
3422 if (wlfc)
3423 wlfc->stats.dhd_hdrpulls++;
3424
3425 dhd_os_wlfc_unblock(dhd);
3426 return BCME_OK;
3427 }
3428
3429 KERNEL_THREAD_RETURN_TYPE
dhd_wlfc_transfer_packets(void * data)3430 dhd_wlfc_transfer_packets(void *data)
3431 {
3432 dhd_pub_t *dhdp = (dhd_pub_t *)data;
3433 int ac, single_ac = 0, rc = BCME_OK;
3434 dhd_wlfc_commit_info_t commit_info;
3435 athost_wl_status_info_t* ctx;
3436 int bus_retry_count = 0;
3437 int pkt_send = 0;
3438 int pkt_send_per_ac = 0;
3439
3440 uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
3441 uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
3442 uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
3443 bool no_credit = FALSE;
3444
3445 int lender;
3446 int pkt_bound = WLFC_PACKET_BOUND;
3447 int highest_lender_ac;
3448
3449 BCM_REFERENCE(highest_lender_ac);
3450
3451 #if defined(DHD_WLFC_THREAD)
3452 /* wait till someone wakeup me up, will change it at running time */
3453 #if defined(LINUX)
3454 int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
3455 #endif /* LINUX */
3456 #endif /* defined(DHD_WLFC_THREAD) */
3457
3458 #if defined(DHD_WLFC_THREAD)
3459 while (1) {
3460 bus_retry_count = 0;
3461 pkt_send = 0;
3462 tx_map = 0;
3463 rx_map = 0;
3464 packets_map = 0;
3465 #if defined(LINUX)
3466 wait_msec = wait_event_interruptible_timeout(dhdp->wlfc_wqhead,
3467 dhdp->wlfc_thread_go, wait_msec);
3468 if (kthread_should_stop()) {
3469 break;
3470 }
3471 dhdp->wlfc_thread_go = FALSE;
3472 #endif /* LINUX */
3473
3474 dhd_os_wlfc_block(dhdp);
3475 #endif /* defined(DHD_WLFC_THREAD) */
3476 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3477 #if defined(DHD_WLFC_THREAD)
3478 if (!ctx)
3479 goto exit;
3480 #endif /* defined(DHD_WLFC_THREAD) */
3481
3482 memset(&commit_info, 0, sizeof(commit_info));
3483
3484 /*
3485 Commit packets for regular AC traffic. Higher priority first.
3486 First, use up FIFO credits available to each AC. Based on distribution
3487 and credits left, borrow from other ACs as applicable
3488
3489 -NOTE:
3490 If the bus between the host and firmware is overwhelmed by the
3491 traffic from host, it is possible that higher priority traffic
3492 starves the lower priority queue. If that occurs often, we may
3493 have to employ weighted round-robin or ucode scheme to avoid
3494 low priority packet starvation.
3495 */
3496
3497 #ifdef BULK_DEQUEUE
3498 pkt_bound = ctx->max_release_count;
3499 #endif
3500
3501 for (ac = AC_COUNT; ac >= 0; ac--) {
3502 if (dhdp->wlfc_rxpkt_chk) {
3503 /* check rx packet */
3504 uint32 curr_t = OSL_SYSUPTIME(), delta;
3505
3506 delta = curr_t - ctx->rx_timestamp[ac];
3507 if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
3508 rx_map |= (1 << ac);
3509 }
3510 }
3511
3512 if (ctx->pkt_cnt_per_ac[ac] == 0) {
3513 continue;
3514 }
3515
3516 tx_map |= (1 << ac);
3517 single_ac = ac + 1;
3518 pkt_send_per_ac = 0;
3519 while ((FALSE == dhdp->proptxstatus_txoff) &&
3520 (pkt_send_per_ac < pkt_bound)) {
3521 /* packets from delayQ with less priority are fresh and
3522 * they'd need header and have no MAC entry
3523 */
3524 no_credit = (ctx->FIFO_credit[ac] < 1);
3525 if (dhdp->proptxstatus_credit_ignore ||
3526 ((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
3527 no_credit = FALSE;
3528 }
3529
3530 lender = -1;
3531 #ifdef LIMIT_BORROW
3532 if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
3533 dhdp->wlfc_borrow_allowed) {
3534 /* try borrow from lower priority */
3535 #ifdef BULK_DEQUEUE
3536 /* Enable credit borrow from higher AC
3537 * to make packet chain longer
3538 */
3539 highest_lender_ac = AC_COUNT;
3540 #else
3541 highest_lender_ac = ac - 1;
3542 #endif /* BULK_DEQUEUE */
3543 lender = _dhd_wlfc_borrow_credit(ctx, highest_lender_ac, ac, FALSE);
3544 if (lender != -1) {
3545 no_credit = FALSE;
3546 }
3547 }
3548 #endif /* LIMIT_BORROW */
3549 commit_info.needs_hdr = 1;
3550 commit_info.mac_entry = NULL;
3551 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3552 &(commit_info.ac_fifo_credit_spent),
3553 &(commit_info.needs_hdr),
3554 &(commit_info.mac_entry),
3555 no_credit);
3556 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3557 eWLFC_PKTTYPE_SUPPRESSED;
3558
3559 if (commit_info.p == NULL) {
3560 #ifdef LIMIT_BORROW
3561 if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3562 _dhd_wlfc_return_credit(ctx, lender, ac);
3563 }
3564 #endif
3565 break;
3566 }
3567
3568 if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
3569 ASSERT(ctx->FIFO_credit[ac] >= commit_info.ac_fifo_credit_spent);
3570 }
3571 /* here we can ensure have credit or no credit needed */
3572 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3573 ctx->fcommit, ctx->commit_ctx);
3574
3575 /* Bus commits may fail (e.g. flow control); abort after retries */
3576 if (rc == BCME_OK) {
3577 pkt_send++;
3578 pkt_send_per_ac++;
3579 if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
3580 ctx->FIFO_credit[ac]--;
3581 }
3582 #ifdef LIMIT_BORROW
3583 else if (!commit_info.ac_fifo_credit_spent && (lender != -1) &&
3584 dhdp->wlfc_borrow_allowed) {
3585 _dhd_wlfc_return_credit(ctx, lender, ac);
3586 }
3587 #endif
3588 } else {
3589 #ifdef LIMIT_BORROW
3590 if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3591 _dhd_wlfc_return_credit(ctx, lender, ac);
3592 }
3593 #endif
3594 bus_retry_count++;
3595 if (bus_retry_count >= BUS_RETRIES) {
3596 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3597 goto exit;
3598 }
3599 }
3600 }
3601
3602 if (ctx->pkt_cnt_per_ac[ac]) {
3603 packets_map |= (1 << ac);
3604 }
3605 }
3606
3607 if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
3608 /* nothing send out or remain in queue */
3609 rc = BCME_OK;
3610 goto exit;
3611 }
3612
3613 if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
3614 /* only one tx ac exist and no higher rx ac */
3615 if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
3616 ac = single_ac - 1;
3617 } else {
3618 uint32 delta;
3619 uint32 curr_t = OSL_SYSUPTIME();
3620
3621 if (single_ac != ctx->single_ac) {
3622 /* new single ac traffic (first single ac or different single ac) */
3623 ctx->allow_credit_borrow = 0;
3624 ctx->single_ac_timestamp = curr_t;
3625 ctx->single_ac = (uint8)single_ac;
3626 rc = BCME_OK;
3627 goto exit;
3628 }
3629 /* same ac traffic, check if it lasts enough time */
3630 delta = curr_t - ctx->single_ac_timestamp;
3631
3632 if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
3633 /* wait enough time, can borrow now */
3634 ctx->allow_credit_borrow = 1;
3635 ac = single_ac - 1;
3636 } else {
3637 rc = BCME_OK;
3638 goto exit;
3639 }
3640 }
3641 } else {
3642 /* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
3643 ctx->allow_credit_borrow = 0;
3644 ctx->single_ac_timestamp = 0;
3645 ctx->single_ac = 0;
3646 rc = BCME_OK;
3647 goto exit;
3648 }
3649
3650 if (packets_map == 0) {
3651 /* nothing to send, skip borrow */
3652 rc = BCME_OK;
3653 goto exit;
3654 }
3655
3656 /* At this point, borrow all credits only for ac */
3657 while (FALSE == dhdp->proptxstatus_txoff) {
3658 #ifdef LIMIT_BORROW
3659 if (dhdp->wlfc_borrow_allowed) {
3660 if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac, TRUE)) == -1) {
3661 break;
3662 }
3663 }
3664 else
3665 break;
3666 #endif
3667 commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3668 &(commit_info.ac_fifo_credit_spent),
3669 &(commit_info.needs_hdr),
3670 &(commit_info.mac_entry),
3671 FALSE);
3672 if (commit_info.p == NULL) {
3673 /* before borrow only one ac exists and now this only ac is empty */
3674 #ifdef LIMIT_BORROW
3675 _dhd_wlfc_return_credit(ctx, lender, ac);
3676 #endif
3677 break;
3678 }
3679
3680 commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3681 eWLFC_PKTTYPE_SUPPRESSED;
3682
3683 rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3684 ctx->fcommit, ctx->commit_ctx);
3685
3686 /* Bus commits may fail (e.g. flow control); abort after retries */
3687 if (rc == BCME_OK) {
3688 pkt_send++;
3689 if (commit_info.ac_fifo_credit_spent) {
3690 #ifndef LIMIT_BORROW
3691 ctx->FIFO_credit[ac]--;
3692 #endif
3693 } else {
3694 #ifdef LIMIT_BORROW
3695 _dhd_wlfc_return_credit(ctx, lender, ac);
3696 #endif
3697 }
3698 } else {
3699 #ifdef LIMIT_BORROW
3700 _dhd_wlfc_return_credit(ctx, lender, ac);
3701 #endif
3702 bus_retry_count++;
3703 if (bus_retry_count >= BUS_RETRIES) {
3704 DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3705 goto exit;
3706 }
3707 }
3708 }
3709
3710 BCM_REFERENCE(pkt_send);
3711
3712 exit:
3713 #if defined(DHD_WLFC_THREAD)
3714 dhd_os_wlfc_unblock(dhdp);
3715 #if defined(LINUX)
3716 if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
3717 wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
3718 } else {
3719 wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
3720 }
3721 #endif /* LINUX */
3722 }
3723 return 0;
3724 #else
3725 return rc;
3726 #endif /* defined(DHD_WLFC_THREAD) */
3727 }
3728
3729 /**
3730 * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
3731 * eg dhd_sendpkt().
3732 * @param[in] dhdp Pointer to public DHD structure
3733 * @param[in] fcommit Pointer to transmit function of next layer
3734 * @param[in] commit_ctx Opaque context used when calling next layer
3735 * @param[in] pktbuf Packet to send
3736 * @param[in] need_toggle_host_if If TRUE, resets flag ctx->toggle_host_if
3737 */
3738 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)3739 dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit,
3740 struct dhd_bus *commit_ctx, void *pktbuf, bool need_toggle_host_if)
3741 {
3742 int rc = BCME_OK;
3743 athost_wl_status_info_t* ctx;
3744
3745 #if defined(DHD_WLFC_THREAD)
3746 if (!pktbuf)
3747 return BCME_OK;
3748 #endif /* defined(DHD_WLFC_THREAD) */
3749
3750 if ((dhdp == NULL) || (fcommit == NULL)) {
3751 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3752 return BCME_BADARG;
3753 }
3754
3755 dhd_os_wlfc_block(dhdp);
3756
3757 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3758 if (pktbuf) {
3759 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
3760 }
3761 rc = WLFC_UNSUPPORTED;
3762 goto exit;
3763 }
3764
3765 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3766
3767 #ifdef BCMDBUS
3768 if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
3769 if (pktbuf) {
3770 PKTFREE(ctx->osh, pktbuf, TRUE);
3771 rc = BCME_OK;
3772 }
3773 goto exit;
3774 }
3775 #endif
3776
3777 if (dhdp->proptxstatus_module_ignore) {
3778 if (pktbuf) {
3779 uint32 htod = 0;
3780 WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
3781 _dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
3782 if (fcommit(commit_ctx, pktbuf)) {
3783 /* free it if failed, otherwise do it in tx complete cb */
3784 PKTFREE(ctx->osh, pktbuf, TRUE);
3785 }
3786 rc = BCME_OK;
3787 }
3788 goto exit;
3789 }
3790
3791 if (pktbuf) {
3792 int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
3793 ASSERT(ac <= AC_COUNT);
3794 DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
3795 /* en-queue the packets to respective queue. */
3796 rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
3797 if (rc) {
3798 _dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
3799 } else {
3800 ctx->stats.pktin++;
3801 ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
3802 }
3803 }
3804
3805 if (!ctx->fcommit) {
3806 ctx->fcommit = fcommit;
3807 } else {
3808 ASSERT(ctx->fcommit == fcommit);
3809 }
3810 if (!ctx->commit_ctx) {
3811 ctx->commit_ctx = commit_ctx;
3812 } else {
3813 ASSERT(ctx->commit_ctx == commit_ctx);
3814 }
3815
3816 #if defined(DHD_WLFC_THREAD)
3817 _dhd_wlfc_thread_wakeup(dhdp);
3818 #else
3819 dhd_wlfc_transfer_packets(dhdp);
3820 #endif /* defined(DHD_WLFC_THREAD) */
3821
3822 exit:
3823 dhd_os_wlfc_unblock(dhdp);
3824 return rc;
3825 } /* dhd_wlfc_commit_packets */
3826
3827 /**
3828 * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
3829 */
3830 int
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)3831 dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
3832 {
3833 athost_wl_status_info_t* wlfc;
3834 wlfc_mac_descriptor_t *entry;
3835 void* pout = NULL;
3836 int rtn = BCME_OK;
3837 if ((dhd == NULL) || (txp == NULL)) {
3838 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3839 return BCME_BADARG;
3840 }
3841
3842 bcm_pkt_validate_chk(txp, "_dhd_wlfc_compressed_txstatus_update");
3843
3844 dhd_os_wlfc_block(dhd);
3845
3846 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3847 rtn = WLFC_UNSUPPORTED;
3848 goto EXIT;
3849 }
3850
3851 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3852 if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
3853 #ifdef PROP_TXSTATUS_DEBUG
3854 wlfc->stats.signal_only_pkts_freed++;
3855 #endif
3856 /* is this a signal-only packet? */
3857 _dhd_wlfc_pullheader(wlfc, txp);
3858 PKTFREE(wlfc->osh, txp, TRUE);
3859 goto EXIT;
3860 }
3861
3862 entry = _dhd_wlfc_find_table_entry(wlfc, txp);
3863 ASSERT(entry);
3864
3865 if (!success || dhd->proptxstatus_txstatus_ignore) {
3866 WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
3867 __FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
3868 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3869 _dhd_wlfc_hanger_poppkt(wlfc->hanger, WL_TXSTATUS_GET_HSLOT(
3870 DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout, TRUE);
3871 ASSERT(txp == pout);
3872 }
3873
3874 /* indicate failure and free the packet */
3875 dhd_txcomplete(dhd, txp, success);
3876
3877 /* return the credit, if necessary */
3878 _dhd_wlfc_return_implied_credit(wlfc, txp);
3879
3880 if (entry->transit_count)
3881 entry->transit_count--;
3882 if (entry->suppr_transit_count)
3883 entry->suppr_transit_count--;
3884 wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))][DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
3885 wlfc->stats.pktout++;
3886 PKTFREE(wlfc->osh, txp, TRUE);
3887 } else {
3888 /* bus confirmed pkt went to firmware side */
3889 if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
3890 _dhd_wlfc_enque_afq(wlfc, txp);
3891 } else {
3892 int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
3893 _dhd_wlfc_hanger_free_pkt(wlfc, hslot,
3894 WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
3895 }
3896 }
3897
3898 ASSERT(entry->onbus_pkts_count > 0);
3899 if (entry->onbus_pkts_count > 0)
3900 entry->onbus_pkts_count--;
3901 if (entry->suppressed &&
3902 (!entry->onbus_pkts_count) &&
3903 (!entry->suppr_transit_count))
3904 entry->suppressed = FALSE;
3905 EXIT:
3906 dhd_os_wlfc_unblock(dhd);
3907 return rtn;
3908 } /* dhd_wlfc_txcomplete */
3909
3910 int
dhd_wlfc_init(dhd_pub_t * dhd)3911 dhd_wlfc_init(dhd_pub_t *dhd)
3912 {
3913 /* enable all signals & indicate host proptxstatus logic is active */
3914 uint32 tlv, mode, fw_caps;
3915 int ret = 0;
3916
3917 if (dhd == NULL) {
3918 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3919 return BCME_BADARG;
3920 }
3921
3922 dhd_os_wlfc_block(dhd);
3923 if (dhd->wlfc_enabled) {
3924 DHD_INFO(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
3925 dhd_os_wlfc_unblock(dhd);
3926 return BCME_OK;
3927 }
3928 dhd->wlfc_enabled = TRUE;
3929 dhd_os_wlfc_unblock(dhd);
3930
3931 tlv = WLFC_FLAGS_RSSI_SIGNALS |
3932 WLFC_FLAGS_XONXOFF_SIGNALS |
3933 WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
3934 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
3935 WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3936
3937 /* XXX dhd->wlfc_state = NULL; */
3938 /* XXX ANDREY:may erase pointer to already created wlfc_state, PR#97824 */
3939
3940 /*
3941 try to enable/disable signaling by sending "tlv" iovar. if that fails,
3942 fallback to no flow control? Print a message for now.
3943 */
3944
3945 /* enable proptxtstatus signaling by default */
3946 if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3947 /*
3948 Leaving the message for now, it should be removed after a while; once
3949 the tlv situation is stable.
3950 */
3951 DHD_INFO(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
3952 dhd->wlfc_enabled?"enabled":"disabled", tlv));
3953 }
3954
3955 mode = 0;
3956
3957 /* query caps */
3958 ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR, FALSE, 0);
3959
3960 if (!ret) {
3961 DHD_INFO(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
3962
3963 if (WLFC_IS_OLD_DEF(fw_caps)) {
3964 #ifdef BCMDBUS
3965 mode = WLFC_MODE_HANGER;
3966 #else
3967 /* enable proptxtstatus v2 by default */
3968 mode = WLFC_MODE_AFQ;
3969 #endif /* BCMDBUS */
3970 } else {
3971 WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
3972 #ifdef BCMDBUS
3973 WLFC_SET_AFQ(mode, 0);
3974 #endif /* BCMDBUS */
3975 WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps));
3976 WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
3977 }
3978 ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR, TRUE, 0);
3979 }
3980
3981 dhd_os_wlfc_block(dhd);
3982
3983 dhd->wlfc_mode = 0;
3984 if (ret >= 0) {
3985 if (WLFC_IS_OLD_DEF(mode)) {
3986 WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
3987 } else {
3988 dhd->wlfc_mode = mode;
3989 }
3990 }
3991
3992 DHD_INFO(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
3993 #ifdef LIMIT_BORROW
3994 dhd->wlfc_borrow_allowed = TRUE;
3995 #endif
3996 dhd_os_wlfc_unblock(dhd);
3997
3998 if (dhd->plat_init)
3999 dhd->plat_init((void *)dhd);
4000
4001 return BCME_OK;
4002 } /* dhd_wlfc_init */
4003
4004 /** AMPDU host reorder specific function */
4005 int
dhd_wlfc_hostreorder_init(dhd_pub_t * dhd)4006 dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
4007 {
4008 /* enable only ampdu hostreorder here */
4009 uint32 tlv;
4010
4011 if (dhd == NULL) {
4012 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4013 return BCME_BADARG;
4014 }
4015
4016 DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
4017
4018 tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4019
4020 /* enable proptxtstatus signaling by default */
4021 if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4022 DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
4023 __FUNCTION__));
4024 } else {
4025 /*
4026 Leaving the message for now, it should be removed after a while; once
4027 the tlv situation is stable.
4028 */
4029 DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
4030 __FUNCTION__, tlv));
4031 }
4032
4033 dhd_os_wlfc_block(dhd);
4034 dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
4035 dhd_os_wlfc_unblock(dhd);
4036 /* terence 20161229: enable ampdu_hostreorder if tlv enable hostreorder */
4037 dhd_conf_set_intiovar(dhd, 0, WLC_SET_VAR, "ampdu_hostreorder", 1, 0, TRUE);
4038
4039 return BCME_OK;
4040 }
4041
4042 int
dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)4043 dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
4044 {
4045 if (dhd == NULL) {
4046 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4047 return BCME_BADARG;
4048 }
4049
4050 dhd_os_wlfc_block(dhd);
4051
4052 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4053 dhd_os_wlfc_unblock(dhd);
4054 return WLFC_UNSUPPORTED;
4055 }
4056
4057 #ifndef BCMDBUS
4058 _dhd_wlfc_cleanup_txq(dhd, fn, arg);
4059 #endif /* !BCMDBUS */
4060
4061 dhd_os_wlfc_unblock(dhd);
4062
4063 return BCME_OK;
4064 }
4065
4066 /** release all packet resources */
4067 int
dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)4068 dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
4069 {
4070 if (dhd == NULL) {
4071 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4072 return BCME_BADARG;
4073 }
4074
4075 dhd_os_wlfc_block(dhd);
4076
4077 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4078 dhd_os_wlfc_unblock(dhd);
4079 return WLFC_UNSUPPORTED;
4080 }
4081
4082 _dhd_wlfc_cleanup(dhd, fn, arg);
4083
4084 dhd_os_wlfc_unblock(dhd);
4085
4086 return BCME_OK;
4087 }
4088
4089 int
dhd_wlfc_deinit(dhd_pub_t * dhd)4090 dhd_wlfc_deinit(dhd_pub_t *dhd)
4091 {
4092 /* cleanup all psq related resources */
4093 athost_wl_status_info_t* wlfc;
4094 uint32 tlv = 0;
4095 uint32 hostreorder = 0;
4096
4097 if (dhd == NULL) {
4098 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4099 return BCME_BADARG;
4100 }
4101
4102 dhd_os_wlfc_block(dhd);
4103 if (!dhd->wlfc_enabled) {
4104 DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
4105 dhd_os_wlfc_unblock(dhd);
4106 return BCME_OK;
4107 }
4108
4109 dhd->wlfc_enabled = FALSE;
4110 dhd_os_wlfc_unblock(dhd);
4111
4112 /* query ampdu hostreorder */
4113 (void) dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder",
4114 &hostreorder, WLC_GET_VAR, FALSE, 0);
4115
4116 if (hostreorder) {
4117 tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4118 DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
4119 __FUNCTION__, __LINE__));
4120 }
4121
4122 /* Disable proptxtstatus signaling for deinit */
4123 (void) dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
4124
4125 dhd_os_wlfc_block(dhd);
4126
4127 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4128 dhd_os_wlfc_unblock(dhd);
4129 return WLFC_UNSUPPORTED;
4130 }
4131
4132 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4133
4134 _dhd_wlfc_cleanup(dhd, NULL, NULL);
4135
4136 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4137 int i;
4138 wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
4139 for (i = 0; i < h->max_items; i++) {
4140 if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
4141 _dhd_wlfc_hanger_free_pkt(wlfc, i,
4142 WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
4143 }
4144 }
4145
4146 /* delete hanger */
4147 _dhd_wlfc_hanger_delete(dhd, h);
4148 }
4149
4150 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
4151 if (wlfc->log_buf) {
4152 MFREE(dhd->osh, wlfc->log_buf, WLFC_LOG_BUF_SIZE);
4153 wlfc->log_buf_offset = 0;
4154 wlfc->log_buf_full = FALSE;
4155 }
4156 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
4157
4158 /* free top structure */
4159 DHD_OS_PREFREE(dhd, dhd->wlfc_state,
4160 sizeof(athost_wl_status_info_t));
4161 dhd->wlfc_state = NULL;
4162 dhd->proptxstatus_mode = hostreorder ?
4163 WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
4164
4165 dhd_os_wlfc_unblock(dhd);
4166
4167 if (dhd->plat_deinit)
4168 dhd->plat_deinit((void *)dhd);
4169 return BCME_OK;
4170 } /* dhd_wlfc_init */
4171
4172 /**
4173 * Called on an interface event (WLC_E_IF) indicated by firmware
4174 * @param[in] dhdp Pointer to public DHD structure
4175 * @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
4176 */
dhd_wlfc_interface_event(dhd_pub_t * dhdp,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)4177 int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
4178 {
4179 int rc;
4180
4181 if (dhdp == NULL) {
4182 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4183 return BCME_BADARG;
4184 }
4185
4186 dhd_os_wlfc_block(dhdp);
4187
4188 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4189 dhd_os_wlfc_unblock(dhdp);
4190 return WLFC_UNSUPPORTED;
4191 }
4192
4193 rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid, iftype, ea);
4194
4195 dhd_os_wlfc_unblock(dhdp);
4196 return rc;
4197 }
4198
4199 /** 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)4200 int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8* event_data)
4201 {
4202 int rc;
4203
4204 if (dhdp == NULL) {
4205 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4206 return BCME_BADARG;
4207 }
4208
4209 dhd_os_wlfc_block(dhdp);
4210
4211 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4212 dhd_os_wlfc_unblock(dhdp);
4213 return WLFC_UNSUPPORTED;
4214 }
4215
4216 rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
4217
4218 dhd_os_wlfc_unblock(dhdp);
4219
4220 return rc;
4221 }
4222 #ifdef LIMIT_BORROW
dhd_wlfc_disable_credit_borrow_event(dhd_pub_t * dhdp,uint8 * event_data)4223 int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8* event_data)
4224 {
4225 if (dhdp == NULL || event_data == NULL) {
4226 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4227 return BCME_BADARG;
4228 }
4229 dhd_os_wlfc_block(dhdp);
4230 dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
4231 dhd_os_wlfc_unblock(dhdp);
4232
4233 return BCME_OK;
4234 }
4235 #endif /* LIMIT_BORROW */
4236
4237 /**
4238 * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
4239 * specific)
4240 */
dhd_wlfc_BCMCCredit_support_event(dhd_pub_t * dhdp)4241 int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
4242 {
4243 int rc;
4244
4245 if (dhdp == NULL) {
4246 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4247 return BCME_BADARG;
4248 }
4249
4250 dhd_os_wlfc_block(dhdp);
4251
4252 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4253 dhd_os_wlfc_unblock(dhdp);
4254 return WLFC_UNSUPPORTED;
4255 }
4256
4257 rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
4258
4259 dhd_os_wlfc_unblock(dhdp);
4260 return rc;
4261 }
4262
4263 /** debug specific function */
4264 int
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)4265 dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
4266 {
4267 int i;
4268 uint8* ea;
4269 athost_wl_status_info_t* wlfc;
4270 wlfc_hanger_t* h;
4271 wlfc_mac_descriptor_t* mac_table;
4272 wlfc_mac_descriptor_t* interfaces;
4273 char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
4274
4275 if (!dhdp || !strbuf) {
4276 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4277 return BCME_BADARG;
4278 }
4279
4280 dhd_os_wlfc_block(dhdp);
4281
4282 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4283 dhd_os_wlfc_unblock(dhdp);
4284 return WLFC_UNSUPPORTED;
4285 }
4286
4287 wlfc = (athost_wl_status_info_t*)dhdp->wlfc_state;
4288
4289 h = (wlfc_hanger_t*)wlfc->hanger;
4290 if (h == NULL) {
4291 bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4292 }
4293
4294 mac_table = wlfc->destination_entries.nodes;
4295 interfaces = wlfc->destination_entries.interfaces;
4296 bcm_bprintf(strbuf, "---- wlfc stats ----\n");
4297
4298 if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
4299 h = (wlfc_hanger_t*)wlfc->hanger;
4300 if (h == NULL) {
4301 bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4302 } else {
4303 bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
4304 "f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
4305 h->pushed,
4306 h->popped,
4307 h->failed_to_push,
4308 h->failed_to_pop,
4309 h->failed_slotfind,
4310 (h->pushed - h->popped));
4311 }
4312 }
4313
4314 bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
4315 "(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
4316 wlfc->stats.tlv_parse_failed,
4317 wlfc->stats.credit_request_failed,
4318 wlfc->stats.mac_update_failed,
4319 wlfc->stats.psmode_update_failed,
4320 wlfc->stats.delayq_full_error,
4321 wlfc->stats.rollback_failed);
4322
4323 bcm_bprintf(strbuf, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
4324 "(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
4325 "AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
4326 wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
4327 wlfc->stats.drop_pkts[0], wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
4328 wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
4329 wlfc->stats.drop_pkts[2], wlfc->stats.drop_pkts[3], wlfc->stats.ooo_pkts[1],
4330 wlfc->Init_FIFO_credit[2], wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
4331 wlfc->stats.drop_pkts[4], wlfc->stats.drop_pkts[5], wlfc->stats.ooo_pkts[2],
4332 wlfc->Init_FIFO_credit[3], wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
4333 wlfc->stats.drop_pkts[6], wlfc->stats.drop_pkts[7], wlfc->stats.ooo_pkts[3],
4334 wlfc->Init_FIFO_credit[4], wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4],
4335 wlfc->stats.drop_pkts[8], wlfc->stats.drop_pkts[9], wlfc->stats.ooo_pkts[4]);
4336
4337 bcm_bprintf(strbuf, "\n");
4338 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4339 if (interfaces[i].occupied) {
4340 char* iftype_desc;
4341
4342 if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
4343 iftype_desc = "<Unknown";
4344 else
4345 iftype_desc = iftypes[interfaces[i].iftype];
4346
4347 ea = interfaces[i].ea;
4348 bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
4349 "["MACDBG"], if:%d, type: %s "
4350 "netif_flow_control:%s\n", i,
4351 MAC2STRDBG(ea), interfaces[i].interface_id,
4352 iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
4353 ? " OFF":" ON"));
4354
4355 bcm_bprintf(strbuf, "INTERFACE[%d].PSQ(len,state,credit),"
4356 "(trans,supp_trans,onbus)"
4357 "= (%d,%s,%d),(%d,%d,%d)\n",
4358 i,
4359 interfaces[i].psq.n_pkts_tot,
4360 ((interfaces[i].state ==
4361 WLFC_STATE_OPEN) ? "OPEN":"CLOSE"),
4362 interfaces[i].requested_credit,
4363 interfaces[i].transit_count,
4364 interfaces[i].suppr_transit_count,
4365 interfaces[i].onbus_pkts_count);
4366
4367 bcm_bprintf(strbuf, "INTERFACE[%d].PSQ"
4368 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4369 "(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
4370 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4371 i,
4372 interfaces[i].psq.q[0].n_pkts,
4373 interfaces[i].psq.q[1].n_pkts,
4374 interfaces[i].afq.q[0].n_pkts,
4375 interfaces[i].psq.q[2].n_pkts,
4376 interfaces[i].psq.q[3].n_pkts,
4377 interfaces[i].afq.q[1].n_pkts,
4378 interfaces[i].psq.q[4].n_pkts,
4379 interfaces[i].psq.q[5].n_pkts,
4380 interfaces[i].afq.q[2].n_pkts,
4381 interfaces[i].psq.q[6].n_pkts,
4382 interfaces[i].psq.q[7].n_pkts,
4383 interfaces[i].afq.q[3].n_pkts,
4384 interfaces[i].psq.q[8].n_pkts,
4385 interfaces[i].psq.q[9].n_pkts,
4386 interfaces[i].afq.q[4].n_pkts);
4387 }
4388 }
4389
4390 bcm_bprintf(strbuf, "\n");
4391 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4392 if (mac_table[i].occupied) {
4393 ea = mac_table[i].ea;
4394 bcm_bprintf(strbuf, "MAC_table[%d].ea = "
4395 "["MACDBG"], if:%d \n", i,
4396 MAC2STRDBG(ea), mac_table[i].interface_id);
4397
4398 bcm_bprintf(strbuf, "MAC_table[%d].PSQ(len,state,credit),"
4399 "(trans,supp_trans,onbus)"
4400 "= (%d,%s,%d),(%d,%d,%d)\n",
4401 i,
4402 mac_table[i].psq.n_pkts_tot,
4403 ((mac_table[i].state ==
4404 WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
4405 mac_table[i].requested_credit,
4406 mac_table[i].transit_count,
4407 mac_table[i].suppr_transit_count,
4408 mac_table[i].onbus_pkts_count);
4409 #ifdef PROP_TXSTATUS_DEBUG
4410 bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
4411 i, mac_table[i].opened_ct, mac_table[i].closed_ct);
4412 #endif
4413 bcm_bprintf(strbuf, "MAC_table[%d].PSQ"
4414 "(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4415 "(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
4416 "(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4417 i,
4418 mac_table[i].psq.q[0].n_pkts,
4419 mac_table[i].psq.q[1].n_pkts,
4420 mac_table[i].afq.q[0].n_pkts,
4421 mac_table[i].psq.q[2].n_pkts,
4422 mac_table[i].psq.q[3].n_pkts,
4423 mac_table[i].afq.q[1].n_pkts,
4424 mac_table[i].psq.q[4].n_pkts,
4425 mac_table[i].psq.q[5].n_pkts,
4426 mac_table[i].afq.q[2].n_pkts,
4427 mac_table[i].psq.q[6].n_pkts,
4428 mac_table[i].psq.q[7].n_pkts,
4429 mac_table[i].afq.q[3].n_pkts,
4430 mac_table[i].psq.q[8].n_pkts,
4431 mac_table[i].psq.q[9].n_pkts,
4432 mac_table[i].afq.q[4].n_pkts);
4433
4434 }
4435 }
4436
4437 #ifdef PROP_TXSTATUS_DEBUG
4438 {
4439 int avg;
4440 int moving_avg = 0;
4441 int moving_samples;
4442
4443 if (wlfc->stats.latency_sample_count) {
4444 moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
4445
4446 for (i = 0; i < moving_samples; i++)
4447 moving_avg += wlfc->stats.deltas[i];
4448 moving_avg /= moving_samples;
4449
4450 avg = (100 * wlfc->stats.total_status_latency) /
4451 wlfc->stats.latency_sample_count;
4452 bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
4453 "(%d.%d, %03d, %03d)\n",
4454 moving_samples, avg/100, (avg - (avg/100)*100),
4455 wlfc->stats.latency_most_recent,
4456 moving_avg);
4457 }
4458 }
4459
4460 bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
4461 "back = (%d,%d,%d,%d,%d,%d)\n",
4462 wlfc->stats.fifo_credits_sent[0],
4463 wlfc->stats.fifo_credits_sent[1],
4464 wlfc->stats.fifo_credits_sent[2],
4465 wlfc->stats.fifo_credits_sent[3],
4466 wlfc->stats.fifo_credits_sent[4],
4467 wlfc->stats.fifo_credits_sent[5],
4468
4469 wlfc->stats.fifo_credits_back[0],
4470 wlfc->stats.fifo_credits_back[1],
4471 wlfc->stats.fifo_credits_back[2],
4472 wlfc->stats.fifo_credits_back[3],
4473 wlfc->stats.fifo_credits_back[4],
4474 wlfc->stats.fifo_credits_back[5]);
4475 {
4476 uint32 fifo_cr_sent = 0;
4477 uint32 fifo_cr_acked = 0;
4478 uint32 request_cr_sent = 0;
4479 uint32 request_cr_ack = 0;
4480 uint32 bc_mc_cr_ack = 0;
4481
4482 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
4483 fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
4484 }
4485
4486 for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
4487 fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
4488 }
4489
4490 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4491 if (wlfc->destination_entries.nodes[i].occupied) {
4492 request_cr_sent +=
4493 wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
4494 }
4495 }
4496 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4497 if (wlfc->destination_entries.interfaces[i].occupied) {
4498 request_cr_sent +=
4499 wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
4500 }
4501 }
4502 for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4503 if (wlfc->destination_entries.nodes[i].occupied) {
4504 request_cr_ack +=
4505 wlfc->destination_entries.nodes[i].dstncredit_acks;
4506 }
4507 }
4508 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4509 if (wlfc->destination_entries.interfaces[i].occupied) {
4510 request_cr_ack +=
4511 wlfc->destination_entries.interfaces[i].dstncredit_acks;
4512 }
4513 }
4514 bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
4515 "other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
4516 fifo_cr_sent, fifo_cr_acked,
4517 request_cr_sent, request_cr_ack,
4518 wlfc->destination_entries.other.dstncredit_acks,
4519 bc_mc_cr_ack,
4520 wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
4521 }
4522 #endif /* PROP_TXSTATUS_DEBUG */
4523 bcm_bprintf(strbuf, "\n");
4524 bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull,out),"
4525 "(dropped,hdr_only,wlc_tossed,wlc_dropped,wlc_exptime)"
4526 "(freed,free_err,rollback)) = "
4527 "((%d,%d,%d,%d,%d),(%d,%d,%d,%d,%d),(%d,%d,%d))\n",
4528 wlfc->stats.pktin,
4529 wlfc->stats.pkt2bus,
4530 wlfc->stats.txstatus_in,
4531 wlfc->stats.dhd_hdrpulls,
4532 wlfc->stats.pktout,
4533
4534 wlfc->stats.pktdropped,
4535 wlfc->stats.wlfc_header_only_pkt,
4536 wlfc->stats.wlc_tossed_pkts,
4537 wlfc->stats.pkt_dropped,
4538 wlfc->stats.pkt_exptime,
4539
4540 wlfc->stats.pkt_freed,
4541 wlfc->stats.pkt_free_err, wlfc->stats.rollback);
4542
4543 bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
4544 "((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
4545 wlfc->stats.d11_suppress,
4546 wlfc->stats.wl_suppress,
4547 wlfc->stats.bad_suppress,
4548
4549 wlfc->stats.psq_d11sup_enq,
4550 wlfc->stats.psq_wlsup_enq,
4551 wlfc->stats.psq_hostq_enq,
4552 wlfc->stats.mac_handle_notfound,
4553
4554 wlfc->stats.psq_d11sup_retx,
4555 wlfc->stats.psq_wlsup_retx,
4556 wlfc->stats.psq_hostq_retx);
4557
4558 bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
4559 wlfc->stats.cleanup_txq_cnt,
4560 wlfc->stats.cleanup_psq_cnt,
4561 wlfc->stats.cleanup_fw_cnt);
4562
4563 bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
4564
4565 for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4566 bcm_bprintf(strbuf, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i,
4567 wlfc->pkt_cnt_in_q[i][0],
4568 wlfc->pkt_cnt_in_q[i][1],
4569 wlfc->pkt_cnt_in_q[i][2],
4570 wlfc->pkt_cnt_in_q[i][3],
4571 wlfc->pkt_cnt_in_q[i][4]);
4572 }
4573 bcm_bprintf(strbuf, "\n");
4574
4575 dhd_os_wlfc_unblock(dhdp);
4576 return BCME_OK;
4577 } /* dhd_wlfc_dump */
4578
dhd_wlfc_clear_counts(dhd_pub_t * dhd)4579 int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
4580 {
4581 athost_wl_status_info_t* wlfc;
4582 wlfc_hanger_t* hanger;
4583
4584 if (dhd == NULL) {
4585 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4586 return BCME_BADARG;
4587 }
4588
4589 dhd_os_wlfc_block(dhd);
4590
4591 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4592 dhd_os_wlfc_unblock(dhd);
4593 return WLFC_UNSUPPORTED;
4594 }
4595
4596 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4597
4598 memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
4599
4600 if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4601 hanger = (wlfc_hanger_t*)wlfc->hanger;
4602
4603 hanger->pushed = 0;
4604 hanger->popped = 0;
4605 hanger->failed_slotfind = 0;
4606 hanger->failed_to_pop = 0;
4607 hanger->failed_to_push = 0;
4608 }
4609
4610 dhd_os_wlfc_unblock(dhd);
4611
4612 return BCME_OK;
4613 }
4614
4615 /** returns TRUE if flow control is enabled */
dhd_wlfc_get_enable(dhd_pub_t * dhd,bool * val)4616 int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
4617 {
4618 if (!dhd || !val) {
4619 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4620 return BCME_BADARG;
4621 }
4622
4623 dhd_os_wlfc_block(dhd);
4624
4625 *val = dhd->wlfc_enabled;
4626
4627 dhd_os_wlfc_unblock(dhd);
4628
4629 return BCME_OK;
4630 }
4631
4632 /** Called via an IOVAR */
dhd_wlfc_get_mode(dhd_pub_t * dhd,int * val)4633 int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
4634 {
4635 if (!dhd || !val) {
4636 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4637 return BCME_BADARG;
4638 }
4639
4640 dhd_os_wlfc_block(dhd);
4641
4642 *val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
4643
4644 dhd_os_wlfc_unblock(dhd);
4645
4646 return BCME_OK;
4647 }
4648
4649 /** Called via an IOVAR */
dhd_wlfc_set_mode(dhd_pub_t * dhd,int val)4650 int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
4651 {
4652 if (!dhd) {
4653 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4654 return BCME_BADARG;
4655 }
4656
4657 dhd_os_wlfc_block(dhd);
4658
4659 if (dhd->wlfc_state) {
4660 dhd->proptxstatus_mode = val & 0xff;
4661 }
4662
4663 dhd_os_wlfc_unblock(dhd);
4664
4665 return BCME_OK;
4666 }
4667
4668 /** Called when rx frame is received from the dongle */
dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd,void * pktbuf)4669 bool dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd, void *pktbuf)
4670 {
4671 athost_wl_status_info_t* wlfc;
4672 bool rc = FALSE;
4673
4674 if (dhd == NULL) {
4675 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4676 return FALSE;
4677 }
4678
4679 dhd_os_wlfc_block(dhd);
4680
4681 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4682 dhd_os_wlfc_unblock(dhd);
4683 return FALSE;
4684 }
4685
4686 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4687
4688 if (PKTLEN(wlfc->osh, pktbuf) == 0) {
4689 wlfc->stats.wlfc_header_only_pkt++;
4690 rc = TRUE;
4691 }
4692
4693 dhd_os_wlfc_unblock(dhd);
4694
4695 return rc;
4696 }
4697
dhd_wlfc_flowcontrol(dhd_pub_t * dhdp,bool state,bool bAcquireLock)4698 int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
4699 {
4700 if (dhdp == NULL) {
4701 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4702 return BCME_BADARG;
4703 }
4704
4705 if (bAcquireLock) {
4706 dhd_os_wlfc_block(dhdp);
4707 }
4708
4709 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
4710 dhdp->proptxstatus_module_ignore) {
4711 if (bAcquireLock) {
4712 dhd_os_wlfc_unblock(dhdp);
4713 }
4714 return WLFC_UNSUPPORTED;
4715 }
4716
4717 if (state != dhdp->proptxstatus_txoff) {
4718 dhdp->proptxstatus_txoff = state;
4719 }
4720
4721 if (bAcquireLock) {
4722 dhd_os_wlfc_unblock(dhdp);
4723 }
4724
4725 return BCME_OK;
4726 }
4727
4728 /** Called when eg an rx frame is received from the dongle */
dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd,uint8 prio)4729 int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd, uint8 prio)
4730 {
4731 athost_wl_status_info_t* wlfc;
4732 int rx_path_ac = -1;
4733
4734 if ((dhd == NULL) || (prio >= NUMPRIO)) {
4735 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4736 return BCME_BADARG;
4737 }
4738
4739 dhd_os_wlfc_block(dhd);
4740
4741 if (!dhd->wlfc_rxpkt_chk) {
4742 dhd_os_wlfc_unblock(dhd);
4743 return BCME_OK;
4744 }
4745
4746 if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4747 dhd_os_wlfc_unblock(dhd);
4748 return WLFC_UNSUPPORTED;
4749 }
4750
4751 wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4752
4753 rx_path_ac = prio2fifo[prio];
4754 wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
4755
4756 dhd_os_wlfc_unblock(dhd);
4757
4758 return BCME_OK;
4759 }
4760
4761 /** called via an IOVAR */
dhd_wlfc_get_module_ignore(dhd_pub_t * dhd,int * val)4762 int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
4763 {
4764 if (!dhd || !val) {
4765 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4766 return BCME_BADARG;
4767 }
4768
4769 dhd_os_wlfc_block(dhd);
4770
4771 *val = dhd->proptxstatus_module_ignore;
4772
4773 dhd_os_wlfc_unblock(dhd);
4774
4775 return BCME_OK;
4776 }
4777
4778 /** called via an IOVAR */
dhd_wlfc_set_module_ignore(dhd_pub_t * dhd,int val)4779 int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
4780 {
4781 uint32 tlv = 0;
4782 bool bChanged = FALSE;
4783
4784 if (!dhd) {
4785 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4786 return BCME_BADARG;
4787 }
4788
4789 dhd_os_wlfc_block(dhd);
4790
4791 if ((bool)val != dhd->proptxstatus_module_ignore) {
4792 dhd->proptxstatus_module_ignore = (val != 0);
4793 /* force txstatus_ignore sync with proptxstatus_module_ignore */
4794 dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
4795 if (FALSE == dhd->proptxstatus_module_ignore) {
4796 tlv = WLFC_FLAGS_RSSI_SIGNALS |
4797 WLFC_FLAGS_XONXOFF_SIGNALS |
4798 WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
4799 WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
4800 }
4801 /* always enable host reorder */
4802 tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4803 bChanged = TRUE;
4804 }
4805
4806 dhd_os_wlfc_unblock(dhd);
4807
4808 if (bChanged) {
4809 /* select enable proptxtstatus signaling */
4810 if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4811 DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
4812 __FUNCTION__, tlv));
4813 } else {
4814 DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
4815 __FUNCTION__, tlv));
4816 }
4817 }
4818
4819 #if defined(DHD_WLFC_THREAD)
4820 _dhd_wlfc_thread_wakeup(dhd);
4821 #endif /* defined(DHD_WLFC_THREAD) */
4822
4823 return BCME_OK;
4824 }
4825
4826 /** called via an IOVAR */
dhd_wlfc_get_credit_ignore(dhd_pub_t * dhd,int * val)4827 int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
4828 {
4829 if (!dhd || !val) {
4830 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4831 return BCME_BADARG;
4832 }
4833
4834 dhd_os_wlfc_block(dhd);
4835
4836 *val = dhd->proptxstatus_credit_ignore;
4837
4838 dhd_os_wlfc_unblock(dhd);
4839
4840 return BCME_OK;
4841 }
4842
4843 /** called via an IOVAR */
dhd_wlfc_set_credit_ignore(dhd_pub_t * dhd,int val)4844 int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
4845 {
4846 if (!dhd) {
4847 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4848 return BCME_BADARG;
4849 }
4850
4851 dhd_os_wlfc_block(dhd);
4852
4853 dhd->proptxstatus_credit_ignore = (val != 0);
4854
4855 dhd_os_wlfc_unblock(dhd);
4856
4857 return BCME_OK;
4858 }
4859
4860 /** called via an IOVAR */
dhd_wlfc_get_txstatus_ignore(dhd_pub_t * dhd,int * val)4861 int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
4862 {
4863 if (!dhd || !val) {
4864 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4865 return BCME_BADARG;
4866 }
4867
4868 dhd_os_wlfc_block(dhd);
4869
4870 *val = dhd->proptxstatus_txstatus_ignore;
4871
4872 dhd_os_wlfc_unblock(dhd);
4873
4874 return BCME_OK;
4875 }
4876
4877 /** called via an IOVAR */
dhd_wlfc_set_txstatus_ignore(dhd_pub_t * dhd,int val)4878 int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
4879 {
4880 if (!dhd) {
4881 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4882 return BCME_BADARG;
4883 }
4884
4885 dhd_os_wlfc_block(dhd);
4886
4887 dhd->proptxstatus_txstatus_ignore = (val != 0);
4888
4889 dhd_os_wlfc_unblock(dhd);
4890
4891 return BCME_OK;
4892 }
4893
4894 /** called via an IOVAR */
dhd_wlfc_get_rxpkt_chk(dhd_pub_t * dhd,int * val)4895 int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
4896 {
4897 if (!dhd || !val) {
4898 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4899 return BCME_BADARG;
4900 }
4901
4902 dhd_os_wlfc_block(dhd);
4903
4904 *val = dhd->wlfc_rxpkt_chk;
4905
4906 dhd_os_wlfc_unblock(dhd);
4907
4908 return BCME_OK;
4909 }
4910
4911 /** called via an IOVAR */
dhd_wlfc_set_rxpkt_chk(dhd_pub_t * dhd,int val)4912 int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
4913 {
4914 if (!dhd) {
4915 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4916 return BCME_BADARG;
4917 }
4918
4919 dhd_os_wlfc_block(dhd);
4920
4921 dhd->wlfc_rxpkt_chk = (val != 0);
4922
4923 dhd_os_wlfc_unblock(dhd);
4924
4925 return BCME_OK;
4926 }
4927
dhd_txpkt_log_and_dump(dhd_pub_t * dhdp,void * pkt,uint16 * pktfate_status)4928 int dhd_txpkt_log_and_dump(dhd_pub_t *dhdp, void* pkt, uint16 *pktfate_status)
4929 {
4930 uint32 pktid;
4931 uint32 pktlen = PKTLEN(dhdp->osh, pkt);
4932 uint8 *pktdata = PKTDATA(dhdp->osh, pkt);
4933 #ifdef BDC
4934 struct bdc_header *bdch;
4935 uint32 bdc_len;
4936 #endif /* BDC */
4937 uint8 ifidx = DHD_PKTTAG_IF(PKTTAG(pkt));
4938 uint8 hcnt = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
4939 uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
4940
4941 if (!pkt) {
4942 DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4943 return BCME_BADARG;
4944 }
4945 pktid = (ifidx << DHD_PKTID_IF_SHIFT) | (fifo_id << DHD_PKTID_FIFO_SHIFT) | hcnt;
4946 #ifdef BDC
4947 bdch = (struct bdc_header *)pktdata;
4948 bdc_len = BDC_HEADER_LEN + (bdch->dataOffset << DHD_WORD_TO_LEN_SHIFT);
4949 pktlen -= bdc_len;
4950 pktdata = pktdata + bdc_len;
4951 #endif /* BDC */
4952 dhd_handle_pktdata(dhdp, ifidx, pkt, pktdata, pktid, pktlen,
4953 pktfate_status, NULL, TRUE, FALSE, TRUE);
4954 return BCME_OK;
4955 }
4956
4957 #ifdef PROPTX_MAXCOUNT
dhd_wlfc_update_maxcount(dhd_pub_t * dhdp,uint8 ifid,int maxcount)4958 int dhd_wlfc_update_maxcount(dhd_pub_t *dhdp, uint8 ifid, int maxcount)
4959 {
4960 athost_wl_status_info_t* ctx;
4961 int rc = 0;
4962
4963 if (dhdp == NULL) {
4964 DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
4965 return BCME_BADARG;
4966 }
4967
4968 dhd_os_wlfc_block(dhdp);
4969
4970 if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4971 rc = WLFC_UNSUPPORTED;
4972 goto exit;
4973 }
4974
4975 if (ifid >= WLFC_MAX_IFNUM) {
4976 DHD_ERROR(("%s: bad ifid\n", __FUNCTION__));
4977 rc = BCME_BADARG;
4978 goto exit;
4979 }
4980
4981 ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
4982 ctx->destination_entries.interfaces[ifid].transit_maxcount = maxcount;
4983 exit:
4984 dhd_os_wlfc_unblock(dhdp);
4985 return rc;
4986 }
4987 #endif /* PROPTX_MAXCOUNT */
4988 #endif /* PROP_TXSTATUS */
4989