• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * DHD PROP_TXSTATUS Module.
3  *
4  * Copyright (C) 1999-2013, Broadcom Corporation
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  * $Id: dhd_wlfc.c 412994 2013-07-17 12:38:03Z $
25  *
26  */
27 
28 #include <typedefs.h>
29 #include <osl.h>
30 
31 #include <bcmutils.h>
32 #include <bcmendian.h>
33 
34 #include <dngl_stats.h>
35 #include <dhd.h>
36 
37 #include <dhd_bus.h>
38 #include <dhd_dbg.h>
39 
40 #ifdef PROP_TXSTATUS
41 #include <wlfc_proto.h>
42 #include <dhd_wlfc.h>
43 #endif
44 
45 
46 
47 
48 #define BUS_RETRIES 1	/* # of retries before aborting a bus tx operation */
49 
50 #ifdef PROP_TXSTATUS
51 typedef struct dhd_wlfc_commit_info {
52 	uint8					needs_hdr;
53 	uint8					ac_fifo_credit_spent;
54 	ewlfc_packet_state_t	pkt_type;
55 	wlfc_mac_descriptor_t*	mac_entry;
56 	void*					p;
57 } dhd_wlfc_commit_info_t;
58 #endif /* PROP_TXSTATUS */
59 
60 
61 #ifdef PROP_TXSTATUS
62 
63 #define DHD_WLFC_QMON_COMPLETE(entry)
64 
65 void
dhd_wlfc_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)66 dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
67 {
68 	int i;
69 	uint8* ea;
70 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
71 		dhdp->wlfc_state;
72 	wlfc_hanger_t* h;
73 	wlfc_mac_descriptor_t* mac_table;
74 	wlfc_mac_descriptor_t* interfaces;
75 	char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
76 
77 	if (wlfc == NULL) {
78 		bcm_bprintf(strbuf, "wlfc not initialized yet\n");
79 		return;
80 	}
81 	h = (wlfc_hanger_t*)wlfc->hanger;
82 	if (h == NULL) {
83 		bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
84 	}
85 
86 	mac_table = wlfc->destination_entries.nodes;
87 	interfaces = wlfc->destination_entries.interfaces;
88 	bcm_bprintf(strbuf, "---- wlfc stats ----\n");
89 	if (h) {
90 		bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
91 			"f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
92 			h->pushed,
93 			h->popped,
94 			h->failed_to_push,
95 			h->failed_to_pop,
96 			h->failed_slotfind,
97 			(h->pushed - h->popped));
98 	}
99 
100 	bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
101 		"(dq_full,rollback_fail) = (%d,%d,%d,%d), (%d,%d)\n",
102 		wlfc->stats.tlv_parse_failed,
103 		wlfc->stats.credit_request_failed,
104 		wlfc->stats.mac_update_failed,
105 		wlfc->stats.psmode_update_failed,
106 		wlfc->stats.delayq_full_error,
107 		wlfc->stats.rollback_failed);
108 
109 	bcm_bprintf(strbuf, "PKTS (credit,sent) "
110 		"(AC0[%d,%d],AC1[%d,%d],AC2[%d,%d],AC3[%d,%d],BC_MC[%d,%d])\n",
111 		wlfc->FIFO_credit[0], wlfc->stats.send_pkts[0],
112 		wlfc->FIFO_credit[1], wlfc->stats.send_pkts[1],
113 		wlfc->FIFO_credit[2], wlfc->stats.send_pkts[2],
114 		wlfc->FIFO_credit[3], wlfc->stats.send_pkts[3],
115 		wlfc->FIFO_credit[4], wlfc->stats.send_pkts[4]);
116 
117 	bcm_bprintf(strbuf, "\n");
118 	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
119 		if (interfaces[i].occupied) {
120 			char* iftype_desc;
121 
122 			if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
123 				iftype_desc = "<Unknown";
124 			else
125 				iftype_desc = iftypes[interfaces[i].iftype];
126 
127 			ea = interfaces[i].ea;
128 			bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
129 				"[%02x:%02x:%02x:%02x:%02x:%02x], if:%d, type: %s"
130 				"netif_flow_control:%s\n", i,
131 				ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
132 				interfaces[i].interface_id,
133 				iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
134 				? " OFF":" ON"));
135 
136 			bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ(len,state,credit)"
137 				"= (%d,%s,%d)\n",
138 				i,
139 				interfaces[i].psq.len,
140 				((interfaces[i].state ==
141 				WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
142 				interfaces[i].requested_credit);
143 
144 			bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ"
145 				"(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
146 				"(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
147 				i,
148 				interfaces[i].psq.q[0].len,
149 				interfaces[i].psq.q[1].len,
150 				interfaces[i].psq.q[2].len,
151 				interfaces[i].psq.q[3].len,
152 				interfaces[i].psq.q[4].len,
153 				interfaces[i].psq.q[5].len,
154 				interfaces[i].psq.q[6].len,
155 				interfaces[i].psq.q[7].len);
156 		}
157 	}
158 
159 	bcm_bprintf(strbuf, "\n");
160 	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
161 		if (mac_table[i].occupied) {
162 			ea = mac_table[i].ea;
163 			bcm_bprintf(strbuf, "MAC_table[%d].ea = "
164 				"[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i,
165 				ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
166 				mac_table[i].interface_id);
167 
168 			bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ(len,state,credit)"
169 				"= (%d,%s,%d)\n",
170 				i,
171 				mac_table[i].psq.len,
172 				((mac_table[i].state ==
173 				WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
174 				mac_table[i].requested_credit);
175 #ifdef PROP_TXSTATUS_DEBUG
176 			bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
177 				i, mac_table[i].opened_ct, mac_table[i].closed_ct);
178 #endif
179 			bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ"
180 				"(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
181 				"(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
182 				i,
183 				mac_table[i].psq.q[0].len,
184 				mac_table[i].psq.q[1].len,
185 				mac_table[i].psq.q[2].len,
186 				mac_table[i].psq.q[3].len,
187 				mac_table[i].psq.q[4].len,
188 				mac_table[i].psq.q[5].len,
189 				mac_table[i].psq.q[6].len,
190 				mac_table[i].psq.q[7].len);
191 		}
192 	}
193 
194 #ifdef PROP_TXSTATUS_DEBUG
195 	{
196 		int avg;
197 		int moving_avg = 0;
198 		int moving_samples;
199 
200 		if (wlfc->stats.latency_sample_count) {
201 			moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
202 
203 			for (i = 0; i < moving_samples; i++)
204 				moving_avg += wlfc->stats.deltas[i];
205 			moving_avg /= moving_samples;
206 
207 			avg = (100 * wlfc->stats.total_status_latency) /
208 				wlfc->stats.latency_sample_count;
209 			bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
210 				"(%d.%d, %03d, %03d)\n",
211 				moving_samples, avg/100, (avg - (avg/100)*100),
212 				wlfc->stats.latency_most_recent,
213 				moving_avg);
214 		}
215 	}
216 
217 	bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
218 		"back = (%d,%d,%d,%d,%d,%d)\n",
219 		wlfc->stats.fifo_credits_sent[0],
220 		wlfc->stats.fifo_credits_sent[1],
221 		wlfc->stats.fifo_credits_sent[2],
222 		wlfc->stats.fifo_credits_sent[3],
223 		wlfc->stats.fifo_credits_sent[4],
224 		wlfc->stats.fifo_credits_sent[5],
225 
226 		wlfc->stats.fifo_credits_back[0],
227 		wlfc->stats.fifo_credits_back[1],
228 		wlfc->stats.fifo_credits_back[2],
229 		wlfc->stats.fifo_credits_back[3],
230 		wlfc->stats.fifo_credits_back[4],
231 		wlfc->stats.fifo_credits_back[5]);
232 	{
233 		uint32 fifo_cr_sent = 0;
234 		uint32 fifo_cr_acked = 0;
235 		uint32 request_cr_sent = 0;
236 		uint32 request_cr_ack = 0;
237 		uint32 bc_mc_cr_ack = 0;
238 
239 		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
240 			fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
241 		}
242 
243 		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
244 			fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
245 		}
246 
247 		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
248 			if (wlfc->destination_entries.nodes[i].occupied) {
249 				request_cr_sent +=
250 					wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
251 			}
252 		}
253 		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
254 			if (wlfc->destination_entries.interfaces[i].occupied) {
255 				request_cr_sent +=
256 				wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
257 			}
258 		}
259 		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
260 			if (wlfc->destination_entries.nodes[i].occupied) {
261 				request_cr_ack +=
262 					wlfc->destination_entries.nodes[i].dstncredit_acks;
263 			}
264 		}
265 		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
266 			if (wlfc->destination_entries.interfaces[i].occupied) {
267 				request_cr_ack +=
268 					wlfc->destination_entries.interfaces[i].dstncredit_acks;
269 			}
270 		}
271 		bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
272 			"other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
273 			fifo_cr_sent, fifo_cr_acked,
274 			request_cr_sent, request_cr_ack,
275 			wlfc->destination_entries.other.dstncredit_acks,
276 			bc_mc_cr_ack,
277 			wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
278 	}
279 #endif /* PROP_TXSTATUS_DEBUG */
280 	bcm_bprintf(strbuf, "\n");
281 	bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)"
282 		"(freed,free_err,rollback)) = "
283 		"((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n",
284 		wlfc->stats.pktin,
285 		wlfc->stats.pkt2bus,
286 		wlfc->stats.txstatus_in,
287 		wlfc->stats.dhd_hdrpulls,
288 
289 		wlfc->stats.pktdropped,
290 		wlfc->stats.wlfc_header_only_pkt,
291 		wlfc->stats.wlc_tossed_pkts,
292 
293 		wlfc->stats.pkt_freed,
294 		wlfc->stats.pkt_free_err, wlfc->stats.rollback);
295 
296 	bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
297 		"((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
298 
299 		wlfc->stats.d11_suppress,
300 		wlfc->stats.wl_suppress,
301 		wlfc->stats.bad_suppress,
302 
303 		wlfc->stats.psq_d11sup_enq,
304 		wlfc->stats.psq_wlsup_enq,
305 		wlfc->stats.psq_hostq_enq,
306 		wlfc->stats.mac_handle_notfound,
307 
308 		wlfc->stats.psq_d11sup_retx,
309 		wlfc->stats.psq_wlsup_retx,
310 		wlfc->stats.psq_hostq_retx);
311 	bcm_bprintf(strbuf, "wlfc- generic error: %d", wlfc->stats.generic_error);
312 
313 	return;
314 }
315 
316 /* Create a place to store all packet pointers submitted to the firmware until
317 	a status comes back, suppress or otherwise.
318 
319 	hang-er: noun, a contrivance on which things are hung, as a hook.
320 */
321 static void*
dhd_wlfc_hanger_create(osl_t * osh,int max_items)322 dhd_wlfc_hanger_create(osl_t *osh, int max_items)
323 {
324 	int i;
325 	wlfc_hanger_t* hanger;
326 
327 	/* allow only up to a specific size for now */
328 	ASSERT(max_items == WLFC_HANGER_MAXITEMS);
329 
330 	if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL)
331 		return NULL;
332 
333 	memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
334 	hanger->max_items = max_items;
335 
336 	for (i = 0; i < hanger->max_items; i++) {
337 		hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
338 	}
339 	return hanger;
340 }
341 
342 static int
dhd_wlfc_hanger_delete(osl_t * osh,void * hanger)343 dhd_wlfc_hanger_delete(osl_t *osh, void* hanger)
344 {
345 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
346 
347 	if (h) {
348 		MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items));
349 		return BCME_OK;
350 	}
351 	return BCME_BADARG;
352 }
353 
354 static uint16
dhd_wlfc_hanger_get_free_slot(void * hanger)355 dhd_wlfc_hanger_get_free_slot(void* hanger)
356 {
357 	uint32 i;
358 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
359 
360 	if (h) {
361 		i = h->slot_pos + 1;
362 		if (i == h->max_items) {
363 			i = 0;
364 		}
365 		while (i != h->slot_pos) {
366 			if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE) {
367 				h->slot_pos = i;
368 				return (uint16)i;
369 			}
370 			i++;
371 			if (i == h->max_items)
372 				i = 0;
373 		}
374 		h->failed_slotfind++;
375 	}
376 	return WLFC_HANGER_MAXITEMS;
377 }
378 
379 static int
dhd_wlfc_hanger_get_genbit(void * hanger,void * pkt,uint32 slot_id,int * gen)380 dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
381 {
382 	int rc = BCME_OK;
383 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
384 
385 	*gen = 0xff;
386 
387 	/* this packet was not pushed at the time it went to the firmware */
388 	if (slot_id == WLFC_HANGER_MAXITEMS)
389 		return BCME_NOTFOUND;
390 
391 	if (h) {
392 		if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
393 			(h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
394 			*gen = h->items[slot_id].gen;
395 		}
396 		else {
397 			rc = BCME_NOTFOUND;
398 		}
399 	}
400 	else
401 		rc = BCME_BADARG;
402 	return rc;
403 }
404 
405 static int
dhd_wlfc_hanger_pushpkt(void * hanger,void * pkt,uint32 slot_id)406 dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
407 {
408 	int rc = BCME_OK;
409 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
410 
411 	if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
412 		if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
413 			h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
414 			h->items[slot_id].pkt = pkt;
415 			h->items[slot_id].identifier = slot_id;
416 			h->pushed++;
417 		}
418 		else {
419 			h->failed_to_push++;
420 			rc = BCME_NOTFOUND;
421 		}
422 	}
423 	else
424 		rc = BCME_BADARG;
425 	return rc;
426 }
427 
428 static int
dhd_wlfc_hanger_poppkt(void * hanger,uint32 slot_id,void ** pktout,int remove_from_hanger)429 dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger)
430 {
431 	int rc = BCME_OK;
432 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
433 
434 	/* this packet was not pushed at the time it went to the firmware */
435 	if (slot_id == WLFC_HANGER_MAXITEMS)
436 		return BCME_NOTFOUND;
437 
438 	if (h) {
439 		if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
440 			*pktout = h->items[slot_id].pkt;
441 			if (remove_from_hanger) {
442 				h->items[slot_id].state =
443 					WLFC_HANGER_ITEM_STATE_FREE;
444 				h->items[slot_id].pkt = NULL;
445 				h->items[slot_id].identifier = 0;
446 				h->items[slot_id].gen = 0xff;
447 				h->popped++;
448 			}
449 		}
450 		else {
451 			h->failed_to_pop++;
452 			rc = BCME_NOTFOUND;
453 		}
454 	}
455 	else
456 		rc = BCME_BADARG;
457 	return rc;
458 }
459 
460 static int
dhd_wlfc_hanger_mark_suppressed(void * hanger,uint32 slot_id,uint8 gen)461 dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
462 {
463 	int rc = BCME_OK;
464 	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
465 
466 	/* this packet was not pushed at the time it went to the firmware */
467 	if (slot_id == WLFC_HANGER_MAXITEMS)
468 		return BCME_NOTFOUND;
469 	if (h) {
470 		h->items[slot_id].gen = gen;
471 		if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
472 			h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
473 		}
474 		else
475 			rc = BCME_BADARG;
476 	}
477 	else
478 		rc = BCME_BADARG;
479 
480 	return rc;
481 }
482 
483 static int
_dhd_wlfc_pushheader(athost_wl_status_info_t * ctx,void * p,bool tim_signal,uint8 tim_bmp,uint8 mac_handle,uint32 htodtag)484 _dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal,
485 	uint8 tim_bmp, uint8 mac_handle, uint32 htodtag)
486 {
487 	uint32 wl_pktinfo = 0;
488 	uint8* wlh;
489 	uint8 dataOffset;
490 	uint8 fillers;
491 	uint8 tim_signal_len = 0;
492 
493 	struct bdc_header *h;
494 
495 	if (tim_signal) {
496 		tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
497 	}
498 
499 	/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
500 	dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len;
501 	fillers = ROUNDUP(dataOffset, 4) - dataOffset;
502 	dataOffset += fillers;
503 
504 	PKTPUSH(ctx->osh, p, dataOffset);
505 	wlh = (uint8*) PKTDATA(ctx->osh, p);
506 
507 	wl_pktinfo = htol32(htodtag);
508 
509 	wlh[0] = WLFC_CTL_TYPE_PKTTAG;
510 	wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG;
511 	memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32));
512 
513 	if (tim_signal_len) {
514 		wlh[dataOffset - fillers - tim_signal_len ] =
515 			WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
516 		wlh[dataOffset - fillers - tim_signal_len + 1] =
517 			WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
518 		wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
519 		wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
520 	}
521 	if (fillers)
522 		memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
523 
524 	PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
525 	h = (struct bdc_header *)PKTDATA(ctx->osh, p);
526 	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
527 	if (PKTSUMNEEDED(p))
528 		h->flags |= BDC_FLAG_SUM_NEEDED;
529 
530 
531 	h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
532 	h->flags2 = 0;
533 	h->dataOffset = dataOffset >> 2;
534 	BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
535 	return BCME_OK;
536 }
537 
538 static int
_dhd_wlfc_pullheader(athost_wl_status_info_t * ctx,void * pktbuf)539 _dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
540 {
541 	struct bdc_header *h;
542 
543 	if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
544 		WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
545 		           PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
546 		return BCME_ERROR;
547 	}
548 	h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
549 
550 	/* pull BDC header */
551 	PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
552 
553 	if (PKTLEN(ctx->osh, pktbuf) < (h->dataOffset << 2)) {
554 		WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
555 		           PKTLEN(ctx->osh, pktbuf), (h->dataOffset << 2)));
556 		return BCME_ERROR;
557 	}
558 
559 	/* pull wl-header */
560 	PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
561 	return BCME_OK;
562 }
563 
564 static wlfc_mac_descriptor_t*
_dhd_wlfc_find_table_entry(athost_wl_status_info_t * ctx,void * p)565 _dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
566 {
567 	int i;
568 	wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
569 	uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
570 	uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
571 	wlfc_mac_descriptor_t* entry = NULL;
572 	int iftype = ctx->destination_entries.interfaces[ifid].iftype;
573 
574 	/* Multicast destination and P2P clients get the interface entry.
575 	 * STA gets the interface entry if there is no exact match. For
576 	 * example, TDLS destinations have their own entry.
577 	 */
578 	if ((iftype == WLC_E_IF_ROLE_STA || ETHER_ISMULTI(dstn) ||
579 		iftype == WLC_E_IF_ROLE_P2P_CLIENT) &&
580 		(ctx->destination_entries.interfaces[ifid].occupied)) {
581 			entry = &ctx->destination_entries.interfaces[ifid];
582 	}
583 
584 	if (entry != NULL && ETHER_ISMULTI(dstn))
585 		return entry;
586 
587 	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
588 		if (table[i].occupied) {
589 			if (table[i].interface_id == ifid) {
590 				if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN)) {
591 					entry = &table[i];
592 					break;
593 				}
594 			}
595 		}
596 	}
597 
598 	return entry != NULL ? entry : &ctx->destination_entries.other;
599 }
600 
601 static int
_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t * ctx,void * p,ewlfc_packet_state_t pkt_type,uint32 hslot)602 _dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
603 	void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
604 {
605 	/*
606 	put the packet back to the head of queue
607 
608 	- suppressed packet goes back to suppress sub-queue
609 	- pull out the header, if new or delayed packet
610 
611 	Note: hslot is used only when header removal is done.
612 	*/
613 	wlfc_mac_descriptor_t* entry;
614 	void* pktout;
615 	int rc = BCME_OK;
616 	int prec;
617 
618 	entry = _dhd_wlfc_find_table_entry(ctx, p);
619 	prec = DHD_PKTTAG_FIFO(PKTTAG(p));
620 	if (entry != NULL) {
621 		if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) {
622 			/* wl-header is saved for suppressed packets */
623 			if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) {
624 				WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
625 				rc = BCME_ERROR;
626 			}
627 		}
628 		else {
629 			/* remove header first */
630 			rc = _dhd_wlfc_pullheader(ctx, p);
631 			if (rc != BCME_OK)          {
632 				WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
633 				/* free the hanger slot */
634 				dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
635 				PKTFREE(ctx->osh, p, TRUE);
636 				ctx->stats.rollback_failed++;
637 				return BCME_ERROR;
638 			}
639 
640 			if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
641 				/* delay-q packets are going to delay-q */
642 				if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) {
643 					WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
644 					rc = BCME_ERROR;
645 				}
646 			}
647 
648 			/* free the hanger slot */
649 			dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
650 
651 			/* decrement sequence count */
652 			WLFC_DECR_SEQCOUNT(entry, prec);
653 		}
654 		/*
655 		if this packet did not count against FIFO credit, it must have
656 		taken a requested_credit from the firmware (for pspoll etc.)
657 		*/
658 		if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
659 			entry->requested_credit++;
660 		}
661 	}
662 	else {
663 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
664 		rc = BCME_ERROR;
665 	}
666 	if (rc != BCME_OK)
667 		ctx->stats.rollback_failed++;
668 	else
669 		ctx->stats.rollback++;
670 
671 	return rc;
672 }
673 
674 static void
_dhd_wlfc_flow_control_check(athost_wl_status_info_t * ctx,struct pktq * pq,uint8 if_id)675 _dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
676 {
677 	dhd_pub_t *dhdp;
678 
679 	ASSERT(ctx);
680 
681 	dhdp = (dhd_pub_t *)ctx->dhdp;
682 
683 	if (dhdp && dhdp->skip_fc && dhdp->skip_fc())
684 		return;
685 
686 	if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
687 		/* start traffic */
688 		ctx->hostif_flow_state[if_id] = OFF;
689 		/*
690 		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
691 		pq->len, if_id, __FUNCTION__));
692 		*/
693 		WLFC_DBGMESG(("F"));
694 
695 		dhd_txflowcontrol(ctx->dhdp, if_id, OFF);
696 
697 		ctx->toggle_host_if = 0;
698 	}
699 	if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) {
700 		/* stop traffic */
701 		ctx->hostif_flow_state[if_id] = ON;
702 		/*
703 		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic   %s()\n",
704 		pq->len, if_id, __FUNCTION__));
705 		*/
706 		WLFC_DBGMESG(("N"));
707 
708 		dhd_txflowcontrol(ctx->dhdp, if_id, ON);
709 
710 		ctx->host_ifidx = if_id;
711 		ctx->toggle_host_if = 1;
712 	}
713 
714 	return;
715 }
716 
717 static int
_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,uint8 ta_bmp)718 _dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
719 	uint8 ta_bmp)
720 {
721 	int rc = BCME_OK;
722 	void* p = NULL;
723 	int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12;
724 
725 	/* allocate a dummy packet */
726 	p = PKTGET(ctx->osh, dummylen, TRUE);
727 	if (p) {
728 		PKTPULL(ctx->osh, p, dummylen);
729 		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
730 		_dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0);
731 		DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
732 #ifdef PROP_TXSTATUS_DEBUG
733 		ctx->stats.signal_only_pkts_sent++;
734 #endif
735 		rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p);
736 		if (rc != BCME_OK) {
737 			PKTFREE(ctx->osh, p, TRUE);
738 		}
739 	}
740 	else {
741 		DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
742 		           __FUNCTION__, dummylen));
743 		rc = BCME_NOMEM;
744 	}
745 	return rc;
746 }
747 
748 /* Return TRUE if traffic availability changed */
749 static bool
_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)750 _dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
751 	int prec)
752 {
753 	bool rc = FALSE;
754 
755 	if (entry->state == WLFC_STATE_CLOSE) {
756 		if ((pktq_plen(&entry->psq, (prec << 1)) == 0) &&
757 			(pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) {
758 
759 			if (entry->traffic_pending_bmp & NBITVAL(prec)) {
760 				rc = TRUE;
761 				entry->traffic_pending_bmp =
762 					entry->traffic_pending_bmp & ~ NBITVAL(prec);
763 			}
764 		}
765 		else {
766 			if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
767 				rc = TRUE;
768 				entry->traffic_pending_bmp =
769 					entry->traffic_pending_bmp | NBITVAL(prec);
770 			}
771 		}
772 	}
773 	if (rc) {
774 		/* request a TIM update to firmware at the next piggyback opportunity */
775 		if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
776 			entry->send_tim_signal = 1;
777 			_dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
778 			entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
779 			entry->send_tim_signal = 0;
780 		}
781 		else {
782 			rc = FALSE;
783 		}
784 	}
785 	return rc;
786 }
787 
788 static int
_dhd_wlfc_enque_suppressed(athost_wl_status_info_t * ctx,int prec,void * p)789 _dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
790 {
791 	wlfc_mac_descriptor_t* entry;
792 
793 	entry = _dhd_wlfc_find_table_entry(ctx, p);
794 	if (entry == NULL) {
795 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
796 		return BCME_NOTFOUND;
797 	}
798 	/*
799 	- suppressed packets go to sub_queue[2*prec + 1] AND
800 	- delayed packets go to sub_queue[2*prec + 0] to ensure
801 	order of delivery.
802 	*/
803 	if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) {
804 		ctx->stats.delayq_full_error++;
805 		/* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
806 		WLFC_DBGMESG(("s"));
807 		return BCME_ERROR;
808 	}
809 	/* A packet has been pushed, update traffic availability bitmap, if applicable */
810 	_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
811 	_dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
812 	return BCME_OK;
813 }
814 
815 static int
_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,void * p,int header_needed,uint32 * slot)816 _dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
817 	wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot)
818 {
819 	int rc = BCME_OK;
820 	int hslot = WLFC_HANGER_MAXITEMS;
821 	bool send_tim_update = FALSE;
822 	uint32 htod = 0;
823 	uint8 free_ctr;
824 
825 	*slot = hslot;
826 
827 	if (entry == NULL) {
828 		entry = _dhd_wlfc_find_table_entry(ctx, p);
829 	}
830 
831 	if (entry == NULL) {
832 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
833 		return BCME_ERROR;
834 	}
835 	if (entry->send_tim_signal) {
836 		send_tim_update = TRUE;
837 		entry->send_tim_signal = 0;
838 		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
839 	}
840 	if (header_needed) {
841 		hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger);
842 		free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
843 		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
844 		WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation);
845 		entry->transit_count++;
846 	}
847 	else {
848 		hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
849 		free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
850 	}
851 	WLFC_PKTID_HSLOT_SET(htod, hslot);
852 	WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr);
853 	DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
854 	WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
855 	WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
856 
857 
858 	if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
859 		/*
860 		Indicate that this packet is being sent in response to an
861 		explicit request from the firmware side.
862 		*/
863 		WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
864 	}
865 	else {
866 		WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
867 	}
868 	if (header_needed) {
869 		rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update,
870 			entry->traffic_lastreported_bmp, entry->mac_handle, htod);
871 		if (rc == BCME_OK) {
872 			DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
873 			/*
874 			a new header was created for this packet.
875 			push to hanger slot and scrub q. Since bus
876 			send succeeded, increment seq number as well.
877 			*/
878 			rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
879 			if (rc == BCME_OK) {
880 				/* increment free running sequence count */
881 				WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
882 #ifdef PROP_TXSTATUS_DEBUG
883 				((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time =
884 					OSL_SYSUPTIME();
885 #endif
886 			}
887 			else {
888 				WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n",
889 					__FUNCTION__, rc));
890 			}
891 		}
892 	}
893 	else {
894 		int gen;
895 
896 		/* remove old header */
897 		rc = _dhd_wlfc_pullheader(ctx, p);
898 		if (rc == BCME_OK) {
899 			hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
900 			dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
901 
902 			WLFC_PKTFLAG_SET_GENERATION(htod, gen);
903 			free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
904 			/* push new header */
905 			_dhd_wlfc_pushheader(ctx, p, send_tim_update,
906 				entry->traffic_lastreported_bmp, entry->mac_handle, htod);
907 		}
908 	}
909 	*slot = hslot;
910 	return rc;
911 }
912 
913 static int
_dhd_wlfc_is_destination_closed(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,int prec)914 _dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx,
915 	wlfc_mac_descriptor_t* entry, int prec)
916 {
917 	if (ctx->destination_entries.interfaces[entry->interface_id].iftype ==
918 		WLC_E_IF_ROLE_P2P_GO) {
919 		/* - destination interface is of type p2p GO.
920 		For a p2pGO interface, if the destination is OPEN but the interface is
921 		CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
922 		destination-specific-credit left send packets. This is because the
923 		firmware storing the destination-specific-requested packet in queue.
924 		*/
925 		if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
926 			(entry->requested_packet == 0))
927 			return 1;
928 	}
929 	/* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
930 	if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
931 		(entry->requested_packet == 0)) ||
932 		(!(entry->ac_bitmap & (1 << prec))))
933 		return 1;
934 
935 	return 0;
936 }
937 
938 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)939 _dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx,
940 	int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out)
941 {
942 	wlfc_mac_descriptor_t* entry;
943 	wlfc_mac_descriptor_t* table;
944 	uint8 token_pos;
945 	int total_entries;
946 	void* p = NULL;
947 	int pout;
948 	int i;
949 
950 	*entry_out = NULL;
951 	token_pos = ctx->token_pos[prec];
952 	/* most cases a packet will count against FIFO credit */
953 	*ac_credit_spent = 1;
954 	*needs_hdr = 1;
955 
956 	/* search all entries, include nodes as well as interfaces */
957 	table = (wlfc_mac_descriptor_t*)&ctx->destination_entries;
958 	total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t);
959 
960 	for (i = 0; i < total_entries; i++) {
961 		entry = &table[(token_pos + i) % total_entries];
962 		if (entry->occupied && !entry->deleting) {
963 			if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) {
964 				p = pktq_mdeq(&entry->psq,
965 					/* higher precedence will be picked up first,
966 					 * i.e. suppressed packets before delayed ones
967 					 */
968 					NBITVAL((prec << 1) + 1), &pout);
969 					*needs_hdr = 0;
970 
971 				if (p == NULL) {
972 					if (entry->suppressed == TRUE) {
973 						if ((entry->suppr_transit_count <=
974 							entry->suppress_count)) {
975 							entry->suppressed = FALSE;
976 						} else {
977 							return NULL;
978 						}
979 					}
980 					/* De-Q from delay Q */
981 					p = pktq_mdeq(&entry->psq,
982 						NBITVAL((prec << 1)),
983 						&pout);
984 					*needs_hdr = 1;
985 				}
986 
987 				if (p != NULL) {
988 					/* did the packet come from suppress sub-queue? */
989 					if (entry->requested_credit > 0) {
990 						entry->requested_credit--;
991 #ifdef PROP_TXSTATUS_DEBUG
992 						entry->dstncredit_sent_packets++;
993 #endif
994 						/*
995 						if the packet was pulled out while destination is in
996 						closed state but had a non-zero packets requested,
997 						then this should not count against the FIFO credit.
998 						That is due to the fact that the firmware will
999 						most likely hold onto this packet until a suitable
1000 						time later to push it to the appropriate  AC FIFO.
1001 						*/
1002 						if (entry->state == WLFC_STATE_CLOSE)
1003 							*ac_credit_spent = 0;
1004 					}
1005 					else if (entry->requested_packet > 0) {
1006 						entry->requested_packet--;
1007 						DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1008 						if (entry->state == WLFC_STATE_CLOSE)
1009 							*ac_credit_spent = 0;
1010 					}
1011 					/* move token to ensure fair round-robin */
1012 					ctx->token_pos[prec] =
1013 						(token_pos + i + 1) % total_entries;
1014 					*entry_out = entry;
1015 					_dhd_wlfc_flow_control_check(ctx, &entry->psq,
1016 						DHD_PKTTAG_IF(PKTTAG(p)));
1017 					/*
1018 					A packet has been picked up, update traffic
1019 					availability bitmap, if applicable
1020 					*/
1021 					_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1022 					return p;
1023 				}
1024 			}
1025 		}
1026 	}
1027 	return NULL;
1028 }
1029 
1030 void *
_dhd_wlfc_pktq_peek_tail(struct pktq * pq,int * prec_out)1031 _dhd_wlfc_pktq_peek_tail(struct pktq *pq, int *prec_out)
1032 {
1033 	int prec;
1034 
1035 	ASSERT(pq);
1036 
1037 	if (pq->len == 0)
1038 		return NULL;
1039 
1040 	for (prec = 0; prec < pq->hi_prec; prec++)
1041 		/* only pick packets from dealyed-q */
1042 		if (((prec & 1) == 0) && pq->q[prec].head)
1043 			break;
1044 
1045 	if (prec_out)
1046 		*prec_out = prec;
1047 
1048 	return (pq->q[prec].tail);
1049 }
1050 
1051 bool
_dhd_wlfc_prec_enq_with_drop(dhd_pub_t * dhdp,struct pktq * pq,void * pkt,int prec)1052 _dhd_wlfc_prec_enq_with_drop(dhd_pub_t *dhdp, struct pktq *pq, void *pkt, int prec)
1053 {
1054 	void *p = NULL;
1055 	int eprec = -1;		/* precedence to evict from */
1056 
1057 	ASSERT(dhdp && pq && pkt);
1058 	ASSERT(prec >= 0 && prec < pq->num_prec);
1059 
1060 	/* Fast case, precedence queue is not full and we are also not
1061 	 * exceeding total queue length
1062 	 */
1063 	if (!pktq_pfull(pq, prec) && !pktq_full(pq)) {
1064 		pktq_penq(pq, prec, pkt);
1065 		return TRUE;
1066 	}
1067 
1068 	/* Determine precedence from which to evict packet, if any */
1069 	if (pktq_pfull(pq, prec))
1070 		eprec = prec;
1071 	else if (pktq_full(pq)) {
1072 		p = _dhd_wlfc_pktq_peek_tail(pq, &eprec);
1073 		if (!p) {
1074 			WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1075 			return FALSE;
1076 		}
1077 		if ((eprec > prec) || (eprec < 0)) {
1078 			if (!pktq_pempty(pq, prec)) {
1079 				eprec = prec;
1080 			} else {
1081 				return FALSE;
1082 			}
1083 		}
1084 	}
1085 
1086 	/* Evict if needed */
1087 	if (eprec >= 0) {
1088 		/* Detect queueing to unconfigured precedence */
1089 		ASSERT(!pktq_pempty(pq, eprec));
1090 		/* Evict all fragmented frames */
1091 		dhd_prec_drop_pkts(dhdp->osh, pq, eprec);
1092 	}
1093 
1094 	/* Enqueue */
1095 	p = pktq_penq(pq, prec, pkt);
1096 	if (!p) {
1097 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1098 		return FALSE;
1099 	}
1100 
1101 	return TRUE;
1102 }
1103 
1104 static int
_dhd_wlfc_enque_delayq(athost_wl_status_info_t * ctx,void * pktbuf,int prec)1105 _dhd_wlfc_enque_delayq(athost_wl_status_info_t* ctx, void* pktbuf, int prec)
1106 {
1107 	wlfc_mac_descriptor_t* entry;
1108 
1109 	if (pktbuf != NULL) {
1110 		entry = _dhd_wlfc_find_table_entry(ctx, pktbuf);
1111 
1112 		if (entry == NULL) {
1113 			WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1114 			return BCME_ERROR;
1115 		}
1116 
1117 		/*
1118 		- suppressed packets go to sub_queue[2*prec + 1] AND
1119 		- delayed packets go to sub_queue[2*prec + 0] to ensure
1120 		order of delivery.
1121 		*/
1122 		if (_dhd_wlfc_prec_enq_with_drop(ctx->dhdp, &entry->psq, pktbuf, (prec << 1))
1123 			== FALSE) {
1124 			WLFC_DBGMESG(("D"));
1125 			/* dhd_txcomplete(ctx->dhdp, pktbuf, FALSE); */
1126 			PKTFREE(ctx->osh, pktbuf, TRUE);
1127 			ctx->stats.delayq_full_error++;
1128 			return BCME_ERROR;
1129 		}
1130 
1131 		/*
1132 		A packet has been pushed, update traffic availability bitmap,
1133 		if applicable
1134 		*/
1135 		_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1136 
1137 	}
1138 	return BCME_OK;
1139 }
1140 
ifpkt_fn(void * p,int ifid)1141 bool ifpkt_fn(void* p, int ifid)
1142 {
1143 	return (ifid == DHD_PKTTAG_IF(PKTTAG(p)));
1144 }
1145 
1146 static int
_dhd_wlfc_mac_entry_update(athost_wl_status_info_t * ctx,wlfc_mac_descriptor_t * entry,ewlfc_mac_entry_action_t action,uint8 ifid,uint8 iftype,uint8 * ea)1147 _dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1148 	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1149 {
1150 	int rc = BCME_OK;
1151 
1152 	if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1153 		entry->occupied = 1;
1154 		entry->state = WLFC_STATE_OPEN;
1155 		entry->requested_credit = 0;
1156 		entry->interface_id = ifid;
1157 		entry->iftype = iftype;
1158 		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1159 		/* for an interface entry we may not care about the MAC address */
1160 		if (ea != NULL)
1161 			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1162 		pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1163 	}
1164 	else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) {
1165 		entry->occupied = 1;
1166 		entry->state = WLFC_STATE_OPEN;
1167 		entry->requested_credit = 0;
1168 		entry->interface_id = ifid;
1169 		entry->iftype = iftype;
1170 		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1171 		/* for an interface entry we may not care about the MAC address */
1172 		if (ea != NULL)
1173 			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1174 	}
1175 	else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1176 		/* When the entry is deleted, the packets that are queued in the entry must be
1177 		   cleanup. The cleanup action should be before the occupied is set as 0. The
1178 		   flag deleting is set to avoid de-queue action when these queues are being
1179 		   cleanup
1180 		*/
1181 		entry->deleting = 1;
1182 		dhd_wlfc_cleanup(ctx->dhdp, ifpkt_fn, ifid);
1183 		_dhd_wlfc_flow_control_check(ctx, &entry->psq, ifid);
1184 		entry->deleting = 0;
1185 
1186 		entry->occupied = 0;
1187 		entry->suppressed = 0;
1188 		entry->state = WLFC_STATE_CLOSE;
1189 		entry->requested_credit = 0;
1190 		entry->transit_count = 0;
1191 		entry->suppr_transit_count = 0;
1192 		entry->suppress_count = 0;
1193 		memset(&entry->ea[0], 0, ETHER_ADDR_LEN);
1194 
1195 		/* enable after packets are queued-deqeued properly.
1196 		pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0);
1197 		*/
1198 	}
1199 	return rc;
1200 }
1201 
1202 int
_dhd_wlfc_borrow_credit(athost_wl_status_info_t * ctx,uint8 available_credit_map,int borrower_ac)1203 _dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac)
1204 {
1205 	int lender_ac;
1206 	int rc = BCME_ERROR;
1207 
1208 	if (ctx == NULL || available_credit_map == 0) {
1209 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1210 		return BCME_BADARG;
1211 	}
1212 
1213 	/* Borrow from lowest priority available AC (including BC/MC credits) */
1214 	for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) {
1215 		if ((available_credit_map && (1 << lender_ac)) &&
1216 		   (ctx->FIFO_credit[lender_ac] > 0)) {
1217 			ctx->credits_borrowed[borrower_ac][lender_ac]++;
1218 			ctx->FIFO_credit[lender_ac]--;
1219 			rc = BCME_OK;
1220 			break;
1221 		}
1222 	}
1223 
1224 	return rc;
1225 }
1226 
1227 int
dhd_wlfc_interface_entry_update(void * state,ewlfc_mac_entry_action_t action,uint8 ifid,uint8 iftype,uint8 * ea)1228 dhd_wlfc_interface_entry_update(void* state,
1229 	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1230 {
1231 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1232 	wlfc_mac_descriptor_t* entry;
1233 	int ret;
1234 
1235 	if (ifid >= WLFC_MAX_IFNUM)
1236 		return BCME_BADARG;
1237 
1238 	entry = &ctx->destination_entries.interfaces[ifid];
1239 	ret = _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea);
1240 	return ret;
1241 }
1242 
1243 int
dhd_wlfc_FIFOcreditmap_update(void * state,uint8 * credits)1244 dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
1245 {
1246 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1247 
1248 	/* update the AC FIFO credit map */
1249 	ctx->FIFO_credit[0] = credits[0];
1250 	ctx->FIFO_credit[1] = credits[1];
1251 	ctx->FIFO_credit[2] = credits[2];
1252 	ctx->FIFO_credit[3] = credits[3];
1253 	/* credit for bc/mc packets */
1254 	ctx->FIFO_credit[4] = credits[4];
1255 	/* credit for ATIM FIFO is not used yet. */
1256 	ctx->FIFO_credit[5] = 0;
1257 	return BCME_OK;
1258 }
1259 
1260 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)1261 _dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
1262     dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
1263 {
1264 	uint32 hslot;
1265 	int	rc;
1266 
1267 	/*
1268 		if ac_fifo_credit_spent = 0
1269 
1270 		This packet will not count against the FIFO credit.
1271 		To ensure the txstatus corresponding to this packet
1272 		does not provide an implied credit (default behavior)
1273 		mark the packet accordingly.
1274 
1275 		if ac_fifo_credit_spent = 1
1276 
1277 		This is a normal packet and it counts against the FIFO
1278 		credit count.
1279 	*/
1280 	DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
1281 	rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p,
1282 	     commit_info->needs_hdr, &hslot);
1283 
1284 	if (rc == BCME_OK)
1285 		rc = fcommit(commit_ctx, commit_info->p);
1286 	else
1287 		ctx->stats.generic_error++;
1288 
1289 	if (rc == BCME_OK) {
1290 		ctx->stats.pkt2bus++;
1291 		if (commit_info->ac_fifo_credit_spent) {
1292 			ctx->stats.send_pkts[ac]++;
1293 			WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
1294 		}
1295 	} else if (rc == BCME_NORESOURCE)
1296 		rc = BCME_ERROR;
1297 	else {
1298 		/*
1299 		   bus commit has failed, rollback.
1300 		   - remove wl-header for a delayed packet
1301 		   - save wl-header header for suppressed packets
1302 		*/
1303 		rc = _dhd_wlfc_rollback_packet_toq(ctx,	commit_info->p,
1304 		     (commit_info->pkt_type), hslot);
1305 
1306 		rc = BCME_ERROR;
1307 	}
1308 
1309 	return rc;
1310 }
1311 
1312 int
dhd_wlfc_commit_packets(void * state,f_commitpkt_t fcommit,void * commit_ctx,void * pktbuf)1313 dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx, void *pktbuf)
1314 {
1315 	int ac;
1316 	int credit;
1317 	int rc;
1318 	dhd_wlfc_commit_info_t  commit_info;
1319 	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1320 	int credit_count = 0;
1321 	int bus_retry_count = 0;
1322 	uint8 ac_available = 0;  /* Bitmask for 4 ACs + BC/MC */
1323 
1324 	if ((state == NULL) ||
1325 		(fcommit == NULL)) {
1326 		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1327 		return BCME_BADARG;
1328 	}
1329 
1330 	memset(&commit_info, 0, sizeof(commit_info));
1331 
1332 	/*
1333 	Commit packets for regular AC traffic. Higher priority first.
1334 	First, use up FIFO credits available to each AC. Based on distribution
1335 	and credits left, borrow from other ACs as applicable
1336 
1337 	-NOTE:
1338 	If the bus between the host and firmware is overwhelmed by the
1339 	traffic from host, it is possible that higher priority traffic
1340 	starves the lower priority queue. If that occurs often, we may
1341 	have to employ weighted round-robin or ucode scheme to avoid
1342 	low priority packet starvation.
1343 	*/
1344 
1345 	if (pktbuf) {
1346 		ac = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1347 		if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(pktbuf)))) {
1348 				ASSERT(ac == AC_COUNT);
1349 			commit_info.needs_hdr = 1;
1350 			commit_info.mac_entry = NULL;
1351 			commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1352 			commit_info.p = pktbuf;
1353 			if (ctx->FIFO_credit[ac]) {
1354 				rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1355 					fcommit, commit_ctx);
1356 
1357 			/* Bus commits may fail (e.g. flow control); abort after retries */
1358 				if (rc == BCME_OK) {
1359 					if (commit_info.ac_fifo_credit_spent) {
1360 						(void) _dhd_wlfc_borrow_credit(ctx,
1361 							ac_available, ac);
1362 						credit_count--;
1363 					}
1364 				} else {
1365 					bus_retry_count++;
1366 					if (bus_retry_count >= BUS_RETRIES) {
1367 						DHD_ERROR((" %s: bus error %d\n",
1368 							__FUNCTION__, rc));
1369 						return rc;
1370 					}
1371 				}
1372 			}
1373 		}
1374 		else {
1375 			/* en-queue the packets to respective queue. */
1376 			rc = _dhd_wlfc_enque_delayq(ctx, pktbuf, ac);
1377 		}
1378 	}
1379 
1380 	for (ac = AC_COUNT; ac >= 0; ac--) {
1381 
1382 		bool bQueueIdle = TRUE;
1383 
1384 		/* packets from delayQ with less priority are fresh and they'd need header and
1385 		  * have no MAC entry
1386 		  */
1387 		commit_info.needs_hdr = 1;
1388 		commit_info.mac_entry = NULL;
1389 		commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1390 
1391 		for (credit = 0; credit < ctx->FIFO_credit[ac];) {
1392 			commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1393 			                &(commit_info.ac_fifo_credit_spent),
1394 			                &(commit_info.needs_hdr),
1395 			                &(commit_info.mac_entry));
1396 
1397 			if (commit_info.p == NULL)
1398 				break;
1399 
1400 			bQueueIdle = FALSE;
1401 
1402 			commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1403 				eWLFC_PKTTYPE_SUPPRESSED;
1404 
1405 			rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1406 			     fcommit, commit_ctx);
1407 
1408 			/* Bus commits may fail (e.g. flow control); abort after retries */
1409 			if (rc == BCME_OK) {
1410 				if (commit_info.ac_fifo_credit_spent) {
1411 					credit++;
1412 				}
1413 			}
1414 			else {
1415 				bus_retry_count++;
1416 				if (bus_retry_count >= BUS_RETRIES) {
1417 					DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
1418 					ctx->FIFO_credit[ac] -= credit;
1419 					return rc;
1420 				}
1421 			}
1422 		}
1423 
1424 		ctx->FIFO_credit[ac] -= credit;
1425 
1426 
1427 		/* If no pkts can be dequed, the credit can be borrowed */
1428 		if (bQueueIdle) {
1429 			ac_available |= (1 << ac);
1430 			credit_count += ctx->FIFO_credit[ac];
1431 		}
1432 	}
1433 
1434 	/* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD
1435 
1436 	   Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to:
1437 	   a) ignore BC/MC for deferring borrow
1438 	   b) ignore AC_BE being available along with other ACs
1439 		  (this should happen only for pure BC/MC traffic)
1440 
1441 	   i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and
1442 	   we do not care if AC_BE and BC/MC are available or not
1443 	   */
1444 	if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) {
1445 
1446 		if (ctx->allow_credit_borrow) {
1447 			ac = 1;  /* Set ac to AC_BE and borrow credits */
1448 		}
1449 		else {
1450 			int delta;
1451 			int curr_t = OSL_SYSUPTIME();
1452 
1453 			if (curr_t > ctx->borrow_defer_timestamp)
1454 				delta = curr_t - ctx->borrow_defer_timestamp;
1455 			else
1456 				delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp;
1457 
1458 			if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
1459 				/* Reset borrow but defer to next iteration (defensive borrowing) */
1460 				ctx->allow_credit_borrow = TRUE;
1461 				ctx->borrow_defer_timestamp = 0;
1462 			}
1463 			return BCME_OK;
1464 		}
1465 	}
1466 	else {
1467 		/* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
1468 		ctx->allow_credit_borrow = FALSE;
1469 		ctx->borrow_defer_timestamp = OSL_SYSUPTIME();
1470 		return BCME_OK;
1471 	}
1472 
1473 	/* At this point, borrow all credits only for "ac" (which should be set above to AC_BE)
1474 	   Generically use "ac" only in case we extend to all ACs in future
1475 	   */
1476 	for (; (credit_count > 0);) {
1477 
1478 		commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1479 		                &(commit_info.ac_fifo_credit_spent),
1480 		                &(commit_info.needs_hdr),
1481 		                &(commit_info.mac_entry));
1482 		if (commit_info.p == NULL)
1483 			break;
1484 
1485 		commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1486 			eWLFC_PKTTYPE_SUPPRESSED;
1487 
1488 		rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1489 		     fcommit, commit_ctx);
1490 
1491 		/* Bus commits may fail (e.g. flow control); abort after retries */
1492 		if (rc == BCME_OK) {
1493 			if (commit_info.ac_fifo_credit_spent) {
1494 				(void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac);
1495 				credit_count--;
1496 			}
1497 		}
1498 		else {
1499 			bus_retry_count++;
1500 			if (bus_retry_count >= BUS_RETRIES) {
1501 				DHD_ERROR(("%s: bus error %d\n", __FUNCTION__, rc));
1502 				return rc;
1503 			}
1504 		}
1505 	}
1506 	return BCME_OK;
1507 }
1508 
1509 static uint8
dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t * dhdp,uint8 * ea)1510 dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea)
1511 {
1512 	wlfc_mac_descriptor_t* table =
1513 		((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
1514 	uint8 table_index;
1515 
1516 	if (ea != NULL) {
1517 		for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
1518 			if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
1519 				table[table_index].occupied)
1520 				return table_index;
1521 		}
1522 	}
1523 	return WLFC_MAC_DESC_ID_INVALID;
1524 }
1525 
1526 void
dhd_wlfc_txcomplete(dhd_pub_t * dhd,void * txp,bool success)1527 dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
1528 {
1529 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1530 		dhd->wlfc_state;
1531 	void* p;
1532 	int fifo_id;
1533 
1534 	if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
1535 #ifdef PROP_TXSTATUS_DEBUG
1536 		wlfc->stats.signal_only_pkts_freed++;
1537 #endif
1538 		/* is this a signal-only packet? */
1539 		if (success)
1540 			PKTFREE(wlfc->osh, txp, TRUE);
1541 		return;
1542 	}
1543 	if (!success) {
1544 		WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
1545 			__FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
1546 		dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG
1547 			(PKTTAG(txp))), &p, 1);
1548 
1549 		/* indicate failure and free the packet */
1550 		dhd_txcomplete(dhd, txp, FALSE);
1551 
1552 		/* return the credit, if necessary */
1553 		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) {
1554 			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1555 
1556 			fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp));
1557 
1558 			/* Return credits to highest priority lender first */
1559 			for (lender = AC_COUNT; lender >= 0; lender--) {
1560 				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1561 					wlfc->FIFO_credit[lender]++;
1562 					wlfc->credits_borrowed[fifo_id][lender]--;
1563 					credit_returned = 1;
1564 					break;
1565 				}
1566 			}
1567 
1568 			if (!credit_returned) {
1569 				wlfc->FIFO_credit[fifo_id]++;
1570 			}
1571 		}
1572 
1573 		PKTFREE(wlfc->osh, txp, TRUE);
1574 	}
1575 	return;
1576 }
1577 
1578 static int
dhd_wlfc_compressed_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info,uint8 len)1579 dhd_wlfc_compressed_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info, uint8 len)
1580 {
1581 	uint8 	status_flag;
1582 	uint32	status;
1583 	int		ret;
1584 	int		remove_from_hanger = 1;
1585 	void*	pktbuf;
1586 	uint8	fifo_id;
1587 	uint8 count = 0;
1588 	uint32 status_g;
1589 	uint32 hslot, hcnt;
1590 	wlfc_mac_descriptor_t* entry = NULL;
1591 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1592 		dhd->wlfc_state;
1593 
1594 	memcpy(&status, pkt_info, sizeof(uint32));
1595 	status_flag = WL_TXSTATUS_GET_FLAGS(status);
1596 	status_g = status & 0xff000000;
1597 	hslot = (status & 0x00ffff00) >> 8;
1598 	hcnt = status & 0xff;
1599 	len =	pkt_info[4];
1600 
1601 	wlfc->stats.txstatus_in++;
1602 
1603 	if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
1604 		wlfc->stats.pkt_freed++;
1605 	}
1606 
1607 	else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
1608 		wlfc->stats.d11_suppress++;
1609 		remove_from_hanger = 0;
1610 	}
1611 
1612 	else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
1613 		wlfc->stats.wl_suppress++;
1614 		remove_from_hanger = 0;
1615 	}
1616 
1617 	else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
1618 		wlfc->stats.wlc_tossed_pkts++;
1619 	}
1620 	while (count < len) {
1621 		status = (status_g << 24) | (hslot << 8) | (hcnt);
1622 		count++;
1623 		hslot++;
1624 		hcnt++;
1625 
1626 		ret = dhd_wlfc_hanger_poppkt(wlfc->hanger,
1627 			WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger);
1628 		if (ret != BCME_OK) {
1629 			/* do something */
1630 			continue;
1631 		}
1632 
1633 		entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1634 
1635 		if (!remove_from_hanger) {
1636 			/* this packet was suppressed */
1637 			if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) {
1638 				entry->suppressed = TRUE;
1639 				entry->suppress_count = pktq_mlen(&entry->psq,
1640 					NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1));
1641 				entry->suppr_transit_count = entry->transit_count;
1642 			}
1643 			entry->generation = WLFC_PKTID_GEN(status);
1644 		}
1645 
1646 #ifdef PROP_TXSTATUS_DEBUG
1647 		{
1648 			uint32 new_t = OSL_SYSUPTIME();
1649 			uint32 old_t;
1650 			uint32 delta;
1651 			old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1652 				WLFC_PKTID_HSLOT_GET(status)].push_time;
1653 
1654 
1655 			wlfc->stats.latency_sample_count++;
1656 			if (new_t > old_t)
1657 				delta = new_t - old_t;
1658 			else
1659 				delta = 0xffffffff + new_t - old_t;
1660 			wlfc->stats.total_status_latency += delta;
1661 			wlfc->stats.latency_most_recent = delta;
1662 
1663 			wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1664 			if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1665 				wlfc->stats.idx_delta = 0;
1666 		}
1667 #endif /* PROP_TXSTATUS_DEBUG */
1668 
1669 		fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1670 
1671 		/* pick up the implicit credit from this packet */
1672 		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1673 			if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1674 
1675 				int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1676 
1677 				/* Return credits to highest priority lender first */
1678 				for (lender = AC_COUNT; lender >= 0; lender--)	{
1679 					if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1680 						wlfc->FIFO_credit[lender]++;
1681 						wlfc->credits_borrowed[fifo_id][lender]--;
1682 						credit_returned = 1;
1683 						break;
1684 					}
1685 				}
1686 
1687 				if (!credit_returned) {
1688 					wlfc->FIFO_credit[fifo_id]++;
1689 				}
1690 			}
1691 		}
1692 		else {
1693 			/*
1694 			if this packet did not count against FIFO credit, it must have
1695 			taken a requested_credit from the destination entry (for pspoll etc.)
1696 			*/
1697 			if (!entry) {
1698 
1699 				entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1700 			}
1701 			if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1702 				entry->requested_credit++;
1703 #ifdef PROP_TXSTATUS_DEBUG
1704 			entry->dstncredit_acks++;
1705 #endif
1706 		}
1707 		if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1708 			(status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1709 
1710 			ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
1711 			if (ret != BCME_OK) {
1712 				/* delay q is full, drop this packet */
1713 				dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1714 				&pktbuf, 1);
1715 
1716 				/* indicate failure and free the packet */
1717 				dhd_txcomplete(dhd, pktbuf, FALSE);
1718 				entry->transit_count--;
1719 				DHD_WLFC_QMON_COMPLETE(entry);
1720 				/* packet is transmitted Successfully by dongle
1721 				 * after first suppress.
1722 				 */
1723 				if (entry->suppressed) {
1724 					entry->suppr_transit_count--;
1725 				}
1726 				PKTFREE(wlfc->osh, pktbuf, TRUE);
1727 			} else {
1728 				/* Mark suppressed to avoid a double free during wlfc cleanup */
1729 
1730 				dhd_wlfc_hanger_mark_suppressed(wlfc->hanger,
1731 				WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status));
1732 				entry->suppress_count++;
1733 			}
1734 		}
1735 		else {
1736 			dhd_txcomplete(dhd, pktbuf, TRUE);
1737 			entry->transit_count--;
1738 			DHD_WLFC_QMON_COMPLETE(entry);
1739 
1740 			/* This packet is transmitted Successfully by dongle
1741 			 * even after first suppress.
1742 			 */
1743 			if (entry->suppressed) {
1744 				entry->suppr_transit_count--;
1745 			}
1746 			/* free the packet */
1747 			PKTFREE(wlfc->osh, pktbuf, TRUE);
1748 		}
1749 	}
1750 	return BCME_OK;
1751 }
1752 
1753 /* Handle discard or suppress indication */
1754 static int
dhd_wlfc_txstatus_update(dhd_pub_t * dhd,uint8 * pkt_info)1755 dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info)
1756 {
1757 	uint8 	status_flag;
1758 	uint32	status;
1759 	int		ret;
1760 	int		remove_from_hanger = 1;
1761 	void*	pktbuf;
1762 	uint8	fifo_id;
1763 	wlfc_mac_descriptor_t* entry = NULL;
1764 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1765 		dhd->wlfc_state;
1766 
1767 	memcpy(&status, pkt_info, sizeof(uint32));
1768 	status_flag = WL_TXSTATUS_GET_FLAGS(status);
1769 	wlfc->stats.txstatus_in++;
1770 
1771 	if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
1772 		wlfc->stats.pkt_freed++;
1773 	}
1774 
1775 	else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
1776 		wlfc->stats.d11_suppress++;
1777 		remove_from_hanger = 0;
1778 	}
1779 
1780 	else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
1781 		wlfc->stats.wl_suppress++;
1782 		remove_from_hanger = 0;
1783 	}
1784 
1785 	else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
1786 		wlfc->stats.wlc_tossed_pkts++;
1787 	}
1788 
1789 	ret = dhd_wlfc_hanger_poppkt(wlfc->hanger,
1790 		WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger);
1791 	if (ret != BCME_OK) {
1792 		/* do something */
1793 		return ret;
1794 	}
1795 
1796 	entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1797 
1798 	if (!remove_from_hanger) {
1799 		/* this packet was suppressed */
1800 		if (!entry->suppressed || entry->generation != WLFC_PKTID_GEN(status)) {
1801 			entry->suppressed = TRUE;
1802 			entry->suppress_count = pktq_mlen(&entry->psq,
1803 				NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1));
1804 			entry->suppr_transit_count = entry->transit_count;
1805 		}
1806 		entry->generation = WLFC_PKTID_GEN(status);
1807 	}
1808 
1809 #ifdef PROP_TXSTATUS_DEBUG
1810 	{
1811 		uint32 new_t = OSL_SYSUPTIME();
1812 		uint32 old_t;
1813 		uint32 delta;
1814 		old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1815 			WLFC_PKTID_HSLOT_GET(status)].push_time;
1816 
1817 
1818 		wlfc->stats.latency_sample_count++;
1819 		if (new_t > old_t)
1820 			delta = new_t - old_t;
1821 		else
1822 			delta = 0xffffffff + new_t - old_t;
1823 		wlfc->stats.total_status_latency += delta;
1824 		wlfc->stats.latency_most_recent = delta;
1825 
1826 		wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1827 		if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1828 			wlfc->stats.idx_delta = 0;
1829 	}
1830 #endif /* PROP_TXSTATUS_DEBUG */
1831 
1832 	fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1833 
1834 	/* pick up the implicit credit from this packet */
1835 	if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1836 		if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1837 
1838 			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1839 
1840 			/* Return credits to highest priority lender first */
1841 			for (lender = AC_COUNT; lender >= 0; lender--)	{
1842 				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1843 					wlfc->FIFO_credit[lender]++;
1844 					wlfc->credits_borrowed[fifo_id][lender]--;
1845 					credit_returned = 1;
1846 					break;
1847 				}
1848 			}
1849 
1850 			if (!credit_returned) {
1851 				wlfc->FIFO_credit[fifo_id]++;
1852 			}
1853 		}
1854 	}
1855 	else {
1856 		/*
1857 		if this packet did not count against FIFO credit, it must have
1858 		taken a requested_credit from the destination entry (for pspoll etc.)
1859 		*/
1860 		if (!entry) {
1861 
1862 			entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1863 		}
1864 		if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1865 			entry->requested_credit++;
1866 #ifdef PROP_TXSTATUS_DEBUG
1867 		entry->dstncredit_acks++;
1868 #endif
1869 	}
1870 	if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1871 		(status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1872 
1873 		ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
1874 		if (ret != BCME_OK) {
1875 			/* delay q is full, drop this packet */
1876 			dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1877 			&pktbuf, 1);
1878 
1879 			/* indicate failure and free the packet */
1880 			dhd_txcomplete(dhd, pktbuf, FALSE);
1881 			entry->transit_count--;
1882 			DHD_WLFC_QMON_COMPLETE(entry);
1883 			/* This packet is transmitted Successfully by
1884 			 *  dongle even after first suppress.
1885 			 */
1886 			if (entry->suppressed) {
1887 				entry->suppr_transit_count--;
1888 			}
1889 			PKTFREE(wlfc->osh, pktbuf, TRUE);
1890 		} else {
1891 			/* Mark suppressed to avoid a double free during wlfc cleanup */
1892 
1893 			dhd_wlfc_hanger_mark_suppressed(wlfc->hanger,
1894 			WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status));
1895 			entry->suppress_count++;
1896 		}
1897 	}
1898 	else {
1899 		dhd_txcomplete(dhd, pktbuf, TRUE);
1900 		entry->transit_count--;
1901 		DHD_WLFC_QMON_COMPLETE(entry);
1902 
1903 		/* This packet is transmitted Successfully by dongle even after first suppress. */
1904 		if (entry->suppressed) {
1905 			entry->suppr_transit_count--;
1906 		}
1907 		/* free the packet */
1908 		PKTFREE(wlfc->osh, pktbuf, TRUE);
1909 	}
1910 	return BCME_OK;
1911 }
1912 
1913 static int
dhd_wlfc_fifocreditback_indicate(dhd_pub_t * dhd,uint8 * credits)1914 dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
1915 {
1916 	int i;
1917 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1918 		dhd->wlfc_state;
1919 	for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
1920 #ifdef PROP_TXSTATUS_DEBUG
1921 		wlfc->stats.fifo_credits_back[i] += credits[i];
1922 #endif
1923 		/* update FIFO credits */
1924 		if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
1925 		{
1926 			int lender; /* Note that borrower is i */
1927 
1928 			/* Return credits to highest priority lender first */
1929 			for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
1930 				if (wlfc->credits_borrowed[i][lender] > 0) {
1931 					if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
1932 						credits[i] -= wlfc->credits_borrowed[i][lender];
1933 						wlfc->FIFO_credit[lender] +=
1934 						    wlfc->credits_borrowed[i][lender];
1935 						wlfc->credits_borrowed[i][lender] = 0;
1936 					}
1937 					else {
1938 						wlfc->credits_borrowed[i][lender] -= credits[i];
1939 						wlfc->FIFO_credit[lender] += credits[i];
1940 						credits[i] = 0;
1941 					}
1942 				}
1943 			}
1944 
1945 			/* If we have more credits left over, these must belong to the AC */
1946 			if (credits[i] > 0) {
1947 				wlfc->FIFO_credit[i] += credits[i];
1948 			}
1949 		}
1950 	}
1951 
1952 	return BCME_OK;
1953 }
1954 
1955 static int
dhd_wlfc_dbg_senum_check(dhd_pub_t * dhd,uint8 * value)1956 dhd_wlfc_dbg_senum_check(dhd_pub_t *dhd, uint8 *value)
1957 {
1958 	uint32 timestamp;
1959 
1960 	(void)dhd;
1961 
1962 	bcopy(&value[2], &timestamp, sizeof(uint32));
1963 	DHD_INFO(("RXPKT: SEQ: %d, timestamp %d\n", value[1], timestamp));
1964 	return BCME_OK;
1965 }
1966 
1967 
1968 static int
dhd_wlfc_rssi_indicate(dhd_pub_t * dhd,uint8 * rssi)1969 dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
1970 {
1971 	(void)dhd;
1972 	(void)rssi;
1973 	return BCME_OK;
1974 }
1975 
1976 static int
dhd_wlfc_mac_table_update(dhd_pub_t * dhd,uint8 * value,uint8 type)1977 dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
1978 {
1979 	int rc;
1980 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1981 		dhd->wlfc_state;
1982 	wlfc_mac_descriptor_t* table;
1983 	uint8 existing_index;
1984 	uint8 table_index;
1985 	uint8 ifid;
1986 	uint8* ea;
1987 
1988 	WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
1989 		__FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7],
1990 		((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
1991 		WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
1992 
1993 	table = wlfc->destination_entries.nodes;
1994 	table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
1995 	ifid = value[1];
1996 	ea = &value[2];
1997 
1998 	if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
1999 		existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2000 		if (existing_index == WLFC_MAC_DESC_ID_INVALID) {
2001 			/* this MAC entry does not exist, create one */
2002 			if (!table[table_index].occupied) {
2003 				table[table_index].mac_handle = value[0];
2004 				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2005 				eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2006 				wlfc->destination_entries.interfaces[ifid].iftype,
2007 				ea);
2008 			}
2009 			else {
2010 				/* the space should have been empty, but it's not */
2011 				wlfc->stats.mac_update_failed++;
2012 			}
2013 		}
2014 		else {
2015 			/*
2016 			there is an existing entry, move it to new index
2017 			if necessary.
2018 			*/
2019 			if (existing_index != table_index) {
2020 				/* if we already have an entry, free the old one */
2021 				table[existing_index].occupied = 0;
2022 				table[existing_index].state = WLFC_STATE_CLOSE;
2023 				table[existing_index].requested_credit = 0;
2024 				table[existing_index].interface_id = 0;
2025 				/* enable after packets are queued-deqeued properly.
2026 				pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0);
2027 				*/
2028 			}
2029 		}
2030 	}
2031 	if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2032 		if (table[table_index].occupied) {
2033 				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2034 					eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2035 					wlfc->destination_entries.interfaces[ifid].iftype,
2036 					ea);
2037 		}
2038 		else {
2039 			/* the space should have been occupied, but it's not */
2040 			wlfc->stats.mac_update_failed++;
2041 		}
2042 	}
2043 	BCM_REFERENCE(rc);
2044 	return BCME_OK;
2045 }
2046 
2047 static int
dhd_wlfc_psmode_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2048 dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2049 {
2050 	/* Handle PS on/off indication */
2051 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2052 		dhd->wlfc_state;
2053 	wlfc_mac_descriptor_t* table;
2054 	wlfc_mac_descriptor_t* desc;
2055 	uint8 mac_handle = value[0];
2056 	int i;
2057 
2058 	table = wlfc->destination_entries.nodes;
2059 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2060 	if (desc->occupied) {
2061 		/* a fresh PS mode should wipe old ps credits? */
2062 		desc->requested_credit = 0;
2063 		if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2064 			desc->state = WLFC_STATE_OPEN;
2065 			DHD_WLFC_CTRINC_MAC_OPEN(desc);
2066 		}
2067 		else {
2068 			desc->state = WLFC_STATE_CLOSE;
2069 			DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2070 			/*
2071 			Indicate to firmware if there is any traffic pending.
2072 			*/
2073 			for (i = AC_BE; i < AC_COUNT; i++) {
2074 				_dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2075 			}
2076 		}
2077 	}
2078 	else {
2079 		wlfc->stats.psmode_update_failed++;
2080 	}
2081 	return BCME_OK;
2082 }
2083 
2084 static int
dhd_wlfc_interface_update(dhd_pub_t * dhd,uint8 * value,uint8 type)2085 dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2086 {
2087 	/* Handle PS on/off indication */
2088 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2089 		dhd->wlfc_state;
2090 	wlfc_mac_descriptor_t* table;
2091 	uint8 if_id = value[0];
2092 
2093 	if (if_id < WLFC_MAX_IFNUM) {
2094 		table = wlfc->destination_entries.interfaces;
2095 		if (table[if_id].occupied) {
2096 			if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2097 				table[if_id].state = WLFC_STATE_OPEN;
2098 				/* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
2099 			}
2100 			else {
2101 				table[if_id].state = WLFC_STATE_CLOSE;
2102 				/* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
2103 			}
2104 			return BCME_OK;
2105 		}
2106 	}
2107 	wlfc->stats.interface_update_failed++;
2108 
2109 	return BCME_OK;
2110 }
2111 
2112 static int
dhd_wlfc_credit_request(dhd_pub_t * dhd,uint8 * value)2113 dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
2114 {
2115 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2116 		dhd->wlfc_state;
2117 	wlfc_mac_descriptor_t* table;
2118 	wlfc_mac_descriptor_t* desc;
2119 	uint8 mac_handle;
2120 	uint8 credit;
2121 
2122 	table = wlfc->destination_entries.nodes;
2123 	mac_handle = value[1];
2124 	credit = value[0];
2125 
2126 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2127 	if (desc->occupied) {
2128 		desc->requested_credit = credit;
2129 
2130 		desc->ac_bitmap = value[2];
2131 	}
2132 	else {
2133 		wlfc->stats.credit_request_failed++;
2134 	}
2135 	return BCME_OK;
2136 }
2137 
2138 static int
dhd_wlfc_packet_request(dhd_pub_t * dhd,uint8 * value)2139 dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
2140 {
2141 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2142 		dhd->wlfc_state;
2143 	wlfc_mac_descriptor_t* table;
2144 	wlfc_mac_descriptor_t* desc;
2145 	uint8 mac_handle;
2146 	uint8 packet_count;
2147 
2148 	table = wlfc->destination_entries.nodes;
2149 	mac_handle = value[1];
2150 	packet_count = value[0];
2151 
2152 	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2153 	if (desc->occupied) {
2154 		desc->requested_packet = packet_count;
2155 
2156 		desc->ac_bitmap = value[2];
2157 	}
2158 	else {
2159 		wlfc->stats.packet_request_failed++;
2160 	}
2161 	return BCME_OK;
2162 }
2163 
2164 static void
dhd_wlfc_reorderinfo_indicate(uint8 * val,uint8 len,uchar * info_buf,uint * info_len)2165 dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
2166 {
2167 	if (info_len) {
2168 		if (info_buf) {
2169 			bcopy(val, info_buf, len);
2170 			*info_len = len;
2171 		}
2172 		else
2173 			*info_len = 0;
2174 	}
2175 }
2176 
2177 int
dhd_wlfc_parse_header_info(dhd_pub_t * dhd,void * pktbuf,int tlv_hdr_len,uchar * reorder_info_buf,uint * reorder_info_len)2178 dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
2179 	uint *reorder_info_len)
2180 {
2181 	uint8 type, len;
2182 	uint8* value;
2183 	uint8* tmpbuf;
2184 	uint16 remainder = tlv_hdr_len;
2185 	uint16 processed = 0;
2186 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2187 		dhd->wlfc_state;
2188 	tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
2189 	if (remainder) {
2190 		while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
2191 			type = tmpbuf[processed];
2192 			if (type == WLFC_CTL_TYPE_FILLER) {
2193 				remainder -= 1;
2194 				processed += 1;
2195 				continue;
2196 			}
2197 
2198 			len  = tmpbuf[processed + 1];
2199 			value = &tmpbuf[processed + 2];
2200 
2201 			if (remainder < (2 + len))
2202 				break;
2203 
2204 			remainder -= 2 + len;
2205 			processed += 2 + len;
2206 			if (type == WLFC_CTL_TYPE_TXSTATUS)
2207 				dhd_wlfc_txstatus_update(dhd, value);
2208 			if (type == WLFC_CTL_TYPE_COMP_TXSTATUS)
2209 				dhd_wlfc_compressed_txstatus_update(dhd, value, len);
2210 
2211 			else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
2212 				dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
2213 					reorder_info_len);
2214 			else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK)
2215 				dhd_wlfc_fifocreditback_indicate(dhd, value);
2216 
2217 			else if (type == WLFC_CTL_TYPE_RSSI)
2218 				dhd_wlfc_rssi_indicate(dhd, value);
2219 
2220 			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT)
2221 				dhd_wlfc_credit_request(dhd, value);
2222 
2223 			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET)
2224 				dhd_wlfc_packet_request(dhd, value);
2225 
2226 			else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
2227 				(type == WLFC_CTL_TYPE_MAC_CLOSE))
2228 				dhd_wlfc_psmode_update(dhd, value, type);
2229 
2230 			else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
2231 				(type == WLFC_CTL_TYPE_MACDESC_DEL))
2232 				dhd_wlfc_mac_table_update(dhd, value, type);
2233 
2234 			else if (type == WLFC_CTL_TYPE_TRANS_ID)
2235 				dhd_wlfc_dbg_senum_check(dhd, value);
2236 
2237 			else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
2238 				(type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
2239 				dhd_wlfc_interface_update(dhd, value, type);
2240 			}
2241 		}
2242 		if (remainder != 0) {
2243 			/* trouble..., something is not right */
2244 			wlfc->stats.tlv_parse_failed++;
2245 		}
2246 	}
2247 	return BCME_OK;
2248 }
2249 
2250 int
dhd_wlfc_init(dhd_pub_t * dhd)2251 dhd_wlfc_init(dhd_pub_t *dhd)
2252 {
2253 	char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */
2254 	/* enable all signals & indicate host proptxstatus logic is active */
2255 	uint32 tlv = dhd->wlfc_enabled?
2256 		WLFC_FLAGS_RSSI_SIGNALS |
2257 		WLFC_FLAGS_XONXOFF_SIGNALS |
2258 		WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
2259 		WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
2260 		WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0;
2261 		/* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */
2262 
2263 
2264 	/*
2265 	try to enable/disable signaling by sending "tlv" iovar. if that fails,
2266 	fallback to no flow control? Print a message for now.
2267 	*/
2268 
2269 	/* enable proptxtstatus signaling by default */
2270 	bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf));
2271 	if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) {
2272 		DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n"));
2273 	}
2274 	else {
2275 		/*
2276 		Leaving the message for now, it should be removed after a while; once
2277 		the tlv situation is stable.
2278 		*/
2279 		DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
2280 			dhd->wlfc_enabled?"enabled":"disabled", tlv));
2281 	}
2282 	return BCME_OK;
2283 }
2284 
2285 int
dhd_wlfc_enable(dhd_pub_t * dhd)2286 dhd_wlfc_enable(dhd_pub_t *dhd)
2287 {
2288 	int i;
2289 	athost_wl_status_info_t* wlfc;
2290 
2291 	if (!dhd->wlfc_enabled || dhd->wlfc_state)
2292 		return BCME_OK;
2293 
2294 	/* allocate space to track txstatus propagated from firmware */
2295 	dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t));
2296 	if (dhd->wlfc_state == NULL)
2297 		return BCME_NOMEM;
2298 
2299 	/* initialize state space */
2300 	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2301 	memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2302 
2303 	/* remember osh & dhdp */
2304 	wlfc->osh = dhd->osh;
2305 	wlfc->dhdp = dhd;
2306 
2307 	wlfc->hanger =
2308 		dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS);
2309 	if (wlfc->hanger == NULL) {
2310 		MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2311 		dhd->wlfc_state = NULL;
2312 		DHD_ERROR(("Failed to malloc dhd->wlfc_state\n"));
2313 		return BCME_NOMEM;
2314 	}
2315 
2316 	/* initialize all interfaces to accept traffic */
2317 	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2318 		wlfc->hostif_flow_state[i] = OFF;
2319 	}
2320 
2321 	wlfc->destination_entries.other.state = WLFC_STATE_OPEN;
2322 	/* bc/mc FIFO is always open [credit aside], i.e. b[5] */
2323 	wlfc->destination_entries.other.ac_bitmap = 0x1f;
2324 	wlfc->destination_entries.other.interface_id = 0;
2325 
2326 	wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2327 
2328 	wlfc->allow_credit_borrow = TRUE;
2329 	wlfc->borrow_defer_timestamp = 0;
2330 
2331 	return BCME_OK;
2332 }
2333 
2334 /* release all packet resources */
2335 void
dhd_wlfc_cleanup(dhd_pub_t * dhd,ifpkt_cb_t fn,int arg)2336 dhd_wlfc_cleanup(dhd_pub_t *dhd, ifpkt_cb_t fn, int arg)
2337 {
2338 	int i;
2339 	int total_entries;
2340 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2341 		dhd->wlfc_state;
2342 	wlfc_mac_descriptor_t* table;
2343 	wlfc_hanger_t* h;
2344 	int prec;
2345 	void *pkt = NULL;
2346 	struct pktq *txq = NULL;
2347 	if (dhd->wlfc_state == NULL)
2348 		return;
2349 	/* flush bus->txq */
2350 	txq = dhd_bus_txq(dhd->bus);
2351 	/* any in the hanger? */
2352 	h = (wlfc_hanger_t*)wlfc->hanger;
2353 	total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
2354 	/* search all entries, include nodes as well as interfaces */
2355 	table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
2356 
2357 	for (i = 0; i < total_entries; i++) {
2358 		if (table[i].occupied && (fn == NULL || (arg == table[i].interface_id))) {
2359 			if (table[i].psq.len) {
2360 				WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n",
2361 					__FUNCTION__, i, table[i].psq.len));
2362 				/* release packets held in DELAYQ */
2363 				pktq_flush(wlfc->osh, &table[i].psq, TRUE, fn, arg);
2364 			}
2365 			if (fn == NULL)
2366 				table[i].occupied = 0;
2367 		}
2368 	}
2369 	for (prec = 0; prec < txq->num_prec; prec++) {
2370 		pkt = pktq_pdeq_with_fn(txq, prec, fn, arg);
2371 		while (pkt) {
2372 			for (i = 0; i < h->max_items; i++) {
2373 				if (pkt == h->items[i].pkt) {
2374 					if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2375 						PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2376 						h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2377 					} else if (h->items[i].state ==
2378 						WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
2379 						/* These are already freed from the psq */
2380 						h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2381 					}
2382 					break;
2383 				}
2384 			}
2385 			pkt = pktq_pdeq(txq, prec);
2386 		}
2387 	}
2388 	/* flush remained pkt in hanger queue, not in bus->txq */
2389 	for (i = 0; i < h->max_items; i++) {
2390 		if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2391 			if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
2392 				PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2393 				h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2394 			}
2395 		} else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
2396 			if (fn == NULL || (*fn)(h->items[i].pkt, arg)) {
2397 				/* These are freed from the psq so no need to free again */
2398 				h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2399 			}
2400 		}
2401 	}
2402 	return;
2403 }
2404 
2405 void
dhd_wlfc_deinit(dhd_pub_t * dhd)2406 dhd_wlfc_deinit(dhd_pub_t *dhd)
2407 {
2408 	/* cleanup all psq related resources */
2409 	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2410 		dhd->wlfc_state;
2411 
2412 	dhd_os_wlfc_block(dhd);
2413 	if (dhd->wlfc_state == NULL) {
2414 		dhd_os_wlfc_unblock(dhd);
2415 		return;
2416 	}
2417 
2418 #ifdef PROP_TXSTATUS_DEBUG
2419 	{
2420 		int i;
2421 		wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2422 		for (i = 0; i < h->max_items; i++) {
2423 			if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
2424 				WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n",
2425 					__FUNCTION__, i, h->items[i].pkt,
2426 					DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt))));
2427 			}
2428 		}
2429 	}
2430 #endif
2431 	/* delete hanger */
2432 	dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger);
2433 
2434 	/* free top structure */
2435 	MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2436 	dhd->wlfc_state = NULL;
2437 	dhd_os_wlfc_unblock(dhd);
2438 
2439 	return;
2440 }
2441 #endif /* PROP_TXSTATUS */
2442