dhd_cdc.c revision 100a2d309a0ff5c4808c91d1b113d13cd236e38a
1/*
2 * DHD Protocol Module for CDC and BDC.
3 *
4 * Copyright (C) 1999-2012, 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_cdc.c 341930 2012-06-29 04:51:25Z $
25 *
26 * BDC is like CDC, except it includes a header for data packets to convey
27 * packet priority over the bus, and flags (e.g. to indicate checksum status
28 * for dongle offload.)
29 */
30
31#include <typedefs.h>
32#include <osl.h>
33
34#include <bcmutils.h>
35#include <bcmcdc.h>
36#include <bcmendian.h>
37
38#include <dngl_stats.h>
39#include <dhd.h>
40#include <dhd_proto.h>
41#include <dhd_bus.h>
42#include <dhd_dbg.h>
43
44
45#ifdef PROP_TXSTATUS
46#include <wlfc_proto.h>
47#include <dhd_wlfc.h>
48#endif
49
50
51#define RETRIES 2		/* # of retries to retrieve matching ioctl response */
52#define BUS_HEADER_LEN	(16+DHD_SDALIGN)	/* Must be at least SDPCM_RESERVE
53				 * defined in dhd_sdio.c (amount of header tha might be added)
54				 * plus any space that might be needed for alignment padding.
55				 */
56#define ROUND_UP_MARGIN	2048	/* Biggest SDIO block size possible for
57				 * round off at the end of buffer
58				 */
59
60#define BUS_RETRIES 1	/* # of retries before aborting a bus tx operation */
61
62#ifdef PROP_TXSTATUS
63typedef struct dhd_wlfc_commit_info {
64	uint8					needs_hdr;
65	uint8					ac_fifo_credit_spent;
66	ewlfc_packet_state_t	pkt_type;
67	wlfc_mac_descriptor_t*	mac_entry;
68	void*					p;
69} dhd_wlfc_commit_info_t;
70#endif /* PROP_TXSTATUS */
71
72typedef struct dhd_prot {
73	uint16 reqid;
74	uint8 pending;
75	uint32 lastcmd;
76	uint8 bus_header[BUS_HEADER_LEN];
77	cdc_ioctl_t msg;
78	unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
79} dhd_prot_t;
80
81
82static int
83dhdcdc_msg(dhd_pub_t *dhd)
84{
85	int err = 0;
86	dhd_prot_t *prot = dhd->prot;
87	int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
88
89	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
90
91	DHD_OS_WAKE_LOCK(dhd);
92
93	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
94	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
95	 *	  is actually sent to the dongle
96	 */
97	if (len > CDC_MAX_MSG_SIZE)
98		len = CDC_MAX_MSG_SIZE;
99
100	/* Send request */
101	err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
102
103	DHD_OS_WAKE_UNLOCK(dhd);
104	return err;
105}
106
107static int
108dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
109{
110	int ret;
111	int cdc_len = len + sizeof(cdc_ioctl_t);
112	dhd_prot_t *prot = dhd->prot;
113
114	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
115
116	do {
117		ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len);
118		if (ret < 0)
119			break;
120	} while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
121
122	return ret;
123}
124
125static int
126dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
127{
128	dhd_prot_t *prot = dhd->prot;
129	cdc_ioctl_t *msg = &prot->msg;
130	void *info;
131	int ret = 0, retries = 0;
132	uint32 id, flags = 0;
133
134	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
135	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
136
137
138	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
139	if (cmd == WLC_GET_VAR && buf)
140	{
141		if (!strcmp((char *)buf, "bcmerrorstr"))
142		{
143			strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
144			goto done;
145		}
146		else if (!strcmp((char *)buf, "bcmerror"))
147		{
148			*(int *)buf = dhd->dongle_error;
149			goto done;
150		}
151	}
152
153	memset(msg, 0, sizeof(cdc_ioctl_t));
154
155	msg->cmd = htol32(cmd);
156	msg->len = htol32(len);
157	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
158	CDC_SET_IF_IDX(msg, ifidx);
159	/* add additional action bits */
160	action &= WL_IOCTL_ACTION_MASK;
161	msg->flags |= (action << CDCF_IOC_ACTION_SHIFT);
162	msg->flags = htol32(msg->flags);
163
164	if (buf)
165		memcpy(prot->buf, buf, len);
166
167	if ((ret = dhdcdc_msg(dhd)) < 0) {
168		if (!dhd->hang_was_sent)
169		DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
170		goto done;
171	}
172
173retry:
174	/* wait for interrupt and get first fragment */
175	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
176		goto done;
177
178	flags = ltoh32(msg->flags);
179	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
180
181	if ((id < prot->reqid) && (++retries < RETRIES))
182		goto retry;
183	if (id != prot->reqid) {
184		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
185		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
186		ret = -EINVAL;
187		goto done;
188	}
189
190	/* Check info buffer */
191	info = (void*)&msg[1];
192
193	/* Copy info buffer */
194	if (buf)
195	{
196		if (ret < (int)len)
197			len = ret;
198		memcpy(buf, info, len);
199	}
200
201	/* Check the ERROR flag */
202	if (flags & CDCF_IOC_ERROR)
203	{
204		ret = ltoh32(msg->status);
205		/* Cache error from dongle */
206		dhd->dongle_error = ret;
207	}
208
209done:
210	return ret;
211}
212
213static int
214dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
215{
216	dhd_prot_t *prot = dhd->prot;
217	cdc_ioctl_t *msg = &prot->msg;
218	int ret = 0;
219	uint32 flags, id;
220
221	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
222	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
223
224	if (dhd->busstate == DHD_BUS_DOWN) {
225		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
226		return -EIO;
227	}
228
229	/* don't talk to the dongle if fw is about to be reloaded */
230	if (dhd->hang_was_sent) {
231		DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
232			__FUNCTION__));
233		return -EIO;
234	}
235
236	memset(msg, 0, sizeof(cdc_ioctl_t));
237
238	msg->cmd = htol32(cmd);
239	msg->len = htol32(len);
240	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
241	CDC_SET_IF_IDX(msg, ifidx);
242	/* add additional action bits */
243	action &= WL_IOCTL_ACTION_MASK;
244	msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET;
245	msg->flags = htol32(msg->flags);
246
247	if (buf)
248		memcpy(prot->buf, buf, len);
249
250	if ((ret = dhdcdc_msg(dhd)) < 0) {
251		DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret));
252		goto done;
253	}
254
255	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
256		goto done;
257
258	flags = ltoh32(msg->flags);
259	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
260
261	if (id != prot->reqid) {
262		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
263		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
264		ret = -EINVAL;
265		goto done;
266	}
267
268	/* Check the ERROR flag */
269	if (flags & CDCF_IOC_ERROR)
270	{
271		ret = ltoh32(msg->status);
272		/* Cache error from dongle */
273		dhd->dongle_error = ret;
274	}
275
276done:
277	return ret;
278}
279
280
281int
282dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
283{
284	dhd_prot_t *prot = dhd->prot;
285	int ret = -1;
286	uint8 action;
287#if defined(NDIS630)
288	bool acquired = FALSE;
289#endif
290
291	if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
292		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
293		goto done;
294	}
295#if defined(NDIS630)
296	if (dhd_os_proto_block(dhd))
297	{
298		acquired = TRUE;
299	}
300	else
301	{
302		/* attempt to acquire protocol mutex timed out. */
303		ret = -1;
304		return ret;
305	}
306#endif /* NDIS630 */
307
308	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
309
310	ASSERT(len <= WLC_IOCTL_MAXLEN);
311
312	if (len > WLC_IOCTL_MAXLEN)
313		goto done;
314
315	if (prot->pending == TRUE) {
316		DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
317			ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
318			(unsigned long)prot->lastcmd));
319		if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
320			DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
321		}
322		goto done;
323	}
324
325	prot->pending = TRUE;
326	prot->lastcmd = ioc->cmd;
327	action = ioc->set;
328	if (action & WL_IOCTL_ACTION_SET)
329		ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
330	else {
331		ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
332		if (ret > 0)
333			ioc->used = ret - sizeof(cdc_ioctl_t);
334	}
335
336	/* Too many programs assume ioctl() returns 0 on success */
337	if (ret >= 0)
338		ret = 0;
339	else {
340		cdc_ioctl_t *msg = &prot->msg;
341		ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
342	}
343
344	/* Intercept the wme_dp ioctl here */
345	if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
346		int slen, val = 0;
347
348		slen = strlen("wme_dp") + 1;
349		if (len >= (int)(slen + sizeof(int)))
350			bcopy(((char *)buf + slen), &val, sizeof(int));
351		dhd->wme_dp = (uint8) ltoh32(val);
352	}
353
354	prot->pending = FALSE;
355
356done:
357#if defined(NDIS630)
358	if (acquired)
359	   dhd_os_proto_unblock(dhd);
360#endif
361	return ret;
362}
363
364int
365dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
366                  void *params, int plen, void *arg, int len, bool set)
367{
368	return BCME_UNSUPPORTED;
369}
370
371#ifdef PROP_TXSTATUS
372void
373dhd_wlfc_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
374{
375	int i;
376	uint8* ea;
377	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
378		dhdp->wlfc_state;
379	wlfc_hanger_t* h;
380	wlfc_mac_descriptor_t* mac_table;
381	wlfc_mac_descriptor_t* interfaces;
382	char* iftypes[] = {"STA", "AP", "WDS", "p2pGO", "p2pCL"};
383
384	if (wlfc == NULL) {
385		bcm_bprintf(strbuf, "wlfc not initialized yet\n");
386		return;
387	}
388	h = (wlfc_hanger_t*)wlfc->hanger;
389	if (h == NULL) {
390		bcm_bprintf(strbuf, "wlfc-hanger not initialized yet\n");
391	}
392
393	mac_table = wlfc->destination_entries.nodes;
394	interfaces = wlfc->destination_entries.interfaces;
395	bcm_bprintf(strbuf, "---- wlfc stats ----\n");
396	if (h) {
397		bcm_bprintf(strbuf, "wlfc hanger (pushed,popped,f_push,"
398			"f_pop,f_slot, pending) = (%d,%d,%d,%d,%d,%d)\n",
399			h->pushed,
400			h->popped,
401			h->failed_to_push,
402			h->failed_to_pop,
403			h->failed_slotfind,
404			(h->pushed - h->popped));
405	}
406
407	bcm_bprintf(strbuf, "wlfc fail(tlv,credit_rqst,mac_update,psmode_update), "
408		"(dq_full,sendq_full, rollback_fail) = (%d,%d,%d,%d), (%d,%d,%d)\n",
409		wlfc->stats.tlv_parse_failed,
410		wlfc->stats.credit_request_failed,
411		wlfc->stats.mac_update_failed,
412		wlfc->stats.psmode_update_failed,
413		wlfc->stats.delayq_full_error,
414		wlfc->stats.sendq_full_error,
415		wlfc->stats.rollback_failed);
416
417	bcm_bprintf(strbuf, "SENDQ (len,credit,sent) "
418		"(AC0[%d,%d,%d],AC1[%d,%d,%d],AC2[%d,%d,%d],AC3[%d,%d,%d],BC_MC[%d,%d,%d])\n",
419		wlfc->SENDQ.q[0].len, wlfc->FIFO_credit[0], wlfc->stats.sendq_pkts[0],
420		wlfc->SENDQ.q[1].len, wlfc->FIFO_credit[1], wlfc->stats.sendq_pkts[1],
421		wlfc->SENDQ.q[2].len, wlfc->FIFO_credit[2], wlfc->stats.sendq_pkts[2],
422		wlfc->SENDQ.q[3].len, wlfc->FIFO_credit[3], wlfc->stats.sendq_pkts[3],
423		wlfc->SENDQ.q[4].len, wlfc->FIFO_credit[4], wlfc->stats.sendq_pkts[4]);
424
425#ifdef PROP_TXSTATUS_DEBUG
426	bcm_bprintf(strbuf, "SENDQ dropped: AC[0-3]:(%d,%d,%d,%d), (bcmc,atim):(%d,%d)\n",
427		wlfc->stats.dropped_qfull[0], wlfc->stats.dropped_qfull[1],
428		wlfc->stats.dropped_qfull[2], wlfc->stats.dropped_qfull[3],
429		wlfc->stats.dropped_qfull[4], wlfc->stats.dropped_qfull[5]);
430#endif
431
432	bcm_bprintf(strbuf, "\n");
433	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
434		if (interfaces[i].occupied) {
435			char* iftype_desc;
436
437			if (interfaces[i].iftype > WLC_E_IF_ROLE_P2P_CLIENT)
438				iftype_desc = "<Unknown";
439			else
440				iftype_desc = iftypes[interfaces[i].iftype];
441
442			ea = interfaces[i].ea;
443			bcm_bprintf(strbuf, "INTERFACE[%d].ea = "
444				"[%02x:%02x:%02x:%02x:%02x:%02x], if:%d, type: %s"
445				"netif_flow_control:%s\n", i,
446				ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
447				interfaces[i].interface_id,
448				iftype_desc, ((wlfc->hostif_flow_state[i] == OFF)
449				? " OFF":" ON"));
450
451			bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ(len,state,credit)"
452				"= (%d,%s,%d)\n",
453				i,
454				interfaces[i].psq.len,
455				((interfaces[i].state ==
456				WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
457				interfaces[i].requested_credit);
458
459			bcm_bprintf(strbuf, "INTERFACE[%d].DELAYQ"
460				"(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
461				"(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
462				i,
463				interfaces[i].psq.q[0].len,
464				interfaces[i].psq.q[1].len,
465				interfaces[i].psq.q[2].len,
466				interfaces[i].psq.q[3].len,
467				interfaces[i].psq.q[4].len,
468				interfaces[i].psq.q[5].len,
469				interfaces[i].psq.q[6].len,
470				interfaces[i].psq.q[7].len);
471		}
472	}
473
474	bcm_bprintf(strbuf, "\n");
475	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
476		if (mac_table[i].occupied) {
477			ea = mac_table[i].ea;
478			bcm_bprintf(strbuf, "MAC_table[%d].ea = "
479				"[%02x:%02x:%02x:%02x:%02x:%02x], if:%d \n", i,
480				ea[0], ea[1], ea[2], ea[3], ea[4], ea[5],
481				mac_table[i].interface_id);
482
483			bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ(len,state,credit)"
484				"= (%d,%s,%d)\n",
485				i,
486				mac_table[i].psq.len,
487				((mac_table[i].state ==
488				WLFC_STATE_OPEN) ? " OPEN":"CLOSE"),
489				mac_table[i].requested_credit);
490#ifdef PROP_TXSTATUS_DEBUG
491			bcm_bprintf(strbuf, "MAC_table[%d]: (opened, closed) = (%d, %d)\n",
492				i, mac_table[i].opened_ct, mac_table[i].closed_ct);
493#endif
494			bcm_bprintf(strbuf, "MAC_table[%d].DELAYQ"
495				"(sup,ac0),(sup,ac1),(sup,ac2),(sup,ac3) = "
496				"(%d,%d),(%d,%d),(%d,%d),(%d,%d)\n",
497				i,
498				mac_table[i].psq.q[0].len,
499				mac_table[i].psq.q[1].len,
500				mac_table[i].psq.q[2].len,
501				mac_table[i].psq.q[3].len,
502				mac_table[i].psq.q[4].len,
503				mac_table[i].psq.q[5].len,
504				mac_table[i].psq.q[6].len,
505				mac_table[i].psq.q[7].len);
506		}
507	}
508
509#ifdef PROP_TXSTATUS_DEBUG
510	{
511		int avg;
512		int moving_avg = 0;
513		int moving_samples;
514
515		if (wlfc->stats.latency_sample_count) {
516			moving_samples = sizeof(wlfc->stats.deltas)/sizeof(uint32);
517
518			for (i = 0; i < moving_samples; i++)
519				moving_avg += wlfc->stats.deltas[i];
520			moving_avg /= moving_samples;
521
522			avg = (100 * wlfc->stats.total_status_latency) /
523				wlfc->stats.latency_sample_count;
524			bcm_bprintf(strbuf, "txstatus latency (average, last, moving[%d]) = "
525				"(%d.%d, %03d, %03d)\n",
526				moving_samples, avg/100, (avg - (avg/100)*100),
527				wlfc->stats.latency_most_recent,
528				moving_avg);
529		}
530	}
531
532	bcm_bprintf(strbuf, "wlfc- fifo[0-5] credit stats: sent = (%d,%d,%d,%d,%d,%d), "
533		"back = (%d,%d,%d,%d,%d,%d)\n",
534		wlfc->stats.fifo_credits_sent[0],
535		wlfc->stats.fifo_credits_sent[1],
536		wlfc->stats.fifo_credits_sent[2],
537		wlfc->stats.fifo_credits_sent[3],
538		wlfc->stats.fifo_credits_sent[4],
539		wlfc->stats.fifo_credits_sent[5],
540
541		wlfc->stats.fifo_credits_back[0],
542		wlfc->stats.fifo_credits_back[1],
543		wlfc->stats.fifo_credits_back[2],
544		wlfc->stats.fifo_credits_back[3],
545		wlfc->stats.fifo_credits_back[4],
546		wlfc->stats.fifo_credits_back[5]);
547	{
548		uint32 fifo_cr_sent = 0;
549		uint32 fifo_cr_acked = 0;
550		uint32 request_cr_sent = 0;
551		uint32 request_cr_ack = 0;
552		uint32 bc_mc_cr_ack = 0;
553
554		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_sent)/sizeof(uint32); i++) {
555			fifo_cr_sent += wlfc->stats.fifo_credits_sent[i];
556		}
557
558		for (i = 0; i < sizeof(wlfc->stats.fifo_credits_back)/sizeof(uint32); i++) {
559			fifo_cr_acked += wlfc->stats.fifo_credits_back[i];
560		}
561
562		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
563			if (wlfc->destination_entries.nodes[i].occupied) {
564				request_cr_sent +=
565					wlfc->destination_entries.nodes[i].dstncredit_sent_packets;
566			}
567		}
568		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
569			if (wlfc->destination_entries.interfaces[i].occupied) {
570				request_cr_sent +=
571				wlfc->destination_entries.interfaces[i].dstncredit_sent_packets;
572			}
573		}
574		for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
575			if (wlfc->destination_entries.nodes[i].occupied) {
576				request_cr_ack +=
577					wlfc->destination_entries.nodes[i].dstncredit_acks;
578			}
579		}
580		for (i = 0; i < WLFC_MAX_IFNUM; i++) {
581			if (wlfc->destination_entries.interfaces[i].occupied) {
582				request_cr_ack +=
583					wlfc->destination_entries.interfaces[i].dstncredit_acks;
584			}
585		}
586		bcm_bprintf(strbuf, "wlfc- (sent, status) => pq(%d,%d), vq(%d,%d),"
587			"other:%d, bc_mc:%d, signal-only, (sent,freed): (%d,%d)",
588			fifo_cr_sent, fifo_cr_acked,
589			request_cr_sent, request_cr_ack,
590			wlfc->destination_entries.other.dstncredit_acks,
591			bc_mc_cr_ack,
592			wlfc->stats.signal_only_pkts_sent, wlfc->stats.signal_only_pkts_freed);
593	}
594#endif /* PROP_TXSTATUS_DEBUG */
595	bcm_bprintf(strbuf, "\n");
596	bcm_bprintf(strbuf, "wlfc- pkt((in,2bus,txstats,hdrpull),(dropped,hdr_only,wlc_tossed)"
597		"(freed,free_err,rollback)) = "
598		"((%d,%d,%d,%d),(%d,%d,%d),(%d,%d,%d))\n",
599		wlfc->stats.pktin,
600		wlfc->stats.pkt2bus,
601		wlfc->stats.txstatus_in,
602		wlfc->stats.dhd_hdrpulls,
603
604		wlfc->stats.pktdropped,
605		wlfc->stats.wlfc_header_only_pkt,
606		wlfc->stats.wlc_tossed_pkts,
607
608		wlfc->stats.pkt_freed,
609		wlfc->stats.pkt_free_err, wlfc->stats.rollback);
610
611	bcm_bprintf(strbuf, "wlfc- suppress((d11,wlc,err),enq(d11,wl,hq,mac?),retx(d11,wlc,hq)) = "
612		"((%d,%d,%d),(%d,%d,%d,%d),(%d,%d,%d))\n",
613
614		wlfc->stats.d11_suppress,
615		wlfc->stats.wl_suppress,
616		wlfc->stats.bad_suppress,
617
618		wlfc->stats.psq_d11sup_enq,
619		wlfc->stats.psq_wlsup_enq,
620		wlfc->stats.psq_hostq_enq,
621		wlfc->stats.mac_handle_notfound,
622
623		wlfc->stats.psq_d11sup_retx,
624		wlfc->stats.psq_wlsup_retx,
625		wlfc->stats.psq_hostq_retx);
626	return;
627}
628
629/* Create a place to store all packet pointers submitted to the firmware until
630	a status comes back, suppress or otherwise.
631
632	hang-er: noun, a contrivance on which things are hung, as a hook.
633*/
634static void*
635dhd_wlfc_hanger_create(osl_t *osh, int max_items)
636{
637	int i;
638	wlfc_hanger_t* hanger;
639
640	/* allow only up to a specific size for now */
641	ASSERT(max_items == WLFC_HANGER_MAXITEMS);
642
643	if ((hanger = (wlfc_hanger_t*)MALLOC(osh, WLFC_HANGER_SIZE(max_items))) == NULL)
644		return NULL;
645
646	memset(hanger, 0, WLFC_HANGER_SIZE(max_items));
647	hanger->max_items = max_items;
648
649	for (i = 0; i < hanger->max_items; i++) {
650		hanger->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
651	}
652	return hanger;
653}
654
655static int
656dhd_wlfc_hanger_delete(osl_t *osh, void* hanger)
657{
658	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
659
660	if (h) {
661		MFREE(osh, h, WLFC_HANGER_SIZE(h->max_items));
662		return BCME_OK;
663	}
664	return BCME_BADARG;
665}
666
667static uint16
668dhd_wlfc_hanger_get_free_slot(void* hanger)
669{
670	int i;
671	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
672
673	if (h) {
674		for (i = 0; i < h->max_items; i++) {
675			if (h->items[i].state == WLFC_HANGER_ITEM_STATE_FREE)
676				return (uint16)i;
677		}
678		h->failed_slotfind++;
679	}
680	return WLFC_HANGER_MAXITEMS;
681}
682
683static int
684dhd_wlfc_hanger_get_genbit(void* hanger, void* pkt, uint32 slot_id, int* gen)
685{
686	int rc = BCME_OK;
687	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
688
689	*gen = 0xff;
690
691	/* this packet was not pushed at the time it went to the firmware */
692	if (slot_id == WLFC_HANGER_MAXITEMS)
693		return BCME_NOTFOUND;
694
695	if (h) {
696		if ((h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) ||
697			(h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED)) {
698			*gen = h->items[slot_id].gen;
699		}
700		else {
701			rc = BCME_NOTFOUND;
702		}
703	}
704	else
705		rc = BCME_BADARG;
706	return rc;
707}
708
709static int
710dhd_wlfc_hanger_pushpkt(void* hanger, void* pkt, uint32 slot_id)
711{
712	int rc = BCME_OK;
713	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
714
715	if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
716		if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
717			h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
718			h->items[slot_id].pkt = pkt;
719			h->items[slot_id].identifier = slot_id;
720			h->pushed++;
721		}
722		else {
723			h->failed_to_push++;
724			rc = BCME_NOTFOUND;
725		}
726	}
727	else
728		rc = BCME_BADARG;
729	return rc;
730}
731
732static int
733dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger)
734{
735	int rc = BCME_OK;
736	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
737
738	/* this packet was not pushed at the time it went to the firmware */
739	if (slot_id == WLFC_HANGER_MAXITEMS)
740		return BCME_NOTFOUND;
741
742	if (h) {
743		if (h->items[slot_id].state != WLFC_HANGER_ITEM_STATE_FREE) {
744			*pktout = h->items[slot_id].pkt;
745			if (remove_from_hanger) {
746				h->items[slot_id].state =
747					WLFC_HANGER_ITEM_STATE_FREE;
748				h->items[slot_id].pkt = NULL;
749				h->items[slot_id].identifier = 0;
750				h->items[slot_id].gen = 0xff;
751				h->popped++;
752			}
753		}
754		else {
755			h->failed_to_pop++;
756			rc = BCME_NOTFOUND;
757		}
758	}
759	else
760		rc = BCME_BADARG;
761	return rc;
762}
763
764static int
765dhd_wlfc_hanger_mark_suppressed(void* hanger, uint32 slot_id, uint8 gen)
766{
767	int rc = BCME_OK;
768	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
769
770	/* this packet was not pushed at the time it went to the firmware */
771	if (slot_id == WLFC_HANGER_MAXITEMS)
772		return BCME_NOTFOUND;
773
774	h->items[slot_id].gen = gen;
775	if (h && (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE)) {
776		h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED;
777	}
778	else
779		rc = BCME_BADARG;
780	return rc;
781}
782
783static int
784_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal,
785	uint8 tim_bmp, uint8 mac_handle, uint32 htodtag)
786{
787	uint32 wl_pktinfo = 0;
788	uint8* wlh;
789	uint8 dataOffset;
790	uint8 fillers;
791	uint8 tim_signal_len = 0;
792
793	struct bdc_header *h;
794
795	if (tim_signal) {
796		tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
797	}
798
799	/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
800	dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len;
801	fillers = ROUNDUP(dataOffset, 4) - dataOffset;
802	dataOffset += fillers;
803
804	PKTPUSH(ctx->osh, p, dataOffset);
805	wlh = (uint8*) PKTDATA(ctx->osh, p);
806
807	wl_pktinfo = htol32(htodtag);
808
809	wlh[0] = WLFC_CTL_TYPE_PKTTAG;
810	wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG;
811	memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32));
812
813	if (tim_signal_len) {
814		wlh[dataOffset - fillers - tim_signal_len ] =
815			WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
816		wlh[dataOffset - fillers - tim_signal_len + 1] =
817			WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
818		wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
819		wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
820	}
821	if (fillers)
822		memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
823
824	PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
825	h = (struct bdc_header *)PKTDATA(ctx->osh, p);
826	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
827	if (PKTSUMNEEDED(p))
828		h->flags |= BDC_FLAG_SUM_NEEDED;
829
830
831	h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
832	h->flags2 = 0;
833	h->dataOffset = dataOffset >> 2;
834	BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
835	return BCME_OK;
836}
837
838static int
839_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
840{
841	struct bdc_header *h;
842
843	if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
844		WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
845		           PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
846		return BCME_ERROR;
847	}
848	h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
849
850	/* pull BDC header */
851	PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
852	/* pull wl-header */
853	PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
854	return BCME_OK;
855}
856
857static wlfc_mac_descriptor_t*
858_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
859{
860	int i;
861	wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
862	uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
863	uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
864
865	if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) ||
866		ETHER_ISMULTI(dstn) ||
867		(ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) &&
868		(ctx->destination_entries.interfaces[ifid].occupied)) {
869			return &ctx->destination_entries.interfaces[ifid];
870	}
871
872	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
873		if (table[i].occupied) {
874			if (table[i].interface_id == ifid) {
875				if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN))
876					return &table[i];
877			}
878		}
879	}
880	return &ctx->destination_entries.other;
881}
882
883static int
884_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
885	void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
886{
887	/*
888	put the packet back to the head of queue
889
890	- a packet from send-q will need to go back to send-q and not delay-q
891	since that will change the order of packets.
892	- suppressed packet goes back to suppress sub-queue
893	- pull out the header, if new or delayed packet
894
895	Note: hslot is used only when header removal is done.
896	*/
897	wlfc_mac_descriptor_t* entry;
898	void* pktout;
899	int rc = BCME_OK;
900	int prec;
901
902	entry = _dhd_wlfc_find_table_entry(ctx, p);
903	prec = DHD_PKTTAG_FIFO(PKTTAG(p));
904	if (entry != NULL) {
905		if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) {
906			/* wl-header is saved for suppressed packets */
907			if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) {
908				WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
909				rc = BCME_ERROR;
910			}
911		}
912		else {
913			/* remove header first */
914			_dhd_wlfc_pullheader(ctx, p);
915
916			if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
917				/* delay-q packets are going to delay-q */
918				if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) {
919					WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
920					rc = BCME_ERROR;
921				}
922			}
923			else {
924				/* these are going to SENDQ */
925				if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) {
926					WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
927					rc = BCME_ERROR;
928				}
929			}
930			/* free the hanger slot */
931			dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
932
933			/* decrement sequence count */
934			WLFC_DECR_SEQCOUNT(entry, prec);
935		}
936		/*
937		if this packet did not count against FIFO credit, it must have
938		taken a requested_credit from the firmware (for pspoll etc.)
939		*/
940		if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
941			entry->requested_credit++;
942		}
943	}
944	else {
945		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
946		rc = BCME_ERROR;
947	}
948	if (rc != BCME_OK)
949		ctx->stats.rollback_failed++;
950	else
951		ctx->stats.rollback++;
952
953	return rc;
954}
955
956static void
957_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
958{
959	if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
960		/* start traffic */
961		ctx->hostif_flow_state[if_id] = OFF;
962		/*
963		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
964		pq->len, if_id, __FUNCTION__));
965		*/
966		WLFC_DBGMESG(("F"));
967		dhd_txflowcontrol(ctx->dhdp, if_id, OFF);
968		ctx->toggle_host_if = 0;
969	}
970	if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) {
971		/* stop traffic */
972		ctx->hostif_flow_state[if_id] = ON;
973		/*
974		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic   %s()\n",
975		pq->len, if_id, __FUNCTION__));
976		*/
977		WLFC_DBGMESG(("N"));
978		dhd_txflowcontrol(ctx->dhdp, if_id, ON);
979		ctx->host_ifidx = if_id;
980		ctx->toggle_host_if = 1;
981	}
982	return;
983}
984
985static int
986_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
987	uint8 ta_bmp)
988{
989	int rc = BCME_OK;
990	void* p = NULL;
991	int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12;
992
993	/* allocate a dummy packet */
994	p = PKTGET(ctx->osh, dummylen, TRUE);
995	if (p) {
996		PKTPULL(ctx->osh, p, dummylen);
997		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
998		_dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0);
999		DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
1000#ifdef PROP_TXSTATUS_DEBUG
1001		ctx->stats.signal_only_pkts_sent++;
1002#endif
1003		rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p);
1004		if (rc != BCME_OK) {
1005			PKTFREE(ctx->osh, p, TRUE);
1006		}
1007	}
1008	else {
1009		DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
1010		           __FUNCTION__, dummylen));
1011		rc = BCME_NOMEM;
1012	}
1013	return rc;
1014}
1015
1016/* Return TRUE if traffic availability changed */
1017static bool
1018_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1019	int prec)
1020{
1021	bool rc = FALSE;
1022
1023	if (entry->state == WLFC_STATE_CLOSE) {
1024		if ((pktq_plen(&entry->psq, (prec << 1)) == 0) &&
1025			(pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) {
1026
1027			if (entry->traffic_pending_bmp & NBITVAL(prec)) {
1028				rc = TRUE;
1029				entry->traffic_pending_bmp =
1030					entry->traffic_pending_bmp & ~ NBITVAL(prec);
1031			}
1032		}
1033		else {
1034			if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
1035				rc = TRUE;
1036				entry->traffic_pending_bmp =
1037					entry->traffic_pending_bmp | NBITVAL(prec);
1038			}
1039		}
1040	}
1041	if (rc) {
1042		/* request a TIM update to firmware at the next piggyback opportunity */
1043		if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
1044			entry->send_tim_signal = 1;
1045			_dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
1046			entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1047			entry->send_tim_signal = 0;
1048		}
1049		else {
1050			rc = FALSE;
1051		}
1052	}
1053	return rc;
1054}
1055
1056static int
1057_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p, uint8 gen)
1058{
1059	wlfc_mac_descriptor_t* entry;
1060
1061	entry = _dhd_wlfc_find_table_entry(ctx, p);
1062	if (entry == NULL) {
1063		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1064		return BCME_NOTFOUND;
1065	}
1066	/*
1067	- suppressed packets go to sub_queue[2*prec + 1] AND
1068	- delayed packets go to sub_queue[2*prec + 0] to ensure
1069	order of delivery.
1070	*/
1071	if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) {
1072		ctx->stats.delayq_full_error++;
1073		/* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
1074		WLFC_DBGMESG(("s"));
1075		return BCME_ERROR;
1076	}
1077	/* A packet has been pushed, update traffic availability bitmap, if applicable */
1078	_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1079	_dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1080	return BCME_OK;
1081}
1082
1083static int
1084_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
1085	wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot, int prec)
1086{
1087	int rc = BCME_OK;
1088	int hslot = WLFC_HANGER_MAXITEMS;
1089	bool send_tim_update = FALSE;
1090	uint32 htod = 0;
1091	uint8 free_ctr;
1092
1093	*slot = hslot;
1094
1095	if (entry == NULL) {
1096		entry = _dhd_wlfc_find_table_entry(ctx, p);
1097	}
1098
1099	if (entry == NULL) {
1100		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1101		return BCME_ERROR;
1102	}
1103	if (entry->send_tim_signal) {
1104		send_tim_update = TRUE;
1105		entry->send_tim_signal = 0;
1106		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1107	}
1108	if (header_needed) {
1109		hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1110		free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1111		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1112		WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation);
1113		entry->transit_count++;
1114	}
1115	else {
1116		hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1117		free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1118	}
1119	WLFC_PKTID_HSLOT_SET(htod, hslot);
1120	WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr);
1121	DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1122	WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1123	WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1124
1125	if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1126		/*
1127		Indicate that this packet is being sent in response to an
1128		explicit request from the firmware side.
1129		*/
1130		WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1131	}
1132	else {
1133		WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1134	}
1135	if (header_needed) {
1136		rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update,
1137			entry->traffic_lastreported_bmp, entry->mac_handle, htod);
1138		if (rc == BCME_OK) {
1139			DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1140			/*
1141			a new header was created for this packet.
1142			push to hanger slot and scrub q. Since bus
1143			send succeeded, increment seq number as well.
1144			*/
1145			rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1146			if (rc == BCME_OK) {
1147				/* increment free running sequence count */
1148				WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1149#ifdef PROP_TXSTATUS_DEBUG
1150				((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time =
1151					OSL_SYSUPTIME();
1152#endif
1153			}
1154			else {
1155				WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n",
1156					__FUNCTION__, rc));
1157			}
1158		}
1159	}
1160	else {
1161		int gen;
1162
1163		/* remove old header */
1164		_dhd_wlfc_pullheader(ctx, p);
1165
1166		hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1167		dhd_wlfc_hanger_get_genbit(ctx->hanger, p, hslot, &gen);
1168
1169		WLFC_PKTFLAG_SET_GENERATION(htod, gen);
1170		free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1171		/* push new header */
1172		_dhd_wlfc_pushheader(ctx, p, send_tim_update,
1173			entry->traffic_lastreported_bmp, entry->mac_handle, htod);
1174	}
1175	*slot = hslot;
1176	return rc;
1177}
1178
1179static int
1180_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx,
1181	wlfc_mac_descriptor_t* entry, int prec)
1182{
1183	if (ctx->destination_entries.interfaces[entry->interface_id].iftype ==
1184		WLC_E_IF_ROLE_P2P_GO) {
1185		/* - destination interface is of type p2p GO.
1186		For a p2pGO interface, if the destination is OPEN but the interface is
1187		CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1188		destination-specific-credit left send packets. This is because the
1189		firmware storing the destination-specific-requested packet in queue.
1190		*/
1191		if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1192			(entry->requested_packet == 0))
1193			return 1;
1194	}
1195	/* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1196	if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1197		(entry->requested_packet == 0)) ||
1198		(!(entry->ac_bitmap & (1 << prec))))
1199		return 1;
1200
1201	return 0;
1202}
1203
1204static void*
1205_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx,
1206	int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out)
1207{
1208	wlfc_mac_descriptor_t* entry;
1209	wlfc_mac_descriptor_t* table;
1210	uint8 token_pos;
1211	int total_entries;
1212	void* p = NULL;
1213	int pout;
1214	int i;
1215
1216	*entry_out = NULL;
1217	token_pos = ctx->token_pos[prec];
1218	/* most cases a packet will count against FIFO credit */
1219	*ac_credit_spent = 1;
1220	*needs_hdr = 1;
1221
1222	/* search all entries, include nodes as well as interfaces */
1223	table = (wlfc_mac_descriptor_t*)&ctx->destination_entries;
1224	total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t);
1225
1226	for (i = 0; i < total_entries; i++) {
1227		entry = &table[(token_pos + i) % total_entries];
1228		if (entry->occupied) {
1229			if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) {
1230				p = pktq_mdeq(&entry->psq,
1231					/* higher precedence will be picked up first,
1232					i.e. suppressed packets before delayed ones
1233					*/
1234					NBITVAL((prec << 1) + 1),
1235					&pout);
1236						*needs_hdr = 0;
1237
1238				if (p == NULL) {
1239					if (entry->suppressed == TRUE) {
1240						if ((entry->suppr_transit_count <=
1241							entry->suppress_count)) {
1242							entry->suppressed = FALSE;
1243						} else {
1244							return NULL;
1245						}
1246					}
1247					/* De-Q from delay Q */
1248					p = pktq_mdeq(&entry->psq,
1249						NBITVAL((prec << 1)),
1250						&pout);
1251					*needs_hdr = 1;
1252					}
1253
1254				if (p != NULL) {
1255					/* did the packet come from suppress sub-queue? */
1256					if (entry->requested_credit > 0) {
1257						entry->requested_credit--;
1258#ifdef PROP_TXSTATUS_DEBUG
1259						entry->dstncredit_sent_packets++;
1260#endif
1261						/*
1262						if the packet was pulled out while destination is in
1263						closed state but had a non-zero packets requested,
1264						then this should not count against the FIFO credit.
1265						That is due to the fact that the firmware will
1266						most likely hold onto this packet until a suitable
1267						time later to push it to the appropriate  AC FIFO.
1268						*/
1269						if (entry->state == WLFC_STATE_CLOSE)
1270							*ac_credit_spent = 0;
1271					}
1272					else if (entry->requested_packet > 0) {
1273						entry->requested_packet--;
1274						DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1275						if (entry->state == WLFC_STATE_CLOSE)
1276							*ac_credit_spent = 0;
1277					}
1278					/* move token to ensure fair round-robin */
1279					ctx->token_pos[prec] =
1280						(token_pos + i + 1) % total_entries;
1281					*entry_out = entry;
1282					_dhd_wlfc_flow_control_check(ctx, &entry->psq,
1283						DHD_PKTTAG_IF(PKTTAG(p)));
1284					/*
1285					A packet has been picked up, update traffic
1286					availability bitmap, if applicable
1287					*/
1288					_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1289					return p;
1290				}
1291			}
1292		}
1293	}
1294	return NULL;
1295}
1296
1297static void*
1298_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec)
1299{
1300	wlfc_mac_descriptor_t* entry;
1301	void* p;
1302
1303
1304	p = pktq_pdeq(&ctx->SENDQ, prec);
1305	if (p != NULL) {
1306		if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))
1307			/* bc/mc packets do not have a delay queue */
1308			return p;
1309
1310		entry = _dhd_wlfc_find_table_entry(ctx, p);
1311
1312		if (entry == NULL) {
1313			WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1314			return p;
1315		}
1316
1317		while ((p != NULL)) {
1318			/*
1319			- suppressed packets go to sub_queue[2*prec + 1] AND
1320			- delayed packets go to sub_queue[2*prec + 0] to ensure
1321			order of delivery.
1322			*/
1323			if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) {
1324				WLFC_DBGMESG(("D"));
1325				/* dhd_txcomplete(ctx->dhdp, p, FALSE); */
1326				PKTFREE(ctx->osh, p, TRUE);
1327				ctx->stats.delayq_full_error++;
1328			}
1329			/*
1330			A packet has been pushed, update traffic availability bitmap,
1331			if applicable
1332			*/
1333			_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1334
1335			p = pktq_pdeq(&ctx->SENDQ, prec);
1336			if (p == NULL)
1337				break;
1338
1339			entry = _dhd_wlfc_find_table_entry(ctx, p);
1340
1341			if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) {
1342				return p;
1343			}
1344		}
1345	}
1346	return p;
1347}
1348
1349static int
1350_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1351	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1352{
1353	int rc = BCME_OK;
1354
1355	if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1356		entry->occupied = 1;
1357		entry->state = WLFC_STATE_OPEN;
1358		entry->requested_credit = 0;
1359		entry->interface_id = ifid;
1360		entry->iftype = iftype;
1361		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1362		/* for an interface entry we may not care about the MAC address */
1363		if (ea != NULL)
1364			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1365		pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1366	}
1367	else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) {
1368		entry->occupied = 1;
1369		entry->state = WLFC_STATE_OPEN;
1370		entry->requested_credit = 0;
1371		entry->interface_id = ifid;
1372		entry->iftype = iftype;
1373		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1374		/* for an interface entry we may not care about the MAC address */
1375		if (ea != NULL)
1376			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1377	}
1378	else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1379		entry->occupied = 0;
1380		entry->state = WLFC_STATE_CLOSE;
1381		entry->requested_credit = 0;
1382		/* enable after packets are queued-deqeued properly.
1383		pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0);
1384		*/
1385	}
1386	return rc;
1387}
1388
1389int
1390_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac)
1391{
1392	int lender_ac;
1393	int rc = BCME_ERROR;
1394
1395	if (ctx == NULL || available_credit_map == 0) {
1396		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1397		return BCME_BADARG;
1398	}
1399
1400	/* Borrow from lowest priority available AC (including BC/MC credits) */
1401	for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) {
1402		if ((available_credit_map && (1 << lender_ac)) &&
1403		   (ctx->FIFO_credit[lender_ac] > 0)) {
1404			ctx->credits_borrowed[borrower_ac][lender_ac]++;
1405			ctx->FIFO_credit[lender_ac]--;
1406			rc = BCME_OK;
1407			break;
1408		}
1409	}
1410
1411	return rc;
1412}
1413
1414int
1415dhd_wlfc_interface_entry_update(void* state,
1416	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1417{
1418	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1419	wlfc_mac_descriptor_t* entry;
1420
1421	if (ifid >= WLFC_MAX_IFNUM)
1422		return BCME_BADARG;
1423
1424	entry = &ctx->destination_entries.interfaces[ifid];
1425	return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea);
1426}
1427
1428int
1429dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
1430{
1431	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1432
1433	/* update the AC FIFO credit map */
1434	ctx->FIFO_credit[0] = credits[0];
1435	ctx->FIFO_credit[1] = credits[1];
1436	ctx->FIFO_credit[2] = credits[2];
1437	ctx->FIFO_credit[3] = credits[3];
1438	/* credit for bc/mc packets */
1439	ctx->FIFO_credit[4] = credits[4];
1440	/* credit for ATIM FIFO is not used yet. */
1441	ctx->FIFO_credit[5] = 0;
1442	return BCME_OK;
1443}
1444
1445int
1446dhd_wlfc_enque_sendq(void* state, int prec, void* p)
1447{
1448	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1449
1450	if ((state == NULL) ||
1451		/* prec = AC_COUNT is used for bc/mc queue */
1452		(prec > AC_COUNT) ||
1453		(p == NULL)) {
1454		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1455		return BCME_BADARG;
1456	}
1457	if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) {
1458		ctx->stats.sendq_full_error++;
1459		/*
1460		WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n",
1461		__FUNCTION__, __LINE__, ctx->SENDQ.len));
1462		*/
1463		WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec);
1464		WLFC_DBGMESG(("Q"));
1465		PKTFREE(ctx->osh, p, TRUE);
1466		return BCME_ERROR;
1467	}
1468	ctx->stats.pktin++;
1469	/* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */
1470	return BCME_OK;
1471}
1472
1473int
1474_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
1475    dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
1476{
1477	uint32 hslot;
1478	int	rc;
1479
1480	/*
1481		if ac_fifo_credit_spent = 0
1482
1483		This packet will not count against the FIFO credit.
1484		To ensure the txstatus corresponding to this packet
1485		does not provide an implied credit (default behavior)
1486		mark the packet accordingly.
1487
1488		if ac_fifo_credit_spent = 1
1489
1490		This is a normal packet and it counts against the FIFO
1491		credit count.
1492	*/
1493	DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
1494	rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p,
1495	     commit_info->needs_hdr, &hslot, ac);
1496
1497	if (rc == BCME_OK)
1498		rc = fcommit(commit_ctx, commit_info->p);
1499	else
1500		ctx->stats.generic_error++;
1501
1502	if (rc == BCME_OK) {
1503		ctx->stats.pkt2bus++;
1504		if (commit_info->ac_fifo_credit_spent) {
1505			ctx->stats.sendq_pkts[ac]++;
1506			WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
1507		}
1508	}
1509	else {
1510		/*
1511		   bus commit has failed, rollback.
1512		   - remove wl-header for a delayed packet
1513		   - save wl-header header for suppressed packets
1514		*/
1515		rc = _dhd_wlfc_rollback_packet_toq(ctx,	commit_info->p,
1516		     (commit_info->pkt_type), hslot);
1517		if (rc != BCME_OK)
1518			ctx->stats.rollback_failed++;
1519
1520		rc = BCME_ERROR;
1521	}
1522
1523	return rc;
1524}
1525
1526int
1527dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx)
1528{
1529	int ac;
1530	int credit;
1531	int rc;
1532	dhd_wlfc_commit_info_t  commit_info;
1533	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1534	int credit_count = 0;
1535	int bus_retry_count = 0;
1536	uint8 ac_available = 0;  /* Bitmask for 4 ACs + BC/MC */
1537
1538	if ((state == NULL) ||
1539		(fcommit == NULL)) {
1540		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1541		return BCME_BADARG;
1542	}
1543
1544	memset(&commit_info, 0, sizeof(commit_info));
1545
1546	/*
1547	Commit packets for regular AC traffic. Higher priority first.
1548	First, use up FIFO credits available to each AC. Based on distribution
1549	and credits left, borrow from other ACs as applicable
1550
1551	-NOTE:
1552	If the bus between the host and firmware is overwhelmed by the
1553	traffic from host, it is possible that higher priority traffic
1554	starves the lower priority queue. If that occurs often, we may
1555	have to employ weighted round-robin or ucode scheme to avoid
1556	low priority packet starvation.
1557	*/
1558
1559	for (ac = AC_COUNT; ac >= 0; ac--) {
1560
1561		int initial_credit_count = ctx->FIFO_credit[ac];
1562
1563		/* packets from SENDQ are fresh and they'd need header and have no MAC entry */
1564		commit_info.needs_hdr = 1;
1565		commit_info.mac_entry = NULL;
1566		commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1567
1568		do {
1569			commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac);
1570			if (commit_info.p == NULL)
1571				break;
1572			else if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(commit_info.p)))) {
1573				ASSERT(ac == AC_COUNT);
1574
1575				if (ctx->FIFO_credit[ac]) {
1576					rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1577						fcommit, commit_ctx);
1578
1579			/* Bus commits may fail (e.g. flow control); abort after retries */
1580					if (rc == BCME_OK) {
1581						if (commit_info.ac_fifo_credit_spent) {
1582							(void) _dhd_wlfc_borrow_credit(ctx,
1583								ac_available, ac);
1584							credit_count--;
1585						}
1586					} else {
1587						bus_retry_count++;
1588						if (bus_retry_count >= BUS_RETRIES) {
1589							DHD_ERROR((" %s: bus error\n",
1590								__FUNCTION__));
1591							return rc;
1592						}
1593					}
1594				}
1595			}
1596
1597		} while (commit_info.p);
1598
1599		for (credit = 0; credit < ctx->FIFO_credit[ac];) {
1600			commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1601			                &(commit_info.ac_fifo_credit_spent),
1602			                &(commit_info.needs_hdr),
1603			                &(commit_info.mac_entry));
1604
1605			if (commit_info.p == NULL)
1606				break;
1607
1608			commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1609				eWLFC_PKTTYPE_SUPPRESSED;
1610
1611			rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1612			     fcommit, commit_ctx);
1613
1614			/* Bus commits may fail (e.g. flow control); abort after retries */
1615			if (rc == BCME_OK) {
1616				if (commit_info.ac_fifo_credit_spent) {
1617					credit++;
1618				}
1619			}
1620			else {
1621				bus_retry_count++;
1622				if (bus_retry_count >= BUS_RETRIES) {
1623					DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n"));
1624					ctx->FIFO_credit[ac] -= credit;
1625					return rc;
1626				}
1627			}
1628		}
1629
1630		ctx->FIFO_credit[ac] -= credit;
1631
1632
1633		/* If no credits were used, the queue is idle and can be re-used
1634		   Note that resv credits cannot be borrowed
1635		   */
1636		if (initial_credit_count == ctx->FIFO_credit[ac]) {
1637			ac_available |= (1 << ac);
1638			credit_count += ctx->FIFO_credit[ac];
1639		}
1640	}
1641
1642	/* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD
1643
1644	   Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to:
1645	   a) ignore BC/MC for deferring borrow
1646	   b) ignore AC_BE being available along with other ACs
1647		  (this should happen only for pure BC/MC traffic)
1648
1649	   i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and
1650	   we do not care if AC_BE and BC/MC are available or not
1651	   */
1652	if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) {
1653
1654		if (ctx->allow_credit_borrow) {
1655			ac = 1;  /* Set ac to AC_BE and borrow credits */
1656		}
1657		else {
1658			int delta;
1659			int curr_t = OSL_SYSUPTIME();
1660
1661			if (curr_t > ctx->borrow_defer_timestamp)
1662				delta = curr_t - ctx->borrow_defer_timestamp;
1663			else
1664				delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp;
1665
1666			if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
1667				/* Reset borrow but defer to next iteration (defensive borrowing) */
1668				ctx->allow_credit_borrow = TRUE;
1669				ctx->borrow_defer_timestamp = 0;
1670			}
1671			return BCME_OK;
1672		}
1673	}
1674	else {
1675		/* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
1676		ctx->allow_credit_borrow = FALSE;
1677		ctx->borrow_defer_timestamp = OSL_SYSUPTIME();
1678		return BCME_OK;
1679	}
1680
1681	/* At this point, borrow all credits only for "ac" (which should be set above to AC_BE)
1682	   Generically use "ac" only in case we extend to all ACs in future
1683	   */
1684	for (; (credit_count > 0);) {
1685
1686		commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1687		                &(commit_info.ac_fifo_credit_spent),
1688		                &(commit_info.needs_hdr),
1689		                &(commit_info.mac_entry));
1690		if (commit_info.p == NULL)
1691			break;
1692
1693		commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1694			eWLFC_PKTTYPE_SUPPRESSED;
1695
1696		rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1697		     fcommit, commit_ctx);
1698
1699		/* Bus commits may fail (e.g. flow control); abort after retries */
1700		if (rc == BCME_OK) {
1701			if (commit_info.ac_fifo_credit_spent) {
1702				(void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac);
1703				credit_count--;
1704			}
1705		}
1706		else {
1707			bus_retry_count++;
1708			if (bus_retry_count >= BUS_RETRIES) {
1709				DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n"));
1710				return rc;
1711			}
1712		}
1713	}
1714
1715	return BCME_OK;
1716}
1717
1718static uint8
1719dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea)
1720{
1721	wlfc_mac_descriptor_t* table =
1722		((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
1723	uint8 table_index;
1724
1725	if (ea != NULL) {
1726		for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
1727			if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
1728				table[table_index].occupied)
1729				return table_index;
1730		}
1731	}
1732	return WLFC_MAC_DESC_ID_INVALID;
1733}
1734
1735void
1736dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
1737{
1738	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1739		dhd->wlfc_state;
1740	void* p;
1741	int fifo_id;
1742
1743	if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
1744#ifdef PROP_TXSTATUS_DEBUG
1745		wlfc->stats.signal_only_pkts_freed++;
1746#endif
1747		/* is this a signal-only packet? */
1748		PKTFREE(wlfc->osh, txp, TRUE);
1749		return;
1750	}
1751	if (!success) {
1752		WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
1753			__FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
1754		dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG
1755			(PKTTAG(txp))), &p, 1);
1756
1757		/* indicate failure and free the packet */
1758		dhd_txcomplete(dhd, txp, FALSE);
1759
1760		/* return the credit, if necessary */
1761		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) {
1762			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1763
1764			fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp));
1765
1766			/* Return credits to highest priority lender first */
1767			for (lender = AC_COUNT; lender >= 0; lender--) {
1768				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1769					wlfc->FIFO_credit[lender]++;
1770					wlfc->credits_borrowed[fifo_id][lender]--;
1771					credit_returned = 1;
1772					break;
1773				}
1774			}
1775
1776			if (!credit_returned) {
1777				wlfc->FIFO_credit[fifo_id]++;
1778			}
1779		}
1780
1781		PKTFREE(wlfc->osh, txp, TRUE);
1782	}
1783	return;
1784}
1785
1786/* Handle discard or suppress indication */
1787static int
1788dhd_wlfc_txstatus_update(dhd_pub_t *dhd, uint8* pkt_info)
1789{
1790	uint8 	status_flag;
1791	uint32	status;
1792	int		ret;
1793	int		remove_from_hanger = 1;
1794	void*	pktbuf;
1795	uint8	fifo_id;
1796	wlfc_mac_descriptor_t* entry = NULL;
1797	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1798		dhd->wlfc_state;
1799
1800	memcpy(&status, pkt_info, sizeof(uint32));
1801	status_flag = WL_TXSTATUS_GET_FLAGS(status);
1802	wlfc->stats.txstatus_in++;
1803
1804	if (status_flag == WLFC_CTL_PKTFLAG_DISCARD) {
1805		wlfc->stats.pkt_freed++;
1806	}
1807
1808	else if (status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) {
1809		wlfc->stats.d11_suppress++;
1810		remove_from_hanger = 0;
1811	}
1812
1813	else if (status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS) {
1814		wlfc->stats.wl_suppress++;
1815		remove_from_hanger = 0;
1816	}
1817
1818	else if (status_flag == WLFC_CTL_PKTFLAG_TOSSED_BYWLC) {
1819		wlfc->stats.wlc_tossed_pkts++;
1820	}
1821
1822	ret = dhd_wlfc_hanger_poppkt(wlfc->hanger,
1823		WLFC_PKTID_HSLOT_GET(status), &pktbuf, remove_from_hanger);
1824	if (ret != BCME_OK) {
1825		/* do something */
1826		return ret;
1827	}
1828
1829	entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1830
1831	if (!remove_from_hanger) {
1832		/* this packet was suppressed */
1833		if (!entry->suppressed ||
1834			entry->generation != WLFC_PKTID_GEN(status)) {
1835			entry->suppressed = TRUE;
1836			entry->suppress_count = pktq_mlen(&entry->psq,
1837			NBITVAL((WL_TXSTATUS_GET_FIFO(status) << 1) + 1));
1838			entry->suppr_transit_count = entry->transit_count;
1839
1840		}
1841
1842		entry->generation = WLFC_PKTID_GEN(status);
1843	}
1844
1845#ifdef PROP_TXSTATUS_DEBUG
1846	{
1847		uint32 new_t = OSL_SYSUPTIME();
1848		uint32 old_t;
1849		uint32 delta;
1850		old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1851			WLFC_PKTID_HSLOT_GET(status)].push_time;
1852
1853
1854		wlfc->stats.latency_sample_count++;
1855		if (new_t > old_t)
1856			delta = new_t - old_t;
1857		else
1858			delta = 0xffffffff + new_t - old_t;
1859		wlfc->stats.total_status_latency += delta;
1860		wlfc->stats.latency_most_recent = delta;
1861
1862		wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1863		if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1864			wlfc->stats.idx_delta = 0;
1865	}
1866#endif /* PROP_TXSTATUS_DEBUG */
1867
1868	fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1869
1870	/* pick up the implicit credit from this packet */
1871	if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1872		if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1873
1874			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1875
1876			/* Return credits to highest priority lender first */
1877			for (lender = AC_COUNT; lender >= 0; lender--)	{
1878				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1879					wlfc->FIFO_credit[lender]++;
1880					wlfc->credits_borrowed[fifo_id][lender]--;
1881					credit_returned = 1;
1882					break;
1883				}
1884			}
1885
1886			if (!credit_returned) {
1887				wlfc->FIFO_credit[fifo_id]++;
1888			}
1889		}
1890	}
1891	else {
1892		/*
1893		if this packet did not count against FIFO credit, it must have
1894		taken a requested_credit from the destination entry (for pspoll etc.)
1895		*/
1896		if (!entry) {
1897
1898			entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1899		}
1900		if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1901			entry->requested_credit++;
1902#ifdef PROP_TXSTATUS_DEBUG
1903		entry->dstncredit_acks++;
1904#endif
1905	}
1906	if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1907		(status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1908		ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf, WLFC_PKTID_GEN(status));
1909		if (ret != BCME_OK) {
1910			/* delay q is full, drop this packet */
1911			dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1912			&pktbuf, 1);
1913
1914			/* indicate failure and free the packet */
1915			dhd_txcomplete(dhd, pktbuf, FALSE);
1916			entry->transit_count--;
1917			/* This packet is transmitted Successfully by
1918			 *  dongle even after first suppress.
1919			 */
1920			if (entry->suppressed) {
1921				entry->suppr_transit_count--;
1922			}
1923			PKTFREE(wlfc->osh, pktbuf, TRUE);
1924		} else {
1925			/* Mark suppressed to avoid a double free during wlfc cleanup */
1926			dhd_wlfc_hanger_mark_suppressed(wlfc->hanger,
1927			WLFC_PKTID_HSLOT_GET(status), WLFC_PKTID_GEN(status));
1928			entry->suppress_count++;
1929		}
1930	}
1931	else {
1932		dhd_txcomplete(dhd, pktbuf, TRUE);
1933		entry->transit_count--;
1934
1935		/* This packet is transmitted Successfully by dongle even after first suppress. */
1936		if (entry->suppressed) {
1937			entry->suppr_transit_count--;
1938		}
1939		/* free the packet */
1940		PKTFREE(wlfc->osh, pktbuf, TRUE);
1941	}
1942	return BCME_OK;
1943}
1944
1945static int
1946dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
1947{
1948	int i;
1949	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1950		dhd->wlfc_state;
1951	for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
1952#ifdef PROP_TXSTATUS_DEBUG
1953		wlfc->stats.fifo_credits_back[i] += credits[i];
1954#endif
1955		/* update FIFO credits */
1956		if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
1957		{
1958			int lender; /* Note that borrower is i */
1959
1960			/* Return credits to highest priority lender first */
1961			for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
1962				if (wlfc->credits_borrowed[i][lender] > 0) {
1963					if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
1964						credits[i] -= wlfc->credits_borrowed[i][lender];
1965						wlfc->FIFO_credit[lender] +=
1966						    wlfc->credits_borrowed[i][lender];
1967						wlfc->credits_borrowed[i][lender] = 0;
1968					}
1969					else {
1970						wlfc->credits_borrowed[i][lender] -= credits[i];
1971						wlfc->FIFO_credit[lender] += credits[i];
1972						credits[i] = 0;
1973					}
1974				}
1975			}
1976
1977			/* If we have more credits left over, these must belong to the AC */
1978			if (credits[i] > 0) {
1979				wlfc->FIFO_credit[i] += credits[i];
1980			}
1981		}
1982	}
1983
1984	return BCME_OK;
1985}
1986
1987static int
1988dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
1989{
1990	(void)dhd;
1991	(void)rssi;
1992	return BCME_OK;
1993}
1994
1995static int
1996dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
1997{
1998	int rc;
1999	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2000		dhd->wlfc_state;
2001	wlfc_mac_descriptor_t* table;
2002	uint8 existing_index;
2003	uint8 table_index;
2004	uint8 ifid;
2005	uint8* ea;
2006
2007	WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
2008		__FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7],
2009		((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
2010		WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
2011
2012	table = wlfc->destination_entries.nodes;
2013	table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
2014	ifid = value[1];
2015	ea = &value[2];
2016
2017	if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
2018		existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
2019		if (existing_index == WLFC_MAC_DESC_ID_INVALID) {
2020			/* this MAC entry does not exist, create one */
2021			if (!table[table_index].occupied) {
2022				table[table_index].mac_handle = value[0];
2023				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2024				eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
2025				wlfc->destination_entries.interfaces[ifid].iftype,
2026				ea);
2027			}
2028			else {
2029				/* the space should have been empty, but it's not */
2030				wlfc->stats.mac_update_failed++;
2031			}
2032		}
2033		else {
2034			/*
2035			there is an existing entry, move it to new index
2036			if necessary.
2037			*/
2038			if (existing_index != table_index) {
2039				/* if we already have an entry, free the old one */
2040				table[existing_index].occupied = 0;
2041				table[existing_index].state = WLFC_STATE_CLOSE;
2042				table[existing_index].requested_credit = 0;
2043				table[existing_index].interface_id = 0;
2044				/* enable after packets are queued-deqeued properly.
2045				pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0);
2046				*/
2047			}
2048		}
2049	}
2050	if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
2051		if (table[table_index].occupied) {
2052				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
2053					eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
2054					wlfc->destination_entries.interfaces[ifid].iftype,
2055					ea);
2056		}
2057		else {
2058			/* the space should have been occupied, but it's not */
2059			wlfc->stats.mac_update_failed++;
2060		}
2061	}
2062	BCM_REFERENCE(rc);
2063	return BCME_OK;
2064}
2065
2066static int
2067dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2068{
2069	/* Handle PS on/off indication */
2070	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2071		dhd->wlfc_state;
2072	wlfc_mac_descriptor_t* table;
2073	wlfc_mac_descriptor_t* desc;
2074	uint8 mac_handle = value[0];
2075	int i;
2076
2077	table = wlfc->destination_entries.nodes;
2078	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2079	if (desc->occupied) {
2080		/* a fresh PS mode should wipe old ps credits? */
2081		desc->requested_credit = 0;
2082		if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2083			desc->state = WLFC_STATE_OPEN;
2084			DHD_WLFC_CTRINC_MAC_OPEN(desc);
2085		}
2086		else {
2087			desc->state = WLFC_STATE_CLOSE;
2088			DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2089			/*
2090			Indicate to firmware if there is any traffic pending.
2091			*/
2092			for (i = AC_BE; i < AC_COUNT; i++) {
2093				_dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2094			}
2095		}
2096	}
2097	else {
2098		wlfc->stats.psmode_update_failed++;
2099	}
2100	return BCME_OK;
2101}
2102
2103static int
2104dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2105{
2106	/* Handle PS on/off indication */
2107	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2108		dhd->wlfc_state;
2109	wlfc_mac_descriptor_t* table;
2110	uint8 if_id = value[0];
2111
2112	if (if_id < WLFC_MAX_IFNUM) {
2113		table = wlfc->destination_entries.interfaces;
2114		if (table[if_id].occupied) {
2115			if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2116				table[if_id].state = WLFC_STATE_OPEN;
2117				/* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
2118			}
2119			else {
2120				table[if_id].state = WLFC_STATE_CLOSE;
2121				/* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
2122			}
2123			return BCME_OK;
2124		}
2125	}
2126	wlfc->stats.interface_update_failed++;
2127
2128	return BCME_OK;
2129}
2130
2131static int
2132dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
2133{
2134	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2135		dhd->wlfc_state;
2136	wlfc_mac_descriptor_t* table;
2137	wlfc_mac_descriptor_t* desc;
2138	uint8 mac_handle;
2139	uint8 credit;
2140
2141	table = wlfc->destination_entries.nodes;
2142	mac_handle = value[1];
2143	credit = value[0];
2144
2145	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2146	if (desc->occupied) {
2147		desc->requested_credit = credit;
2148
2149		desc->ac_bitmap = value[2];
2150	}
2151	else {
2152		wlfc->stats.credit_request_failed++;
2153	}
2154	return BCME_OK;
2155}
2156
2157static int
2158dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
2159{
2160	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2161		dhd->wlfc_state;
2162	wlfc_mac_descriptor_t* table;
2163	wlfc_mac_descriptor_t* desc;
2164	uint8 mac_handle;
2165	uint8 packet_count;
2166
2167	table = wlfc->destination_entries.nodes;
2168	mac_handle = value[1];
2169	packet_count = value[0];
2170
2171	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2172	if (desc->occupied) {
2173		desc->requested_packet = packet_count;
2174
2175		desc->ac_bitmap = value[2];
2176	}
2177	else {
2178		wlfc->stats.packet_request_failed++;
2179	}
2180	return BCME_OK;
2181}
2182
2183static void
2184dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
2185{
2186	if (info_len) {
2187		if (info_buf) {
2188			bcopy(val, info_buf, len);
2189			*info_len = len;
2190		}
2191		else
2192			*info_len = 0;
2193	}
2194}
2195
2196static int
2197dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
2198	uint *reorder_info_len)
2199{
2200	uint8 type, len;
2201	uint8* value;
2202	uint8* tmpbuf;
2203	uint16 remainder = tlv_hdr_len;
2204	uint16 processed = 0;
2205	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2206		dhd->wlfc_state;
2207	tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
2208	if (remainder) {
2209		while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
2210			type = tmpbuf[processed];
2211			if (type == WLFC_CTL_TYPE_FILLER) {
2212				remainder -= 1;
2213				processed += 1;
2214				continue;
2215			}
2216
2217			len  = tmpbuf[processed + 1];
2218			value = &tmpbuf[processed + 2];
2219
2220			if (remainder < (2 + len))
2221				break;
2222
2223			remainder -= 2 + len;
2224			processed += 2 + len;
2225			if (type == WLFC_CTL_TYPE_TXSTATUS)
2226				dhd_wlfc_txstatus_update(dhd, value);
2227
2228			else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
2229				dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
2230					reorder_info_len);
2231			else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK)
2232				dhd_wlfc_fifocreditback_indicate(dhd, value);
2233
2234			else if (type == WLFC_CTL_TYPE_RSSI)
2235				dhd_wlfc_rssi_indicate(dhd, value);
2236
2237			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT)
2238				dhd_wlfc_credit_request(dhd, value);
2239
2240			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET)
2241				dhd_wlfc_packet_request(dhd, value);
2242
2243			else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
2244				(type == WLFC_CTL_TYPE_MAC_CLOSE))
2245				dhd_wlfc_psmode_update(dhd, value, type);
2246
2247			else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
2248				(type == WLFC_CTL_TYPE_MACDESC_DEL))
2249				dhd_wlfc_mac_table_update(dhd, value, type);
2250
2251			else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
2252				(type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
2253				dhd_wlfc_interface_update(dhd, value, type);
2254			}
2255		}
2256		if (remainder != 0) {
2257			/* trouble..., something is not right */
2258			wlfc->stats.tlv_parse_failed++;
2259		}
2260	}
2261	return BCME_OK;
2262}
2263
2264int
2265dhd_wlfc_init(dhd_pub_t *dhd)
2266{
2267	char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */
2268	/* enable all signals & indicate host proptxstatus logic is active */
2269	uint32 tlv = dhd->wlfc_enabled?
2270		WLFC_FLAGS_RSSI_SIGNALS |
2271		WLFC_FLAGS_XONXOFF_SIGNALS |
2272		WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
2273		WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
2274		WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0;
2275		/* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */
2276
2277
2278	/*
2279	try to enable/disable signaling by sending "tlv" iovar. if that fails,
2280	fallback to no flow control? Print a message for now.
2281	*/
2282
2283	/* enable proptxtstatus signaling by default */
2284	bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf));
2285	if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) {
2286		DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n"));
2287	}
2288	else {
2289		/*
2290		Leaving the message for now, it should be removed after a while; once
2291		the tlv situation is stable.
2292		*/
2293		DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
2294			dhd->wlfc_enabled?"enabled":"disabled", tlv));
2295	}
2296	return BCME_OK;
2297}
2298
2299int
2300dhd_wlfc_enable(dhd_pub_t *dhd)
2301{
2302	int i;
2303	athost_wl_status_info_t* wlfc;
2304
2305	if (!dhd->wlfc_enabled || dhd->wlfc_state)
2306		return BCME_OK;
2307
2308	/* allocate space to track txstatus propagated from firmware */
2309	dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t));
2310	if (dhd->wlfc_state == NULL)
2311		return BCME_NOMEM;
2312
2313	/* initialize state space */
2314	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2315	memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2316
2317	/* remember osh & dhdp */
2318	wlfc->osh = dhd->osh;
2319	wlfc->dhdp = dhd;
2320
2321	wlfc->hanger =
2322		dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS);
2323	if (wlfc->hanger == NULL) {
2324		MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2325		dhd->wlfc_state = NULL;
2326		return BCME_NOMEM;
2327	}
2328
2329	/* initialize all interfaces to accept traffic */
2330	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2331		wlfc->hostif_flow_state[i] = OFF;
2332	}
2333
2334	/*
2335	create the SENDQ containing
2336	sub-queues for all AC precedences + 1 for bc/mc traffic
2337	*/
2338	pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN);
2339
2340	wlfc->destination_entries.other.state = WLFC_STATE_OPEN;
2341	/* bc/mc FIFO is always open [credit aside], i.e. b[5] */
2342	wlfc->destination_entries.other.ac_bitmap = 0x1f;
2343	wlfc->destination_entries.other.interface_id = 0;
2344
2345	wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2346
2347	wlfc->allow_credit_borrow = TRUE;
2348	wlfc->borrow_defer_timestamp = 0;
2349
2350	return BCME_OK;
2351}
2352
2353/* release all packet resources */
2354void
2355dhd_wlfc_cleanup(dhd_pub_t *dhd)
2356{
2357	int i;
2358	int total_entries;
2359	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2360		dhd->wlfc_state;
2361	wlfc_mac_descriptor_t* table;
2362	wlfc_hanger_t* h;
2363
2364	if (dhd->wlfc_state == NULL)
2365		return;
2366
2367	total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
2368	/* search all entries, include nodes as well as interfaces */
2369	table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
2370
2371	for (i = 0; i < total_entries; i++) {
2372		if (table[i].occupied) {
2373			if (table[i].psq.len) {
2374				WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n",
2375					__FUNCTION__, i, table[i].psq.len));
2376				/* release packets held in DELAYQ */
2377				pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0);
2378			}
2379			table[i].occupied = 0;
2380		}
2381	}
2382	/* release packets held in SENDQ */
2383	if (wlfc->SENDQ.len)
2384		pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0);
2385	/* any in the hanger? */
2386	h = (wlfc_hanger_t*)wlfc->hanger;
2387	for (i = 0; i < h->max_items; i++) {
2388		if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2389			PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2390			h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2391		} else if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE_SUPPRESSED) {
2392			/* These are freed from the psq so no need to free again */
2393			h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2394		}
2395	}
2396	return;
2397}
2398
2399void
2400dhd_wlfc_deinit(dhd_pub_t *dhd)
2401{
2402	/* cleanup all psq related resources */
2403	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2404		dhd->wlfc_state;
2405
2406	if (dhd->wlfc_state == NULL)
2407		return;
2408
2409#ifdef PROP_TXSTATUS_DEBUG
2410	{
2411		int i;
2412		wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2413		for (i = 0; i < h->max_items; i++) {
2414			if (h->items[i].state != WLFC_HANGER_ITEM_STATE_FREE) {
2415				WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n",
2416					__FUNCTION__, i, h->items[i].pkt,
2417					DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt))));
2418			}
2419		}
2420	}
2421#endif
2422	/* delete hanger */
2423	dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger);
2424
2425	/* free top structure */
2426	MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2427	dhd->wlfc_state = NULL;
2428	return;
2429}
2430#endif /* PROP_TXSTATUS */
2431
2432void
2433dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
2434{
2435	bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
2436#ifdef PROP_TXSTATUS
2437	if (dhdp->wlfc_state)
2438		dhd_wlfc_dump(dhdp, strbuf);
2439#endif
2440}
2441
2442void
2443dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
2444{
2445#ifdef BDC
2446	struct bdc_header *h;
2447#endif /* BDC */
2448
2449	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2450
2451#ifdef BDC
2452	/* Push BDC header used to convey priority for buses that don't */
2453
2454	PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
2455
2456	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
2457
2458	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
2459	if (PKTSUMNEEDED(pktbuf))
2460		h->flags |= BDC_FLAG_SUM_NEEDED;
2461
2462
2463	h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
2464	h->flags2 = 0;
2465	h->dataOffset = 0;
2466#endif /* BDC */
2467	BDC_SET_IF_IDX(h, ifidx);
2468}
2469
2470int
2471dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info,
2472	uint *reorder_info_len)
2473{
2474#ifdef BDC
2475	struct bdc_header *h;
2476#endif
2477
2478	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2479
2480#ifdef BDC
2481	if (reorder_info_len)
2482		*reorder_info_len = 0;
2483	/* Pop BDC header used to convey priority for buses that don't */
2484
2485	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
2486		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
2487		           PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
2488		return BCME_ERROR;
2489	}
2490
2491	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
2492
2493	if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
2494		DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
2495		           __FUNCTION__, *ifidx));
2496		return BCME_ERROR;
2497	}
2498
2499#if defined(NDIS630)
2500	h->dataOffset = 0;
2501#endif
2502	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
2503		DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
2504		           dhd_ifname(dhd, *ifidx), h->flags));
2505		if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1)
2506			h->dataOffset = 0;
2507		else
2508		return BCME_ERROR;
2509	}
2510
2511	if (h->flags & BDC_FLAG_SUM_GOOD) {
2512		DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
2513		          dhd_ifname(dhd, *ifidx), h->flags));
2514		PKTSETSUMGOOD(pktbuf, TRUE);
2515	}
2516
2517	PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
2518	PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
2519#endif /* BDC */
2520
2521#if !defined(NDIS630)
2522	if (PKTLEN(dhd->osh, pktbuf) < (uint32) (h->dataOffset << 2)) {
2523		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
2524		           PKTLEN(dhd->osh, pktbuf), (h->dataOffset * 4)));
2525		return BCME_ERROR;
2526	}
2527#endif
2528#ifdef PROP_TXSTATUS
2529	if (dhd->wlfc_state &&
2530		((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode
2531		!= WLFC_FCMODE_NONE &&
2532		(!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) {
2533		/*
2534		- parse txstatus only for packets that came from the firmware
2535		*/
2536		dhd_os_wlfc_block(dhd);
2537		dhd_wlfc_parse_header_info(dhd, pktbuf, (h->dataOffset << 2),
2538			reorder_buf_info, reorder_info_len);
2539		((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++;
2540		dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
2541			(void *)dhd->bus);
2542		dhd_os_wlfc_unblock(dhd);
2543	}
2544#endif /* PROP_TXSTATUS */
2545#if !defined(NDIS630)
2546		PKTPULL(dhd->osh, pktbuf, (h->dataOffset << 2));
2547#endif
2548	return 0;
2549}
2550
2551int
2552dhd_prot_attach(dhd_pub_t *dhd)
2553{
2554	dhd_prot_t *cdc;
2555
2556	if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT,
2557		sizeof(dhd_prot_t)))) {
2558			DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
2559			goto fail;
2560		}
2561	memset(cdc, 0, sizeof(dhd_prot_t));
2562
2563	/* ensure that the msg buf directly follows the cdc msg struct */
2564	if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
2565		DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
2566		goto fail;
2567	}
2568
2569	dhd->prot = cdc;
2570#ifdef BDC
2571	dhd->hdrlen += BDC_HEADER_LEN;
2572#endif
2573	dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
2574	return 0;
2575
2576fail:
2577#ifndef CONFIG_DHD_USE_STATIC_BUF
2578	if (cdc != NULL)
2579		MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
2580#endif /* CONFIG_DHD_USE_STATIC_BUF */
2581	return BCME_NOMEM;
2582}
2583
2584/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
2585void
2586dhd_prot_detach(dhd_pub_t *dhd)
2587{
2588#ifdef PROP_TXSTATUS
2589	dhd_wlfc_deinit(dhd);
2590#endif
2591#ifndef CONFIG_DHD_USE_STATIC_BUF
2592	MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
2593#endif /* CONFIG_DHD_USE_STATIC_BUF */
2594	dhd->prot = NULL;
2595}
2596
2597void
2598dhd_prot_dstats(dhd_pub_t *dhd)
2599{
2600	/* No stats from dongle added yet, copy bus stats */
2601	dhd->dstats.tx_packets = dhd->tx_packets;
2602	dhd->dstats.tx_errors = dhd->tx_errors;
2603	dhd->dstats.rx_packets = dhd->rx_packets;
2604	dhd->dstats.rx_errors = dhd->rx_errors;
2605	dhd->dstats.rx_dropped = dhd->rx_dropped;
2606	dhd->dstats.multicast = dhd->rx_multicast;
2607	return;
2608}
2609
2610int
2611dhd_prot_init(dhd_pub_t *dhd)
2612{
2613	int ret = 0;
2614	wlc_rev_info_t revinfo;
2615	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2616
2617
2618	/* Get the device rev info */
2619	memset(&revinfo, 0, sizeof(revinfo));
2620	ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
2621	if (ret < 0)
2622		goto done;
2623
2624
2625#ifdef PROP_TXSTATUS
2626	ret = dhd_wlfc_init(dhd);
2627#endif
2628
2629#if defined(WL_CFG80211)
2630	if (dhd_download_fw_on_driverload)
2631#endif /* defined(WL_CFG80211) */
2632		ret = dhd_preinit_ioctls(dhd);
2633
2634	/* Always assumes wl for now */
2635	dhd->iswl = TRUE;
2636
2637done:
2638	return ret;
2639}
2640
2641void
2642dhd_prot_stop(dhd_pub_t *dhd)
2643{
2644	/* Nothing to do for CDC */
2645}
2646
2647
2648static void
2649dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt,
2650	uint32 *pkt_count, void **pplast, uint8 start, uint8 end)
2651{
2652	uint i;
2653	void *plast = NULL, *p;
2654	uint32 pkt_cnt = 0;
2655
2656	if (ptr->pend_pkts == 0) {
2657		DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
2658		*pplast = NULL;
2659		*pkt_count = 0;
2660		*pkt = NULL;
2661		return;
2662	}
2663	if (start == end)
2664		i = ptr->max_idx + 1;
2665	else {
2666		if (start > end)
2667			i = (ptr->max_idx - end) + start;
2668		else
2669			i = end - start;
2670	}
2671	while (i) {
2672		p = (void *)(ptr->p[start]);
2673		ptr->p[start] = NULL;
2674
2675		if (p != NULL) {
2676			if (plast == NULL)
2677				*pkt = p;
2678			else
2679				PKTSETNEXT(osh, plast, p);
2680
2681			plast = p;
2682			pkt_cnt++;
2683		}
2684		i--;
2685		if (start++ == ptr->max_idx)
2686			start = 0;
2687	}
2688	*pplast = plast;
2689	*pkt_count = (uint32)pkt_cnt;
2690}
2691
2692int
2693dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len,
2694	void **pkt, uint32 *pkt_count)
2695{
2696	uint8 flow_id, max_idx, cur_idx, exp_idx;
2697	struct reorder_info *ptr;
2698	uint8 flags;
2699	void *cur_pkt, *plast = NULL;
2700	uint32 cnt = 0;
2701
2702	if (pkt == NULL) {
2703		if (pkt_count != NULL)
2704			*pkt_count = 0;
2705		return 0;
2706	}
2707	cur_pkt = *pkt;
2708	*pkt = NULL;
2709
2710	flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
2711	flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
2712
2713	DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
2714		reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
2715		reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
2716		reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
2717
2718	/* validate flags and flow id */
2719	if (flags == 0xFF) {
2720		DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
2721		*pkt_count = 1;
2722		return 0;
2723	}
2724
2725	ptr = dhd->reorder_bufs[flow_id];
2726	if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
2727		uint32 buf_size = sizeof(struct reorder_info);
2728
2729		DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
2730			__FUNCTION__, flow_id));
2731
2732		if (ptr == NULL) {
2733			DHD_ERROR(("%s: received flags to cleanup, but no flow (%d) yet\n",
2734				__FUNCTION__, flow_id));
2735			*pkt_count = 1;
2736			return 0;
2737		}
2738
2739		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2740			ptr->exp_idx, ptr->exp_idx);
2741		/* set it to the last packet */
2742		if (plast) {
2743			PKTSETNEXT(dhd->osh, plast, cur_pkt);
2744			cnt++;
2745		}
2746		else {
2747			if (cnt != 0) {
2748				DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n",
2749					__FUNCTION__, cnt));
2750			}
2751			*pkt = cur_pkt;
2752			cnt = 1;
2753		}
2754		buf_size += ((ptr->max_idx + 1) * sizeof(void *));
2755		MFREE(dhd->osh, ptr, buf_size);
2756		dhd->reorder_bufs[flow_id] = NULL;
2757		*pkt_count = cnt;
2758		return 0;
2759	}
2760	/* all the other cases depend on the existance of the reorder struct for that flow id */
2761	if (ptr == NULL) {
2762		uint32 buf_size_alloc = sizeof(reorder_info_t);
2763		max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
2764
2765		buf_size_alloc += ((max_idx + 1) * sizeof(void*));
2766		/* allocate space to hold the buffers, index etc */
2767
2768		DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
2769			__FUNCTION__, buf_size_alloc, flow_id, max_idx));
2770		ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
2771		if (ptr == NULL) {
2772			DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
2773			*pkt_count = 1;
2774			return 0;
2775		}
2776		bzero(ptr, buf_size_alloc);
2777		dhd->reorder_bufs[flow_id] = ptr;
2778		ptr->p = (void *)(ptr+1);
2779		ptr->max_idx = max_idx;
2780	}
2781	if (flags & WLHOST_REORDERDATA_NEW_HOLE)  {
2782		DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
2783		if (ptr->pend_pkts) {
2784			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2785				ptr->exp_idx, ptr->exp_idx);
2786			ptr->pend_pkts = 0;
2787		}
2788		ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
2789		ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2790		ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
2791		ptr->p[ptr->cur_idx] = cur_pkt;
2792		ptr->pend_pkts++;
2793		*pkt_count = cnt;
2794	}
2795	else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
2796		cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
2797		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2798
2799
2800		if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
2801			/* still in the current hole */
2802			/* enqueue the current on the buffer chain */
2803			if (ptr->p[cur_idx] != NULL) {
2804				DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
2805					__FUNCTION__));
2806				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
2807				ptr->p[cur_idx] = NULL;
2808			}
2809			ptr->p[cur_idx] = cur_pkt;
2810			ptr->pend_pkts++;
2811			ptr->cur_idx = cur_idx;
2812			DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
2813				__FUNCTION__, ptr->pend_pkts));
2814			*pkt_count = 0;
2815			*pkt = NULL;
2816		}
2817		else if (ptr->exp_idx == cur_idx) {
2818			/* got the right one ..flush from cur to exp and update exp */
2819			DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
2820				__FUNCTION__, cur_idx));
2821			if (ptr->p[cur_idx] != NULL) {
2822				DHD_REORDER(("%s: Error buffer pending..free it\n",
2823					__FUNCTION__));
2824				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
2825				ptr->p[cur_idx] = NULL;
2826			}
2827			ptr->p[cur_idx] = cur_pkt;
2828			ptr->pend_pkts++;
2829
2830			ptr->cur_idx = cur_idx;
2831			ptr->exp_idx = exp_idx;
2832
2833			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2834				cur_idx, exp_idx);
2835			ptr->pend_pkts -= (uint8)cnt;
2836			*pkt_count = cnt;
2837			DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
2838				__FUNCTION__, cnt, ptr->pend_pkts));
2839		}
2840		else {
2841			uint8 end_idx;
2842			bool flush_current = FALSE;
2843			/* both cur and exp are moved now .. */
2844			DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
2845				__FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
2846				ptr->exp_idx, exp_idx));
2847			if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
2848				end_idx = ptr->exp_idx;
2849			else
2850				end_idx = exp_idx;
2851
2852			/* flush pkts first */
2853			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2854				ptr->exp_idx, end_idx);
2855
2856			if (cur_idx == ptr->max_idx) {
2857				if (exp_idx == 0)
2858					flush_current = TRUE;
2859			} else {
2860				if (exp_idx == cur_idx + 1)
2861					flush_current = TRUE;
2862			}
2863			if (flush_current) {
2864				if (plast)
2865					PKTSETNEXT(dhd->osh, plast, cur_pkt);
2866				else
2867					*pkt = cur_pkt;
2868				cnt++;
2869			}
2870			else {
2871				ptr->p[cur_idx] = cur_pkt;
2872				ptr->pend_pkts++;
2873			}
2874			ptr->exp_idx = exp_idx;
2875			ptr->cur_idx = cur_idx;
2876			*pkt_count = cnt;
2877		}
2878	}
2879	else {
2880		uint8 end_idx;
2881		/* no real packet but update to exp_seq...that means explicit window move */
2882		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2883
2884		DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
2885			__FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
2886		if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
2887			end_idx =  ptr->exp_idx;
2888		else
2889			end_idx =  exp_idx;
2890
2891		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx);
2892		ptr->pend_pkts -= (uint8)cnt;
2893		if (plast)
2894			PKTSETNEXT(dhd->osh, plast, cur_pkt);
2895		else
2896			*pkt = cur_pkt;
2897		cnt++;
2898		*pkt_count = cnt;
2899		/* set the new expected idx */
2900		ptr->exp_idx = exp_idx;
2901	}
2902	return 0;
2903}
2904