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