xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/rkwifi/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 (dhdp->skip_fc && dhdp->skip_fc((void *)dhdp, if_id))
1063 		return;
1064 
1065 	if ((ctx->hostif_flow_state[if_id] == OFF) && !_dhd_wlfc_allow_fc(ctx, if_id))
1066 		return;
1067 
1068 	if ((pq->n_pkts_tot <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
1069 		/* start traffic */
1070 		ctx->hostif_flow_state[if_id] = OFF;
1071 		/*
1072 		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
1073 		pq->n_pkts_tot, if_id, __FUNCTION__));
1074 		*/
1075 		WLFC_DBGMESG(("F"));
1076 
1077 		dhd_txflowcontrol(dhdp, if_id, OFF);
1078 
1079 		ctx->toggle_host_if = 0;
1080 	}
1081 
1082 	if (pq->n_pkts_tot >= WLFC_FLOWCONTROL_HIWATER && ctx->hostif_flow_state[if_id] == OFF) {
1083 		/* stop traffic */
1084 		ctx->hostif_flow_state[if_id] = ON;
1085 		/*
1086 		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic   %s()\n",
1087 		pq->n_pkts_tot, if_id, __FUNCTION__));
1088 		*/
1089 		WLFC_DBGMESG(("N"));
1090 
1091 		dhd_txflowcontrol(dhdp, if_id, ON);
1092 
1093 		ctx->host_ifidx = if_id;
1094 		ctx->toggle_host_if = 1;
1095 	}
1096 
1097 	return;
1098 } /* _dhd_wlfc_flow_control_check */
1099 
1100 /** XXX: Warning: this function directly accesses bus-transmit function */
1101 static int
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)1102 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1103 	uint8 ta_bmp)
1104 {
1105 	int rc = BCME_OK;
1106 	void* p = NULL;
1107 	int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 16;
1108 	dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1109 
1110 	if (dhdp->proptxstatus_txoff) {
1111 		rc = BCME_NORESOURCE;
1112 		return rc;
1113 	}
1114 
1115 	/* allocate a dummy packet */
1116 	p = PKTGET(ctx->osh, dummylen, TRUE);
1117 	if (p) {
1118 		PKTPULL(ctx->osh, p, dummylen);
1119 		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
1120 		_dhd_wlfc_pushheader(ctx, &p, TRUE, ta_bmp, entry->mac_handle, 0, 0, FALSE);
1121 		DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
1122 		DHD_PKTTAG_WLFCPKT_SET(PKTTAG(p), 1);
1123 #ifdef PROP_TXSTATUS_DEBUG
1124 		ctx->stats.signal_only_pkts_sent++;
1125 #endif
1126 
1127 #if defined(BCMPCIE)
1128 		/* XXX : RAHUL : Verify the ifidx */
1129 		rc = dhd_bus_txdata(dhdp->bus, p, ctx->host_ifidx);
1130 #else
1131 		rc = dhd_bus_txdata(dhdp->bus, p);
1132 #endif
1133 		if (rc != BCME_OK) {
1134 			_dhd_wlfc_pullheader(ctx, p);
1135 			PKTFREE(ctx->osh, p, TRUE);
1136 		}
1137 	} else {
1138 		DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
1139 		           __FUNCTION__, dummylen));
1140 		rc = BCME_NOMEM;
1141 		dhdp->tx_pktgetfail++;
1142 	}
1143 
1144 	return rc;
1145 } /* _dhd_wlfc_send_signalonly_packet */
1146 
1147 /**
1148  * Called on eg receiving 'mac close' indication from dongle. Updates the per-MAC administration
1149  * maintained in caller supplied parameter 'entry'.
1150  *
1151  *    @param[in/out] entry  administration about a remote MAC entity
1152  *    @param[in]     prec   precedence queue for this remote MAC entitity
1153  *
1154  * Return value: TRUE if traffic availability changed
1155  */
1156 static bool
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1157 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1158 	int prec)
1159 {
1160 	bool rc = FALSE;
1161 
1162 	if (entry->state == WLFC_STATE_CLOSE) {
1163 		if ((pktqprec_n_pkts(&entry->psq, (prec << 1)) == 0) &&
1164 			(pktqprec_n_pkts(&entry->psq, ((prec << 1) + 1)) == 0)) {
1165 			/* no packets in both 'normal' and 'suspended' queues */
1166 			if (entry->traffic_pending_bmp & NBITVAL(prec)) {
1167 				rc = TRUE;
1168 				entry->traffic_pending_bmp =
1169 					entry->traffic_pending_bmp & ~ NBITVAL(prec);
1170 			}
1171 		} else {
1172 			/* packets are queued in host for transmission to dongle */
1173 			if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
1174 				rc = TRUE;
1175 				entry->traffic_pending_bmp =
1176 					entry->traffic_pending_bmp | NBITVAL(prec);
1177 			}
1178 		}
1179 	}
1180 
1181 	if (rc) {
1182 		/* request a TIM update to firmware at the next piggyback opportunity */
1183 		if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
1184 			entry->send_tim_signal = 1;
1185 			/*
1186 			XXX: send a header only packet from the same context.
1187 			--this should change to sending from a timeout or similar.
1188 			*/
1189 			_dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
1190 			entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1191 			entry->send_tim_signal = 0;
1192 		} else {
1193 			rc = FALSE;
1194 		}
1195 	}
1196 
1197 	return rc;
1198 } /* _dhd_wlfc_traffic_pending_check */
1199 
1200 /**
1201  * Called on receiving a 'd11 suppressed' or 'wl suppressed' tx status from the firmware. Enqueues
1202  * the packet to transmit to firmware again at a later opportunity.
1203  */
1204 static int
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)1205 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
1206 {
1207 	wlfc_mac_descriptor_t* entry;
1208 
1209 	entry = _dhd_wlfc_find_table_entry(ctx, p);
1210 	if (entry == NULL) {
1211 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1212 		return BCME_NOTFOUND;
1213 	}
1214 	/*
1215 	- suppressed packets go to sub_queue[2*prec + 1] AND
1216 	- delayed packets go to sub_queue[2*prec + 0] to ensure
1217 	order of delivery.
1218 	*/
1219 	if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, p, ((prec << 1) + 1), FALSE,
1220 		WLFC_SEQCOUNT(entry, prec))
1221 		== FALSE) {
1222 		ctx->stats.delayq_full_error++;
1223 		/* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
1224 		WLFC_DBGMESG(("s"));
1225 		return BCME_ERROR;
1226 	}
1227 
1228 	/* A packet has been pushed, update traffic availability bitmap, if applicable */
1229 	_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1230 	_dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1231 	return BCME_OK;
1232 }
1233 
1234 /**
1235  * Called when a transmit packet is about to be 'committed' from the OS layer to a lower layer
1236  * towards the dongle (eg the DBUS layer). Updates wlfc administration. May modify packet.
1237  *
1238  *     @param[in/out] ctx    driver specific flow control administration
1239  *     @param[in/out] entry  The remote MAC entity for which the packet is destined.
1240  *     @param[in/out] packet Packet to send. This function optionally adds TLVs to the packet.
1241  *     @param[in] header_needed True if packet is 'new' to flow control
1242  *     @param[out] slot Handle to container in which the packet was 'parked'
1243  */
1244 static int
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void ** packet,int header_needed,uint32 * slot)1245 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
1246 	wlfc_mac_descriptor_t* entry, void** packet, int header_needed, uint32* slot)
1247 {
1248 	int rc = BCME_OK;
1249 	int hslot = WLFC_HANGER_MAXITEMS;
1250 	bool send_tim_update = FALSE;
1251 	uint32 htod = 0;
1252 	uint16 htodseq = 0;
1253 	uint8 free_ctr;
1254 	int gen = 0xff;
1255 	dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1256 	void * p = *packet;
1257 
1258 	*slot = hslot;
1259 
1260 	if (entry == NULL) {
1261 		entry = _dhd_wlfc_find_table_entry(ctx, p);
1262 	}
1263 
1264 	if (entry == NULL) {
1265 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1266 		return BCME_ERROR;
1267 	}
1268 
1269 	if (entry->send_tim_signal) {
1270 		/* sends a traffic indication bitmap to the dongle */
1271 		send_tim_update = TRUE;
1272 		entry->send_tim_signal = 0;
1273 		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1274 	}
1275 
1276 	if (header_needed) {
1277 		if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1278 			hslot = (uint)(entry - &ctx->destination_entries.nodes[0]);
1279 		} else {
1280 			hslot = _dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1281 		}
1282 		gen = entry->generation;
1283 		free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1284 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1285 		_dhd_wlfc_bprint(ctx, "d%u.%u.%u-",
1286 			(uint8)(entry - &ctx->destination_entries.nodes[0]), gen, free_ctr);
1287 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1288 	} else {
1289 		if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1290 			htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(p));
1291 		}
1292 
1293 		hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1294 
1295 		if (WLFC_GET_REORDERSUPP(dhdp->wlfc_mode)) {
1296 			gen = entry->generation;
1297 		} else if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1298 			gen = WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1299 		} else {
1300 			_dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
1301 		}
1302 
1303 		free_ctr = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1304 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1305 		_dhd_wlfc_bprint(ctx, "s%u.%u.%u-",
1306 		(uint8)(entry - &ctx->destination_entries.nodes[0]), gen, free_ctr);
1307 		if (WLFC_GET_REUSESEQ(dhdp->wlfc_mode)) {
1308 			_dhd_wlfc_bprint(ctx, "%u.%u-",
1309 				IS_WL_TO_REUSE_SEQ(DHD_PKTTAG_H2DSEQ(PKTTAG(p))),
1310 				WL_SEQ_GET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(p))));
1311 		}
1312 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1313 		/* remove old header */
1314 		_dhd_wlfc_pullheader(ctx, p);
1315 	}
1316 
1317 	if (hslot >= WLFC_HANGER_MAXITEMS) {
1318 		DHD_ERROR(("Error: %s():no hanger slot available\n", __FUNCTION__));
1319 		return BCME_ERROR;
1320 	}
1321 
1322 	WL_TXSTATUS_SET_FREERUNCTR(htod, free_ctr);
1323 	WL_TXSTATUS_SET_HSLOT(htod, hslot);
1324 	WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1325 	WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1326 	WL_TXSTATUS_SET_GENERATION(htod, gen);
1327 	DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1328 
1329 	if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1330 		/*
1331 		Indicate that this packet is being sent in response to an
1332 		explicit request from the firmware side.
1333 		*/
1334 		WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1335 	} else {
1336 		WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1337 	}
1338 
1339 	rc = _dhd_wlfc_pushheader(ctx, &p, send_tim_update,
1340 		entry->traffic_lastreported_bmp, entry->mac_handle, htod, htodseq, FALSE);
1341 	if (rc == BCME_OK) {
1342 		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1343 
1344 		if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
1345 			wlfc_hanger_t *h = (wlfc_hanger_t*)(ctx->hanger);
1346 			if (header_needed) {
1347 				/*
1348 				a new header was created for this packet.
1349 				push to hanger slot and scrub q. Since bus
1350 				send succeeded, increment seq number as well.
1351 				*/
1352 				rc = _dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1353 				if (rc == BCME_OK) {
1354 #ifdef PROP_TXSTATUS_DEBUG
1355 					h->items[hslot].push_time =
1356 						OSL_SYSUPTIME();
1357 #endif
1358 				} else {
1359 					DHD_ERROR(("%s() hanger_pushpkt() failed, rc: %d\n",
1360 						__FUNCTION__, rc));
1361 				}
1362 			} else {
1363 				/* clear hanger state */
1364 				if (((wlfc_hanger_t*)(ctx->hanger))->items[hslot].pkt != p)
1365 					DHD_ERROR(("%s() pkt not match: cur %p, hanger pkt %p\n",
1366 						__FUNCTION__, p, h->items[hslot].pkt));
1367 				ASSERT(h->items[hslot].pkt == p);
1368 				bcm_object_feature_set(h->items[hslot].pkt,
1369 					BCM_OBJECT_FEATURE_PKT_STATE, 0);
1370 				h->items[hslot].pkt_state = 0;
1371 				h->items[hslot].pkt_txstatus = 0;
1372 				h->items[hslot].state = WLFC_HANGER_ITEM_STATE_INUSE;
1373 			}
1374 		}
1375 
1376 		if ((rc == BCME_OK) && header_needed) {
1377 			/* increment free running sequence count */
1378 			WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1379 		}
1380 	}
1381 	*slot = hslot;
1382 	*packet = p;
1383 	return rc;
1384 } /* _dhd_wlfc_pretx_pktprocess */
1385 
1386 /**
1387  * A remote wireless mac may be temporarily 'closed' due to power management. Returns '1' if remote
1388  * mac is in the 'open' state, otherwise '0'.
1389  */
1390 static int
_dhd_wlfc_is_destination_open(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)1391 _dhd_wlfc_is_destination_open(athost_wl_status_info_t* ctx,
1392 	wlfc_mac_descriptor_t* entry, int prec)
1393 {
1394 	wlfc_mac_descriptor_t* interfaces = ctx->destination_entries.interfaces;
1395 
1396 	if (entry->interface_id >= WLFC_MAX_IFNUM) {
1397 		ASSERT(&ctx->destination_entries.other == entry);
1398 		return 1;
1399 	}
1400 
1401 	if (interfaces[entry->interface_id].iftype ==
1402 		WLC_E_IF_ROLE_P2P_GO) {
1403 		/* - destination interface is of type p2p GO.
1404 		For a p2pGO interface, if the destination is OPEN but the interface is
1405 		CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1406 		destination-specific-credit left send packets. This is because the
1407 		firmware storing the destination-specific-requested packet in queue.
1408 		*/
1409 		/* XXX: This behavior will change one PM1 protocol mod is complete */
1410 		if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1411 			(entry->requested_packet == 0)) {
1412 			return 0;
1413 		}
1414 	}
1415 
1416 	/* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1417 	if ((((entry->state == WLFC_STATE_CLOSE) ||
1418 		(interfaces[entry->interface_id].state == WLFC_STATE_CLOSE)) &&
1419 		(entry->requested_credit == 0) &&
1420 		(entry->requested_packet == 0)) ||
1421 		(!(entry->ac_bitmap & (1 << prec)))) {
1422 		return 0;
1423 	}
1424 
1425 	return 1;
1426 } /* _dhd_wlfc_is_destination_open */
1427 
1428 /**
1429  * Dequeues a suppressed or delayed packet from a queue
1430  *    @param[in/out] ctx          Driver specific flow control administration
1431  *    @param[in]  prec            Precedence of queue to dequeue from
1432  *    @param[out] ac_credit_spent Boolean, returns 0 or 1
1433  *    @param[out] needs_hdr       Boolean, returns 0 or 1
1434  *    @param[out] entry_out       The remote MAC for which the packet is destined
1435  *    @param[in]  only_no_credit  If TRUE, searches all entries instead of just the active ones
1436  *
1437  * Return value: the dequeued packet
1438  */
1439 
1440 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)1441 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx, int prec,
1442 	uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out,
1443 	bool only_no_credit)
1444 {
1445 	wlfc_mac_descriptor_t* entry;
1446 	int total_entries;
1447 	void* p = NULL;
1448 	int i;
1449 	uint8 credit_spent = ((prec == AC_COUNT) && !ctx->bcmc_credit_supported) ? 0 : 1;
1450 	uint16 qlen;
1451 	bool change_entry = FALSE;
1452 
1453 	BCM_REFERENCE(qlen);
1454 	BCM_REFERENCE(change_entry);
1455 
1456 	*entry_out = NULL;
1457 	/* most cases a packet will count against FIFO credit */
1458 	*ac_credit_spent = credit_spent;
1459 
1460 	/* search all entries, include nodes as well as interfaces */
1461 	if (only_no_credit) {
1462 		total_entries = ctx->requested_entry_count;
1463 	} else {
1464 		total_entries = ctx->active_entry_count;
1465 	}
1466 
1467 	for (i = 0; i < total_entries; i++) {
1468 		if (only_no_credit) {
1469 			entry = ctx->requested_entry[i];
1470 		} else {
1471 			entry = ctx->active_entry_head;
1472 		}
1473 		ASSERT(entry);
1474 
1475 		if (entry->occupied && _dhd_wlfc_is_destination_open(ctx, entry, prec) &&
1476 #ifdef PROPTX_MAXCOUNT
1477 			(entry->transit_count < entry->transit_maxcount) &&
1478 #endif /* PROPTX_MAXCOUNT */
1479 			(entry->transit_count < WL_TXSTATUS_FREERUNCTR_MASK) &&
1480 			(!entry->suppressed)) {
1481 			*ac_credit_spent = credit_spent;
1482 			if (entry->state == WLFC_STATE_CLOSE) {
1483 				*ac_credit_spent = 0;
1484 			}
1485 
1486 			/* higher precedence will be picked up first,
1487 			 * i.e. suppressed packets before delayed ones
1488 			 */
1489 			p = pktq_pdeq(&entry->psq, PSQ_SUP_IDX(prec));
1490 			*needs_hdr = 0;
1491 			if (p == NULL) {
1492 				/* De-Q from delay Q */
1493 				p = pktq_pdeq(&entry->psq, PSQ_DLY_IDX(prec));
1494 				*needs_hdr = 1;
1495 			}
1496 
1497 			if (p != NULL) {
1498 				bcm_pkt_validate_chk(p, "_dhd_wlfc_deque_afq");
1499 				/* did the packet come from suppress sub-queue? */
1500 				if (entry->requested_credit > 0) {
1501 					entry->requested_credit--;
1502 #ifdef PROP_TXSTATUS_DEBUG
1503 					entry->dstncredit_sent_packets++;
1504 #endif
1505 				} else if (entry->requested_packet > 0) {
1506 					entry->requested_packet--;
1507 					DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1508 				}
1509 
1510 				*entry_out = entry;
1511 				ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
1512 				ctx->pkt_cnt_per_ac[prec]--;
1513 				ctx->pkt_cnt_in_psq--;
1514 #ifdef BULK_DEQUEUE
1515 				/* Check pkts in delayq */
1516 				if (entry->state == WLFC_STATE_OPEN) {
1517 					entry->release_count[prec]++;
1518 					qlen = pktq_mlen(&entry->psq,
1519 						(1 << PSQ_SUP_IDX(prec) | 1 << PSQ_DLY_IDX(prec)));
1520 
1521 					if (entry->release_count[prec] == ctx->max_release_count ||
1522 						qlen == 0) {
1523 						change_entry = TRUE;
1524 						entry->release_count[prec] = 0;
1525 					}
1526 
1527 					if (change_entry) {
1528 						/* move head */
1529 						ctx->active_entry_head =
1530 							ctx->active_entry_head->next;
1531 					}
1532 				}
1533 #endif /* BULK_DEQUEUE */
1534 				_dhd_wlfc_flow_control_check(ctx, &entry->psq,
1535 					DHD_PKTTAG_IF(PKTTAG(p)));
1536 				/*
1537 				 * A packet has been picked up, update traffic availability bitmap,
1538 				 * if applicable.
1539 				 */
1540 				_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1541 				return p;
1542 			}
1543 		}
1544 		if (!only_no_credit) {
1545 			/* move head */
1546 			ctx->active_entry_head = ctx->active_entry_head->next;
1547 		}
1548 	}
1549 	return NULL;
1550 } /* _dhd_wlfc_deque_delayedq */
1551 
1552 /** Enqueues caller supplied packet on either a 'suppressed' or 'delayed' queue */
1553 static int
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1554 _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
1555 {
1556 	wlfc_mac_descriptor_t* entry;
1557 
1558 	if (pktbuf != NULL) {
1559 		entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1560 		if (entry == NULL) {
1561 			DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1562 			return BCME_ERROR;
1563 		}
1564 
1565 		/*
1566 		- suppressed packets go to sub_queue[2*prec + 1] AND
1567 		- delayed packets go to sub_queue[2*prec + 0] to ensure
1568 		order of delivery.
1569 		*/
1570 		if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1),
1571 			FALSE, WLFC_SEQCOUNT(entry, prec))
1572 			== FALSE) {
1573 			WLFC_DBGMESG(("D"));
1574 			ctx->stats.delayq_full_error++;
1575 			return BCME_ERROR;
1576 		}
1577 
1578 #ifdef QMONITOR
1579 		dhd_qmon_tx(&entry->qmon);
1580 #endif
1581 
1582 		/* A packet has been pushed, update traffic availability bitmap, if applicable */
1583 		_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1584 	}
1585 
1586 	return BCME_OK;
1587 } /* _dhd_wlfc_enque_delayq */
1588 
1589 /** Returns TRUE if caller supplied packet is destined for caller supplied interface */
_dhd_wlfc_ifpkt_fn(void * p,void * p_ifid)1590 static bool _dhd_wlfc_ifpkt_fn(void* p, void *p_ifid)
1591 {
1592 	if (!p || !p_ifid)
1593 		return FALSE;
1594 
1595 	return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (*((uint8 *)p_ifid) == DHD_PKTTAG_IF(PKTTAG(p))));
1596 }
1597 
1598 /** Returns TRUE if caller supplied packet is destined for caller supplied remote MAC */
_dhd_wlfc_entrypkt_fn(void * p,void * entry)1599 static bool _dhd_wlfc_entrypkt_fn(void* p, void *entry)
1600 {
1601 	if (!p || !entry)
1602 		return FALSE;
1603 
1604 	return (DHD_PKTTAG_WLFCPKT(PKTTAG(p))&& (entry == DHD_PKTTAG_ENTRY(PKTTAG(p))));
1605 }
1606 
1607 static void
_dhd_wlfc_return_implied_credit(athost_wl_status_info_t * wlfc,void * pkt)1608 _dhd_wlfc_return_implied_credit(athost_wl_status_info_t* wlfc, void* pkt)
1609 {
1610 	dhd_pub_t *dhdp;
1611 	bool credit_return = FALSE;
1612 
1613 	if (!wlfc || !pkt) {
1614 		return;
1615 	}
1616 
1617 	dhdp = (dhd_pub_t *)(wlfc->dhdp);
1618 	if (dhdp && (dhdp->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) &&
1619 		DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
1620 		int lender, credit_returned = 0;
1621 		uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
1622 
1623 		credit_return = TRUE;
1624 
1625 		/* Note that borrower is fifo_id */
1626 		/* Return credits to highest priority lender first */
1627 		for (lender = AC_COUNT; lender >= 0; lender--) {
1628 			if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1629 				wlfc->FIFO_credit[lender]++;
1630 				wlfc->credits_borrowed[fifo_id][lender]--;
1631 				credit_returned = 1;
1632 				break;
1633 			}
1634 		}
1635 
1636 		if (!credit_returned) {
1637 			wlfc->FIFO_credit[fifo_id]++;
1638 		}
1639 	}
1640 
1641 	BCM_REFERENCE(credit_return);
1642 #if defined(DHD_WLFC_THREAD)
1643 	if (credit_return) {
1644 		_dhd_wlfc_thread_wakeup(dhdp);
1645 	}
1646 #endif /* defined(DHD_WLFC_THREAD) */
1647 }
1648 
1649 /** Removes and frees a packet from the hanger. Called during eg tx complete. */
1650 static void
_dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t * wlfc,uint32 slot_id,uint8 pkt_state,int pkt_txstatus)1651 _dhd_wlfc_hanger_free_pkt(athost_wl_status_info_t* wlfc, uint32 slot_id, uint8 pkt_state,
1652 	int pkt_txstatus)
1653 {
1654 	wlfc_hanger_t* hanger;
1655 	wlfc_hanger_item_t* item;
1656 
1657 	if (!wlfc)
1658 		return;
1659 
1660 	hanger = (wlfc_hanger_t*)wlfc->hanger;
1661 	if (!hanger)
1662 		return;
1663 
1664 	if (slot_id == WLFC_HANGER_MAXITEMS)
1665 		return;
1666 
1667 	item = &hanger->items[slot_id];
1668 
1669 	if (item->pkt) {
1670 		item->pkt_state |= pkt_state;
1671 		if (pkt_txstatus != -1)
1672 			item->pkt_txstatus = (uint8)pkt_txstatus;
1673 		bcm_object_feature_set(item->pkt, BCM_OBJECT_FEATURE_PKT_STATE, item->pkt_state);
1674 		if (item->pkt_state == WLFC_HANGER_PKT_STATE_COMPLETE) {
1675 			void *p = NULL;
1676 			void *pkt = item->pkt;
1677 			uint8 old_state = item->state;
1678 			int ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, slot_id, &p, TRUE);
1679 			BCM_REFERENCE(ret);
1680 			BCM_REFERENCE(pkt);
1681 			ASSERT((ret == BCME_OK) && p && (pkt == p));
1682 			if (old_state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
1683 				printf("ERROR: free a suppressed pkt %p state %d pkt_state %d\n",
1684 					pkt, old_state, item->pkt_state);
1685 			}
1686 			ASSERT(old_state != WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED);
1687 
1688 			/* free packet */
1689 			wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))]
1690 				[DHD_PKTTAG_FIFO(PKTTAG(p))]--;
1691 			wlfc->stats.pktout++;
1692 			dhd_txcomplete((dhd_pub_t *)wlfc->dhdp, p, item->pkt_txstatus);
1693 			PKTFREE(wlfc->osh, p, TRUE);
1694 		}
1695 	} else {
1696 		/* free slot */
1697 		if (item->state == WLFC_HANGER_ITEM_STATE_FREE)
1698 			DHD_ERROR(("Error: %s():%d Multiple TXSTATUS or BUSRETURNED: %d (%d)\n",
1699 			    __FUNCTION__, __LINE__, item->pkt_state, pkt_state));
1700 		item->state = WLFC_HANGER_ITEM_STATE_FREE;
1701 	}
1702 } /* _dhd_wlfc_hanger_free_pkt */
1703 
1704 /** Called during eg detach() */
1705 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)1706 _dhd_wlfc_pktq_flush(athost_wl_status_info_t* ctx, struct pktq *pq,
1707 	bool dir, f_processpkt_t fn, void *arg, q_type_t q_type)
1708 {
1709 	int prec;
1710 	dhd_pub_t *dhdp = (dhd_pub_t *)ctx->dhdp;
1711 
1712 	ASSERT(dhdp);
1713 
1714 	/* Optimize flush, if pktq len = 0, just return.
1715 	 * pktq len of 0 means pktq's prec q's are all empty.
1716 	 */
1717 	if (pq->n_pkts_tot == 0) {
1718 		return;
1719 	}
1720 
1721 	for (prec = 0; prec < pq->num_prec; prec++) {
1722 		struct pktq_prec *q;
1723 		void *p, *prev = NULL;
1724 
1725 		q = &pq->q[prec];
1726 		p = q->head;
1727 		while (p) {
1728 			bcm_pkt_validate_chk(p, "_dhd_wlfc_pktq_flush");
1729 			if (fn == NULL || (*fn)(p, arg)) {
1730 				bool head = (p == q->head);
1731 				if (head)
1732 					q->head = PKTLINK(p);
1733 				else
1734 					PKTSETLINK(prev, PKTLINK(p));
1735 				if (q_type == Q_TYPE_PSQ) {
1736 					if (!WLFC_GET_AFQ(dhdp->wlfc_mode) && (prec & 1)) {
1737 						_dhd_wlfc_hanger_remove_reference(ctx->hanger, p);
1738 					}
1739 					ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1740 					ctx->pkt_cnt_per_ac[prec>>1]--;
1741 					ctx->pkt_cnt_in_psq--;
1742 					ctx->stats.cleanup_psq_cnt++;
1743 					if (!(prec & 1)) {
1744 						/* pkt in delayed q, so fake push BDC header for
1745 						 * dhd_tcpack_check_xmit() and dhd_txcomplete().
1746 						 */
1747 						_dhd_wlfc_pushheader(ctx, &p, FALSE, 0, 0,
1748 							0, 0, TRUE);
1749 #ifdef DHDTCPACK_SUPPRESS
1750 						if (dhd_tcpack_check_xmit(dhdp, p) == BCME_ERROR) {
1751 							DHD_ERROR(("%s %d: tcpack_suppress ERROR!!!"
1752 								" Stop using it\n",
1753 								__FUNCTION__, __LINE__));
1754 							dhd_tcpack_suppress_set(dhdp,
1755 								TCPACK_SUP_OFF);
1756 						}
1757 #endif /* DHDTCPACK_SUPPRESS */
1758 					}
1759 				} else if (q_type == Q_TYPE_AFQ) {
1760 					wlfc_mac_descriptor_t* entry =
1761 						_dhd_wlfc_find_table_entry(ctx, p);
1762 					if (entry->transit_count)
1763 						entry->transit_count--;
1764 					if (entry->suppr_transit_count) {
1765 						entry->suppr_transit_count--;
1766 						if (entry->suppressed &&
1767 							(!entry->onbus_pkts_count) &&
1768 							(!entry->suppr_transit_count))
1769 							entry->suppressed = FALSE;
1770 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1771 							_dhd_wlfc_bprint(ctx, "[sc]-");
1772 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1773 					}
1774 					_dhd_wlfc_return_implied_credit(ctx, p);
1775 					ctx->stats.cleanup_fw_cnt++;
1776 				}
1777 				PKTSETLINK(p, NULL);
1778 				if (dir) {
1779 					ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(p))][prec>>1]--;
1780 					ctx->stats.pktout++;
1781 					dhd_txcomplete(dhdp, p, FALSE);
1782 				}
1783 				PKTFREE(ctx->osh, p, dir);
1784 
1785 				q->n_pkts--;
1786 				pq->n_pkts_tot--;
1787 #ifdef WL_TXQ_STALL
1788 				q->dequeue_count++;
1789 #endif
1790 
1791 				p = (head ? q->head : PKTLINK(prev));
1792 			} else {
1793 				prev = p;
1794 				p = PKTLINK(p);
1795 			}
1796 		}
1797 
1798 		if (q->head == NULL) {
1799 			ASSERT(q->n_pkts == 0);
1800 			q->tail = NULL;
1801 		}
1802 
1803 	}
1804 
1805 	if (fn == NULL)
1806 		ASSERT(pq->n_pkts_tot == 0);
1807 } /* _dhd_wlfc_pktq_flush */
1808 
1809 #ifndef BCMDBUS
1810 /** !BCMDBUS specific function. Dequeues a packet from the caller supplied queue. */
1811 static void*
_dhd_wlfc_pktq_pdeq_with_fn(struct pktq * pq,int prec,f_processpkt_t fn,void * arg)1812 _dhd_wlfc_pktq_pdeq_with_fn(struct pktq *pq, int prec, f_processpkt_t fn, void *arg)
1813 {
1814 	struct pktq_prec *q;
1815 	void *p, *prev = NULL;
1816 
1817 	ASSERT(prec >= 0 && prec < pq->num_prec);
1818 
1819 	q = &pq->q[prec];
1820 	p = q->head;
1821 
1822 	while (p) {
1823 		if (fn == NULL || (*fn)(p, arg)) {
1824 			break;
1825 		} else {
1826 			prev = p;
1827 			p = PKTLINK(p);
1828 		}
1829 	}
1830 	if (p == NULL)
1831 		return NULL;
1832 
1833 	bcm_pkt_validate_chk(p, "_dhd_wlfc_pktq_flush");
1834 
1835 	if (prev == NULL) {
1836 		if ((q->head = PKTLINK(p)) == NULL) {
1837 			q->tail = NULL;
1838 		}
1839 	} else {
1840 		PKTSETLINK(prev, PKTLINK(p));
1841 		if (q->tail == p) {
1842 			q->tail = prev;
1843 		}
1844 	}
1845 
1846 	q->n_pkts--;
1847 
1848 	pq->n_pkts_tot--;
1849 
1850 #ifdef WL_TXQ_STALL
1851 	q->dequeue_count++;
1852 #endif
1853 
1854 	PKTSETLINK(p, NULL);
1855 
1856 	return p;
1857 }
1858 
1859 /** !BCMDBUS specific function */
1860 static void
_dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1861 _dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1862 {
1863 	int prec;
1864 	void *pkt = NULL, *head = NULL, *tail = NULL;
1865 	struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
1866 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1867 	wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1868 	wlfc_mac_descriptor_t* entry;
1869 
1870 	dhd_os_sdlock_txq(dhd);
1871 	for (prec = 0; prec < txq->num_prec; prec++) {
1872 		while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
1873 #ifdef DHDTCPACK_SUPPRESS
1874 			if (dhd_tcpack_check_xmit(dhd, pkt) == BCME_ERROR) {
1875 				DHD_ERROR(("%s %d: tcpack_suppress ERROR!!! Stop using it\n",
1876 					__FUNCTION__, __LINE__));
1877 				dhd_tcpack_suppress_set(dhd, TCPACK_SUP_OFF);
1878 			}
1879 #endif /* DHDTCPACK_SUPPRESS */
1880 			if (!head) {
1881 				head = pkt;
1882 			}
1883 			if (tail) {
1884 				PKTSETLINK(tail, pkt);
1885 			}
1886 			tail = pkt;
1887 		}
1888 	}
1889 	dhd_os_sdunlock_txq(dhd);
1890 
1891 	while ((pkt = head)) {
1892 		head = PKTLINK(pkt);
1893 		PKTSETLINK(pkt, NULL);
1894 		entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
1895 
1896 		if (!WLFC_GET_AFQ(dhd->wlfc_mode) &&
1897 			!_dhd_wlfc_hanger_remove_reference(h, pkt)) {
1898 			DHD_ERROR(("%s: can't find pkt(%p) in hanger, free it anyway\n",
1899 				__FUNCTION__, pkt));
1900 		}
1901 		if (entry->transit_count)
1902 			entry->transit_count--;
1903 		if (entry->suppr_transit_count) {
1904 			entry->suppr_transit_count--;
1905 			if (entry->suppressed &&
1906 				(!entry->onbus_pkts_count) &&
1907 				(!entry->suppr_transit_count))
1908 				entry->suppressed = FALSE;
1909 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
1910 				_dhd_wlfc_bprint(wlfc, "[sc]-");
1911 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
1912 		}
1913 		_dhd_wlfc_return_implied_credit(wlfc, pkt);
1914 		wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pkt))][DHD_PKTTAG_FIFO(PKTTAG(pkt))]--;
1915 		wlfc->stats.pktout++;
1916 		wlfc->stats.cleanup_txq_cnt++;
1917 		dhd_txcomplete(dhd, pkt, FALSE);
1918 		PKTFREE(wlfc->osh, pkt, TRUE);
1919 	}
1920 } /* _dhd_wlfc_cleanup_txq */
1921 
1922 #endif /* !BCMDBUS */
1923 
1924 /** called during eg detach */
1925 void
_dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)1926 _dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
1927 {
1928 	int i;
1929 	int total_entries;
1930 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
1931 	wlfc_mac_descriptor_t* table;
1932 	wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
1933 
1934 	wlfc->stats.cleanup_txq_cnt = 0;
1935 	wlfc->stats.cleanup_psq_cnt = 0;
1936 	wlfc->stats.cleanup_fw_cnt = 0;
1937 
1938 	/*
1939 	*  flush sequence should be txq -> psq -> hanger/afq, hanger has to be last one
1940 	*/
1941 #ifndef BCMDBUS
1942 	/* flush bus->txq */
1943 	_dhd_wlfc_cleanup_txq(dhd, fn, arg);
1944 #endif /* BCMDBUS */
1945 
1946 	/* flush psq, search all entries, include nodes as well as interfaces */
1947 	total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
1948 	table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
1949 
1950 	for (i = 0; i < total_entries; i++) {
1951 		if (table[i].occupied) {
1952 			/* release packets held in PSQ (both delayed and suppressed) */
1953 			if (table[i].psq.n_pkts_tot) {
1954 				WLFC_DBGMESG(("%s(): PSQ[%d].len = %d\n",
1955 					__FUNCTION__, i, table[i].psq.n_pkts_tot));
1956 				_dhd_wlfc_pktq_flush(wlfc, &table[i].psq, TRUE,
1957 					fn, arg, Q_TYPE_PSQ);
1958 			}
1959 
1960 			/* free packets held in AFQ */
1961 			if (WLFC_GET_AFQ(dhd->wlfc_mode) && (table[i].afq.n_pkts_tot)) {
1962 				_dhd_wlfc_pktq_flush(wlfc, &table[i].afq, TRUE,
1963 					fn, arg, Q_TYPE_AFQ);
1964 			}
1965 
1966 			if ((fn == NULL) && (&table[i] != &wlfc->destination_entries.other)) {
1967 				table[i].occupied = 0;
1968 				if (table[i].transit_count || table[i].suppr_transit_count) {
1969 					DHD_ERROR(("%s: table[%d] transit(%d), suppr_tansit(%d)\n",
1970 						__FUNCTION__, i,
1971 						table[i].transit_count,
1972 						table[i].suppr_transit_count));
1973 				}
1974 			}
1975 		}
1976 	}
1977 
1978 	/*
1979 		. flush remained pkt in hanger queue, not in bus->txq nor psq.
1980 		. the remained pkt was successfully downloaded to dongle already.
1981 		. hanger slot state cannot be set to free until receive txstatus update.
1982 	*/
1983 	if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
1984 		for (i = 0; i < h->max_items; i++) {
1985 			if ((h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
1986 				(h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
1987 				if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
1988 					h->items[i].state = WLFC_HANGER_ITEM_STATE_FLUSHED;
1989 				}
1990 			}
1991 		}
1992 	}
1993 
1994 	return;
1995 } /* _dhd_wlfc_cleanup */
1996 
1997 /** Called after eg the dongle signalled a new remote MAC that it connected with to the DHD */
1998 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)1999 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
2000 	uint8 action, uint8 ifid, uint8 iftype, uint8* ea,
2001 	f_processpkt_t fn, void *arg)
2002 {
2003 	int rc = BCME_OK;
2004 	uint8 i;
2005 
2006 	BCM_REFERENCE(i);
2007 
2008 #ifdef QMONITOR
2009 	dhd_qmon_reset(&entry->qmon);
2010 #endif
2011 
2012 	if ((action == eWLFC_MAC_ENTRY_ACTION_ADD) || (action == eWLFC_MAC_ENTRY_ACTION_UPDATE)) {
2013 		entry->occupied = 1;
2014 		entry->state = WLFC_STATE_OPEN;
2015 		entry->requested_credit = 0;
2016 		entry->interface_id = ifid;
2017 		entry->iftype = iftype;
2018 		entry->ac_bitmap = 0xff; /* update this when handling APSD */
2019 #ifdef BULK_DEQUEUE
2020 		for (i = 0; i < AC_COUNT + 1; i++) {
2021 			entry->release_count[i] = 0;
2022 		}
2023 #endif /* BULK_DEQUEUE */
2024 		/* for an interface entry we may not care about the MAC address */
2025 		if (ea != NULL)
2026 			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
2027 
2028 		if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
2029 			entry->suppressed = FALSE;
2030 			entry->transit_count = 0;
2031 #if defined(WL_EXT_IAPSTA) && defined(PROPTX_MAXCOUNT)
2032 			entry->transit_maxcount = wl_ext_get_wlfc_maxcount(ctx->dhdp, ifid);
2033 #endif /* PROPTX_MAXCOUNT */
2034 			entry->suppr_transit_count = 0;
2035 			entry->onbus_pkts_count = 0;
2036 		}
2037 
2038 		if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
2039 			dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2040 
2041 			pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
2042 			_dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
2043 
2044 			if (WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2045 				pktq_init(&entry->afq, WLFC_AFQ_PREC_COUNT, WLFC_PSQ_LEN);
2046 			}
2047 
2048 			if (entry->next == NULL) {
2049 				/* not linked to anywhere, add to tail */
2050 				if (ctx->active_entry_head) {
2051 					entry->prev = ctx->active_entry_head->prev;
2052 					ctx->active_entry_head->prev->next = entry;
2053 					ctx->active_entry_head->prev = entry;
2054 					entry->next = ctx->active_entry_head;
2055 				} else {
2056 					ASSERT(ctx->active_entry_count == 0);
2057 					entry->prev = entry->next = entry;
2058 					ctx->active_entry_head = entry;
2059 				}
2060 				ctx->active_entry_count++;
2061 			} else {
2062 				DHD_ERROR(("%s():%d, entry(%d)\n", __FUNCTION__, __LINE__,
2063 					(int)(entry - &ctx->destination_entries.nodes[0])));
2064 			}
2065 		}
2066 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2067 		for (i = 0; i < (AC_COUNT + 1); i++) {
2068 			entry->last_send_seq[i] = 255;
2069 			entry->last_complete_seq[i] = 255;
2070 		}
2071 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2072 	} else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
2073 		/* When the entry is deleted, the packets that are queued in the entry must be
2074 		   cleanup. The cleanup action should be before the occupied is set as 0.
2075 		*/
2076 		_dhd_wlfc_cleanup(ctx->dhdp, fn, arg);
2077 		_dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
2078 
2079 		entry->occupied = 0;
2080 		entry->state = WLFC_STATE_CLOSE;
2081 		memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
2082 
2083 		if (entry->next) {
2084 			/* not floating, remove from Q */
2085 			if (ctx->active_entry_count <= 1) {
2086 				/* last item */
2087 				ctx->active_entry_head = NULL;
2088 				ctx->active_entry_count = 0;
2089 			} else {
2090 				entry->prev->next = entry->next;
2091 				entry->next->prev = entry->prev;
2092 				if (entry == ctx->active_entry_head) {
2093 					ctx->active_entry_head = entry->next;
2094 				}
2095 				ctx->active_entry_count--;
2096 			}
2097 			entry->next = entry->prev = NULL;
2098 		} else {
2099 			DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2100 		}
2101 	}
2102 	return rc;
2103 } /* _dhd_wlfc_mac_entry_update */
2104 
2105 #ifdef LIMIT_BORROW
2106 
2107 /** LIMIT_BORROW specific function */
2108 static int
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,int highest_lender_ac,int borrower_ac,bool bBorrowAll)2109 _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, int highest_lender_ac, int borrower_ac,
2110 	bool bBorrowAll)
2111 {
2112 	int lender_ac, borrow_limit = 0;
2113 	int rc = -1;
2114 
2115 	if (ctx == NULL) {
2116 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
2117 		return -1;
2118 	}
2119 
2120 	/* Borrow from lowest priority available AC (including BC/MC credits) */
2121 	for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
2122 		if (!bBorrowAll) {
2123 			borrow_limit = ctx->Init_FIFO_credit[lender_ac]/WLFC_BORROW_LIMIT_RATIO;
2124 		} else {
2125 			borrow_limit = 0;
2126 		}
2127 
2128 		if (ctx->FIFO_credit[lender_ac] > borrow_limit) {
2129 			ctx->credits_borrowed[borrower_ac][lender_ac]++;
2130 			ctx->FIFO_credit[lender_ac]--;
2131 			rc = lender_ac;
2132 			break;
2133 		}
2134 	}
2135 
2136 	return rc;
2137 }
2138 
2139 /** LIMIT_BORROW specific function */
_dhd_wlfc_return_credit(athost_wl_status_info_t * ctx,int lender_ac,int borrower_ac)2140 static int _dhd_wlfc_return_credit(athost_wl_status_info_t* ctx, int lender_ac, int borrower_ac)
2141 {
2142 	if ((ctx == NULL) || (lender_ac < 0) || (lender_ac > AC_COUNT) ||
2143 		(borrower_ac < 0) || (borrower_ac > AC_COUNT)) {
2144 		DHD_ERROR(("Error: %s():%d, ctx(%p), lender_ac(%d), borrower_ac(%d)\n",
2145 			__FUNCTION__, __LINE__, ctx, lender_ac, borrower_ac));
2146 
2147 		return BCME_BADARG;
2148 	}
2149 
2150 	ctx->credits_borrowed[borrower_ac][lender_ac]--;
2151 	ctx->FIFO_credit[lender_ac]++;
2152 
2153 	return BCME_OK;
2154 }
2155 
2156 #endif /* LIMIT_BORROW */
2157 
2158 /**
2159  * Called on an interface event (WLC_E_IF) indicated by firmware.
2160  *     @param action : eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
2161  */
2162 static int
_dhd_wlfc_interface_entry_update(void * state,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)2163 _dhd_wlfc_interface_entry_update(void* state,
2164 	uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
2165 {
2166 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2167 	wlfc_mac_descriptor_t* entry;
2168 
2169 	if (ifid >= WLFC_MAX_IFNUM)
2170 		return BCME_BADARG;
2171 
2172 	entry = &ctx->destination_entries.interfaces[ifid];
2173 
2174 	return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea,
2175 		_dhd_wlfc_ifpkt_fn, &ifid);
2176 }
2177 
2178 /**
2179  * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
2180  * specific)
2181  */
2182 static int
_dhd_wlfc_BCMCCredit_support_update(void * state)2183 _dhd_wlfc_BCMCCredit_support_update(void* state)
2184 {
2185 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2186 
2187 	ctx->bcmc_credit_supported = TRUE;
2188 	return BCME_OK;
2189 }
2190 
2191 /** Called eg on receiving a WLC_E_FIFO_CREDIT_MAP event from the dongle */
2192 static int
_dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)2193 _dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
2194 {
2195 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
2196 	int i;
2197 
2198 	for (i = 0; i <= 4; i++) {
2199 		if (ctx->Init_FIFO_credit[i] != ctx->FIFO_credit[i]) {
2200 			DHD_ERROR(("%s: credit[i] is not returned, (%d %d)\n",
2201 				__FUNCTION__, ctx->Init_FIFO_credit[i], ctx->FIFO_credit[i]));
2202 		}
2203 	}
2204 
2205 	/* update the AC FIFO credit map */
2206 	ctx->FIFO_credit[0] += (credits[0] - ctx->Init_FIFO_credit[0]);
2207 	ctx->FIFO_credit[1] += (credits[1] - ctx->Init_FIFO_credit[1]);
2208 	ctx->FIFO_credit[2] += (credits[2] - ctx->Init_FIFO_credit[2]);
2209 	ctx->FIFO_credit[3] += (credits[3] - ctx->Init_FIFO_credit[3]);
2210 	ctx->FIFO_credit[4] += (credits[4] - ctx->Init_FIFO_credit[4]);
2211 
2212 	ctx->Init_FIFO_credit[0] = credits[0];
2213 	ctx->Init_FIFO_credit[1] = credits[1];
2214 	ctx->Init_FIFO_credit[2] = credits[2];
2215 	ctx->Init_FIFO_credit[3] = credits[3];
2216 	ctx->Init_FIFO_credit[4] = credits[4];
2217 
2218 	/* credit for ATIM FIFO is not used yet. */
2219 	ctx->Init_FIFO_credit[5] = ctx->FIFO_credit[5] = 0;
2220 
2221 	return BCME_OK;
2222 }
2223 
2224 /**
2225  * Called during committing of a transmit packet from the OS DHD layer to the next layer towards
2226  * the dongle (eg the DBUS layer). All transmit packets flow via this function to the next layer.
2227  *
2228  *     @param[in/out] ctx      Driver specific flow control administration
2229  *     @param[in] ac           Access Category (QoS) of called supplied packet
2230  *     @param[in] commit_info  Contains eg the packet to send
2231  *     @param[in] fcommit      Function pointer to transmit function of next software layer
2232  *     @param[in] commit_ctx   Opaque context used when calling next layer
2233  */
2234 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)2235 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
2236     dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
2237 {
2238 	uint32 hslot;
2239 	int	rc;
2240 	dhd_pub_t *dhdp = (dhd_pub_t *)(ctx->dhdp);
2241 
2242 	/*
2243 		if ac_fifo_credit_spent = 0
2244 
2245 		This packet will not count against the FIFO credit.
2246 		To ensure the txstatus corresponding to this packet
2247 		does not provide an implied credit (default behavior)
2248 		mark the packet accordingly.
2249 
2250 		if ac_fifo_credit_spent = 1
2251 
2252 		This is a normal packet and it counts against the FIFO
2253 		credit count.
2254 	*/
2255 	DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
2256 	rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, &commit_info->p,
2257 	     commit_info->needs_hdr, &hslot);
2258 
2259 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2260 	_dhd_wlfc_check_send_order(ctx, commit_info->mac_entry, commit_info->p);
2261 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2262 	if (rc == BCME_OK) {
2263 		rc = fcommit(commit_ctx, commit_info->p);
2264 		if (rc == BCME_OK) {
2265 			uint8 gen = WL_TXSTATUS_GET_GENERATION(
2266 				DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p)));
2267 			dhd_txpkt_log_and_dump(dhdp, commit_info->p, NULL);
2268 			ctx->stats.pkt2bus++;
2269 			if (commit_info->ac_fifo_credit_spent || (ac == AC_COUNT)) {
2270 				ctx->stats.send_pkts[ac]++;
2271 				WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
2272 			}
2273 
2274 			if (gen != commit_info->mac_entry->generation) {
2275 				/* will be suppressed back by design */
2276 				if (!commit_info->mac_entry->suppressed) {
2277 					commit_info->mac_entry->suppressed = TRUE;
2278 				}
2279 				commit_info->mac_entry->suppr_transit_count++;
2280 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2281 				_dhd_wlfc_bprint(ctx, "[si%u]-",
2282 					commit_info->mac_entry->suppr_transit_count);
2283 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2284 			}
2285 			commit_info->mac_entry->transit_count++;
2286 			commit_info->mac_entry->onbus_pkts_count++;
2287 		} else if (commit_info->needs_hdr) {
2288 			if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
2289 				void *pout = NULL;
2290 				/* pop hanger for delayed packet */
2291 				_dhd_wlfc_hanger_poppkt(ctx->hanger, WL_TXSTATUS_GET_HSLOT(
2292 					DHD_PKTTAG_H2DTAG(PKTTAG(commit_info->p))), &pout, TRUE);
2293 				ASSERT(commit_info->p == pout);
2294 			}
2295 		}
2296 	} else {
2297 		ctx->stats.generic_error++;
2298 	}
2299 
2300 	if (rc != BCME_OK) {
2301 		/*
2302 		   pretx pkt process or bus commit has failed, rollback.
2303 		   - remove wl-header for a delayed packet
2304 		   - save wl-header header for suppressed packets
2305 		   - reset credit check flag
2306 		*/
2307 		_dhd_wlfc_rollback_packet_toq(ctx, commit_info->p, commit_info->pkt_type, hslot);
2308 		DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), 0);
2309 	}
2310 
2311 	return rc;
2312 } /* _dhd_wlfc_handle_packet_commit */
2313 
2314 /** Returns remote MAC descriptor for caller supplied MAC address */
2315 static uint8
_dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)2316 _dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8 *ea)
2317 {
2318 	wlfc_mac_descriptor_t* table =
2319 		((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
2320 	uint8 table_index;
2321 
2322 	if (ea != NULL) {
2323 		for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
2324 			if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
2325 				table[table_index].occupied)
2326 				return table_index;
2327 		}
2328 	}
2329 	return WLFC_MAC_DESC_ID_INVALID;
2330 }
2331 
2332 /**
2333  * Called when the host receives a WLFC_CTL_TYPE_TXSTATUS event from the dongle, indicating the
2334  * status of a frame that the dongle attempted to transmit over the wireless medium.
2335  */
2336 static int
dhd_wlfc_suppressed_acked_update(dhd_pub_t * dhd,uint16 hslot,uint8 prec,uint8 hcnt)2337 dhd_wlfc_suppressed_acked_update(dhd_pub_t *dhd, uint16 hslot, uint8 prec, uint8 hcnt)
2338 {
2339 	athost_wl_status_info_t* ctx;
2340 	wlfc_mac_descriptor_t* entry = NULL;
2341 	struct pktq *pq;
2342 	struct pktq_prec *q;
2343 	void *p, *b;
2344 
2345 	if (!dhd) {
2346 		DHD_ERROR(("%s: dhd(%p)\n", __FUNCTION__, dhd));
2347 		return BCME_BADARG;
2348 	}
2349 	ctx = (athost_wl_status_info_t*)dhd->wlfc_state;
2350 	if (!ctx) {
2351 		DHD_ERROR(("%s: ctx(%p)\n", __FUNCTION__, ctx));
2352 		return BCME_ERROR;
2353 	}
2354 
2355 	ASSERT(hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM + 1));
2356 
2357 	if (hslot < WLFC_MAC_DESC_TABLE_SIZE)
2358 		entry  = &ctx->destination_entries.nodes[hslot];
2359 	else if (hslot < (WLFC_MAC_DESC_TABLE_SIZE + WLFC_MAX_IFNUM))
2360 		entry = &ctx->destination_entries.interfaces[hslot - WLFC_MAC_DESC_TABLE_SIZE];
2361 	else
2362 		entry = &ctx->destination_entries.other;
2363 
2364 	pq = &entry->psq;
2365 
2366 	ASSERT(((prec << 1) + 1) < pq->num_prec);
2367 
2368 	q = &pq->q[((prec << 1) + 1)];
2369 
2370 	b = NULL;
2371 	p = q->head;
2372 
2373 	while (p && (hcnt != WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(p))))) {
2374 		b = p;
2375 		p = PKTLINK(p);
2376 	}
2377 
2378 	if (p == NULL) {
2379 		/* none is matched */
2380 		if (b) {
2381 			DHD_ERROR(("%s: can't find matching seq(%d)\n", __FUNCTION__, hcnt));
2382 		} else {
2383 			DHD_ERROR(("%s: queue is empty\n", __FUNCTION__));
2384 		}
2385 
2386 		return BCME_ERROR;
2387 	}
2388 
2389 	if (!b) {
2390 		/* head packet is matched */
2391 		if ((q->head = PKTLINK(p)) == NULL) {
2392 			q->tail = NULL;
2393 		}
2394 	} else {
2395 		/* middle packet is matched */
2396 		PKTSETLINK(b, PKTLINK(p));
2397 		if (PKTLINK(p) == NULL) {
2398 			q->tail = b;
2399 		}
2400 	}
2401 
2402 	q->n_pkts--;
2403 	pq->n_pkts_tot--;
2404 
2405 #ifdef WL_TXQ_STALL
2406 	q->dequeue_count++;
2407 #endif
2408 
2409 	ctx->pkt_cnt_in_q[DHD_PKTTAG_IF(PKTTAG(p))][prec]--;
2410 	ctx->pkt_cnt_per_ac[prec]--;
2411 
2412 	PKTSETLINK(p, NULL);
2413 
2414 	if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2415 		_dhd_wlfc_enque_afq(ctx, p);
2416 	} else {
2417 		_dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
2418 	}
2419 
2420 	entry->transit_count++;
2421 
2422 	return BCME_OK;
2423 }
2424 
2425 static int
_dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len,void ** p_mac)2426 _dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len, void** p_mac)
2427 {
2428 	uint8 status_flag_ori, status_flag;
2429 	uint32 status;
2430 	int ret = BCME_OK;
2431 	int remove_from_hanger_ori, remove_from_hanger = 1;
2432 	void* pktbuf = NULL;
2433 	uint8 fifo_id = 0, gen = 0, count = 0, hcnt;
2434 	uint16 hslot;
2435 	wlfc_mac_descriptor_t* entry = NULL;
2436 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2437 	uint16 seq = 0, seq_fromfw = 0, seq_num = 0;
2438 	uint16 pktfate_status;
2439 
2440 	memcpy(&status, pkt_info, sizeof(uint32));
2441 	status = ltoh32(status);
2442 	status_flag = WL_TXSTATUS_GET_FLAGS(status);
2443 	hcnt = WL_TXSTATUS_GET_FREERUNCTR(status);
2444 	hslot = WL_TXSTATUS_GET_HSLOT(status);
2445 	fifo_id = WL_TXSTATUS_GET_FIFO(status);
2446 	gen = WL_TXSTATUS_GET_GENERATION(status);
2447 
2448 	if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2449 		memcpy(&seq, pkt_info + WLFC_CTL_VALUE_LEN_TXSTATUS, WLFC_CTL_VALUE_LEN_SEQ);
2450 		seq = ltoh16(seq);
2451 		seq_fromfw = GET_WL_HAS_ASSIGNED_SEQ(seq);
2452 		seq_num = WL_SEQ_GET_NUM(seq);
2453 	}
2454 
2455 	wlfc->stats.txstatus_in += len;
2456 
2457 	if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
2458 		wlfc->stats.pkt_freed += len;
2459 	} else if (status_flag == WLFC_CTL_PKTFLAG_DISCARD_NOACK) {
2460 		wlfc->stats.pkt_freed += len;
2461 	} else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
2462 		wlfc->stats.d11_suppress += len;
2463 		remove_from_hanger = 0;
2464 	} else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
2465 		wlfc->stats.wl_suppress += len;
2466 		remove_from_hanger = 0;
2467 	} else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
2468 		wlfc->stats.wlc_tossed_pkts += len;
2469 	} else if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2470 		wlfc->stats.pkt_freed += len;
2471 	} else if (status_flag == WLFC_CTL_PKTFLAG_EXPIRED) {
2472 		wlfc->stats.pkt_exptime += len;
2473 	} else if (status_flag == WLFC_CTL_PKTFLAG_DROPPED) {
2474 		wlfc->stats.pkt_dropped += len;
2475 	}
2476 
2477 	if (dhd->proptxstatus_txstatus_ignore) {
2478 		if (!remove_from_hanger) {
2479 			DHD_ERROR(("suppress txstatus: %d\n", status_flag));
2480 		}
2481 		return BCME_OK;
2482 	}
2483 
2484 	status_flag_ori = status_flag;
2485 	remove_from_hanger_ori = remove_from_hanger;
2486 
2487 	while (count < len) {
2488 		if (status_flag == WLFC_CTL_PKTFLAG_SUPPRESS_ACKED) {
2489 			dhd_wlfc_suppressed_acked_update(dhd, hslot, fifo_id, hcnt);
2490 		}
2491 		if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2492 			ret = _dhd_wlfc_deque_afq(wlfc, hslot, hcnt, fifo_id, &pktbuf);
2493 		} else {
2494 			status_flag = status_flag_ori;
2495 			remove_from_hanger = remove_from_hanger_ori;
2496 			ret = _dhd_wlfc_hanger_poppkt(wlfc->hanger, hslot, &pktbuf, FALSE);
2497 			if (!pktbuf) {
2498 				_dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2499 					WLFC_HANGER_PKT_STATE_TXSTATUS, -1);
2500 				goto cont;
2501 			} else {
2502 				wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2503 				if (h->items[hslot].state == WLFC_HANGER_ITEM_STATE_FLUSHED) {
2504 					status_flag = WLFC_CTL_PKTFLAG_DISCARD;
2505 					remove_from_hanger = 1;
2506 				}
2507 			}
2508 		}
2509 
2510 		if ((ret != BCME_OK) || !pktbuf) {
2511 			goto cont;
2512 		}
2513 
2514 		bcm_pkt_validate_chk(pktbuf, "_dhd_wlfc_compressed_txstatus_update");
2515 
2516 		pktfate_status = ltoh16(status_flag_ori) & WLFC_CTL_PKTFLAG_MASK;
2517 		dhd_txpkt_log_and_dump(dhd, pktbuf, &pktfate_status);
2518 
2519 		/* set fifo_id to correct value because not all FW does that */
2520 		fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
2521 
2522 		entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
2523 
2524 		if (!remove_from_hanger) {
2525 			/* this packet was suppressed */
2526 			if (!entry->suppressed || (entry->generation != gen)) {
2527 				if (!entry->suppressed) {
2528 					entry->suppr_transit_count = entry->transit_count;
2529 					if (p_mac) {
2530 						*p_mac = entry;
2531 					}
2532 				} else {
2533 					DHD_ERROR(("gen(%d), entry->generation(%d)\n",
2534 						gen, entry->generation));
2535 				}
2536 				entry->suppressed = TRUE;
2537 
2538 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2539 				_dhd_wlfc_bprint(wlfc, "[ss%u.%u.%u]-",
2540 					(uint8)(entry - &wlfc->destination_entries.nodes[0]),
2541 					entry->generation,
2542 					entry->suppr_transit_count);
2543 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2544 			}
2545 			entry->generation = gen;
2546 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2547 			if (gen == WL_TXSTATUS_GET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)))) {
2548 				printf("==%d.%d==\n", gen, hcnt);
2549 			}
2550 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2551 		}
2552 
2553 #ifdef PROP_TXSTATUS_DEBUG
2554 		if (!WLFC_GET_AFQ(dhd->wlfc_mode))
2555 		{
2556 			uint32 new_t = OSL_SYSUPTIME();
2557 			uint32 old_t;
2558 			uint32 delta;
2559 			old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[hslot].push_time;
2560 
2561 			wlfc->stats.latency_sample_count++;
2562 			if (new_t > old_t)
2563 				delta = new_t - old_t;
2564 			else
2565 				delta = 0xffffffff + new_t - old_t;
2566 			wlfc->stats.total_status_latency += delta;
2567 			wlfc->stats.latency_most_recent = delta;
2568 
2569 			wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
2570 			if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
2571 				wlfc->stats.idx_delta = 0;
2572 		}
2573 #endif /* PROP_TXSTATUS_DEBUG */
2574 
2575 		/* pick up the implicit credit from this packet */
2576 		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
2577 			_dhd_wlfc_return_implied_credit(wlfc, pktbuf);
2578 		} else {
2579 			/*
2580 			if this packet did not count against FIFO credit, it must have
2581 			taken a requested_credit from the destination entry (for pspoll etc.)
2582 			*/
2583 			if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf))) {
2584 				entry->requested_credit++;
2585 #if defined(DHD_WLFC_THREAD)
2586 				_dhd_wlfc_thread_wakeup(dhd);
2587 #endif /* DHD_WLFC_THREAD */
2588 			}
2589 #ifdef PROP_TXSTATUS_DEBUG
2590 			entry->dstncredit_acks++;
2591 #endif
2592 		}
2593 
2594 		if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
2595 			(status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
2596 			/* save generation bit inside packet */
2597 			WL_TXSTATUS_SET_GENERATION(DHD_PKTTAG_H2DTAG(PKTTAG(pktbuf)), gen);
2598 
2599 			if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2600 				WL_SEQ_SET_REUSE(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_fromfw);
2601 				WL_SEQ_SET_NUM(DHD_PKTTAG_H2DSEQ(PKTTAG(pktbuf)), seq_num);
2602 			}
2603 
2604 			ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
2605 			if (ret != BCME_OK) {
2606 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2607 				_dhd_wlfc_bprint(wlfc, "f%u.%u.%u-",
2608 					(uint8)(entry - &wlfc->destination_entries.nodes[0]),
2609 					gen,
2610 					hcnt);
2611 				_dhd_wlfc_check_complete_order(wlfc, entry, pktbuf);
2612 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2613 				/* delay q is full, drop this packet */
2614 				DHD_WLFC_QMON_COMPLETE(entry);
2615 				_dhd_wlfc_prec_drop(dhd, (fifo_id << 1) + 1, pktbuf, FALSE);
2616 			} else {
2617 				if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2618 					/* Mark suppressed to avoid a double free
2619 					during wlfc cleanup
2620 					*/
2621 					_dhd_wlfc_hanger_mark_suppressed(wlfc->hanger, hslot, gen);
2622 				}
2623 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2624 				_dhd_wlfc_bprint(wlfc, "r%u.%u.%u.%u-",
2625 					status_flag,
2626 					(uint8)(entry - &wlfc->destination_entries.nodes[0]),
2627 					gen,
2628 					hcnt);
2629 				if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2630 					_dhd_wlfc_bprint(wlfc, "%u.%u-", seq_fromfw, seq_num);
2631 				}
2632 
2633 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2634 			}
2635 		} else {
2636 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2637 			_dhd_wlfc_bprint(wlfc, "c%u.%u.%u.%u-",
2638 				status_flag,
2639 				(uint8)(entry - &wlfc->destination_entries.nodes[0]),
2640 				gen,
2641 				hcnt);
2642 			_dhd_wlfc_check_complete_order(wlfc, entry, pktbuf);
2643 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2644 
2645 			DHD_WLFC_QMON_COMPLETE(entry);
2646 
2647 			if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2648 				_dhd_wlfc_hanger_free_pkt(wlfc, hslot,
2649 					WLFC_HANGER_PKT_STATE_TXSTATUS, TRUE);
2650 			} else {
2651 				dhd_txcomplete(dhd, pktbuf, TRUE);
2652 				wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))]
2653 					[DHD_PKTTAG_FIFO(PKTTAG(pktbuf))]--;
2654 				wlfc->stats.pktout++;
2655 				/* free the packet */
2656 				PKTFREE(wlfc->osh, pktbuf, TRUE);
2657 			}
2658 		}
2659 		/* pkt back from firmware side */
2660 		if (entry->transit_count)
2661 			entry->transit_count--;
2662 		if (entry->suppr_transit_count) {
2663 			entry->suppr_transit_count--;
2664 			if (entry->suppressed &&
2665 				(!entry->onbus_pkts_count) &&
2666 				(!entry->suppr_transit_count))
2667 				entry->suppressed = FALSE;
2668 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2669 				_dhd_wlfc_bprint(wlfc, "[sc]-");
2670 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2671 		}
2672 
2673 cont:
2674 		hcnt = (hcnt + 1) & WL_TXSTATUS_FREERUNCTR_MASK;
2675 		if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
2676 			hslot = (hslot + 1) & WL_TXSTATUS_HSLOT_MASK;
2677 		}
2678 
2679 		if (WLFC_GET_REUSESEQ(dhd->wlfc_mode) && seq_fromfw) {
2680 			seq_num = (seq_num + 1) & WL_SEQ_NUM_MASK;
2681 		}
2682 
2683 		count++;
2684 	}
2685 
2686 	return BCME_OK;
2687 } /* _dhd_wlfc_compressed_txstatus_update */
2688 
2689 /**
2690  * Called when eg host receives a 'WLFC_CTL_TYPE_FIFO_CREDITBACK' event from the dongle.
2691  *    @param[in] credits caller supplied credit that will be added to the host credit.
2692  */
2693 static int
_dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)2694 _dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
2695 {
2696 	int i;
2697 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2698 	for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
2699 #ifdef PROP_TXSTATUS_DEBUG
2700 		wlfc->stats.fifo_credits_back[i] += credits[i];
2701 #endif
2702 
2703 		/* update FIFO credits */
2704 		if (dhd->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
2705 		{
2706 			int lender; /* Note that borrower is i */
2707 
2708 			/* Return credits to highest priority lender first */
2709 			for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
2710 				if (wlfc->credits_borrowed[i][lender] > 0) {
2711 					if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
2712 						credits[i] -=
2713 							(uint8)wlfc->credits_borrowed[i][lender];
2714 						wlfc->FIFO_credit[lender] +=
2715 						    wlfc->credits_borrowed[i][lender];
2716 						wlfc->credits_borrowed[i][lender] = 0;
2717 					} else {
2718 						wlfc->credits_borrowed[i][lender] -= credits[i];
2719 						wlfc->FIFO_credit[lender] += credits[i];
2720 						credits[i] = 0;
2721 					}
2722 				}
2723 			}
2724 
2725 			/* If we have more credits left over, these must belong to the AC */
2726 			if (credits[i] > 0) {
2727 				wlfc->FIFO_credit[i] += credits[i];
2728 			}
2729 
2730 			if (wlfc->FIFO_credit[i] > wlfc->Init_FIFO_credit[i]) {
2731 				wlfc->FIFO_credit[i] = wlfc->Init_FIFO_credit[i];
2732 			}
2733 		}
2734 	}
2735 
2736 #if defined(DHD_WLFC_THREAD)
2737 	_dhd_wlfc_thread_wakeup(dhd);
2738 #endif /* defined(DHD_WLFC_THREAD) */
2739 
2740 	return BCME_OK;
2741 } /* _dhd_wlfc_fifocreditback_indicate */
2742 
2743 #ifndef BCMDBUS
2744 /** !BCMDBUS specific function */
2745 static void
_dhd_wlfc_suppress_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)2746 _dhd_wlfc_suppress_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
2747 {
2748 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2749 	wlfc_mac_descriptor_t* entry;
2750 	int prec;
2751 	void *pkt = NULL, *head = NULL, *tail = NULL;
2752 	struct pktq *txq = (struct pktq *)dhd_bus_txq(dhd->bus);
2753 	uint8	results[WLFC_CTL_VALUE_LEN_TXSTATUS+WLFC_CTL_VALUE_LEN_SEQ];
2754 	uint8 credits[WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK] = {0};
2755 	uint32 htod = 0;
2756 	uint16 htodseq = 0;
2757 	bool bCreditUpdate = FALSE;
2758 
2759 	dhd_os_sdlock_txq(dhd);
2760 	for (prec = 0; prec < txq->num_prec; prec++) {
2761 		while ((pkt = _dhd_wlfc_pktq_pdeq_with_fn(txq, prec, fn, arg))) {
2762 			if (!head) {
2763 				head = pkt;
2764 			}
2765 			if (tail) {
2766 				PKTSETLINK(tail, pkt);
2767 			}
2768 			tail = pkt;
2769 		}
2770 	}
2771 	dhd_os_sdunlock_txq(dhd);
2772 
2773 	while ((pkt = head)) {
2774 		head = PKTLINK(pkt);
2775 		PKTSETLINK(pkt, NULL);
2776 
2777 		entry = _dhd_wlfc_find_table_entry(wlfc, pkt);
2778 		if (!entry) {
2779 			PKTFREE(dhd->osh, pkt, TRUE);
2780 			continue;
2781 		}
2782 		if (entry->onbus_pkts_count > 0) {
2783 			entry->onbus_pkts_count--;
2784 		}
2785 		if (entry->suppressed &&
2786 				(!entry->onbus_pkts_count) &&
2787 				(!entry->suppr_transit_count)) {
2788 			entry->suppressed = FALSE;
2789 		}
2790 		/* fake a suppression txstatus */
2791 		htod = DHD_PKTTAG_H2DTAG(PKTTAG(pkt));
2792 		WL_TXSTATUS_SET_FLAGS(htod, WLFC_CTL_PKTFLAG_WLSUPPRESS);
2793 		WL_TXSTATUS_SET_GENERATION(htod, entry->generation);
2794 		htod = htol32(htod);
2795 		memcpy(results, &htod, WLFC_CTL_VALUE_LEN_TXSTATUS);
2796 		if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
2797 			htodseq = DHD_PKTTAG_H2DSEQ(PKTTAG(pkt));
2798 			if (IS_WL_TO_REUSE_SEQ(htodseq)) {
2799 				SET_WL_HAS_ASSIGNED_SEQ(htodseq);
2800 				RESET_WL_TO_REUSE_SEQ(htodseq);
2801 			}
2802 			htodseq = htol16(htodseq);
2803 			memcpy(results + WLFC_CTL_VALUE_LEN_TXSTATUS, &htodseq,
2804 				WLFC_CTL_VALUE_LEN_SEQ);
2805 		}
2806 		if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
2807 			_dhd_wlfc_enque_afq(wlfc, pkt);
2808 		}
2809 		_dhd_wlfc_compressed_txstatus_update(dhd, results, 1, NULL);
2810 
2811 		/* fake a fifo credit back */
2812 		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pkt))) {
2813 			credits[DHD_PKTTAG_FIFO(PKTTAG(pkt))]++;
2814 			bCreditUpdate = TRUE;
2815 		}
2816 	}
2817 
2818 	if (bCreditUpdate) {
2819 		_dhd_wlfc_fifocreditback_indicate(dhd, credits);
2820 	}
2821 } /* _dhd_wlfc_suppress_txq */
2822 
2823 #endif /* !BCMDBUS */
2824 
2825 static int
_dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)2826 _dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
2827 {
2828 	uint32 timestamp;
2829 
2830 	(void)dhd;
2831 
2832 	bcopy(&value[2], &timestamp, sizeof(uint32));
2833 	timestamp = ltoh32(timestamp);
2834 	DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
2835 	return BCME_OK;
2836 }
2837 
2838 static int
_dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)2839 _dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
2840 {
2841 	(void)dhd;
2842 	(void)rssi;
2843 	return BCME_OK;
2844 }
2845 
2846 static void
_dhd_wlfc_add_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2847 _dhd_wlfc_add_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2848 {
2849 	int i;
2850 
2851 	if (!wlfc || !entry) {
2852 		return;
2853 	}
2854 
2855 	for (i = 0; i < wlfc->requested_entry_count; i++) {
2856 		if (entry == wlfc->requested_entry[i]) {
2857 			break;
2858 		}
2859 	}
2860 
2861 	if (i == wlfc->requested_entry_count) {
2862 		/* no match entry found */
2863 		ASSERT(wlfc->requested_entry_count <= (WLFC_MAC_DESC_TABLE_SIZE-1));
2864 		wlfc->requested_entry[wlfc->requested_entry_count++] = entry;
2865 	}
2866 }
2867 
2868 /** called on eg receiving 'mac open' event from the dongle. */
2869 static void
_dhd_wlfc_remove_requested_entry(athost_wl_status_info_t * wlfc,wlfc_mac_descriptor_t * entry)2870 _dhd_wlfc_remove_requested_entry(athost_wl_status_info_t* wlfc, wlfc_mac_descriptor_t* entry)
2871 {
2872 	int i;
2873 
2874 	if (!wlfc || !entry) {
2875 		return;
2876 	}
2877 
2878 	for (i = 0; i < wlfc->requested_entry_count; i++) {
2879 		if (entry == wlfc->requested_entry[i]) {
2880 			break;
2881 		}
2882 	}
2883 
2884 	if (i < wlfc->requested_entry_count) {
2885 		/* found */
2886 		ASSERT(wlfc->requested_entry_count > 0);
2887 		wlfc->requested_entry_count--;
2888 		if (i != wlfc->requested_entry_count) {
2889 			wlfc->requested_entry[i] =
2890 				wlfc->requested_entry[wlfc->requested_entry_count];
2891 		}
2892 		wlfc->requested_entry[wlfc->requested_entry_count] = NULL;
2893 	}
2894 }
2895 
2896 /** called on eg receiving a WLFC_CTL_TYPE_MACDESC_ADD TLV from the dongle */
2897 static int
_dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2898 _dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2899 {
2900 	int rc;
2901 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2902 	wlfc_mac_descriptor_t* table;
2903 	uint8 existing_index;
2904 	uint8 table_index;
2905 	uint8 ifid;
2906 	uint8* ea;
2907 
2908 	WLFC_DBGMESG(("%s(), mac ["MACDBG"],%s,idx:%d,id:0x%02x\n",
2909 		__FUNCTION__, MAC2STRDBG(&value[2]),
2910 		((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
2911 		WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
2912 
2913 	table = wlfc->destination_entries.nodes;
2914 	table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
2915 	ifid = value[1];
2916 	ea = &value[2];
2917 
2918 	_dhd_wlfc_remove_requested_entry(wlfc, &table[table_index]);
2919 	if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
2920 		existing_index = _dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2921 		if ((existing_index != WLFC_MAC_DESC_ID_INVALID) &&
2922 			(existing_index != table_index) && table[existing_index].occupied) {
2923 			/*
2924 			there is an existing different entry, free the old one
2925 			and move it to new index if necessary.
2926 			*/
2927 			rc = _dhd_wlfc_mac_entry_update(wlfc, &table[existing_index],
2928 				eWLFC_MAC_ENTRY_ACTION_DEL, table[existing_index].interface_id,
2929 				table[existing_index].iftype, NULL, _dhd_wlfc_entrypkt_fn,
2930 				&table[existing_index]);
2931 		}
2932 
2933 		if (!table[table_index].occupied) {
2934 			/* this new MAC entry does not exist, create one */
2935 			table[table_index].mac_handle = value[0];
2936 			rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2937 				eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2938 				wlfc->destination_entries.interfaces[ifid].iftype,
2939 				ea, NULL, NULL);
2940 		} else {
2941 			/* the space should have been empty, but it's not */
2942 			wlfc->stats.mac_update_failed++;
2943 		}
2944 	}
2945 
2946 	if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2947 		if (table[table_index].occupied) {
2948 				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2949 					eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2950 					wlfc->destination_entries.interfaces[ifid].iftype,
2951 					ea, _dhd_wlfc_entrypkt_fn, &table[table_index]);
2952 		} else {
2953 			/* the space should have been occupied, but it's not */
2954 			wlfc->stats.mac_update_failed++;
2955 		}
2956 	}
2957 	BCM_REFERENCE(rc);
2958 	return BCME_OK;
2959 } /* _dhd_wlfc_mac_table_update */
2960 
2961 /** Called on a 'mac open' or 'mac close' event indicated by the dongle */
2962 static int
_dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2963 _dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2964 {
2965 	/* Handle PS on/off indication */
2966 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2967 	wlfc_mac_descriptor_t* table;
2968 	wlfc_mac_descriptor_t* desc; /* a table maps from mac handle to mac descriptor */
2969 	uint8 mac_handle = value[0];
2970 	int i;
2971 
2972 	table = wlfc->destination_entries.nodes;
2973 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2974 	if (desc->occupied) {
2975 #ifdef BULK_DEQUEUE
2976 		for (i = 0; i < AC_COUNT + 1; i++) {
2977 			desc->release_count[i] = 0;
2978 		}
2979 #endif /* BULK_DEQUEUE */
2980 		if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2981 			desc->state = WLFC_STATE_OPEN;
2982 			desc->ac_bitmap = 0xff;
2983 			DHD_WLFC_CTRINC_MAC_OPEN(desc);
2984 			desc->requested_credit = 0;
2985 			desc->requested_packet = 0;
2986 			_dhd_wlfc_remove_requested_entry(wlfc, desc);
2987 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2988 			_dhd_wlfc_bprint(wlfc, "[op%u.%u]-",
2989 				(uint8)(table - &wlfc->destination_entries.nodes[0]),
2990 				OSL_SYSUPTIME());
2991 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
2992 		} else {
2993 			desc->state = WLFC_STATE_CLOSE;
2994 			DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2995 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
2996 			_dhd_wlfc_bprint(wlfc, "[cl%u.%u]-",
2997 				(uint8)(table - &wlfc->destination_entries.nodes[0]),
2998 				OSL_SYSUPTIME());
2999 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3000 			/* Indicate to firmware if there is any traffic pending. */
3001 			for (i = 0; i < AC_COUNT; i++) {
3002 				_dhd_wlfc_traffic_pending_check(wlfc, desc, i);
3003 			}
3004 		}
3005 	} else {
3006 		wlfc->stats.psmode_update_failed++;
3007 	}
3008 
3009 	return BCME_OK;
3010 } /* _dhd_wlfc_psmode_update */
3011 
3012 /** called upon receiving 'interface open' or 'interface close' event from the dongle */
3013 static int
_dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)3014 _dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
3015 {
3016 	/* Handle PS on/off indication */
3017 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3018 	wlfc_mac_descriptor_t* table;
3019 	uint8 if_id = value[0];
3020 	uint8 i;
3021 
3022 	BCM_REFERENCE(i);
3023 
3024 	if (if_id < WLFC_MAX_IFNUM) {
3025 		table = wlfc->destination_entries.interfaces;
3026 		if (table[if_id].occupied) {
3027 #ifdef BULK_DEQUEUE
3028 			for (i = 0; i < AC_COUNT + 1; i++) {
3029 				table->release_count[i] = 0;
3030 			}
3031 #endif /* BULK_DEQUEUE */
3032 			if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
3033 				table[if_id].state = WLFC_STATE_OPEN;
3034 				/* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
3035 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3036 				_dhd_wlfc_bprint(wlfc, "[op%u.%u]-",
3037 					(uint8)(table - &wlfc->destination_entries.nodes[0]),
3038 					OSL_SYSUPTIME());
3039 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3040 			} else {
3041 				table[if_id].state = WLFC_STATE_CLOSE;
3042 				/* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
3043 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3044 				_dhd_wlfc_bprint(wlfc, "[cl%u.%u]-",
3045 					(uint8)(table - &wlfc->destination_entries.nodes[0]),
3046 					OSL_SYSUPTIME());
3047 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3048 			}
3049 			return BCME_OK;
3050 		}
3051 	}
3052 	wlfc->stats.interface_update_failed++;
3053 
3054 	/* XXX: what is an appropriate error? */
3055 	return BCME_OK;
3056 }
3057 
3058 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_CREDIT TLV from the dongle */
3059 static int
_dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)3060 _dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
3061 {
3062 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3063 	wlfc_mac_descriptor_t* table;
3064 	wlfc_mac_descriptor_t* desc;
3065 	uint8 mac_handle;
3066 	uint8 credit;
3067 
3068 	table = wlfc->destination_entries.nodes;
3069 	mac_handle = value[1];
3070 	credit = value[0];
3071 
3072 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
3073 	if (desc->occupied) {
3074 		desc->requested_credit = credit;
3075 
3076 		/* XXX: toggle AC prec bitmap based on received bmp, exclude ac/bc pkt */
3077 		desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
3078 		_dhd_wlfc_add_requested_entry(wlfc, desc);
3079 #if defined(DHD_WLFC_THREAD)
3080 		if (credit) {
3081 			_dhd_wlfc_thread_wakeup(dhd);
3082 		}
3083 #endif /* DHD_WLFC_THREAD */
3084 	} else {
3085 		wlfc->stats.credit_request_failed++;
3086 	}
3087 
3088 	return BCME_OK;
3089 }
3090 
3091 /** Called on receiving a WLFC_CTL_TYPE_MAC_REQUEST_PACKET TLV from the dongle */
3092 static int
_dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)3093 _dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
3094 {
3095 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3096 	wlfc_mac_descriptor_t* table;
3097 	wlfc_mac_descriptor_t* desc;
3098 	uint8 mac_handle;
3099 	uint8 packet_count;
3100 
3101 	table = wlfc->destination_entries.nodes;
3102 	mac_handle = value[1];
3103 	packet_count = value[0];
3104 
3105 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
3106 	if (desc->occupied) {
3107 		desc->requested_packet = packet_count;
3108 
3109 		/* XXX: toggle AC prec bitmap based on received bmp, exclude ac/bc pkt */
3110 		desc->ac_bitmap = value[2] & (~(1<<AC_COUNT));
3111 		_dhd_wlfc_add_requested_entry(wlfc, desc);
3112 #if defined(DHD_WLFC_THREAD)
3113 		if (packet_count) {
3114 			_dhd_wlfc_thread_wakeup(dhd);
3115 		}
3116 #endif /* DHD_WLFC_THREAD */
3117 	} else {
3118 		wlfc->stats.packet_request_failed++;
3119 	}
3120 
3121 	return BCME_OK;
3122 }
3123 
3124 /** Called when host receives a WLFC_CTL_TYPE_HOST_REORDER_RXPKTS TLV from the dongle */
3125 static void
_dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)3126 _dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
3127 {
3128 	if (info_len) {
3129 		/* Check copy length to avoid buffer overrun. In case of length exceeding
3130 		*  WLHOST_REORDERDATA_TOTLEN, return failure instead sending incomplete result
3131 		*  of length WLHOST_REORDERDATA_TOTLEN
3132 		*/
3133 		if ((info_buf) && (len <= WLHOST_REORDERDATA_TOTLEN)) {
3134 			bcopy(val, info_buf, len);
3135 			*info_len = len;
3136 		} else {
3137 			*info_len = 0;
3138 		}
3139 	}
3140 }
3141 
3142 /*
3143  * public functions
3144  */
3145 
dhd_wlfc_is_supported(dhd_pub_t * dhd)3146 bool dhd_wlfc_is_supported(dhd_pub_t *dhd)
3147 {
3148 	bool rc = TRUE;
3149 
3150 	if (dhd == NULL) {
3151 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3152 		return FALSE;
3153 	}
3154 
3155 	dhd_os_wlfc_block(dhd);
3156 
3157 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3158 		rc =  FALSE;
3159 	}
3160 
3161 	dhd_os_wlfc_unblock(dhd);
3162 
3163 	return rc;
3164 }
3165 
3166 #ifdef BULK_DEQUEUE
3167 #ifndef WLFC_MAX_RELEASE_CNT
3168 #ifdef CUSTOM_AMPDU_MPDU
3169 #define WLFC_MAX_RELEASE_CNT	CUSTOM_AMPDU_MPDU
3170 #else
3171 #define WLFC_MAX_RELEASE_CNT	16
3172 #endif /* CUSTOM_AMPDU_MPDU */
3173 #endif /* WLFC_MAX_RELEASE_CNT */
3174 #endif /* BULK_DEQUEUE */
3175 
dhd_wlfc_enable(dhd_pub_t * dhd)3176 int dhd_wlfc_enable(dhd_pub_t *dhd)
3177 {
3178 	int i, rc = BCME_OK;
3179 	athost_wl_status_info_t* wlfc;
3180 
3181 	if (dhd == NULL) {
3182 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3183 		return BCME_BADARG;
3184 	}
3185 
3186 	dhd_os_wlfc_block(dhd);
3187 
3188 	if (!dhd->wlfc_enabled || dhd->wlfc_state) {
3189 		rc = BCME_OK;
3190 		goto exit;
3191 	}
3192 
3193 	/* allocate space to track txstatus propagated from firmware */
3194 	dhd->wlfc_state = DHD_OS_PREALLOC(dhd, DHD_PREALLOC_DHD_WLFC_INFO,
3195 		sizeof(athost_wl_status_info_t));
3196 	if (dhd->wlfc_state == NULL) {
3197 		rc = BCME_NOMEM;
3198 		goto exit;
3199 	}
3200 
3201 	/* initialize state space */
3202 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3203 	memset(wlfc, 0, sizeof(athost_wl_status_info_t));
3204 
3205 	/* remember osh & dhdp */
3206 	wlfc->osh = dhd->osh;
3207 	wlfc->dhdp = dhd;
3208 #ifdef BULK_DEQUEUE
3209 	wlfc->max_release_count = WLFC_MAX_RELEASE_CNT;
3210 #endif /* BULK_DEQUEUE */
3211 	if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3212 		wlfc->hanger = _dhd_wlfc_hanger_create(dhd, WLFC_HANGER_MAXITEMS);
3213 		if (wlfc->hanger == NULL) {
3214 			DHD_OS_PREFREE(dhd, dhd->wlfc_state,
3215 				sizeof(athost_wl_status_info_t));
3216 			dhd->wlfc_state = NULL;
3217 			rc = BCME_NOMEM;
3218 			goto exit;
3219 		}
3220 	}
3221 
3222 	dhd->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
3223 	/* default to check rx pkt */
3224 	dhd->wlfc_rxpkt_chk = TRUE;
3225 #if defined (LINUX) || defined(linux)
3226 	if (dhd->op_mode & DHD_FLAG_IBSS_MODE) {
3227 		dhd->wlfc_rxpkt_chk = FALSE;
3228 	}
3229 #endif /* defined (LINUX) || defined(linux) */
3230 
3231 	/* initialize all interfaces to accept traffic */
3232 	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
3233 		wlfc->hostif_flow_state[i] = OFF;
3234 	}
3235 
3236 	_dhd_wlfc_mac_entry_update(wlfc, &wlfc->destination_entries.other,
3237 		eWLFC_MAC_ENTRY_ACTION_ADD, 0xff, 0, NULL, NULL, NULL);
3238 
3239 	wlfc->allow_credit_borrow = 0;
3240 	wlfc->single_ac = 0;
3241 	wlfc->single_ac_timestamp = 0;
3242 
3243 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
3244 	wlfc->log_buf = MALLOC(dhd->osh, WLFC_LOG_BUF_SIZE);
3245 	wlfc->log_buf[WLFC_LOG_BUF_SIZE - 1] = 0;
3246 	wlfc->log_buf_offset = 0;
3247 	wlfc->log_buf_full = FALSE;
3248 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
3249 
3250 exit:
3251 	DHD_ERROR(("%s: ret=%d\n", __FUNCTION__, rc));
3252 	dhd_os_wlfc_unblock(dhd);
3253 
3254 	return rc;
3255 } /* dhd_wlfc_enable */
3256 
3257 #ifdef SUPPORT_P2P_GO_PS
3258 
3259 /**
3260  * Called when the host platform enters a lower power mode, eg right before a system hibernate.
3261  * SUPPORT_P2P_GO_PS specific function.
3262  */
3263 int
dhd_wlfc_suspend(dhd_pub_t * dhd)3264 dhd_wlfc_suspend(dhd_pub_t *dhd)
3265 {
3266 	uint32 tlv = 0;
3267 
3268 	DHD_TRACE(("%s: masking wlfc events\n", __FUNCTION__));
3269 	if (!dhd->wlfc_enabled)
3270 		return -1;
3271 
3272 	if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
3273 		return -1;
3274 	if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) == 0)
3275 		return 0;
3276 	tlv &= ~(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3277 	if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
3278 		return -1;
3279 
3280 	return 0;
3281 }
3282 
3283 /**
3284  * Called when the host platform resumes from a power management operation, eg resume after a
3285  * system hibernate. SUPPORT_P2P_GO_PS specific function.
3286  */
3287 int
dhd_wlfc_resume(dhd_pub_t * dhd)3288 dhd_wlfc_resume(dhd_pub_t *dhd)
3289 {
3290 	uint32 tlv = 0;
3291 
3292 	DHD_TRACE(("%s: unmasking wlfc events\n", __FUNCTION__));
3293 	if (!dhd->wlfc_enabled)
3294 		return -1;
3295 
3296 	if (!dhd_wl_ioctl_get_intiovar(dhd, "tlv", &tlv, WLC_GET_VAR, FALSE, 0))
3297 		return -1;
3298 	if ((tlv & (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS)) ==
3299 		(WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS))
3300 		return 0;
3301 	tlv |= (WLFC_FLAGS_RSSI_SIGNALS | WLFC_FLAGS_XONXOFF_SIGNALS);
3302 	if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0))
3303 		return -1;
3304 
3305 	return 0;
3306 }
3307 
3308 #endif /* SUPPORT_P2P_GO_PS */
3309 
3310 /** A flow control header was received from firmware, containing one or more TLVs */
3311 int
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)3312 dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
3313 	uint *reorder_info_len)
3314 {
3315 	uint8 type, len;
3316 	uint8* value;
3317 	uint8* tmpbuf;
3318 	uint16 remainder = (uint16)tlv_hdr_len;
3319 	uint16 processed = 0;
3320 	athost_wl_status_info_t* wlfc = NULL;
3321 	void* entry;
3322 
3323 	if ((dhd == NULL) || (pktbuf == NULL)) {
3324 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3325 		return BCME_BADARG;
3326 	}
3327 
3328 	dhd_os_wlfc_block(dhd);
3329 
3330 	if (dhd->proptxstatus_mode != WLFC_ONLY_AMPDU_HOSTREORDER) {
3331 		if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3332 			dhd_os_wlfc_unblock(dhd);
3333 			return WLFC_UNSUPPORTED;
3334 		}
3335 		wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3336 	}
3337 
3338 	tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
3339 
3340 	if (remainder) {
3341 		while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
3342 			type = tmpbuf[processed];
3343 			if (type == WLFC_CTL_TYPE_FILLER) {
3344 				remainder -= 1;
3345 				processed += 1;
3346 				continue;
3347 			}
3348 
3349 			len  = tmpbuf[processed + 1];
3350 			value = &tmpbuf[processed + 2];
3351 
3352 			if (remainder < (2 + len))
3353 				break;
3354 
3355 			remainder -= 2 + len;
3356 			processed += 2 + len;
3357 			entry = NULL;
3358 
3359 			DHD_INFO(("%s():%d type %d remainder %d processed %d\n",
3360 				__FUNCTION__, __LINE__, type, remainder, processed));
3361 
3362 			if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
3363 				_dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
3364 					reorder_info_len);
3365 
3366 			if (wlfc == NULL) {
3367 				ASSERT(dhd->proptxstatus_mode == WLFC_ONLY_AMPDU_HOSTREORDER);
3368 
3369 				if (type != WLFC_CTL_TYPE_HOST_REORDER_RXPKTS &&
3370 					type != WLFC_CTL_TYPE_TRANS_ID)
3371 					DHD_INFO(("%s():%d dhd->wlfc_state is NULL yet!"
3372 					" type %d remainder %d processed %d\n",
3373 					__FUNCTION__, __LINE__, type, remainder, processed));
3374 				continue;
3375 			}
3376 
3377 			if (type == WLFC_CTL_TYPE_TXSTATUS) {
3378 				_dhd_wlfc_compressed_txstatus_update(dhd, value, 1, &entry);
3379 			} else if (type == WLFC_CTL_TYPE_COMP_TXSTATUS) {
3380 				uint8 compcnt_offset = WLFC_CTL_VALUE_LEN_TXSTATUS;
3381 
3382 				if (WLFC_GET_REUSESEQ(dhd->wlfc_mode)) {
3383 					compcnt_offset += WLFC_CTL_VALUE_LEN_SEQ;
3384 				}
3385 				_dhd_wlfc_compressed_txstatus_update(dhd, value,
3386 					value[compcnt_offset], &entry);
3387 			} else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK) {
3388 				_dhd_wlfc_fifocreditback_indicate(dhd, value);
3389 			} else if (type == WLFC_CTL_TYPE_RSSI) {
3390 				_dhd_wlfc_rssi_indicate(dhd, value);
3391 			} else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT) {
3392 				_dhd_wlfc_credit_request(dhd, value);
3393 			} else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET) {
3394 				_dhd_wlfc_packet_request(dhd, value);
3395 			} else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
3396 				(type == WLFC_CTL_TYPE_MAC_CLOSE)) {
3397 				_dhd_wlfc_psmode_update(dhd, value, type);
3398 			} else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
3399 				(type == WLFC_CTL_TYPE_MACDESC_DEL)) {
3400 				_dhd_wlfc_mac_table_update(dhd, value, type);
3401 			} else if (type == WLFC_CTL_TYPE_TRANS_ID) {
3402 				_dhd_wlfc_dbg_senum_check(dhd, value);
3403 			} else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
3404 				(type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
3405 				_dhd_wlfc_interface_update(dhd, value, type);
3406 			}
3407 
3408 #ifndef BCMDBUS
3409 			if (entry && WLFC_GET_REORDERSUPP(dhd->wlfc_mode)) {
3410 				/* suppress all packets for this mac entry from bus->txq */
3411 				_dhd_wlfc_suppress_txq(dhd, _dhd_wlfc_entrypkt_fn, entry);
3412 			}
3413 #endif /* !BCMDBUS */
3414 		} /* while */
3415 
3416 		if (remainder != 0 && wlfc) {
3417 			/* trouble..., something is not right */
3418 			wlfc->stats.tlv_parse_failed++;
3419 		}
3420 	} /* if */
3421 
3422 	if (wlfc)
3423 		wlfc->stats.dhd_hdrpulls++;
3424 
3425 	dhd_os_wlfc_unblock(dhd);
3426 	return BCME_OK;
3427 }
3428 
3429 KERNEL_THREAD_RETURN_TYPE
dhd_wlfc_transfer_packets(void * data)3430 dhd_wlfc_transfer_packets(void *data)
3431 {
3432 	dhd_pub_t *dhdp = (dhd_pub_t *)data;
3433 	int ac, single_ac = 0, rc = BCME_OK;
3434 	dhd_wlfc_commit_info_t  commit_info;
3435 	athost_wl_status_info_t* ctx;
3436 	int bus_retry_count = 0;
3437 	int pkt_send = 0;
3438 	int pkt_send_per_ac = 0;
3439 
3440 	uint8 tx_map = 0; /* packets (send + in queue), Bitmask for 4 ACs + BC/MC */
3441 	uint8 rx_map = 0; /* received packets, Bitmask for 4 ACs + BC/MC */
3442 	uint8 packets_map = 0; /* packets in queue, Bitmask for 4 ACs + BC/MC */
3443 	bool no_credit = FALSE;
3444 
3445 	int lender;
3446 	int pkt_bound = WLFC_PACKET_BOUND;
3447 	int highest_lender_ac;
3448 
3449 	BCM_REFERENCE(highest_lender_ac);
3450 
3451 #if defined(DHD_WLFC_THREAD)
3452 	/* wait till someone wakeup me up, will change it at running time */
3453 #if defined(LINUX)
3454 	int wait_msec = msecs_to_jiffies(0xFFFFFFFF);
3455 #endif /* LINUX */
3456 #endif /* defined(DHD_WLFC_THREAD) */
3457 
3458 #if defined(DHD_WLFC_THREAD)
3459 	while (1) {
3460 		bus_retry_count = 0;
3461 		pkt_send = 0;
3462 		tx_map = 0;
3463 		rx_map = 0;
3464 		packets_map = 0;
3465 #if defined(LINUX)
3466 		wait_msec = wait_event_interruptible_timeout(dhdp->wlfc_wqhead,
3467 			dhdp->wlfc_thread_go, wait_msec);
3468 		if (kthread_should_stop()) {
3469 			break;
3470 		}
3471 		dhdp->wlfc_thread_go = FALSE;
3472 #endif /* LINUX */
3473 
3474 		dhd_os_wlfc_block(dhdp);
3475 #endif /* defined(DHD_WLFC_THREAD) */
3476 		ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3477 #if defined(DHD_WLFC_THREAD)
3478 		if (!ctx)
3479 			goto exit;
3480 #endif /* defined(DHD_WLFC_THREAD) */
3481 
3482 	memset(&commit_info, 0, sizeof(commit_info));
3483 
3484 	/*
3485 	Commit packets for regular AC traffic. Higher priority first.
3486 	First, use up FIFO credits available to each AC. Based on distribution
3487 	and credits left, borrow from other ACs as applicable
3488 
3489 	-NOTE:
3490 	If the bus between the host and firmware is overwhelmed by the
3491 	traffic from host, it is possible that higher priority traffic
3492 	starves the lower priority queue. If that occurs often, we may
3493 	have to employ weighted round-robin or ucode scheme to avoid
3494 	low priority packet starvation.
3495 	*/
3496 
3497 #ifdef BULK_DEQUEUE
3498 	pkt_bound = ctx->max_release_count;
3499 #endif
3500 
3501 	for (ac = AC_COUNT; ac >= 0; ac--) {
3502 		if (dhdp->wlfc_rxpkt_chk) {
3503 			/* check rx packet */
3504 			uint32 curr_t = OSL_SYSUPTIME(), delta;
3505 
3506 			delta = curr_t - ctx->rx_timestamp[ac];
3507 			if (delta < WLFC_RX_DETECTION_THRESHOLD_MS) {
3508 				rx_map |= (1 << ac);
3509 			}
3510 		}
3511 
3512 		if (ctx->pkt_cnt_per_ac[ac] == 0) {
3513 			continue;
3514 		}
3515 
3516 		tx_map |= (1 << ac);
3517 		single_ac = ac + 1;
3518 		pkt_send_per_ac = 0;
3519 		while ((FALSE == dhdp->proptxstatus_txoff) &&
3520 				(pkt_send_per_ac < pkt_bound)) {
3521 			/* packets from delayQ with less priority are fresh and
3522 			 * they'd need header and have no MAC entry
3523 			 */
3524 			no_credit = (ctx->FIFO_credit[ac] < 1);
3525 			if (dhdp->proptxstatus_credit_ignore ||
3526 				((ac == AC_COUNT) && !ctx->bcmc_credit_supported)) {
3527 				no_credit = FALSE;
3528 			}
3529 
3530 			lender = -1;
3531 #ifdef LIMIT_BORROW
3532 			if (no_credit && (ac < AC_COUNT) && (tx_map >= rx_map) &&
3533 				dhdp->wlfc_borrow_allowed) {
3534 				/* try borrow from lower priority */
3535 #ifdef BULK_DEQUEUE
3536 				/* Enable credit borrow from higher AC
3537 				 * to make packet chain longer
3538 				 */
3539 				highest_lender_ac = AC_COUNT;
3540 #else
3541 				highest_lender_ac = ac - 1;
3542 #endif /* BULK_DEQUEUE */
3543 				lender = _dhd_wlfc_borrow_credit(ctx, highest_lender_ac, ac, FALSE);
3544 				if (lender != -1) {
3545 					no_credit = FALSE;
3546 				}
3547 			}
3548 #endif /* LIMIT_BORROW */
3549 			commit_info.needs_hdr = 1;
3550 			commit_info.mac_entry = NULL;
3551 			commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3552 				&(commit_info.ac_fifo_credit_spent),
3553 				&(commit_info.needs_hdr),
3554 				&(commit_info.mac_entry),
3555 				no_credit);
3556 			commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3557 				eWLFC_PKTTYPE_SUPPRESSED;
3558 
3559 			if (commit_info.p == NULL) {
3560 #ifdef LIMIT_BORROW
3561 				if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3562 					_dhd_wlfc_return_credit(ctx, lender, ac);
3563 				}
3564 #endif
3565 				break;
3566 			}
3567 
3568 			if (!dhdp->proptxstatus_credit_ignore && (lender == -1)) {
3569 				ASSERT(ctx->FIFO_credit[ac] >= commit_info.ac_fifo_credit_spent);
3570 			}
3571 			/* here we can ensure have credit or no credit needed */
3572 			rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3573 				ctx->fcommit, ctx->commit_ctx);
3574 
3575 			/* Bus commits may fail (e.g. flow control); abort after retries */
3576 			if (rc == BCME_OK) {
3577 				pkt_send++;
3578 				pkt_send_per_ac++;
3579 				if (commit_info.ac_fifo_credit_spent && (lender == -1)) {
3580 					ctx->FIFO_credit[ac]--;
3581 				}
3582 #ifdef LIMIT_BORROW
3583 				else if (!commit_info.ac_fifo_credit_spent && (lender != -1) &&
3584 					dhdp->wlfc_borrow_allowed) {
3585 					_dhd_wlfc_return_credit(ctx, lender, ac);
3586 				}
3587 #endif
3588 			} else {
3589 #ifdef LIMIT_BORROW
3590 				if (lender != -1 && dhdp->wlfc_borrow_allowed) {
3591 					_dhd_wlfc_return_credit(ctx, lender, ac);
3592 				}
3593 #endif
3594 				bus_retry_count++;
3595 				if (bus_retry_count >= BUS_RETRIES) {
3596 					DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3597 					goto exit;
3598 				}
3599 			}
3600 		}
3601 
3602 		if (ctx->pkt_cnt_per_ac[ac]) {
3603 			packets_map |= (1 << ac);
3604 		}
3605 	}
3606 
3607 	if ((tx_map == 0) || dhdp->proptxstatus_credit_ignore) {
3608 		/* nothing send out or remain in queue */
3609 		rc = BCME_OK;
3610 		goto exit;
3611 	}
3612 
3613 	if (((tx_map & (tx_map - 1)) == 0) && (tx_map >= rx_map)) {
3614 		/* only one tx ac exist and no higher rx ac */
3615 		if ((single_ac == ctx->single_ac) && ctx->allow_credit_borrow) {
3616 			ac = single_ac - 1;
3617 		} else {
3618 			uint32 delta;
3619 			uint32 curr_t = OSL_SYSUPTIME();
3620 
3621 			if (single_ac != ctx->single_ac) {
3622 				/* new single ac traffic (first single ac or different single ac) */
3623 				ctx->allow_credit_borrow = 0;
3624 				ctx->single_ac_timestamp = curr_t;
3625 				ctx->single_ac = (uint8)single_ac;
3626 				rc = BCME_OK;
3627 				goto exit;
3628 			}
3629 			/* same ac traffic, check if it lasts enough time */
3630 			delta = curr_t - ctx->single_ac_timestamp;
3631 
3632 			if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
3633 				/* wait enough time, can borrow now */
3634 				ctx->allow_credit_borrow = 1;
3635 				ac = single_ac - 1;
3636 			} else {
3637 				rc = BCME_OK;
3638 				goto exit;
3639 			}
3640 		}
3641 	} else {
3642 		/* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
3643 		ctx->allow_credit_borrow = 0;
3644 		ctx->single_ac_timestamp = 0;
3645 		ctx->single_ac = 0;
3646 		rc = BCME_OK;
3647 		goto exit;
3648 	}
3649 
3650 	if (packets_map == 0) {
3651 		/* nothing to send, skip borrow */
3652 		rc = BCME_OK;
3653 		goto exit;
3654 	}
3655 
3656 	/* At this point, borrow all credits only for ac */
3657 	while (FALSE == dhdp->proptxstatus_txoff) {
3658 #ifdef LIMIT_BORROW
3659 		if (dhdp->wlfc_borrow_allowed) {
3660 			if ((lender = _dhd_wlfc_borrow_credit(ctx, AC_COUNT, ac, TRUE)) == -1) {
3661 				break;
3662 			}
3663 		}
3664 		else
3665 			break;
3666 #endif
3667 		commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
3668 			&(commit_info.ac_fifo_credit_spent),
3669 			&(commit_info.needs_hdr),
3670 			&(commit_info.mac_entry),
3671 			FALSE);
3672 		if (commit_info.p == NULL) {
3673 			/* before borrow only one ac exists and now this only ac is empty */
3674 #ifdef LIMIT_BORROW
3675 			_dhd_wlfc_return_credit(ctx, lender, ac);
3676 #endif
3677 			break;
3678 		}
3679 
3680 		commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
3681 			eWLFC_PKTTYPE_SUPPRESSED;
3682 
3683 		rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
3684 		     ctx->fcommit, ctx->commit_ctx);
3685 
3686 		/* Bus commits may fail (e.g. flow control); abort after retries */
3687 		if (rc == BCME_OK) {
3688 			pkt_send++;
3689 			if (commit_info.ac_fifo_credit_spent) {
3690 #ifndef LIMIT_BORROW
3691 				ctx->FIFO_credit[ac]--;
3692 #endif
3693 			} else {
3694 #ifdef LIMIT_BORROW
3695 				_dhd_wlfc_return_credit(ctx, lender, ac);
3696 #endif
3697 			}
3698 		} else {
3699 #ifdef LIMIT_BORROW
3700 			_dhd_wlfc_return_credit(ctx, lender, ac);
3701 #endif
3702 			bus_retry_count++;
3703 			if (bus_retry_count >= BUS_RETRIES) {
3704 				DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
3705 				goto exit;
3706 			}
3707 		}
3708 	}
3709 
3710 	BCM_REFERENCE(pkt_send);
3711 
3712 exit:
3713 #if defined(DHD_WLFC_THREAD)
3714 		dhd_os_wlfc_unblock(dhdp);
3715 #if defined(LINUX)
3716 		if (ctx && ctx->pkt_cnt_in_psq && pkt_send) {
3717 			wait_msec = msecs_to_jiffies(WLFC_THREAD_QUICK_RETRY_WAIT_MS);
3718 		} else {
3719 			wait_msec = msecs_to_jiffies(WLFC_THREAD_RETRY_WAIT_MS);
3720 		}
3721 #endif /* LINUX */
3722 	}
3723 	return 0;
3724 #else
3725 	return rc;
3726 #endif /* defined(DHD_WLFC_THREAD) */
3727 }
3728 
3729 /**
3730  * Enqueues a transmit packet in the next layer towards the dongle, eg the DBUS layer. Called by
3731  * eg dhd_sendpkt().
3732  *     @param[in] dhdp                  Pointer to public DHD structure
3733  *     @param[in] fcommit               Pointer to transmit function of next layer
3734  *     @param[in] commit_ctx            Opaque context used when calling next layer
3735  *     @param[in] pktbuf                Packet to send
3736  *     @param[in] need_toggle_host_if   If TRUE, resets flag ctx->toggle_host_if
3737  */
3738 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)3739 dhd_wlfc_commit_packets(dhd_pub_t *dhdp, f_commitpkt_t fcommit,
3740 	struct dhd_bus *commit_ctx, void *pktbuf, bool need_toggle_host_if)
3741 {
3742 	int rc = BCME_OK;
3743 	athost_wl_status_info_t* ctx;
3744 
3745 #if defined(DHD_WLFC_THREAD)
3746 	if (!pktbuf)
3747 		return BCME_OK;
3748 #endif /* defined(DHD_WLFC_THREAD) */
3749 
3750 	if ((dhdp == NULL) || (fcommit == NULL)) {
3751 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3752 		return BCME_BADARG;
3753 	}
3754 
3755 	dhd_os_wlfc_block(dhdp);
3756 
3757 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3758 		if (pktbuf) {
3759 			DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 0);
3760 		}
3761 		rc =  WLFC_UNSUPPORTED;
3762 		goto exit;
3763 	}
3764 
3765 	ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
3766 
3767 #ifdef BCMDBUS
3768 	if (!dhdp->up || (dhdp->busstate == DHD_BUS_DOWN)) {
3769 		if (pktbuf) {
3770 			PKTFREE(ctx->osh, pktbuf, TRUE);
3771 			rc = BCME_OK;
3772 		}
3773 		goto exit;
3774 	}
3775 #endif
3776 
3777 	if (dhdp->proptxstatus_module_ignore) {
3778 		if (pktbuf) {
3779 			uint32 htod = 0;
3780 			WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
3781 			_dhd_wlfc_pushheader(ctx, &pktbuf, FALSE, 0, 0, htod, 0, FALSE);
3782 			if (fcommit(commit_ctx, pktbuf)) {
3783 				/* free it if failed, otherwise do it in tx complete cb */
3784 				PKTFREE(ctx->osh, pktbuf, TRUE);
3785 			}
3786 			rc = BCME_OK;
3787 		}
3788 		goto exit;
3789 	}
3790 
3791 	if (pktbuf) {
3792 		int ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
3793 		ASSERT(ac <= AC_COUNT);
3794 		DHD_PKTTAG_WLFCPKT_SET(PKTTAG(pktbuf), 1);
3795 		/* en-queue the packets to respective queue. */
3796 		rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
3797 		if (rc) {
3798 			_dhd_wlfc_prec_drop(ctx->dhdp, (ac << 1), pktbuf, FALSE);
3799 		} else {
3800 			ctx->stats.pktin++;
3801 			ctx->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(pktbuf))][ac]++;
3802 		}
3803 	}
3804 
3805 	if (!ctx->fcommit) {
3806 		ctx->fcommit = fcommit;
3807 	} else {
3808 		ASSERT(ctx->fcommit == fcommit);
3809 	}
3810 	if (!ctx->commit_ctx) {
3811 		ctx->commit_ctx = commit_ctx;
3812 	} else {
3813 		ASSERT(ctx->commit_ctx == commit_ctx);
3814 	}
3815 
3816 #if defined(DHD_WLFC_THREAD)
3817 	_dhd_wlfc_thread_wakeup(dhdp);
3818 #else
3819 	dhd_wlfc_transfer_packets(dhdp);
3820 #endif /* defined(DHD_WLFC_THREAD) */
3821 
3822 exit:
3823 	dhd_os_wlfc_unblock(dhdp);
3824 	return rc;
3825 } /* dhd_wlfc_commit_packets */
3826 
3827 /**
3828  * Called when the (lower) DBUS layer indicates completion (succesfull or not) of a transmit packet
3829  */
3830 int
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)3831 dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
3832 {
3833 	athost_wl_status_info_t* wlfc;
3834 	wlfc_mac_descriptor_t *entry;
3835 	void* pout = NULL;
3836 	int rtn = BCME_OK;
3837 	if ((dhd == NULL) || (txp == NULL)) {
3838 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3839 		return BCME_BADARG;
3840 	}
3841 
3842 	bcm_pkt_validate_chk(txp, "_dhd_wlfc_compressed_txstatus_update");
3843 
3844 	dhd_os_wlfc_block(dhd);
3845 
3846 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
3847 		rtn = WLFC_UNSUPPORTED;
3848 		goto EXIT;
3849 	}
3850 
3851 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
3852 	if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
3853 #ifdef PROP_TXSTATUS_DEBUG
3854 		wlfc->stats.signal_only_pkts_freed++;
3855 #endif
3856 		/* is this a signal-only packet? */
3857 		_dhd_wlfc_pullheader(wlfc, txp);
3858 		PKTFREE(wlfc->osh, txp, TRUE);
3859 		goto EXIT;
3860 	}
3861 
3862 	entry = _dhd_wlfc_find_table_entry(wlfc, txp);
3863 	ASSERT(entry);
3864 
3865 	if (!success || dhd->proptxstatus_txstatus_ignore) {
3866 		WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
3867 			__FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
3868 		if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
3869 			_dhd_wlfc_hanger_poppkt(wlfc->hanger, WL_TXSTATUS_GET_HSLOT(
3870 				DHD_PKTTAG_H2DTAG(PKTTAG(txp))), &pout, TRUE);
3871 			ASSERT(txp == pout);
3872 		}
3873 
3874 		/* indicate failure and free the packet */
3875 		dhd_txcomplete(dhd, txp, success);
3876 
3877 		/* return the credit, if necessary */
3878 		_dhd_wlfc_return_implied_credit(wlfc, txp);
3879 
3880 		if (entry->transit_count)
3881 			entry->transit_count--;
3882 		if (entry->suppr_transit_count)
3883 			entry->suppr_transit_count--;
3884 		wlfc->pkt_cnt_in_drv[DHD_PKTTAG_IF(PKTTAG(txp))][DHD_PKTTAG_FIFO(PKTTAG(txp))]--;
3885 		wlfc->stats.pktout++;
3886 		PKTFREE(wlfc->osh, txp, TRUE);
3887 	} else {
3888 		/* bus confirmed pkt went to firmware side */
3889 		if (WLFC_GET_AFQ(dhd->wlfc_mode)) {
3890 			_dhd_wlfc_enque_afq(wlfc, txp);
3891 		} else {
3892 			int hslot = WL_TXSTATUS_GET_HSLOT(DHD_PKTTAG_H2DTAG(PKTTAG(txp)));
3893 			_dhd_wlfc_hanger_free_pkt(wlfc, hslot,
3894 				WLFC_HANGER_PKT_STATE_BUSRETURNED, -1);
3895 		}
3896 	}
3897 
3898 	ASSERT(entry->onbus_pkts_count > 0);
3899 	if (entry->onbus_pkts_count > 0)
3900 		entry->onbus_pkts_count--;
3901 	if (entry->suppressed &&
3902 		(!entry->onbus_pkts_count) &&
3903 		(!entry->suppr_transit_count))
3904 		entry->suppressed = FALSE;
3905 EXIT:
3906 	dhd_os_wlfc_unblock(dhd);
3907 	return rtn;
3908 } /* dhd_wlfc_txcomplete */
3909 
3910 int
dhd_wlfc_init(dhd_pub_t * dhd)3911 dhd_wlfc_init(dhd_pub_t *dhd)
3912 {
3913 	/* enable all signals & indicate host proptxstatus logic is active */
3914 	uint32 tlv, mode, fw_caps;
3915 	int ret = 0;
3916 
3917 	if (dhd == NULL) {
3918 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
3919 		return BCME_BADARG;
3920 	}
3921 
3922 	dhd_os_wlfc_block(dhd);
3923 	if (dhd->wlfc_enabled) {
3924 		DHD_INFO(("%s():%d, Already enabled!\n", __FUNCTION__, __LINE__));
3925 		dhd_os_wlfc_unblock(dhd);
3926 		return BCME_OK;
3927 	}
3928 	dhd->wlfc_enabled = TRUE;
3929 	dhd_os_wlfc_unblock(dhd);
3930 
3931 	tlv = WLFC_FLAGS_RSSI_SIGNALS |
3932 		WLFC_FLAGS_XONXOFF_SIGNALS |
3933 		WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
3934 		WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
3935 		WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
3936 
3937 	/* XXX dhd->wlfc_state = NULL; */
3938 	/* XXX ANDREY:may erase pointer to already created wlfc_state, PR#97824  */
3939 
3940 	/*
3941 	try to enable/disable signaling by sending "tlv" iovar. if that fails,
3942 	fallback to no flow control? Print a message for now.
3943 	*/
3944 
3945 	/* enable proptxtstatus signaling by default */
3946 	if (!dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
3947 		/*
3948 		Leaving the message for now, it should be removed after a while; once
3949 		the tlv situation is stable.
3950 		*/
3951 		DHD_INFO(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
3952 			dhd->wlfc_enabled?"enabled":"disabled", tlv));
3953 	}
3954 
3955 	mode = 0;
3956 
3957 	/* query caps */
3958 	ret = dhd_wl_ioctl_get_intiovar(dhd, "wlfc_mode", &fw_caps, WLC_GET_VAR, FALSE, 0);
3959 
3960 	if (!ret) {
3961 		DHD_INFO(("%s: query wlfc_mode succeed, fw_caps=0x%x\n", __FUNCTION__, fw_caps));
3962 
3963 		if (WLFC_IS_OLD_DEF(fw_caps)) {
3964 #ifdef BCMDBUS
3965 			mode = WLFC_MODE_HANGER;
3966 #else
3967 			/* enable proptxtstatus v2 by default */
3968 			mode = WLFC_MODE_AFQ;
3969 #endif /* BCMDBUS */
3970 		} else {
3971 			WLFC_SET_AFQ(mode, WLFC_GET_AFQ(fw_caps));
3972 #ifdef BCMDBUS
3973 			WLFC_SET_AFQ(mode, 0);
3974 #endif /* BCMDBUS */
3975 			WLFC_SET_REUSESEQ(mode, WLFC_GET_REUSESEQ(fw_caps));
3976 			WLFC_SET_REORDERSUPP(mode, WLFC_GET_REORDERSUPP(fw_caps));
3977 		}
3978 		ret = dhd_wl_ioctl_set_intiovar(dhd, "wlfc_mode", mode, WLC_SET_VAR, TRUE, 0);
3979 	}
3980 
3981 	dhd_os_wlfc_block(dhd);
3982 
3983 	dhd->wlfc_mode = 0;
3984 	if (ret >= 0) {
3985 		if (WLFC_IS_OLD_DEF(mode)) {
3986 			WLFC_SET_AFQ(dhd->wlfc_mode, (mode == WLFC_MODE_AFQ));
3987 		} else {
3988 			dhd->wlfc_mode = mode;
3989 		}
3990 	}
3991 
3992 	DHD_INFO(("dhd_wlfc_init(): wlfc_mode=0x%x, ret=%d\n", dhd->wlfc_mode, ret));
3993 #ifdef LIMIT_BORROW
3994 	dhd->wlfc_borrow_allowed = TRUE;
3995 #endif
3996 	dhd_os_wlfc_unblock(dhd);
3997 
3998 	if (dhd->plat_init)
3999 		dhd->plat_init((void *)dhd);
4000 
4001 	return BCME_OK;
4002 } /* dhd_wlfc_init */
4003 
4004 /** AMPDU host reorder specific function */
4005 int
dhd_wlfc_hostreorder_init(dhd_pub_t * dhd)4006 dhd_wlfc_hostreorder_init(dhd_pub_t *dhd)
4007 {
4008 	/* enable only ampdu hostreorder here */
4009 	uint32 tlv;
4010 
4011 	if (dhd == NULL) {
4012 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4013 		return BCME_BADARG;
4014 	}
4015 
4016 	DHD_TRACE(("%s():%d Enter\n", __FUNCTION__, __LINE__));
4017 
4018 	tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4019 
4020 	/* enable proptxtstatus signaling by default */
4021 	if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4022 		DHD_ERROR(("%s(): failed to enable/disable bdcv2 tlv signaling\n",
4023 			__FUNCTION__));
4024 	} else {
4025 		/*
4026 		Leaving the message for now, it should be removed after a while; once
4027 		the tlv situation is stable.
4028 		*/
4029 		DHD_ERROR(("%s(): successful bdcv2 tlv signaling, %d\n",
4030 			__FUNCTION__, tlv));
4031 	}
4032 
4033 	dhd_os_wlfc_block(dhd);
4034 	dhd->proptxstatus_mode = WLFC_ONLY_AMPDU_HOSTREORDER;
4035 	dhd_os_wlfc_unblock(dhd);
4036 	/* terence 20161229: enable ampdu_hostreorder if tlv enable hostreorder */
4037 	dhd_conf_set_intiovar(dhd, 0, WLC_SET_VAR, "ampdu_hostreorder", 1, 0, TRUE);
4038 
4039 	return BCME_OK;
4040 }
4041 
4042 int
dhd_wlfc_cleanup_txq(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)4043 dhd_wlfc_cleanup_txq(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
4044 {
4045 	if (dhd == NULL) {
4046 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4047 		return BCME_BADARG;
4048 	}
4049 
4050 	dhd_os_wlfc_block(dhd);
4051 
4052 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4053 		dhd_os_wlfc_unblock(dhd);
4054 		return WLFC_UNSUPPORTED;
4055 	}
4056 
4057 #ifndef BCMDBUS
4058 	_dhd_wlfc_cleanup_txq(dhd, fn, arg);
4059 #endif /* !BCMDBUS */
4060 
4061 	dhd_os_wlfc_unblock(dhd);
4062 
4063 	return BCME_OK;
4064 }
4065 
4066 /** release all packet resources */
4067 int
dhd_wlfc_cleanup(dhd_pub_t * dhd,f_processpkt_t fn,void * arg)4068 dhd_wlfc_cleanup(dhd_pub_t *dhd, f_processpkt_t fn, void *arg)
4069 {
4070 	if (dhd == NULL) {
4071 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4072 		return BCME_BADARG;
4073 	}
4074 
4075 	dhd_os_wlfc_block(dhd);
4076 
4077 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4078 		dhd_os_wlfc_unblock(dhd);
4079 		return WLFC_UNSUPPORTED;
4080 	}
4081 
4082 	_dhd_wlfc_cleanup(dhd, fn, arg);
4083 
4084 	dhd_os_wlfc_unblock(dhd);
4085 
4086 	return BCME_OK;
4087 }
4088 
4089 int
dhd_wlfc_deinit(dhd_pub_t * dhd)4090 dhd_wlfc_deinit(dhd_pub_t *dhd)
4091 {
4092 	/* cleanup all psq related resources */
4093 	athost_wl_status_info_t* wlfc;
4094 	uint32 tlv = 0;
4095 	uint32 hostreorder = 0;
4096 
4097 	if (dhd == NULL) {
4098 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4099 		return BCME_BADARG;
4100 	}
4101 
4102 	dhd_os_wlfc_block(dhd);
4103 	if (!dhd->wlfc_enabled) {
4104 		DHD_ERROR(("%s():%d, Already disabled!\n", __FUNCTION__, __LINE__));
4105 		dhd_os_wlfc_unblock(dhd);
4106 		return BCME_OK;
4107 	}
4108 
4109 	dhd->wlfc_enabled = FALSE;
4110 	dhd_os_wlfc_unblock(dhd);
4111 
4112 	/* query ampdu hostreorder */
4113 	(void) dhd_wl_ioctl_get_intiovar(dhd, "ampdu_hostreorder",
4114 		&hostreorder, WLC_GET_VAR, FALSE, 0);
4115 
4116 	if (hostreorder) {
4117 		tlv = WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4118 		DHD_ERROR(("%s():%d, maintain HOST RXRERODER flag in tvl\n",
4119 			__FUNCTION__, __LINE__));
4120 	}
4121 
4122 	/* Disable proptxtstatus signaling for deinit */
4123 	(void) dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0);
4124 
4125 	dhd_os_wlfc_block(dhd);
4126 
4127 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4128 		dhd_os_wlfc_unblock(dhd);
4129 		return WLFC_UNSUPPORTED;
4130 	}
4131 
4132 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4133 
4134 	_dhd_wlfc_cleanup(dhd, NULL, NULL);
4135 
4136 	if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4137 		int i;
4138 		wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
4139 		for (i = 0; i < h->max_items; i++) {
4140 			if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
4141 				_dhd_wlfc_hanger_free_pkt(wlfc, i,
4142 					WLFC_HANGER_PKT_STATE_COMPLETE, TRUE);
4143 			}
4144 		}
4145 
4146 		/* delete hanger */
4147 		_dhd_wlfc_hanger_delete(dhd, h);
4148 	}
4149 
4150 #if defined(BCMINTERNAL) && defined(OOO_DEBUG)
4151 	if (wlfc->log_buf) {
4152 		MFREE(dhd->osh, wlfc->log_buf, WLFC_LOG_BUF_SIZE);
4153 		wlfc->log_buf_offset = 0;
4154 		wlfc->log_buf_full = FALSE;
4155 	}
4156 #endif /* defined(BCMINTERNAL) && defined(OOO_DEBUG) */
4157 
4158 	/* free top structure */
4159 	DHD_OS_PREFREE(dhd, dhd->wlfc_state,
4160 		sizeof(athost_wl_status_info_t));
4161 	dhd->wlfc_state = NULL;
4162 	dhd->proptxstatus_mode = hostreorder ?
4163 		WLFC_ONLY_AMPDU_HOSTREORDER : WLFC_FCMODE_NONE;
4164 
4165 	dhd_os_wlfc_unblock(dhd);
4166 
4167 	if (dhd->plat_deinit)
4168 		dhd->plat_deinit((void *)dhd);
4169 	return BCME_OK;
4170 } /* dhd_wlfc_init */
4171 
4172 /**
4173  * Called on an interface event (WLC_E_IF) indicated by firmware
4174  *     @param[in] dhdp   Pointer to public DHD structure
4175  *     @param[in] action eg eWLFC_MAC_ENTRY_ACTION_UPDATE or eWLFC_MAC_ENTRY_ACTION_ADD
4176  */
dhd_wlfc_interface_event(dhd_pub_t * dhdp,uint8 action,uint8 ifid,uint8 iftype,uint8 * ea)4177 int dhd_wlfc_interface_event(dhd_pub_t *dhdp, uint8 action, uint8 ifid, uint8 iftype, uint8* ea)
4178 {
4179 	int rc;
4180 
4181 	if (dhdp == NULL) {
4182 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4183 		return BCME_BADARG;
4184 	}
4185 
4186 	dhd_os_wlfc_block(dhdp);
4187 
4188 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4189 		dhd_os_wlfc_unblock(dhdp);
4190 		return WLFC_UNSUPPORTED;
4191 	}
4192 
4193 	rc = _dhd_wlfc_interface_entry_update(dhdp->wlfc_state, action, ifid, iftype, ea);
4194 
4195 	dhd_os_wlfc_unblock(dhdp);
4196 	return rc;
4197 }
4198 
4199 /** 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)4200 int dhd_wlfc_FIFOcreditmap_event(dhd_pub_t *dhdp, uint8* event_data)
4201 {
4202 	int rc;
4203 
4204 	if (dhdp == NULL) {
4205 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4206 		return BCME_BADARG;
4207 	}
4208 
4209 	dhd_os_wlfc_block(dhdp);
4210 
4211 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4212 		dhd_os_wlfc_unblock(dhdp);
4213 		return WLFC_UNSUPPORTED;
4214 	}
4215 
4216 	rc = _dhd_wlfc_FIFOcreditmap_update(dhdp->wlfc_state, event_data);
4217 
4218 	dhd_os_wlfc_unblock(dhdp);
4219 
4220 	return rc;
4221 }
4222 #ifdef LIMIT_BORROW
dhd_wlfc_disable_credit_borrow_event(dhd_pub_t * dhdp,uint8 * event_data)4223 int dhd_wlfc_disable_credit_borrow_event(dhd_pub_t *dhdp, uint8* event_data)
4224 {
4225 	if (dhdp == NULL || event_data == NULL) {
4226 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4227 		return BCME_BADARG;
4228 	}
4229 	dhd_os_wlfc_block(dhdp);
4230 	dhdp->wlfc_borrow_allowed = (bool)(*(uint32 *)event_data);
4231 	dhd_os_wlfc_unblock(dhdp);
4232 
4233 	return BCME_OK;
4234 }
4235 #endif /* LIMIT_BORROW */
4236 
4237 /**
4238  * Called eg on receiving a WLC_E_BCMC_CREDIT_SUPPORT event from the dongle (broadcast/multicast
4239  * specific)
4240  */
dhd_wlfc_BCMCCredit_support_event(dhd_pub_t * dhdp)4241 int dhd_wlfc_BCMCCredit_support_event(dhd_pub_t *dhdp)
4242 {
4243 	int rc;
4244 
4245 	if (dhdp == NULL) {
4246 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4247 		return BCME_BADARG;
4248 	}
4249 
4250 	dhd_os_wlfc_block(dhdp);
4251 
4252 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4253 		dhd_os_wlfc_unblock(dhdp);
4254 		return WLFC_UNSUPPORTED;
4255 	}
4256 
4257 	rc = _dhd_wlfc_BCMCCredit_support_update(dhdp->wlfc_state);
4258 
4259 	dhd_os_wlfc_unblock(dhdp);
4260 	return rc;
4261 }
4262 
4263 /** debug specific function */
4264 int
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)4265 dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
4266 {
4267 	int i;
4268 	uint8* ea;
4269 	athost_wl_status_info_t* wlfc;
4270 	wlfc_hanger_t* h;
4271 	wlfc_mac_descriptor_t* mac_table;
4272 	wlfc_mac_descriptor_t* interfaces;
4273 	char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
4274 
4275 	if (!dhdp || !strbuf) {
4276 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4277 		return BCME_BADARG;
4278 	}
4279 
4280 	dhd_os_wlfc_block(dhdp);
4281 
4282 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4283 		dhd_os_wlfc_unblock(dhdp);
4284 		return WLFC_UNSUPPORTED;
4285 	}
4286 
4287 	wlfc = (athost_wl_status_info_t*)dhdp->wlfc_state;
4288 
4289 	h = (wlfc_hanger_t*)wlfc->hanger;
4290 	if (h == NULL) {
4291 		bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4292 	}
4293 
4294 	mac_table = wlfc->destination_entries.nodes;
4295 	interfaces = wlfc->destination_entries.interfaces;
4296 	bcm_bprintf(strbuf, "---- wlfc stats ----\n");
4297 
4298 	if (!WLFC_GET_AFQ(dhdp->wlfc_mode)) {
4299 		h = (wlfc_hanger_t*)wlfc->hanger;
4300 		if (h == NULL) {
4301 			bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
4302 		} else {
4303 			bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
4304 				"f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
4305 				h->pushed,
4306 				h->popped,
4307 				h->failed_to_push,
4308 				h->failed_to_pop,
4309 				h->failed_slotfind,
4310 				(h->pushed - h->popped));
4311 		}
4312 	}
4313 
4314 	bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
4315 		"(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
4316 		wlfc->stats.tlv_parse_failed,
4317 		wlfc->stats.credit_request_failed,
4318 		wlfc->stats.mac_update_failed,
4319 		wlfc->stats.psmode_update_failed,
4320 		wlfc->stats.delayq_full_error,
4321 		wlfc->stats.rollback_failed);
4322 
4323 	bcm_bprintf(strbuf, "PKTS (init_credit,credit,sent,drop_d,drop_s,outoforder) "
4324 		"(AC0[%d,%d,%d,%d,%d,%d],AC1[%d,%d,%d,%d,%d,%d],AC2[%d,%d,%d,%d,%d,%d],"
4325 		"AC3[%d,%d,%d,%d,%d,%d],BC_MC[%d,%d,%d,%d,%d,%d])\n",
4326 		wlfc->Init_FIFO_credit[0], wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
4327 		wlfc->stats.drop_pkts[0], wlfc->stats.drop_pkts[1], wlfc->stats.ooo_pkts[0],
4328 		wlfc->Init_FIFO_credit[1], wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
4329 		wlfc->stats.drop_pkts[2], wlfc->stats.drop_pkts[3], wlfc->stats.ooo_pkts[1],
4330 		wlfc->Init_FIFO_credit[2], wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
4331 		wlfc->stats.drop_pkts[4], wlfc->stats.drop_pkts[5], wlfc->stats.ooo_pkts[2],
4332 		wlfc->Init_FIFO_credit[3], wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
4333 		wlfc->stats.drop_pkts[6], wlfc->stats.drop_pkts[7], wlfc->stats.ooo_pkts[3],
4334 		wlfc->Init_FIFO_credit[4], wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4],
4335 		wlfc->stats.drop_pkts[8], wlfc->stats.drop_pkts[9], wlfc->stats.ooo_pkts[4]);
4336 
4337 	bcm_bprintf(strbuf, "\n");
4338 	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4339 		if (interfaces[i].occupied) {
4340 			char* iftype_desc;
4341 
4342 			if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
4343 				iftype_desc = "<Unknown";
4344 			else
4345 				iftype_desc = iftypes[interfaces[i].iftype];
4346 
4347 			ea = interfaces[i].ea;
4348 			bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
4349 				"["MACDBG"], if:%d, type: %s "
4350 				"netif_flow_control:%s\n", i,
4351 				MAC2STRDBG(ea), interfaces[i].interface_id,
4352 				iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
4353 				? " OFF":" ON"));
4354 
4355 			bcm_bprintf(strbuf, "INTERFACE[%d].PSQ(len,state,credit),"
4356 				"(trans,supp_trans,onbus)"
4357 				"= (%d,%s,%d),(%d,%d,%d)\n",
4358 				i,
4359 				interfaces[i].psq.n_pkts_tot,
4360 				((interfaces[i].state ==
4361 				WLFC_STATE_OPEN) ? "OPEN":"CLOSE"),
4362 				interfaces[i].requested_credit,
4363 				interfaces[i].transit_count,
4364 				interfaces[i].suppr_transit_count,
4365 				interfaces[i].onbus_pkts_count);
4366 
4367 			bcm_bprintf(strbuf, "INTERFACE[%d].PSQ"
4368 				"(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4369 				"(delay3,sup3,afq3),(delay4,sup4,afq4) = (%d,%d,%d),"
4370 				"(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4371 				i,
4372 				interfaces[i].psq.q[0].n_pkts,
4373 				interfaces[i].psq.q[1].n_pkts,
4374 				interfaces[i].afq.q[0].n_pkts,
4375 				interfaces[i].psq.q[2].n_pkts,
4376 				interfaces[i].psq.q[3].n_pkts,
4377 				interfaces[i].afq.q[1].n_pkts,
4378 				interfaces[i].psq.q[4].n_pkts,
4379 				interfaces[i].psq.q[5].n_pkts,
4380 				interfaces[i].afq.q[2].n_pkts,
4381 				interfaces[i].psq.q[6].n_pkts,
4382 				interfaces[i].psq.q[7].n_pkts,
4383 				interfaces[i].afq.q[3].n_pkts,
4384 				interfaces[i].psq.q[8].n_pkts,
4385 				interfaces[i].psq.q[9].n_pkts,
4386 				interfaces[i].afq.q[4].n_pkts);
4387 		}
4388 	}
4389 
4390 	bcm_bprintf(strbuf, "\n");
4391 	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4392 		if (mac_table[i].occupied) {
4393 			ea = mac_table[i].ea;
4394 			bcm_bprintf(strbuf, "MAC_table[%d].ea = "
4395 				"["MACDBG"], if:%d \n", i,
4396 				MAC2STRDBG(ea),	mac_table[i].interface_id);
4397 
4398 			bcm_bprintf(strbuf, "MAC_table[%d].PSQ(len,state,credit),"
4399 				"(trans,supp_trans,onbus)"
4400 				"= (%d,%s,%d),(%d,%d,%d)\n",
4401 				i,
4402 				mac_table[i].psq.n_pkts_tot,
4403 				((mac_table[i].state ==
4404 				WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
4405 				mac_table[i].requested_credit,
4406 				mac_table[i].transit_count,
4407 				mac_table[i].suppr_transit_count,
4408 				mac_table[i].onbus_pkts_count);
4409 #ifdef PROP_TXSTATUS_DEBUG
4410 			bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
4411 				i, mac_table[i].opened_ct, mac_table[i].closed_ct);
4412 #endif
4413 			bcm_bprintf(strbuf, "MAC_table[%d].PSQ"
4414 				"(delay0,sup0,afq0),(delay1,sup1,afq1),(delay2,sup2,afq2),"
4415 				"(delay3,sup3,afq3),(delay4,sup4,afq4) =(%d,%d,%d),"
4416 				"(%d,%d,%d),(%d,%d,%d),(%d,%d,%d),(%d,%d,%d)\n",
4417 				i,
4418 				mac_table[i].psq.q[0].n_pkts,
4419 				mac_table[i].psq.q[1].n_pkts,
4420 				mac_table[i].afq.q[0].n_pkts,
4421 				mac_table[i].psq.q[2].n_pkts,
4422 				mac_table[i].psq.q[3].n_pkts,
4423 				mac_table[i].afq.q[1].n_pkts,
4424 				mac_table[i].psq.q[4].n_pkts,
4425 				mac_table[i].psq.q[5].n_pkts,
4426 				mac_table[i].afq.q[2].n_pkts,
4427 				mac_table[i].psq.q[6].n_pkts,
4428 				mac_table[i].psq.q[7].n_pkts,
4429 				mac_table[i].afq.q[3].n_pkts,
4430 				mac_table[i].psq.q[8].n_pkts,
4431 				mac_table[i].psq.q[9].n_pkts,
4432 				mac_table[i].afq.q[4].n_pkts);
4433 
4434 		}
4435 	}
4436 
4437 #ifdef PROP_TXSTATUS_DEBUG
4438 	{
4439 		int avg;
4440 		int moving_avg = 0;
4441 		int moving_samples;
4442 
4443 		if (wlfc->stats.latency_sample_count) {
4444 			moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
4445 
4446 			for (i = 0; i < moving_samples; i++)
4447 				moving_avg += wlfc->stats.deltas[i];
4448 			moving_avg /= moving_samples;
4449 
4450 			avg = (100 * wlfc->stats.total_status_latency) /
4451 				wlfc->stats.latency_sample_count;
4452 			bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
4453 				"(%d.%d, %03d, %03d)\n",
4454 				moving_samples, avg/100, (avg - (avg/100)*100),
4455 				wlfc->stats.latency_most_recent,
4456 				moving_avg);
4457 		}
4458 	}
4459 
4460 	bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
4461 		"back = (%d,%d,%d,%d,%d,%d)\n",
4462 		wlfc->stats.fifo_credits_sent[0],
4463 		wlfc->stats.fifo_credits_sent[1],
4464 		wlfc->stats.fifo_credits_sent[2],
4465 		wlfc->stats.fifo_credits_sent[3],
4466 		wlfc->stats.fifo_credits_sent[4],
4467 		wlfc->stats.fifo_credits_sent[5],
4468 
4469 		wlfc->stats.fifo_credits_back[0],
4470 		wlfc->stats.fifo_credits_back[1],
4471 		wlfc->stats.fifo_credits_back[2],
4472 		wlfc->stats.fifo_credits_back[3],
4473 		wlfc->stats.fifo_credits_back[4],
4474 		wlfc->stats.fifo_credits_back[5]);
4475 	{
4476 		uint32 fifo_cr_sent = 0;
4477 		uint32 fifo_cr_acked = 0;
4478 		uint32 request_cr_sent = 0;
4479 		uint32 request_cr_ack = 0;
4480 		uint32 bc_mc_cr_ack = 0;
4481 
4482 		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
4483 			fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
4484 		}
4485 
4486 		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
4487 			fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
4488 		}
4489 
4490 		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4491 			if (wlfc->destination_entries.nodes[i].occupied) {
4492 				request_cr_sent +=
4493 					wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
4494 			}
4495 		}
4496 		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4497 			if (wlfc->destination_entries.interfaces[i].occupied) {
4498 				request_cr_sent +=
4499 				wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
4500 			}
4501 		}
4502 		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
4503 			if (wlfc->destination_entries.nodes[i].occupied) {
4504 				request_cr_ack +=
4505 					wlfc->destination_entries.nodes[i].dstncredit_acks;
4506 			}
4507 		}
4508 		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4509 			if (wlfc->destination_entries.interfaces[i].occupied) {
4510 				request_cr_ack +=
4511 					wlfc->destination_entries.interfaces[i].dstncredit_acks;
4512 			}
4513 		}
4514 		bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
4515 			"other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
4516 			fifo_cr_sent, fifo_cr_acked,
4517 			request_cr_sent, request_cr_ack,
4518 			wlfc->destination_entries.other.dstncredit_acks,
4519 			bc_mc_cr_ack,
4520 			wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
4521 	}
4522 #endif /* PROP_TXSTATUS_DEBUG */
4523 	bcm_bprintf(strbuf, "\n");
4524 	bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull,out),"
4525 		"(dropped,hdr_only,wlc_tossed,wlc_dropped,wlc_exptime)"
4526 		"(freed,free_err,rollback)) = "
4527 		"((%d,%d,%d,%d,%d),(%d,%d,%d,%d,%d),(%d,%d,%d))\n",
4528 		wlfc->stats.pktin,
4529 		wlfc->stats.pkt2bus,
4530 		wlfc->stats.txstatus_in,
4531 		wlfc->stats.dhd_hdrpulls,
4532 		wlfc->stats.pktout,
4533 
4534 		wlfc->stats.pktdropped,
4535 		wlfc->stats.wlfc_header_only_pkt,
4536 		wlfc->stats.wlc_tossed_pkts,
4537 		wlfc->stats.pkt_dropped,
4538 		wlfc->stats.pkt_exptime,
4539 
4540 		wlfc->stats.pkt_freed,
4541 		wlfc->stats.pkt_free_err, wlfc->stats.rollback);
4542 
4543 	bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
4544 		"((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
4545 		wlfc->stats.d11_suppress,
4546 		wlfc->stats.wl_suppress,
4547 		wlfc->stats.bad_suppress,
4548 
4549 		wlfc->stats.psq_d11sup_enq,
4550 		wlfc->stats.psq_wlsup_enq,
4551 		wlfc->stats.psq_hostq_enq,
4552 		wlfc->stats.mac_handle_notfound,
4553 
4554 		wlfc->stats.psq_d11sup_retx,
4555 		wlfc->stats.psq_wlsup_retx,
4556 		wlfc->stats.psq_hostq_retx);
4557 
4558 	bcm_bprintf(strbuf, "wlfc- cleanup(txq,psq,fw) = (%d,%d,%d)\n",
4559 		wlfc->stats.cleanup_txq_cnt,
4560 		wlfc->stats.cleanup_psq_cnt,
4561 		wlfc->stats.cleanup_fw_cnt);
4562 
4563 	bcm_bprintf(strbuf, "wlfc- generic error: %d\n", wlfc->stats.generic_error);
4564 
4565 	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
4566 		bcm_bprintf(strbuf, "wlfc- if[%d], pkt_cnt_in_q/AC[0-4] = (%d,%d,%d,%d,%d)\n", i,
4567 			wlfc->pkt_cnt_in_q[i][0],
4568 			wlfc->pkt_cnt_in_q[i][1],
4569 			wlfc->pkt_cnt_in_q[i][2],
4570 			wlfc->pkt_cnt_in_q[i][3],
4571 			wlfc->pkt_cnt_in_q[i][4]);
4572 	}
4573 	bcm_bprintf(strbuf, "\n");
4574 
4575 	dhd_os_wlfc_unblock(dhdp);
4576 	return BCME_OK;
4577 } /* dhd_wlfc_dump */
4578 
dhd_wlfc_clear_counts(dhd_pub_t * dhd)4579 int dhd_wlfc_clear_counts(dhd_pub_t *dhd)
4580 {
4581 	athost_wl_status_info_t* wlfc;
4582 	wlfc_hanger_t* hanger;
4583 
4584 	if (dhd == NULL) {
4585 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4586 		return BCME_BADARG;
4587 	}
4588 
4589 	dhd_os_wlfc_block(dhd);
4590 
4591 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4592 		dhd_os_wlfc_unblock(dhd);
4593 		return WLFC_UNSUPPORTED;
4594 	}
4595 
4596 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4597 
4598 	memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
4599 
4600 	if (!WLFC_GET_AFQ(dhd->wlfc_mode)) {
4601 		hanger = (wlfc_hanger_t*)wlfc->hanger;
4602 
4603 		hanger->pushed = 0;
4604 		hanger->popped = 0;
4605 		hanger->failed_slotfind = 0;
4606 		hanger->failed_to_pop = 0;
4607 		hanger->failed_to_push = 0;
4608 	}
4609 
4610 	dhd_os_wlfc_unblock(dhd);
4611 
4612 	return BCME_OK;
4613 }
4614 
4615 /** returns TRUE if flow control is enabled */
dhd_wlfc_get_enable(dhd_pub_t * dhd,bool * val)4616 int dhd_wlfc_get_enable(dhd_pub_t *dhd, bool *val)
4617 {
4618 	if (!dhd || !val) {
4619 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4620 		return BCME_BADARG;
4621 	}
4622 
4623 	dhd_os_wlfc_block(dhd);
4624 
4625 	*val = dhd->wlfc_enabled;
4626 
4627 	dhd_os_wlfc_unblock(dhd);
4628 
4629 	return BCME_OK;
4630 }
4631 
4632 /** Called via an IOVAR */
dhd_wlfc_get_mode(dhd_pub_t * dhd,int * val)4633 int dhd_wlfc_get_mode(dhd_pub_t *dhd, int *val)
4634 {
4635 	if (!dhd || !val) {
4636 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4637 		return BCME_BADARG;
4638 	}
4639 
4640 	dhd_os_wlfc_block(dhd);
4641 
4642 	*val = dhd->wlfc_state ? dhd->proptxstatus_mode : 0;
4643 
4644 	dhd_os_wlfc_unblock(dhd);
4645 
4646 	return BCME_OK;
4647 }
4648 
4649 /** Called via an IOVAR */
dhd_wlfc_set_mode(dhd_pub_t * dhd,int val)4650 int dhd_wlfc_set_mode(dhd_pub_t *dhd, int val)
4651 {
4652 	if (!dhd) {
4653 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4654 		return BCME_BADARG;
4655 	}
4656 
4657 	dhd_os_wlfc_block(dhd);
4658 
4659 	if (dhd->wlfc_state) {
4660 		dhd->proptxstatus_mode = val & 0xff;
4661 	}
4662 
4663 	dhd_os_wlfc_unblock(dhd);
4664 
4665 	return BCME_OK;
4666 }
4667 
4668 /** Called when rx frame is received from the dongle */
dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd,void * pktbuf)4669 bool dhd_wlfc_is_header_only_pkt(dhd_pub_t * dhd, void *pktbuf)
4670 {
4671 	athost_wl_status_info_t* wlfc;
4672 	bool rc = FALSE;
4673 
4674 	if (dhd == NULL) {
4675 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4676 		return FALSE;
4677 	}
4678 
4679 	dhd_os_wlfc_block(dhd);
4680 
4681 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4682 		dhd_os_wlfc_unblock(dhd);
4683 		return FALSE;
4684 	}
4685 
4686 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4687 
4688 	if (PKTLEN(wlfc->osh, pktbuf) == 0) {
4689 		wlfc->stats.wlfc_header_only_pkt++;
4690 		rc = TRUE;
4691 	}
4692 
4693 	dhd_os_wlfc_unblock(dhd);
4694 
4695 	return rc;
4696 }
4697 
dhd_wlfc_flowcontrol(dhd_pub_t * dhdp,bool state,bool bAcquireLock)4698 int dhd_wlfc_flowcontrol(dhd_pub_t *dhdp, bool state, bool bAcquireLock)
4699 {
4700 	if (dhdp == NULL) {
4701 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4702 		return BCME_BADARG;
4703 	}
4704 
4705 	if (bAcquireLock) {
4706 		dhd_os_wlfc_block(dhdp);
4707 	}
4708 
4709 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE) ||
4710 		dhdp->proptxstatus_module_ignore) {
4711 		if (bAcquireLock) {
4712 			dhd_os_wlfc_unblock(dhdp);
4713 		}
4714 		return WLFC_UNSUPPORTED;
4715 	}
4716 
4717 	if (state != dhdp->proptxstatus_txoff) {
4718 		dhdp->proptxstatus_txoff = state;
4719 	}
4720 
4721 	if (bAcquireLock) {
4722 		dhd_os_wlfc_unblock(dhdp);
4723 	}
4724 
4725 	return BCME_OK;
4726 }
4727 
4728 /** Called when eg an rx frame is received from the dongle */
dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd,uint8 prio)4729 int dhd_wlfc_save_rxpath_ac_time(dhd_pub_t * dhd, uint8 prio)
4730 {
4731 	athost_wl_status_info_t* wlfc;
4732 	int rx_path_ac = -1;
4733 
4734 	if ((dhd == NULL) || (prio >= NUMPRIO)) {
4735 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4736 		return BCME_BADARG;
4737 	}
4738 
4739 	dhd_os_wlfc_block(dhd);
4740 
4741 	if (!dhd->wlfc_rxpkt_chk) {
4742 		dhd_os_wlfc_unblock(dhd);
4743 		return BCME_OK;
4744 	}
4745 
4746 	if (!dhd->wlfc_state || (dhd->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4747 		dhd_os_wlfc_unblock(dhd);
4748 		return WLFC_UNSUPPORTED;
4749 	}
4750 
4751 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
4752 
4753 	rx_path_ac = prio2fifo[prio];
4754 	wlfc->rx_timestamp[rx_path_ac] = OSL_SYSUPTIME();
4755 
4756 	dhd_os_wlfc_unblock(dhd);
4757 
4758 	return BCME_OK;
4759 }
4760 
4761 /** called via an IOVAR */
dhd_wlfc_get_module_ignore(dhd_pub_t * dhd,int * val)4762 int dhd_wlfc_get_module_ignore(dhd_pub_t *dhd, int *val)
4763 {
4764 	if (!dhd || !val) {
4765 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4766 		return BCME_BADARG;
4767 	}
4768 
4769 	dhd_os_wlfc_block(dhd);
4770 
4771 	*val = dhd->proptxstatus_module_ignore;
4772 
4773 	dhd_os_wlfc_unblock(dhd);
4774 
4775 	return BCME_OK;
4776 }
4777 
4778 /** called via an IOVAR */
dhd_wlfc_set_module_ignore(dhd_pub_t * dhd,int val)4779 int dhd_wlfc_set_module_ignore(dhd_pub_t *dhd, int val)
4780 {
4781 	uint32 tlv = 0;
4782 	bool bChanged = FALSE;
4783 
4784 	if (!dhd) {
4785 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4786 		return BCME_BADARG;
4787 	}
4788 
4789 	dhd_os_wlfc_block(dhd);
4790 
4791 	if ((bool)val != dhd->proptxstatus_module_ignore) {
4792 		dhd->proptxstatus_module_ignore = (val != 0);
4793 		/* force txstatus_ignore sync with proptxstatus_module_ignore */
4794 		dhd->proptxstatus_txstatus_ignore = dhd->proptxstatus_module_ignore;
4795 		if (FALSE == dhd->proptxstatus_module_ignore) {
4796 			tlv = WLFC_FLAGS_RSSI_SIGNALS |
4797 				WLFC_FLAGS_XONXOFF_SIGNALS |
4798 				WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
4799 				WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
4800 		}
4801 		/* always enable host reorder */
4802 		tlv |= WLFC_FLAGS_HOST_RXRERODER_ACTIVE;
4803 		bChanged = TRUE;
4804 	}
4805 
4806 	dhd_os_wlfc_unblock(dhd);
4807 
4808 	if (bChanged) {
4809 		/* select enable proptxtstatus signaling */
4810 		if (dhd_wl_ioctl_set_intiovar(dhd, "tlv", tlv, WLC_SET_VAR, TRUE, 0)) {
4811 			DHD_ERROR(("%s: failed to set bdcv2 tlv signaling to 0x%x\n",
4812 				__FUNCTION__, tlv));
4813 		} else {
4814 			DHD_ERROR(("%s: successfully set bdcv2 tlv signaling to 0x%x\n",
4815 				__FUNCTION__, tlv));
4816 		}
4817 	}
4818 
4819 #if defined(DHD_WLFC_THREAD)
4820 	_dhd_wlfc_thread_wakeup(dhd);
4821 #endif /* defined(DHD_WLFC_THREAD) */
4822 
4823 	return BCME_OK;
4824 }
4825 
4826 /** called via an IOVAR */
dhd_wlfc_get_credit_ignore(dhd_pub_t * dhd,int * val)4827 int dhd_wlfc_get_credit_ignore(dhd_pub_t *dhd, int *val)
4828 {
4829 	if (!dhd || !val) {
4830 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4831 		return BCME_BADARG;
4832 	}
4833 
4834 	dhd_os_wlfc_block(dhd);
4835 
4836 	*val = dhd->proptxstatus_credit_ignore;
4837 
4838 	dhd_os_wlfc_unblock(dhd);
4839 
4840 	return BCME_OK;
4841 }
4842 
4843 /** called via an IOVAR */
dhd_wlfc_set_credit_ignore(dhd_pub_t * dhd,int val)4844 int dhd_wlfc_set_credit_ignore(dhd_pub_t *dhd, int val)
4845 {
4846 	if (!dhd) {
4847 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4848 		return BCME_BADARG;
4849 	}
4850 
4851 	dhd_os_wlfc_block(dhd);
4852 
4853 	dhd->proptxstatus_credit_ignore = (val != 0);
4854 
4855 	dhd_os_wlfc_unblock(dhd);
4856 
4857 	return BCME_OK;
4858 }
4859 
4860 /** called via an IOVAR */
dhd_wlfc_get_txstatus_ignore(dhd_pub_t * dhd,int * val)4861 int dhd_wlfc_get_txstatus_ignore(dhd_pub_t *dhd, int *val)
4862 {
4863 	if (!dhd || !val) {
4864 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4865 		return BCME_BADARG;
4866 	}
4867 
4868 	dhd_os_wlfc_block(dhd);
4869 
4870 	*val = dhd->proptxstatus_txstatus_ignore;
4871 
4872 	dhd_os_wlfc_unblock(dhd);
4873 
4874 	return BCME_OK;
4875 }
4876 
4877 /** called via an IOVAR */
dhd_wlfc_set_txstatus_ignore(dhd_pub_t * dhd,int val)4878 int dhd_wlfc_set_txstatus_ignore(dhd_pub_t *dhd, int val)
4879 {
4880 	if (!dhd) {
4881 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4882 		return BCME_BADARG;
4883 	}
4884 
4885 	dhd_os_wlfc_block(dhd);
4886 
4887 	dhd->proptxstatus_txstatus_ignore = (val != 0);
4888 
4889 	dhd_os_wlfc_unblock(dhd);
4890 
4891 	return BCME_OK;
4892 }
4893 
4894 /** called via an IOVAR */
dhd_wlfc_get_rxpkt_chk(dhd_pub_t * dhd,int * val)4895 int dhd_wlfc_get_rxpkt_chk(dhd_pub_t *dhd, int *val)
4896 {
4897 	if (!dhd || !val) {
4898 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4899 		return BCME_BADARG;
4900 	}
4901 
4902 	dhd_os_wlfc_block(dhd);
4903 
4904 	*val = dhd->wlfc_rxpkt_chk;
4905 
4906 	dhd_os_wlfc_unblock(dhd);
4907 
4908 	return BCME_OK;
4909 }
4910 
4911 /** called via an IOVAR */
dhd_wlfc_set_rxpkt_chk(dhd_pub_t * dhd,int val)4912 int dhd_wlfc_set_rxpkt_chk(dhd_pub_t *dhd, int val)
4913 {
4914 	if (!dhd) {
4915 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4916 		return BCME_BADARG;
4917 	}
4918 
4919 	dhd_os_wlfc_block(dhd);
4920 
4921 	dhd->wlfc_rxpkt_chk = (val != 0);
4922 
4923 	dhd_os_wlfc_unblock(dhd);
4924 
4925 	return BCME_OK;
4926 }
4927 
dhd_txpkt_log_and_dump(dhd_pub_t * dhdp,void * pkt,uint16 * pktfate_status)4928 int dhd_txpkt_log_and_dump(dhd_pub_t *dhdp, void* pkt, uint16 *pktfate_status)
4929 {
4930 	uint32 pktid;
4931 	uint32 pktlen = PKTLEN(dhdp->osh, pkt);
4932 	uint8 *pktdata = PKTDATA(dhdp->osh, pkt);
4933 #ifdef BDC
4934 	struct bdc_header *bdch;
4935 	uint32 bdc_len;
4936 #endif /* BDC */
4937 	uint8 ifidx = DHD_PKTTAG_IF(PKTTAG(pkt));
4938 	uint8 hcnt = WL_TXSTATUS_GET_FREERUNCTR(DHD_PKTTAG_H2DTAG(PKTTAG(pkt)));
4939 	uint8 fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pkt));
4940 
4941 	if (!pkt) {
4942 		DHD_ERROR(("Error: %s():%d\n", __FUNCTION__, __LINE__));
4943 		return BCME_BADARG;
4944 	}
4945 	pktid = (ifidx << DHD_PKTID_IF_SHIFT) | (fifo_id << DHD_PKTID_FIFO_SHIFT) | hcnt;
4946 #ifdef BDC
4947 	bdch = (struct bdc_header *)pktdata;
4948 	bdc_len = BDC_HEADER_LEN + (bdch->dataOffset << DHD_WORD_TO_LEN_SHIFT);
4949 	pktlen -= bdc_len;
4950 	pktdata = pktdata + bdc_len;
4951 #endif /* BDC */
4952 	dhd_handle_pktdata(dhdp, ifidx, pkt, pktdata, pktid, pktlen,
4953 		pktfate_status, NULL, TRUE, FALSE, TRUE);
4954 	return BCME_OK;
4955 }
4956 
4957 #ifdef PROPTX_MAXCOUNT
dhd_wlfc_update_maxcount(dhd_pub_t * dhdp,uint8 ifid,int maxcount)4958 int dhd_wlfc_update_maxcount(dhd_pub_t *dhdp, uint8 ifid, int maxcount)
4959 {
4960 	athost_wl_status_info_t* ctx;
4961 	int rc = 0;
4962 
4963 	if (dhdp == NULL) {
4964 		DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__));
4965 		return BCME_BADARG;
4966 	}
4967 
4968 	dhd_os_wlfc_block(dhdp);
4969 
4970 	if (!dhdp->wlfc_state || (dhdp->proptxstatus_mode == WLFC_FCMODE_NONE)) {
4971 		rc = WLFC_UNSUPPORTED;
4972 		goto exit;
4973 	}
4974 
4975 	if (ifid >= WLFC_MAX_IFNUM) {
4976 		DHD_ERROR(("%s: bad ifid\n", __FUNCTION__));
4977 		rc = BCME_BADARG;
4978 		goto exit;
4979 	}
4980 
4981 	ctx = (athost_wl_status_info_t*)dhdp->wlfc_state;
4982 	ctx->destination_entries.interfaces[ifid].transit_maxcount = maxcount;
4983 exit:
4984 	dhd_os_wlfc_unblock(dhdp);
4985 	return rc;
4986 }
4987 #endif /* PROPTX_MAXCOUNT */
4988 #endif /* PROP_TXSTATUS */
4989