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