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