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