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