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