dhd_cdc.c revision bf0a60dff01ce71898435787f292f9c12bb40a85
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 337515 2012-06-07 13:40:55Z $
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_pushpkt(void* hanger, void* pkt, uint32 slot_id)
685{
686	int rc = BCME_OK;
687	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
688
689	if (h && (slot_id < WLFC_HANGER_MAXITEMS)) {
690		if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_FREE) {
691			h->items[slot_id].state = WLFC_HANGER_ITEM_STATE_INUSE;
692			h->items[slot_id].pkt = pkt;
693			h->items[slot_id].identifier = slot_id;
694			h->pushed++;
695		}
696		else {
697			h->failed_to_push++;
698			rc = BCME_NOTFOUND;
699		}
700	}
701	else
702		rc = BCME_BADARG;
703	return rc;
704}
705
706static int
707dhd_wlfc_hanger_poppkt(void* hanger, uint32 slot_id, void** pktout, int remove_from_hanger)
708{
709	int rc = BCME_OK;
710	wlfc_hanger_t* h = (wlfc_hanger_t*)hanger;
711
712	/* this packet was not pushed at the time it went to the firmware */
713	if (slot_id == WLFC_HANGER_MAXITEMS)
714		return BCME_NOTFOUND;
715
716	if (h) {
717		if (h->items[slot_id].state == WLFC_HANGER_ITEM_STATE_INUSE) {
718			*pktout = h->items[slot_id].pkt;
719			if (remove_from_hanger) {
720				h->items[slot_id].state =
721					WLFC_HANGER_ITEM_STATE_FREE;
722				h->items[slot_id].pkt = NULL;
723				h->items[slot_id].identifier = 0;
724				h->popped++;
725			}
726		}
727		else {
728			h->failed_to_pop++;
729			rc = BCME_NOTFOUND;
730		}
731	}
732	else
733		rc = BCME_BADARG;
734	return rc;
735}
736
737static int
738_dhd_wlfc_pushheader(athost_wl_status_info_t* ctx, void* p, bool tim_signal,
739	uint8 tim_bmp, uint8 mac_handle, uint32 htodtag)
740{
741	uint32 wl_pktinfo = 0;
742	uint8* wlh;
743	uint8 dataOffset;
744	uint8 fillers;
745	uint8 tim_signal_len = 0;
746
747	struct bdc_header *h;
748
749	if (tim_signal) {
750		tim_signal_len = 1 + 1 + WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
751	}
752
753	/* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */
754	dataOffset = WLFC_CTL_VALUE_LEN_PKTTAG + 2 + tim_signal_len;
755	fillers = ROUNDUP(dataOffset, 4) - dataOffset;
756	dataOffset += fillers;
757
758	PKTPUSH(ctx->osh, p, dataOffset);
759	wlh = (uint8*) PKTDATA(ctx->osh, p);
760
761	wl_pktinfo = htol32(htodtag);
762
763	wlh[0] = WLFC_CTL_TYPE_PKTTAG;
764	wlh[1] = WLFC_CTL_VALUE_LEN_PKTTAG;
765	memcpy(&wlh[2], &wl_pktinfo, sizeof(uint32));
766
767	if (tim_signal_len) {
768		wlh[dataOffset - fillers - tim_signal_len ] =
769			WLFC_CTL_TYPE_PENDING_TRAFFIC_BMP;
770		wlh[dataOffset - fillers - tim_signal_len + 1] =
771			WLFC_CTL_VALUE_LEN_PENDING_TRAFFIC_BMP;
772		wlh[dataOffset - fillers - tim_signal_len + 2] = mac_handle;
773		wlh[dataOffset - fillers - tim_signal_len + 3] = tim_bmp;
774	}
775	if (fillers)
776		memset(&wlh[dataOffset - fillers], WLFC_CTL_TYPE_FILLER, fillers);
777
778	PKTPUSH(ctx->osh, p, BDC_HEADER_LEN);
779	h = (struct bdc_header *)PKTDATA(ctx->osh, p);
780	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
781	if (PKTSUMNEEDED(p))
782		h->flags |= BDC_FLAG_SUM_NEEDED;
783
784
785	h->priority = (PKTPRIO(p) & BDC_PRIORITY_MASK);
786	h->flags2 = 0;
787	h->dataOffset = dataOffset >> 2;
788	BDC_SET_IF_IDX(h, DHD_PKTTAG_IF(PKTTAG(p)));
789	return BCME_OK;
790}
791
792static int
793_dhd_wlfc_pullheader(athost_wl_status_info_t* ctx, void* pktbuf)
794{
795	struct bdc_header *h;
796
797	if (PKTLEN(ctx->osh, pktbuf) < BDC_HEADER_LEN) {
798		WLFC_DBGMESG(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
799		           PKTLEN(ctx->osh, pktbuf), BDC_HEADER_LEN));
800		return BCME_ERROR;
801	}
802	h = (struct bdc_header *)PKTDATA(ctx->osh, pktbuf);
803
804	/* pull BDC header */
805	PKTPULL(ctx->osh, pktbuf, BDC_HEADER_LEN);
806	/* pull wl-header */
807	PKTPULL(ctx->osh, pktbuf, (h->dataOffset << 2));
808	return BCME_OK;
809}
810
811static wlfc_mac_descriptor_t*
812_dhd_wlfc_find_table_entry(athost_wl_status_info_t* ctx, void* p)
813{
814	int i;
815	wlfc_mac_descriptor_t* table = ctx->destination_entries.nodes;
816	uint8 ifid = DHD_PKTTAG_IF(PKTTAG(p));
817	uint8* dstn = DHD_PKTTAG_DSTN(PKTTAG(p));
818
819	if (((ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_STA) ||
820		ETHER_ISMULTI(dstn) ||
821		(ctx->destination_entries.interfaces[ifid].iftype == WLC_E_IF_ROLE_P2P_CLIENT)) &&
822		(ctx->destination_entries.interfaces[ifid].occupied)) {
823			return &ctx->destination_entries.interfaces[ifid];
824	}
825
826	for (i = 0; i < WLFC_MAC_DESC_TABLE_SIZE; i++) {
827		if (table[i].occupied) {
828			if (table[i].interface_id == ifid) {
829				if (!memcmp(table[i].ea, dstn, ETHER_ADDR_LEN))
830					return &table[i];
831			}
832		}
833	}
834	return &ctx->destination_entries.other;
835}
836
837static int
838_dhd_wlfc_rollback_packet_toq(athost_wl_status_info_t* ctx,
839	void* p, ewlfc_packet_state_t pkt_type, uint32 hslot)
840{
841	/*
842	put the packet back to the head of queue
843
844	- a packet from send-q will need to go back to send-q and not delay-q
845	since that will change the order of packets.
846	- suppressed packet goes back to suppress sub-queue
847	- pull out the header, if new or delayed packet
848
849	Note: hslot is used only when header removal is done.
850	*/
851	wlfc_mac_descriptor_t* entry;
852	void* pktout;
853	int rc = BCME_OK;
854	int prec;
855
856	entry = _dhd_wlfc_find_table_entry(ctx, p);
857	prec = DHD_PKTTAG_FIFO(PKTTAG(p));
858	if (entry != NULL) {
859		if (pkt_type == eWLFC_PKTTYPE_SUPPRESSED) {
860			/* wl-header is saved for suppressed packets */
861			if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, ((prec << 1) + 1), p) == NULL) {
862				WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
863				rc = BCME_ERROR;
864			}
865		}
866		else {
867			/* remove header first */
868			_dhd_wlfc_pullheader(ctx, p);
869
870			if (pkt_type == eWLFC_PKTTYPE_DELAYED) {
871				/* delay-q packets are going to delay-q */
872				if (WLFC_PKTQ_PENQ_HEAD(&entry->psq, (prec << 1), p) == NULL) {
873					WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
874					rc = BCME_ERROR;
875				}
876			}
877			else {
878				/* these are going to SENDQ */
879				if (WLFC_PKTQ_PENQ_HEAD(&ctx->SENDQ, prec, p) == NULL) {
880					WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
881					rc = BCME_ERROR;
882				}
883			}
884			/* free the hanger slot */
885			dhd_wlfc_hanger_poppkt(ctx->hanger, hslot, &pktout, 1);
886
887			/* decrement sequence count */
888			WLFC_DECR_SEQCOUNT(entry, prec);
889		}
890		/*
891		if this packet did not count against FIFO credit, it must have
892		taken a requested_credit from the firmware (for pspoll etc.)
893		*/
894		if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
895			entry->requested_credit++;
896		}
897	}
898	else {
899		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
900		rc = BCME_ERROR;
901	}
902	if (rc != BCME_OK)
903		ctx->stats.rollback_failed++;
904	else
905		ctx->stats.rollback++;
906
907	return rc;
908}
909
910static void
911_dhd_wlfc_flow_control_check(athost_wl_status_info_t* ctx, struct pktq* pq, uint8 if_id)
912{
913	if ((pq->len <= WLFC_FLOWCONTROL_LOWATER) && (ctx->hostif_flow_state[if_id] == ON)) {
914		/* start traffic */
915		ctx->hostif_flow_state[if_id] = OFF;
916		/*
917		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->OFF, start traffic %s()\n",
918		pq->len, if_id, __FUNCTION__));
919		*/
920		WLFC_DBGMESG(("F"));
921		dhd_txflowcontrol(ctx->dhdp, if_id, OFF);
922		ctx->toggle_host_if = 0;
923	}
924	if ((pq->len >= WLFC_FLOWCONTROL_HIWATER) && (ctx->hostif_flow_state[if_id] == OFF)) {
925		/* stop traffic */
926		ctx->hostif_flow_state[if_id] = ON;
927		/*
928		WLFC_DBGMESG(("qlen:%02d, if:%02d, ->ON, stop traffic   %s()\n",
929		pq->len, if_id, __FUNCTION__));
930		*/
931		WLFC_DBGMESG(("N"));
932		dhd_txflowcontrol(ctx->dhdp, if_id, ON);
933		ctx->host_ifidx = if_id;
934		ctx->toggle_host_if = 1;
935	}
936	return;
937}
938
939static int
940_dhd_wlfc_send_signalonly_packet(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
941	uint8 ta_bmp)
942{
943	int rc = BCME_OK;
944	void* p = NULL;
945	int dummylen = ((dhd_pub_t *)ctx->dhdp)->hdrlen+ 12;
946
947	/* allocate a dummy packet */
948	p = PKTGET(ctx->osh, dummylen, TRUE);
949	if (p) {
950		PKTPULL(ctx->osh, p, dummylen);
951		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), 0);
952		_dhd_wlfc_pushheader(ctx, p, TRUE, ta_bmp, entry->mac_handle, 0);
953		DHD_PKTTAG_SETSIGNALONLY(PKTTAG(p), 1);
954#ifdef PROP_TXSTATUS_DEBUG
955		ctx->stats.signal_only_pkts_sent++;
956#endif
957		rc = dhd_bus_txdata(((dhd_pub_t *)ctx->dhdp)->bus, p);
958		if (rc != BCME_OK) {
959			PKTFREE(ctx->osh, p, TRUE);
960		}
961	}
962	else {
963		DHD_ERROR(("%s: couldn't allocate new %d-byte packet\n",
964		           __FUNCTION__, dummylen));
965		rc = BCME_NOMEM;
966	}
967	return rc;
968}
969
970/* Return TRUE if traffic availability changed */
971static bool
972_dhd_wlfc_traffic_pending_check(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
973	int prec)
974{
975	bool rc = FALSE;
976
977	if (entry->state == WLFC_STATE_CLOSE) {
978		if ((pktq_plen(&entry->psq, (prec << 1)) == 0) &&
979			(pktq_plen(&entry->psq, ((prec << 1) + 1)) == 0)) {
980
981			if (entry->traffic_pending_bmp & NBITVAL(prec)) {
982				rc = TRUE;
983				entry->traffic_pending_bmp =
984					entry->traffic_pending_bmp & ~ NBITVAL(prec);
985			}
986		}
987		else {
988			if (!(entry->traffic_pending_bmp & NBITVAL(prec))) {
989				rc = TRUE;
990				entry->traffic_pending_bmp =
991					entry->traffic_pending_bmp | NBITVAL(prec);
992			}
993		}
994	}
995	if (rc) {
996		/* request a TIM update to firmware at the next piggyback opportunity */
997		if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) {
998			entry->send_tim_signal = 1;
999			_dhd_wlfc_send_signalonly_packet(ctx, entry, entry->traffic_pending_bmp);
1000			entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1001			entry->send_tim_signal = 0;
1002		}
1003		else {
1004			rc = FALSE;
1005		}
1006	}
1007	return rc;
1008}
1009
1010static int
1011_dhd_wlfc_enque_suppressed(athost_wl_status_info_t* ctx, int prec, void* p)
1012{
1013	wlfc_mac_descriptor_t* entry;
1014
1015	entry = _dhd_wlfc_find_table_entry(ctx, p);
1016	if (entry == NULL) {
1017		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1018		return BCME_NOTFOUND;
1019	}
1020	/*
1021	- suppressed packets go to sub_queue[2*prec + 1] AND
1022	- delayed packets go to sub_queue[2*prec + 0] to ensure
1023	order of delivery.
1024	*/
1025	if (WLFC_PKTQ_PENQ(&entry->psq, ((prec << 1) + 1), p) == NULL) {
1026		ctx->stats.delayq_full_error++;
1027		/* WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__)); */
1028		WLFC_DBGMESG(("s"));
1029		return BCME_ERROR;
1030	}
1031	/* A packet has been pushed, update traffic availability bitmap, if applicable */
1032	_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1033	_dhd_wlfc_flow_control_check(ctx, &entry->psq, DHD_PKTTAG_IF(PKTTAG(p)));
1034	return BCME_OK;
1035}
1036
1037static int
1038_dhd_wlfc_pretx_pktprocess(athost_wl_status_info_t* ctx,
1039	wlfc_mac_descriptor_t* entry, void* p, int header_needed, uint32* slot)
1040{
1041	int rc = BCME_OK;
1042	int hslot = WLFC_HANGER_MAXITEMS;
1043	bool send_tim_update = FALSE;
1044	uint32 htod = 0;
1045	uint8 free_ctr;
1046
1047	*slot = hslot;
1048
1049	if (entry == NULL) {
1050		entry = _dhd_wlfc_find_table_entry(ctx, p);
1051	}
1052
1053	if (entry == NULL) {
1054		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1055		return BCME_ERROR;
1056	}
1057	if (entry->send_tim_signal) {
1058		send_tim_update = TRUE;
1059		entry->send_tim_signal = 0;
1060		entry->traffic_lastreported_bmp = entry->traffic_pending_bmp;
1061	}
1062	if (header_needed) {
1063		hslot = dhd_wlfc_hanger_get_free_slot(ctx->hanger);
1064		free_ctr = WLFC_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1065		DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1066	}
1067	else {
1068		hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1069		free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1070	}
1071	WLFC_PKTID_HSLOT_SET(htod, hslot);
1072	WLFC_PKTID_FREERUNCTR_SET(htod, free_ctr);
1073	DHD_PKTTAG_SETPKTDIR(PKTTAG(p), 1);
1074	WL_TXSTATUS_SET_FLAGS(htod, WLFC_PKTFLAG_PKTFROMHOST);
1075	WL_TXSTATUS_SET_FIFO(htod, DHD_PKTTAG_FIFO(PKTTAG(p)));
1076	WLFC_PKTFLAG_SET_GENERATION(htod, entry->generation);
1077
1078	if (!DHD_PKTTAG_CREDITCHECK(PKTTAG(p))) {
1079		/*
1080		Indicate that this packet is being sent in response to an
1081		explicit request from the firmware side.
1082		*/
1083		WLFC_PKTFLAG_SET_PKTREQUESTED(htod);
1084	}
1085	else {
1086		WLFC_PKTFLAG_CLR_PKTREQUESTED(htod);
1087	}
1088	if (header_needed) {
1089		rc = _dhd_wlfc_pushheader(ctx, p, send_tim_update,
1090			entry->traffic_lastreported_bmp, entry->mac_handle, htod);
1091		if (rc == BCME_OK) {
1092			DHD_PKTTAG_SET_H2DTAG(PKTTAG(p), htod);
1093			/*
1094			a new header was created for this packet.
1095			push to hanger slot and scrub q. Since bus
1096			send succeeded, increment seq number as well.
1097			*/
1098			rc = dhd_wlfc_hanger_pushpkt(ctx->hanger, p, hslot);
1099			if (rc == BCME_OK) {
1100				/* increment free running sequence count */
1101				WLFC_INCR_SEQCOUNT(entry, DHD_PKTTAG_FIFO(PKTTAG(p)));
1102#ifdef PROP_TXSTATUS_DEBUG
1103				((wlfc_hanger_t*)(ctx->hanger))->items[hslot].push_time =
1104					OSL_SYSUPTIME();
1105#endif
1106			}
1107			else {
1108				WLFC_DBGMESG(("%s() hanger_pushpkt() failed, rc: %d\n",
1109					__FUNCTION__, rc));
1110			}
1111		}
1112	}
1113	else {
1114		/* remove old header */
1115		_dhd_wlfc_pullheader(ctx, p);
1116
1117		hslot = WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1118		free_ctr = WLFC_PKTID_FREERUNCTR_GET(DHD_PKTTAG_H2DTAG(PKTTAG(p)));
1119		/* push new header */
1120		_dhd_wlfc_pushheader(ctx, p, send_tim_update,
1121			entry->traffic_lastreported_bmp, entry->mac_handle, htod);
1122	}
1123	*slot = hslot;
1124	return rc;
1125}
1126
1127static int
1128_dhd_wlfc_is_destination_closed(athost_wl_status_info_t* ctx,
1129	wlfc_mac_descriptor_t* entry, int prec)
1130{
1131	if (ctx->destination_entries.interfaces[entry->interface_id].iftype ==
1132		WLC_E_IF_ROLE_P2P_GO) {
1133		/* - destination interface is of type p2p GO.
1134		For a p2pGO interface, if the destination is OPEN but the interface is
1135		CLOSEd, do not send traffic. But if the dstn is CLOSEd while there is
1136		destination-specific-credit left send packets. This is because the
1137		firmware storing the destination-specific-requested packet in queue.
1138		*/
1139		if ((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1140			(entry->requested_packet == 0))
1141			return 1;
1142	}
1143	/* AP, p2p_go -> unicast desc entry, STA/p2p_cl -> interface desc. entry */
1144	if (((entry->state == WLFC_STATE_CLOSE) && (entry->requested_credit == 0) &&
1145		(entry->requested_packet == 0)) ||
1146		(!(entry->ac_bitmap & (1 << prec))))
1147		return 1;
1148
1149	return 0;
1150}
1151
1152static void*
1153_dhd_wlfc_deque_delayedq(athost_wl_status_info_t* ctx,
1154	int prec, uint8* ac_credit_spent, uint8* needs_hdr, wlfc_mac_descriptor_t** entry_out)
1155{
1156	wlfc_mac_descriptor_t* entry;
1157	wlfc_mac_descriptor_t* table;
1158	uint8 token_pos;
1159	int total_entries;
1160	void* p = NULL;
1161	int pout;
1162	int i;
1163
1164	*entry_out = NULL;
1165	token_pos = ctx->token_pos[prec];
1166	/* most cases a packet will count against FIFO credit */
1167	*ac_credit_spent = 1;
1168	*needs_hdr = 1;
1169
1170	/* search all entries, include nodes as well as interfaces */
1171	table = (wlfc_mac_descriptor_t*)&ctx->destination_entries;
1172	total_entries = sizeof(ctx->destination_entries)/sizeof(wlfc_mac_descriptor_t);
1173
1174	for (i = 0; i < total_entries; i++) {
1175		entry = &table[(token_pos + i) % total_entries];
1176		if (entry->occupied) {
1177			if (!_dhd_wlfc_is_destination_closed(ctx, entry, prec)) {
1178				p = pktq_mdeq(&entry->psq,
1179					/* higher precedence will be picked up first,
1180					i.e. suppressed packets before delayed ones
1181					*/
1182					(NBITVAL((prec << 1) + 1) | NBITVAL((prec << 1))),
1183					&pout);
1184				if (p != NULL) {
1185					/* did the packet come from suppress sub-queue? */
1186					if (pout == ((prec << 1) + 1)) {
1187						/*
1188						this packet was suppressed and was sent on the bus
1189						previously; this already has a header
1190						*/
1191						*needs_hdr = 0;
1192					}
1193					if (entry->requested_credit > 0) {
1194						entry->requested_credit--;
1195#ifdef PROP_TXSTATUS_DEBUG
1196						entry->dstncredit_sent_packets++;
1197#endif
1198						/*
1199						if the packet was pulled out while destination is in
1200						closed state but had a non-zero packets requested,
1201						then this should not count against the FIFO credit.
1202						That is due to the fact that the firmware will
1203						most likely hold onto this packet until a suitable
1204						time later to push it to the appropriate  AC FIFO.
1205						*/
1206						if (entry->state == WLFC_STATE_CLOSE)
1207							*ac_credit_spent = 0;
1208					}
1209					else if (entry->requested_packet > 0) {
1210						entry->requested_packet--;
1211						DHD_PKTTAG_SETONETIMEPKTRQST(PKTTAG(p));
1212						if (entry->state == WLFC_STATE_CLOSE)
1213							*ac_credit_spent = 0;
1214					}
1215					/* move token to ensure fair round-robin */
1216					ctx->token_pos[prec] =
1217						(token_pos + i + 1) % total_entries;
1218					*entry_out = entry;
1219					_dhd_wlfc_flow_control_check(ctx, &entry->psq,
1220						DHD_PKTTAG_IF(PKTTAG(p)));
1221					/*
1222					A packet has been picked up, update traffic
1223					availability bitmap, if applicable
1224					*/
1225					_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1226					return p;
1227				}
1228			}
1229		}
1230	}
1231	return NULL;
1232}
1233
1234static void*
1235_dhd_wlfc_deque_sendq(athost_wl_status_info_t* ctx, int prec)
1236{
1237	wlfc_mac_descriptor_t* entry;
1238	void* p;
1239
1240
1241	p = pktq_pdeq(&ctx->SENDQ, prec);
1242	if (p != NULL) {
1243		if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))
1244			/* bc/mc packets do not have a delay queue */
1245			return p;
1246
1247		entry = _dhd_wlfc_find_table_entry(ctx, p);
1248
1249		if (entry == NULL) {
1250			WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1251			return p;
1252		}
1253
1254		while ((p != NULL)) {
1255			/*
1256			- suppressed packets go to sub_queue[2*prec + 1] AND
1257			- delayed packets go to sub_queue[2*prec + 0] to ensure
1258			order of delivery.
1259			*/
1260			if (WLFC_PKTQ_PENQ(&entry->psq, (prec << 1), p) == NULL) {
1261				WLFC_DBGMESG(("D"));
1262				/* dhd_txcomplete(ctx->dhdp, p, FALSE); */
1263				PKTFREE(ctx->osh, p, TRUE);
1264				ctx->stats.delayq_full_error++;
1265			}
1266			/*
1267			A packet has been pushed, update traffic availability bitmap,
1268			if applicable
1269			*/
1270			_dhd_wlfc_traffic_pending_check(ctx, entry, prec);
1271
1272			p = pktq_pdeq(&ctx->SENDQ, prec);
1273			if (p == NULL)
1274				break;
1275
1276			entry = _dhd_wlfc_find_table_entry(ctx, p);
1277
1278			if ((entry == NULL) || (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(p))))) {
1279				return p;
1280			}
1281		}
1282	}
1283	return p;
1284}
1285
1286static int
1287_dhd_wlfc_mac_entry_update(athost_wl_status_info_t* ctx, wlfc_mac_descriptor_t* entry,
1288	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1289{
1290	int rc = BCME_OK;
1291
1292	if (action == eWLFC_MAC_ENTRY_ACTION_ADD) {
1293		entry->occupied = 1;
1294		entry->state = WLFC_STATE_OPEN;
1295		entry->requested_credit = 0;
1296		entry->interface_id = ifid;
1297		entry->iftype = iftype;
1298		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1299		/* for an interface entry we may not care about the MAC address */
1300		if (ea != NULL)
1301			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1302		pktq_init(&entry->psq, WLFC_PSQ_PREC_COUNT, WLFC_PSQ_LEN);
1303	}
1304	else if (action == eWLFC_MAC_ENTRY_ACTION_UPDATE) {
1305		entry->occupied = 1;
1306		entry->state = WLFC_STATE_OPEN;
1307		entry->requested_credit = 0;
1308		entry->interface_id = ifid;
1309		entry->iftype = iftype;
1310		entry->ac_bitmap = 0xff; /* update this when handling APSD */
1311		/* for an interface entry we may not care about the MAC address */
1312		if (ea != NULL)
1313			memcpy(&entry->ea[0], ea, ETHER_ADDR_LEN);
1314	}
1315	else if (action == eWLFC_MAC_ENTRY_ACTION_DEL) {
1316		entry->occupied = 0;
1317		entry->state = WLFC_STATE_CLOSE;
1318		entry->requested_credit = 0;
1319		/* enable after packets are queued-deqeued properly.
1320		pktq_flush(dhd->osh, &entry->psq, FALSE, NULL, 0);
1321		*/
1322	}
1323	return rc;
1324}
1325
1326int
1327_dhd_wlfc_borrow_credit(athost_wl_status_info_t* ctx, uint8 available_credit_map, int borrower_ac)
1328{
1329	int lender_ac;
1330	int rc = BCME_ERROR;
1331
1332	if (ctx == NULL || available_credit_map == 0) {
1333		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1334		return BCME_BADARG;
1335	}
1336
1337	/* Borrow from lowest priority available AC (including BC/MC credits) */
1338	for (lender_ac = 0; lender_ac <= AC_COUNT; lender_ac++) {
1339		if ((available_credit_map && (1 << lender_ac)) &&
1340		   (ctx->FIFO_credit[lender_ac] > 0)) {
1341			ctx->credits_borrowed[borrower_ac][lender_ac]++;
1342			ctx->FIFO_credit[lender_ac]--;
1343			rc = BCME_OK;
1344			break;
1345		}
1346	}
1347
1348	return rc;
1349}
1350
1351int
1352dhd_wlfc_interface_entry_update(void* state,
1353	ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea)
1354{
1355	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1356	wlfc_mac_descriptor_t* entry;
1357
1358	if (ifid >= WLFC_MAX_IFNUM)
1359		return BCME_BADARG;
1360
1361	entry = &ctx->destination_entries.interfaces[ifid];
1362	return _dhd_wlfc_mac_entry_update(ctx, entry, action, ifid, iftype, ea);
1363}
1364
1365int
1366dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits)
1367{
1368	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1369
1370	/* update the AC FIFO credit map */
1371	ctx->FIFO_credit[0] = credits[0];
1372	ctx->FIFO_credit[1] = credits[1];
1373	ctx->FIFO_credit[2] = credits[2];
1374	ctx->FIFO_credit[3] = credits[3];
1375	/* credit for bc/mc packets */
1376	ctx->FIFO_credit[4] = credits[4];
1377	/* credit for ATIM FIFO is not used yet. */
1378	ctx->FIFO_credit[5] = 0;
1379	return BCME_OK;
1380}
1381
1382int
1383dhd_wlfc_enque_sendq(void* state, int prec, void* p)
1384{
1385	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1386
1387	if ((state == NULL) ||
1388		/* prec = AC_COUNT is used for bc/mc queue */
1389		(prec > AC_COUNT) ||
1390		(p == NULL)) {
1391		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1392		return BCME_BADARG;
1393	}
1394	if (FALSE == dhd_prec_enq(ctx->dhdp, &ctx->SENDQ, p, prec)) {
1395		ctx->stats.sendq_full_error++;
1396		/*
1397		WLFC_DBGMESG(("Error: %s():%d, qlen:%d\n",
1398		__FUNCTION__, __LINE__, ctx->SENDQ.len));
1399		*/
1400		WLFC_HOST_FIFO_DROPPEDCTR_INC(ctx, prec);
1401		WLFC_DBGMESG(("Q"));
1402		PKTFREE(ctx->osh, p, TRUE);
1403		return BCME_ERROR;
1404	}
1405	ctx->stats.pktin++;
1406	/* _dhd_wlfc_flow_control_check(ctx, &ctx->SENDQ, DHD_PKTTAG_IF(PKTTAG(p))); */
1407	return BCME_OK;
1408}
1409
1410int
1411_dhd_wlfc_handle_packet_commit(athost_wl_status_info_t* ctx, int ac,
1412    dhd_wlfc_commit_info_t *commit_info, f_commitpkt_t fcommit, void* commit_ctx)
1413{
1414	uint32 hslot;
1415	int	rc;
1416
1417	/*
1418		if ac_fifo_credit_spent = 0
1419
1420		This packet will not count against the FIFO credit.
1421		To ensure the txstatus corresponding to this packet
1422		does not provide an implied credit (default behavior)
1423		mark the packet accordingly.
1424
1425		if ac_fifo_credit_spent = 1
1426
1427		This is a normal packet and it counts against the FIFO
1428		credit count.
1429	*/
1430	DHD_PKTTAG_SETCREDITCHECK(PKTTAG(commit_info->p), commit_info->ac_fifo_credit_spent);
1431	rc = _dhd_wlfc_pretx_pktprocess(ctx, commit_info->mac_entry, commit_info->p,
1432	     commit_info->needs_hdr, &hslot);
1433
1434	if (rc == BCME_OK)
1435		rc = fcommit(commit_ctx, commit_info->p);
1436	else
1437		ctx->stats.generic_error++;
1438
1439	if (rc == BCME_OK) {
1440		ctx->stats.pkt2bus++;
1441		if (commit_info->ac_fifo_credit_spent) {
1442			ctx->stats.sendq_pkts[ac]++;
1443			WLFC_HOST_FIFO_CREDIT_INC_SENTCTRS(ctx, ac);
1444		}
1445	}
1446	else {
1447		/*
1448		   bus commit has failed, rollback.
1449		   - remove wl-header for a delayed packet
1450		   - save wl-header header for suppressed packets
1451		*/
1452		rc = _dhd_wlfc_rollback_packet_toq(ctx,	commit_info->p,
1453		     (commit_info->pkt_type), hslot);
1454		if (rc != BCME_OK)
1455			ctx->stats.rollback_failed++;
1456
1457		rc = BCME_ERROR;
1458	}
1459
1460	return rc;
1461}
1462
1463int
1464dhd_wlfc_commit_packets(void* state, f_commitpkt_t fcommit, void* commit_ctx)
1465{
1466	int ac;
1467	int credit;
1468	int rc;
1469	dhd_wlfc_commit_info_t  commit_info;
1470	athost_wl_status_info_t* ctx = (athost_wl_status_info_t*)state;
1471	int credit_count = 0;
1472	int bus_retry_count = 0;
1473	uint8 ac_available = 0;  /* Bitmask for 4 ACs + BC/MC */
1474
1475	if ((state == NULL) ||
1476		(fcommit == NULL)) {
1477		WLFC_DBGMESG(("Error: %s():%d\n", __FUNCTION__, __LINE__));
1478		return BCME_BADARG;
1479	}
1480
1481	memset(&commit_info, 0, sizeof(commit_info));
1482
1483	/*
1484	Commit packets for regular AC traffic. Higher priority first.
1485	First, use up FIFO credits available to each AC. Based on distribution
1486	and credits left, borrow from other ACs as applicable
1487
1488	-NOTE:
1489	If the bus between the host and firmware is overwhelmed by the
1490	traffic from host, it is possible that higher priority traffic
1491	starves the lower priority queue. If that occurs often, we may
1492	have to employ weighted round-robin or ucode scheme to avoid
1493	low priority packet starvation.
1494	*/
1495
1496	for (ac = AC_COUNT; ac >= 0; ac--) {
1497
1498		int initial_credit_count = ctx->FIFO_credit[ac];
1499
1500		/* packets from SENDQ are fresh and they'd need header and have no MAC entry */
1501		commit_info.needs_hdr = 1;
1502		commit_info.mac_entry = NULL;
1503		commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1504
1505		do {
1506			commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac);
1507			if (commit_info.p == NULL)
1508				break;
1509			else if (ETHER_ISMULTI(DHD_PKTTAG_DSTN(PKTTAG(commit_info.p)))) {
1510				ASSERT(ac == AC_COUNT);
1511
1512				if (ctx->FIFO_credit[ac]) {
1513					rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1514						fcommit, commit_ctx);
1515
1516			/* Bus commits may fail (e.g. flow control); abort after retries */
1517					if (rc == BCME_OK) {
1518						if (commit_info.ac_fifo_credit_spent) {
1519							(void) _dhd_wlfc_borrow_credit(ctx,
1520								ac_available, ac);
1521							credit_count--;
1522						}
1523					} else {
1524						bus_retry_count++;
1525						if (bus_retry_count >= BUS_RETRIES) {
1526							DHD_ERROR((" %s: bus error\n",
1527								__FUNCTION__));
1528							return rc;
1529						}
1530					}
1531				}
1532			}
1533
1534		} while (commit_info.p);
1535
1536		for (credit = 0; credit < ctx->FIFO_credit[ac];) {
1537			commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1538			                &(commit_info.ac_fifo_credit_spent),
1539			                &(commit_info.needs_hdr),
1540			                &(commit_info.mac_entry));
1541
1542			if (commit_info.p == NULL)
1543				break;
1544
1545			commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1546				eWLFC_PKTTYPE_SUPPRESSED;
1547
1548			rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1549			     fcommit, commit_ctx);
1550
1551			/* Bus commits may fail (e.g. flow control); abort after retries */
1552			if (rc == BCME_OK) {
1553				if (commit_info.ac_fifo_credit_spent) {
1554					credit++;
1555				}
1556			}
1557			else {
1558				bus_retry_count++;
1559				if (bus_retry_count >= BUS_RETRIES) {
1560					DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n"));
1561					ctx->FIFO_credit[ac] -= credit;
1562					return rc;
1563				}
1564			}
1565		}
1566
1567		ctx->FIFO_credit[ac] -= credit;
1568
1569
1570		/* If no credits were used, the queue is idle and can be re-used
1571		   Note that resv credits cannot be borrowed
1572		   */
1573		if (initial_credit_count == ctx->FIFO_credit[ac]) {
1574			ac_available |= (1 << ac);
1575			credit_count += ctx->FIFO_credit[ac];
1576		}
1577	}
1578
1579	/* We borrow only for AC_BE and only if no other traffic seen for DEFER_PERIOD
1580
1581	   Note that (ac_available & WLFC_AC_BE_TRAFFIC_ONLY) is done to:
1582	   a) ignore BC/MC for deferring borrow
1583	   b) ignore AC_BE being available along with other ACs
1584		  (this should happen only for pure BC/MC traffic)
1585
1586	   i.e. AC_VI, AC_VO, AC_BK all MUST be available (i.e. no traffic) and
1587	   we do not care if AC_BE and BC/MC are available or not
1588	   */
1589	if ((ac_available & WLFC_AC_BE_TRAFFIC_ONLY) == WLFC_AC_BE_TRAFFIC_ONLY) {
1590
1591		if (ctx->allow_credit_borrow) {
1592			ac = 1;  /* Set ac to AC_BE and borrow credits */
1593		}
1594		else {
1595			int delta;
1596			int curr_t = OSL_SYSUPTIME();
1597
1598			if (curr_t > ctx->borrow_defer_timestamp)
1599				delta = curr_t - ctx->borrow_defer_timestamp;
1600			else
1601				delta = 0xffffffff + curr_t - ctx->borrow_defer_timestamp;
1602
1603			if (delta >= WLFC_BORROW_DEFER_PERIOD_MS) {
1604				/* Reset borrow but defer to next iteration (defensive borrowing) */
1605				ctx->allow_credit_borrow = TRUE;
1606				ctx->borrow_defer_timestamp = 0;
1607			}
1608			return BCME_OK;
1609		}
1610	}
1611	else {
1612		/* If we have multiple AC traffic, turn off borrowing, mark time and bail out */
1613		ctx->allow_credit_borrow = FALSE;
1614		ctx->borrow_defer_timestamp = OSL_SYSUPTIME();
1615		return BCME_OK;
1616	}
1617
1618	/* At this point, borrow all credits only for "ac" (which should be set above to AC_BE)
1619	   Generically use "ac" only in case we extend to all ACs in future
1620	   */
1621	for (; (credit_count > 0);) {
1622
1623		commit_info.p = _dhd_wlfc_deque_delayedq(ctx, ac,
1624		                &(commit_info.ac_fifo_credit_spent),
1625		                &(commit_info.needs_hdr),
1626		                &(commit_info.mac_entry));
1627		if (commit_info.p == NULL)
1628			break;
1629
1630		commit_info.pkt_type = (commit_info.needs_hdr) ? eWLFC_PKTTYPE_DELAYED :
1631			eWLFC_PKTTYPE_SUPPRESSED;
1632
1633		rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1634		     fcommit, commit_ctx);
1635
1636		/* Bus commits may fail (e.g. flow control); abort after retries */
1637		if (rc == BCME_OK) {
1638			if (commit_info.ac_fifo_credit_spent) {
1639				(void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac);
1640				credit_count--;
1641			}
1642		}
1643		else {
1644			bus_retry_count++;
1645			if (bus_retry_count >= BUS_RETRIES) {
1646				DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n"));
1647				return rc;
1648			}
1649		}
1650	}
1651
1652	/* packets from SENDQ are fresh and they'd need header and have no MAC entry */
1653	commit_info.needs_hdr = 1;
1654	commit_info.mac_entry = NULL;
1655	commit_info.pkt_type = eWLFC_PKTTYPE_NEW;
1656
1657	for (; (credit_count > 0);) {
1658
1659		commit_info.p = _dhd_wlfc_deque_sendq(ctx, ac);
1660		if (commit_info.p == NULL)
1661			break;
1662
1663		rc = _dhd_wlfc_handle_packet_commit(ctx, ac, &commit_info,
1664		     fcommit, commit_ctx);
1665
1666		/* Bus commits may fail (e.g. flow control); abort after retries */
1667		if (rc == BCME_OK) {
1668			if (commit_info.ac_fifo_credit_spent) {
1669				(void) _dhd_wlfc_borrow_credit(ctx, ac_available, ac);
1670				credit_count--;
1671			}
1672		}
1673		else {
1674			bus_retry_count++;
1675			if (bus_retry_count >= BUS_RETRIES) {
1676				DHD_ERROR(("dhd_wlfc_commit_packets(): bus error\n"));
1677				return rc;
1678			}
1679		}
1680	}
1681
1682	return BCME_OK;
1683}
1684
1685static uint8
1686dhd_wlfc_find_mac_desc_id_from_mac(dhd_pub_t *dhdp, uint8* ea)
1687{
1688	wlfc_mac_descriptor_t* table =
1689		((athost_wl_status_info_t*)dhdp->wlfc_state)->destination_entries.nodes;
1690	uint8 table_index;
1691
1692	if (ea != NULL) {
1693		for (table_index = 0; table_index < WLFC_MAC_DESC_TABLE_SIZE; table_index++) {
1694			if ((memcmp(ea, &table[table_index].ea[0], ETHER_ADDR_LEN) == 0) &&
1695				table[table_index].occupied)
1696				return table_index;
1697		}
1698	}
1699	return WLFC_MAC_DESC_ID_INVALID;
1700}
1701
1702void
1703dhd_wlfc_txcomplete(dhd_pub_t *dhd, void *txp, bool success)
1704{
1705	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1706		dhd->wlfc_state;
1707	void* p;
1708	int fifo_id;
1709
1710	if (DHD_PKTTAG_SIGNALONLY(PKTTAG(txp))) {
1711#ifdef PROP_TXSTATUS_DEBUG
1712		wlfc->stats.signal_only_pkts_freed++;
1713#endif
1714		/* is this a signal-only packet? */
1715		PKTFREE(wlfc->osh, txp, TRUE);
1716		return;
1717	}
1718	if (!success) {
1719		WLFC_DBGMESG(("At: %s():%d, bus_complete() failure for %p, htod_tag:0x%08x\n",
1720			__FUNCTION__, __LINE__, txp, DHD_PKTTAG_H2DTAG(PKTTAG(txp))));
1721		dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(DHD_PKTTAG_H2DTAG
1722			(PKTTAG(txp))), &p, 1);
1723
1724		/* indicate failure and free the packet */
1725		dhd_txcomplete(dhd, txp, FALSE);
1726
1727		/* return the credit, if necessary */
1728		if (DHD_PKTTAG_CREDITCHECK(PKTTAG(txp))) {
1729			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1730
1731			fifo_id = DHD_PKTTAG_FIFO(PKTTAG(txp));
1732
1733			/* Return credits to highest priority lender first */
1734			for (lender = AC_COUNT; lender >= 0; lender--) {
1735				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1736					wlfc->FIFO_credit[lender]++;
1737					wlfc->credits_borrowed[fifo_id][lender]--;
1738					credit_returned = 1;
1739					break;
1740				}
1741			}
1742
1743			if (!credit_returned) {
1744				wlfc->FIFO_credit[fifo_id]++;
1745			}
1746		}
1747
1748		PKTFREE(wlfc->osh, txp, TRUE);
1749	}
1750	return;
1751}
1752
1753/* Handle discard or suppress indication */
1754static int
1755dhd_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	if (!remove_from_hanger) {
1797		/* this packet was suppressed */
1798
1799		entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1800		entry->generation = WLFC_PKTID_GEN(status);
1801	}
1802
1803#ifdef PROP_TXSTATUS_DEBUG
1804	{
1805		uint32 new_t = OSL_SYSUPTIME();
1806		uint32 old_t;
1807		uint32 delta;
1808		old_t = ((wlfc_hanger_t*)(wlfc->hanger))->items[
1809			WLFC_PKTID_HSLOT_GET(status)].push_time;
1810
1811
1812		wlfc->stats.latency_sample_count++;
1813		if (new_t > old_t)
1814			delta = new_t - old_t;
1815		else
1816			delta = 0xffffffff + new_t - old_t;
1817		wlfc->stats.total_status_latency += delta;
1818		wlfc->stats.latency_most_recent = delta;
1819
1820		wlfc->stats.deltas[wlfc->stats.idx_delta++] = delta;
1821		if (wlfc->stats.idx_delta == sizeof(wlfc->stats.deltas)/sizeof(uint32))
1822			wlfc->stats.idx_delta = 0;
1823	}
1824#endif /* PROP_TXSTATUS_DEBUG */
1825
1826	fifo_id = DHD_PKTTAG_FIFO(PKTTAG(pktbuf));
1827
1828	/* pick up the implicit credit from this packet */
1829	if (DHD_PKTTAG_CREDITCHECK(PKTTAG(pktbuf))) {
1830		if (wlfc->proptxstatus_mode == WLFC_FCMODE_IMPLIED_CREDIT) {
1831
1832			int lender, credit_returned = 0; /* Note that borrower is fifo_id */
1833
1834			/* Return credits to highest priority lender first */
1835			for (lender = AC_COUNT; lender >= 0; lender--)	{
1836				if (wlfc->credits_borrowed[fifo_id][lender] > 0) {
1837					wlfc->FIFO_credit[lender]++;
1838					wlfc->credits_borrowed[fifo_id][lender]--;
1839					credit_returned = 1;
1840					break;
1841				}
1842			}
1843
1844			if (!credit_returned) {
1845				wlfc->FIFO_credit[fifo_id]++;
1846			}
1847		}
1848	}
1849	else {
1850		/*
1851		if this packet did not count against FIFO credit, it must have
1852		taken a requested_credit from the destination entry (for pspoll etc.)
1853		*/
1854		if (!entry) {
1855
1856			entry = _dhd_wlfc_find_table_entry(wlfc, pktbuf);
1857		}
1858		if (!DHD_PKTTAG_ONETIMEPKTRQST(PKTTAG(pktbuf)))
1859			entry->requested_credit++;
1860#ifdef PROP_TXSTATUS_DEBUG
1861		entry->dstncredit_acks++;
1862#endif
1863	}
1864	if ((status_flag == WLFC_CTL_PKTFLAG_D11SUPPRESS) ||
1865		(status_flag == WLFC_CTL_PKTFLAG_WLSUPPRESS)) {
1866		ret = _dhd_wlfc_enque_suppressed(wlfc, fifo_id, pktbuf);
1867		if (ret != BCME_OK) {
1868			/* delay q is full, drop this packet */
1869			dhd_wlfc_hanger_poppkt(wlfc->hanger, WLFC_PKTID_HSLOT_GET(status),
1870			&pktbuf, 1);
1871
1872			/* indicate failure and free the packet */
1873			dhd_txcomplete(dhd, pktbuf, FALSE);
1874			PKTFREE(wlfc->osh, pktbuf, TRUE);
1875		}
1876	}
1877	else {
1878		dhd_txcomplete(dhd, pktbuf, TRUE);
1879		/* free the packet */
1880		PKTFREE(wlfc->osh, pktbuf, TRUE);
1881	}
1882	return BCME_OK;
1883}
1884
1885static int
1886dhd_wlfc_fifocreditback_indicate(dhd_pub_t *dhd, uint8* credits)
1887{
1888	int i;
1889	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1890		dhd->wlfc_state;
1891	for (i = 0; i < WLFC_CTL_VALUE_LEN_FIFO_CREDITBACK; i++) {
1892#ifdef PROP_TXSTATUS_DEBUG
1893		wlfc->stats.fifo_credits_back[i] += credits[i];
1894#endif
1895		/* update FIFO credits */
1896		if (wlfc->proptxstatus_mode == WLFC_FCMODE_EXPLICIT_CREDIT)
1897		{
1898			int lender; /* Note that borrower is i */
1899
1900			/* Return credits to highest priority lender first */
1901			for (lender = AC_COUNT; (lender >= 0) && (credits[i] > 0); lender--) {
1902				if (wlfc->credits_borrowed[i][lender] > 0) {
1903					if (credits[i] >= wlfc->credits_borrowed[i][lender]) {
1904						credits[i] -= wlfc->credits_borrowed[i][lender];
1905						wlfc->FIFO_credit[lender] +=
1906						    wlfc->credits_borrowed[i][lender];
1907						wlfc->credits_borrowed[i][lender] = 0;
1908					}
1909					else {
1910						wlfc->credits_borrowed[i][lender] -= credits[i];
1911						wlfc->FIFO_credit[lender] += credits[i];
1912						credits[i] = 0;
1913					}
1914				}
1915			}
1916
1917			/* If we have more credits left over, these must belong to the AC */
1918			if (credits[i] > 0) {
1919				wlfc->FIFO_credit[i] += credits[i];
1920			}
1921		}
1922	}
1923
1924	return BCME_OK;
1925}
1926
1927static int
1928dhd_wlfc_rssi_indicate(dhd_pub_t *dhd, uint8* rssi)
1929{
1930	(void)dhd;
1931	(void)rssi;
1932	return BCME_OK;
1933}
1934
1935static int
1936dhd_wlfc_mac_table_update(dhd_pub_t *dhd, uint8* value, uint8 type)
1937{
1938	int rc;
1939	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
1940		dhd->wlfc_state;
1941	wlfc_mac_descriptor_t* table;
1942	uint8 existing_index;
1943	uint8 table_index;
1944	uint8 ifid;
1945	uint8* ea;
1946
1947	WLFC_DBGMESG(("%s(), mac [%02x:%02x:%02x:%02x:%02x:%02x],%s,idx:%d,id:0x%02x\n",
1948		__FUNCTION__, value[2], value[3], value[4], value[5], value[6], value[7],
1949		((type == WLFC_CTL_TYPE_MACDESC_ADD) ? "ADD":"DEL"),
1950		WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]), value[0]));
1951
1952	table = wlfc->destination_entries.nodes;
1953	table_index = WLFC_MAC_DESC_GET_LOOKUP_INDEX(value[0]);
1954	ifid = value[1];
1955	ea = &value[2];
1956
1957	if (type == WLFC_CTL_TYPE_MACDESC_ADD) {
1958		existing_index = dhd_wlfc_find_mac_desc_id_from_mac(dhd, &value[2]);
1959		if (existing_index == WLFC_MAC_DESC_ID_INVALID) {
1960			/* this MAC entry does not exist, create one */
1961			if (!table[table_index].occupied) {
1962				table[table_index].mac_handle = value[0];
1963				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
1964				eWLFC_MAC_ENTRY_ACTION_ADD, ifid,
1965				wlfc->destination_entries.interfaces[ifid].iftype,
1966				ea);
1967			}
1968			else {
1969				/* the space should have been empty, but it's not */
1970				wlfc->stats.mac_update_failed++;
1971			}
1972		}
1973		else {
1974			/*
1975			there is an existing entry, move it to new index
1976			if necessary.
1977			*/
1978			if (existing_index != table_index) {
1979				/* if we already have an entry, free the old one */
1980				table[existing_index].occupied = 0;
1981				table[existing_index].state = WLFC_STATE_CLOSE;
1982				table[existing_index].requested_credit = 0;
1983				table[existing_index].interface_id = 0;
1984				/* enable after packets are queued-deqeued properly.
1985				pktq_flush(dhd->osh, &table[existing_index].psq, FALSE, NULL, 0);
1986				*/
1987			}
1988		}
1989	}
1990	if (type == WLFC_CTL_TYPE_MACDESC_DEL) {
1991		if (table[table_index].occupied) {
1992				rc = _dhd_wlfc_mac_entry_update(wlfc, &table[table_index],
1993					eWLFC_MAC_ENTRY_ACTION_DEL, ifid,
1994					wlfc->destination_entries.interfaces[ifid].iftype,
1995					ea);
1996		}
1997		else {
1998			/* the space should have been occupied, but it's not */
1999			wlfc->stats.mac_update_failed++;
2000		}
2001	}
2002	BCM_REFERENCE(rc);
2003	return BCME_OK;
2004}
2005
2006static int
2007dhd_wlfc_psmode_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2008{
2009	/* Handle PS on/off indication */
2010	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2011		dhd->wlfc_state;
2012	wlfc_mac_descriptor_t* table;
2013	wlfc_mac_descriptor_t* desc;
2014	uint8 mac_handle = value[0];
2015	int i;
2016
2017	table = wlfc->destination_entries.nodes;
2018	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2019	if (desc->occupied) {
2020		/* a fresh PS mode should wipe old ps credits? */
2021		desc->requested_credit = 0;
2022		if (type == WLFC_CTL_TYPE_MAC_OPEN) {
2023			desc->state = WLFC_STATE_OPEN;
2024			DHD_WLFC_CTRINC_MAC_OPEN(desc);
2025		}
2026		else {
2027			desc->state = WLFC_STATE_CLOSE;
2028			DHD_WLFC_CTRINC_MAC_CLOSE(desc);
2029			/*
2030			Indicate to firmware if there is any traffic pending.
2031			*/
2032			for (i = AC_BE; i < AC_COUNT; i++) {
2033				_dhd_wlfc_traffic_pending_check(wlfc, desc, i);
2034			}
2035		}
2036	}
2037	else {
2038		wlfc->stats.psmode_update_failed++;
2039	}
2040	return BCME_OK;
2041}
2042
2043static int
2044dhd_wlfc_interface_update(dhd_pub_t *dhd, uint8* value, uint8 type)
2045{
2046	/* Handle PS on/off indication */
2047	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2048		dhd->wlfc_state;
2049	wlfc_mac_descriptor_t* table;
2050	uint8 if_id = value[0];
2051
2052	if (if_id < WLFC_MAX_IFNUM) {
2053		table = wlfc->destination_entries.interfaces;
2054		if (table[if_id].occupied) {
2055			if (type == WLFC_CTL_TYPE_INTERFACE_OPEN) {
2056				table[if_id].state = WLFC_STATE_OPEN;
2057				/* WLFC_DBGMESG(("INTERFACE[%d] OPEN\n", if_id)); */
2058			}
2059			else {
2060				table[if_id].state = WLFC_STATE_CLOSE;
2061				/* WLFC_DBGMESG(("INTERFACE[%d] CLOSE\n", if_id)); */
2062			}
2063			return BCME_OK;
2064		}
2065	}
2066	wlfc->stats.interface_update_failed++;
2067
2068	return BCME_OK;
2069}
2070
2071static int
2072dhd_wlfc_credit_request(dhd_pub_t *dhd, uint8* value)
2073{
2074	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2075		dhd->wlfc_state;
2076	wlfc_mac_descriptor_t* table;
2077	wlfc_mac_descriptor_t* desc;
2078	uint8 mac_handle;
2079	uint8 credit;
2080
2081	table = wlfc->destination_entries.nodes;
2082	mac_handle = value[1];
2083	credit = value[0];
2084
2085	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2086	if (desc->occupied) {
2087		desc->requested_credit = credit;
2088
2089		desc->ac_bitmap = value[2];
2090	}
2091	else {
2092		wlfc->stats.credit_request_failed++;
2093	}
2094	return BCME_OK;
2095}
2096
2097static int
2098dhd_wlfc_packet_request(dhd_pub_t *dhd, uint8* value)
2099{
2100	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2101		dhd->wlfc_state;
2102	wlfc_mac_descriptor_t* table;
2103	wlfc_mac_descriptor_t* desc;
2104	uint8 mac_handle;
2105	uint8 packet_count;
2106
2107	table = wlfc->destination_entries.nodes;
2108	mac_handle = value[1];
2109	packet_count = value[0];
2110
2111	desc = &table[WLFC_MAC_DESC_GET_LOOKUP_INDEX(mac_handle)];
2112	if (desc->occupied) {
2113		desc->requested_packet = packet_count;
2114
2115		desc->ac_bitmap = value[2];
2116	}
2117	else {
2118		wlfc->stats.packet_request_failed++;
2119	}
2120	return BCME_OK;
2121}
2122
2123static void
2124dhd_wlfc_reorderinfo_indicate(uint8 *val, uint8 len, uchar *info_buf, uint *info_len)
2125{
2126	if (info_len) {
2127		if (info_buf) {
2128			bcopy(val, info_buf, len);
2129			*info_len = len;
2130		}
2131		else
2132			*info_len = 0;
2133	}
2134}
2135
2136static int
2137dhd_wlfc_parse_header_info(dhd_pub_t *dhd, void* pktbuf, int tlv_hdr_len, uchar *reorder_info_buf,
2138	uint *reorder_info_len)
2139{
2140	uint8 type, len;
2141	uint8* value;
2142	uint8* tmpbuf;
2143	uint16 remainder = tlv_hdr_len;
2144	uint16 processed = 0;
2145	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2146		dhd->wlfc_state;
2147	tmpbuf = (uint8*)PKTDATA(dhd->osh, pktbuf);
2148	if (remainder) {
2149		while ((processed < (WLFC_MAX_PENDING_DATALEN * 2)) && (remainder > 0)) {
2150			type = tmpbuf[processed];
2151			if (type == WLFC_CTL_TYPE_FILLER) {
2152				remainder -= 1;
2153				processed += 1;
2154				continue;
2155			}
2156
2157			len  = tmpbuf[processed + 1];
2158			value = &tmpbuf[processed + 2];
2159
2160			if (remainder < (2 + len))
2161				break;
2162
2163			remainder -= 2 + len;
2164			processed += 2 + len;
2165			if (type == WLFC_CTL_TYPE_TXSTATUS)
2166				dhd_wlfc_txstatus_update(dhd, value);
2167
2168			else if (type == WLFC_CTL_TYPE_HOST_REORDER_RXPKTS)
2169				dhd_wlfc_reorderinfo_indicate(value, len, reorder_info_buf,
2170					reorder_info_len);
2171			else if (type == WLFC_CTL_TYPE_FIFO_CREDITBACK)
2172				dhd_wlfc_fifocreditback_indicate(dhd, value);
2173
2174			else if (type == WLFC_CTL_TYPE_RSSI)
2175				dhd_wlfc_rssi_indicate(dhd, value);
2176
2177			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_CREDIT)
2178				dhd_wlfc_credit_request(dhd, value);
2179
2180			else if (type == WLFC_CTL_TYPE_MAC_REQUEST_PACKET)
2181				dhd_wlfc_packet_request(dhd, value);
2182
2183			else if ((type == WLFC_CTL_TYPE_MAC_OPEN) ||
2184				(type == WLFC_CTL_TYPE_MAC_CLOSE))
2185				dhd_wlfc_psmode_update(dhd, value, type);
2186
2187			else if ((type == WLFC_CTL_TYPE_MACDESC_ADD) ||
2188				(type == WLFC_CTL_TYPE_MACDESC_DEL))
2189				dhd_wlfc_mac_table_update(dhd, value, type);
2190
2191			else if ((type == WLFC_CTL_TYPE_INTERFACE_OPEN) ||
2192				(type == WLFC_CTL_TYPE_INTERFACE_CLOSE)) {
2193				dhd_wlfc_interface_update(dhd, value, type);
2194			}
2195		}
2196		if (remainder != 0) {
2197			/* trouble..., something is not right */
2198			wlfc->stats.tlv_parse_failed++;
2199		}
2200	}
2201	return BCME_OK;
2202}
2203
2204int
2205dhd_wlfc_init(dhd_pub_t *dhd)
2206{
2207	char iovbuf[12]; /* Room for "tlv" + '\0' + parameter */
2208	/* enable all signals & indicate host proptxstatus logic is active */
2209	uint32 tlv = dhd->wlfc_enabled?
2210		WLFC_FLAGS_RSSI_SIGNALS |
2211		WLFC_FLAGS_XONXOFF_SIGNALS |
2212		WLFC_FLAGS_CREDIT_STATUS_SIGNALS |
2213		WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0;
2214		/* WLFC_FLAGS_HOST_PROPTXSTATUS_ACTIVE | WLFC_FLAGS_HOST_RXRERODER_ACTIVE : 0; */
2215
2216
2217	/*
2218	try to enable/disable signaling by sending "tlv" iovar. if that fails,
2219	fallback to no flow control? Print a message for now.
2220	*/
2221
2222	/* enable proptxtstatus signaling by default */
2223	bcm_mkiovar("tlv", (char *)&tlv, 4, iovbuf, sizeof(iovbuf));
2224	if (dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0) < 0) {
2225		DHD_ERROR(("dhd_wlfc_init(): failed to enable/disable bdcv2 tlv signaling\n"));
2226	}
2227	else {
2228		/*
2229		Leaving the message for now, it should be removed after a while; once
2230		the tlv situation is stable.
2231		*/
2232		DHD_ERROR(("dhd_wlfc_init(): successfully %s bdcv2 tlv signaling, %d\n",
2233			dhd->wlfc_enabled?"enabled":"disabled", tlv));
2234	}
2235	return BCME_OK;
2236}
2237
2238int
2239dhd_wlfc_enable(dhd_pub_t *dhd)
2240{
2241	int i;
2242	athost_wl_status_info_t* wlfc;
2243
2244	if (!dhd->wlfc_enabled || dhd->wlfc_state)
2245		return BCME_OK;
2246
2247	/* allocate space to track txstatus propagated from firmware */
2248	dhd->wlfc_state = MALLOC(dhd->osh, sizeof(athost_wl_status_info_t));
2249	if (dhd->wlfc_state == NULL)
2250		return BCME_NOMEM;
2251
2252	/* initialize state space */
2253	wlfc = (athost_wl_status_info_t*)dhd->wlfc_state;
2254	memset(wlfc, 0, sizeof(athost_wl_status_info_t));
2255
2256	/* remember osh & dhdp */
2257	wlfc->osh = dhd->osh;
2258	wlfc->dhdp = dhd;
2259
2260	wlfc->hanger =
2261		dhd_wlfc_hanger_create(dhd->osh, WLFC_HANGER_MAXITEMS);
2262	if (wlfc->hanger == NULL) {
2263		MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2264		dhd->wlfc_state = NULL;
2265		return BCME_NOMEM;
2266	}
2267
2268	/* initialize all interfaces to accept traffic */
2269	for (i = 0; i < WLFC_MAX_IFNUM; i++) {
2270		wlfc->hostif_flow_state[i] = OFF;
2271	}
2272
2273	/*
2274	create the SENDQ containing
2275	sub-queues for all AC precedences + 1 for bc/mc traffic
2276	*/
2277	pktq_init(&wlfc->SENDQ, (AC_COUNT + 1), WLFC_SENDQ_LEN);
2278
2279	wlfc->destination_entries.other.state = WLFC_STATE_OPEN;
2280	/* bc/mc FIFO is always open [credit aside], i.e. b[5] */
2281	wlfc->destination_entries.other.ac_bitmap = 0x1f;
2282	wlfc->destination_entries.other.interface_id = 0;
2283
2284	wlfc->proptxstatus_mode = WLFC_FCMODE_EXPLICIT_CREDIT;
2285
2286	wlfc->allow_credit_borrow = TRUE;
2287	wlfc->borrow_defer_timestamp = 0;
2288
2289	return BCME_OK;
2290}
2291
2292/* release all packet resources */
2293void
2294dhd_wlfc_cleanup(dhd_pub_t *dhd)
2295{
2296	int i;
2297	int total_entries;
2298	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2299		dhd->wlfc_state;
2300	wlfc_mac_descriptor_t* table;
2301	wlfc_hanger_t* h;
2302
2303	if (dhd->wlfc_state == NULL)
2304		return;
2305
2306	total_entries = sizeof(wlfc->destination_entries)/sizeof(wlfc_mac_descriptor_t);
2307	/* search all entries, include nodes as well as interfaces */
2308	table = (wlfc_mac_descriptor_t*)&wlfc->destination_entries;
2309
2310	for (i = 0; i < total_entries; i++) {
2311		if (table[i].occupied) {
2312			if (table[i].psq.len) {
2313				WLFC_DBGMESG(("%s(): DELAYQ[%d].len = %d\n",
2314					__FUNCTION__, i, table[i].psq.len));
2315				/* release packets held in DELAYQ */
2316				pktq_flush(wlfc->osh, &table[i].psq, TRUE, NULL, 0);
2317			}
2318			table[i].occupied = 0;
2319		}
2320	}
2321	/* release packets held in SENDQ */
2322	if (wlfc->SENDQ.len)
2323		pktq_flush(wlfc->osh, &wlfc->SENDQ, TRUE, NULL, 0);
2324	/* any in the hanger? */
2325	h = (wlfc_hanger_t*)wlfc->hanger;
2326	for (i = 0; i < h->max_items; i++) {
2327		if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2328			PKTFREE(wlfc->osh, h->items[i].pkt, TRUE);
2329			h->items[i].state = WLFC_HANGER_ITEM_STATE_FREE;
2330			h->items[i].pkt = NULL;
2331			h->items[i].identifier = 0;
2332		}
2333	}
2334	return;
2335}
2336
2337void
2338dhd_wlfc_deinit(dhd_pub_t *dhd)
2339{
2340	/* cleanup all psq related resources */
2341	athost_wl_status_info_t* wlfc = (athost_wl_status_info_t*)
2342		dhd->wlfc_state;
2343
2344	if (dhd->wlfc_state == NULL)
2345		return;
2346
2347#ifdef PROP_TXSTATUS_DEBUG
2348	{
2349		int i;
2350		wlfc_hanger_t* h = (wlfc_hanger_t*)wlfc->hanger;
2351		for (i = 0; i < h->max_items; i++) {
2352			if (h->items[i].state == WLFC_HANGER_ITEM_STATE_INUSE) {
2353				WLFC_DBGMESG(("%s() pkt[%d] = 0x%p, FIFO_credit_used:%d\n",
2354					__FUNCTION__, i, h->items[i].pkt,
2355					DHD_PKTTAG_CREDITCHECK(PKTTAG(h->items[i].pkt))));
2356			}
2357		}
2358	}
2359#endif
2360	/* delete hanger */
2361	dhd_wlfc_hanger_delete(dhd->osh, wlfc->hanger);
2362
2363	/* free top structure */
2364	MFREE(dhd->osh, dhd->wlfc_state, sizeof(athost_wl_status_info_t));
2365	dhd->wlfc_state = NULL;
2366	return;
2367}
2368#endif /* PROP_TXSTATUS */
2369
2370void
2371dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
2372{
2373	bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
2374#ifdef PROP_TXSTATUS
2375	if (dhdp->wlfc_state)
2376		dhd_wlfc_dump(dhdp, strbuf);
2377#endif
2378}
2379
2380void
2381dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *pktbuf)
2382{
2383#ifdef BDC
2384	struct bdc_header *h;
2385#endif /* BDC */
2386
2387	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2388
2389#ifdef BDC
2390	/* Push BDC header used to convey priority for buses that don't */
2391
2392	PKTPUSH(dhd->osh, pktbuf, BDC_HEADER_LEN);
2393
2394	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
2395
2396	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
2397	if (PKTSUMNEEDED(pktbuf))
2398		h->flags |= BDC_FLAG_SUM_NEEDED;
2399
2400
2401	h->priority = (PKTPRIO(pktbuf) & BDC_PRIORITY_MASK);
2402	h->flags2 = 0;
2403	h->dataOffset = 0;
2404#endif /* BDC */
2405	BDC_SET_IF_IDX(h, ifidx);
2406}
2407
2408int
2409dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info,
2410	uint *reorder_info_len)
2411{
2412#ifdef BDC
2413	struct bdc_header *h;
2414#endif
2415
2416	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2417
2418#ifdef BDC
2419	if (reorder_info_len)
2420		*reorder_info_len = 0;
2421	/* Pop BDC header used to convey priority for buses that don't */
2422
2423	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
2424		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
2425		           PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
2426		return BCME_ERROR;
2427	}
2428
2429	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
2430
2431	if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
2432		DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
2433		           __FUNCTION__, *ifidx));
2434		return BCME_ERROR;
2435	}
2436
2437#if defined(NDIS630)
2438	h->dataOffset = 0;
2439#endif
2440	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
2441		DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
2442		           dhd_ifname(dhd, *ifidx), h->flags));
2443		if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1)
2444			h->dataOffset = 0;
2445		else
2446		return BCME_ERROR;
2447	}
2448
2449	if (h->flags & BDC_FLAG_SUM_GOOD) {
2450		DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
2451		          dhd_ifname(dhd, *ifidx), h->flags));
2452		PKTSETSUMGOOD(pktbuf, TRUE);
2453	}
2454
2455	PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
2456	PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
2457#endif /* BDC */
2458
2459#if !defined(NDIS630)
2460	if (PKTLEN(dhd->osh, pktbuf) < (uint32) (h->dataOffset << 2)) {
2461		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
2462		           PKTLEN(dhd->osh, pktbuf), (h->dataOffset * 4)));
2463		return BCME_ERROR;
2464	}
2465#endif
2466#ifdef PROP_TXSTATUS
2467	if (dhd->wlfc_state &&
2468		((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode
2469		!= WLFC_FCMODE_NONE &&
2470		(!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) {
2471		/*
2472		- parse txstatus only for packets that came from the firmware
2473		*/
2474		dhd_os_wlfc_block(dhd);
2475		dhd_wlfc_parse_header_info(dhd, pktbuf, (h->dataOffset << 2),
2476			reorder_buf_info, reorder_info_len);
2477		((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++;
2478		dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
2479			(void *)dhd->bus);
2480		dhd_os_wlfc_unblock(dhd);
2481	}
2482#endif /* PROP_TXSTATUS */
2483#if !defined(NDIS630)
2484		PKTPULL(dhd->osh, pktbuf, (h->dataOffset << 2));
2485#endif
2486	return 0;
2487}
2488
2489int
2490dhd_prot_attach(dhd_pub_t *dhd)
2491{
2492	dhd_prot_t *cdc;
2493
2494	if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT,
2495		sizeof(dhd_prot_t)))) {
2496			DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
2497			goto fail;
2498		}
2499	memset(cdc, 0, sizeof(dhd_prot_t));
2500
2501	/* ensure that the msg buf directly follows the cdc msg struct */
2502	if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
2503		DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
2504		goto fail;
2505	}
2506
2507	dhd->prot = cdc;
2508#ifdef BDC
2509	dhd->hdrlen += BDC_HEADER_LEN;
2510#endif
2511	dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
2512	return 0;
2513
2514fail:
2515#ifndef CONFIG_DHD_USE_STATIC_BUF
2516	if (cdc != NULL)
2517		MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
2518#endif /* CONFIG_DHD_USE_STATIC_BUF */
2519	return BCME_NOMEM;
2520}
2521
2522/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
2523void
2524dhd_prot_detach(dhd_pub_t *dhd)
2525{
2526#ifdef PROP_TXSTATUS
2527	dhd_wlfc_deinit(dhd);
2528#endif
2529#ifndef CONFIG_DHD_USE_STATIC_BUF
2530	MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
2531#endif /* CONFIG_DHD_USE_STATIC_BUF */
2532	dhd->prot = NULL;
2533}
2534
2535void
2536dhd_prot_dstats(dhd_pub_t *dhd)
2537{
2538	/* No stats from dongle added yet, copy bus stats */
2539	dhd->dstats.tx_packets = dhd->tx_packets;
2540	dhd->dstats.tx_errors = dhd->tx_errors;
2541	dhd->dstats.rx_packets = dhd->rx_packets;
2542	dhd->dstats.rx_errors = dhd->rx_errors;
2543	dhd->dstats.rx_dropped = dhd->rx_dropped;
2544	dhd->dstats.multicast = dhd->rx_multicast;
2545	return;
2546}
2547
2548int
2549dhd_prot_init(dhd_pub_t *dhd)
2550{
2551	int ret = 0;
2552	wlc_rev_info_t revinfo;
2553	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
2554
2555
2556	/* Get the device rev info */
2557	memset(&revinfo, 0, sizeof(revinfo));
2558	ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
2559	if (ret < 0)
2560		goto done;
2561
2562
2563#ifdef PROP_TXSTATUS
2564	ret = dhd_wlfc_init(dhd);
2565#endif
2566
2567#if defined(WL_CFG80211)
2568	if (dhd_download_fw_on_driverload)
2569#endif /* defined(WL_CFG80211) */
2570		ret = dhd_preinit_ioctls(dhd);
2571
2572	/* Always assumes wl for now */
2573	dhd->iswl = TRUE;
2574
2575done:
2576	return ret;
2577}
2578
2579void
2580dhd_prot_stop(dhd_pub_t *dhd)
2581{
2582	/* Nothing to do for CDC */
2583}
2584
2585
2586static void
2587dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt,
2588	uint32 *pkt_count, void **pplast, uint8 start, uint8 end)
2589{
2590	uint i;
2591	void *plast = NULL, *p;
2592	uint32 pkt_cnt = 0;
2593
2594	if (ptr->pend_pkts == 0) {
2595		DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
2596		*pplast = NULL;
2597		*pkt_count = 0;
2598		*pkt = NULL;
2599		return;
2600	}
2601	if (start == end)
2602		i = ptr->max_idx + 1;
2603	else {
2604		if (start > end)
2605			i = (ptr->max_idx - end) + start;
2606		else
2607			i = end - start;
2608	}
2609	while (i) {
2610		p = (void *)(ptr->p[start]);
2611		ptr->p[start] = NULL;
2612
2613		if (p != NULL) {
2614			if (plast == NULL)
2615				*pkt = p;
2616			else
2617				PKTSETNEXT(osh, plast, p);
2618
2619			plast = p;
2620			pkt_cnt++;
2621		}
2622		i--;
2623		if (start++ == ptr->max_idx)
2624			start = 0;
2625	}
2626	*pplast = plast;
2627	*pkt_count = (uint32)pkt_cnt;
2628}
2629
2630int
2631dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len,
2632	void **pkt, uint32 *pkt_count)
2633{
2634	uint8 flow_id, max_idx, cur_idx, exp_idx;
2635	struct reorder_info *ptr;
2636	uint8 flags;
2637	void *cur_pkt, *plast = NULL;
2638	uint32 cnt = 0;
2639
2640	if (pkt == NULL) {
2641		if (pkt_count != NULL)
2642			*pkt_count = 0;
2643		return 0;
2644	}
2645	cur_pkt = *pkt;
2646	*pkt = NULL;
2647
2648	flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
2649	flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
2650
2651	DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
2652		reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
2653		reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
2654		reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
2655
2656	/* validate flags and flow id */
2657	if (flags == 0xFF) {
2658		DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
2659		*pkt_count = 1;
2660		return 0;
2661	}
2662
2663	ptr = dhd->reorder_bufs[flow_id];
2664	if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
2665		uint32 buf_size = sizeof(struct reorder_info);
2666
2667		DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
2668			__FUNCTION__, flow_id));
2669
2670		if (ptr == NULL) {
2671			DHD_ERROR(("%s: received flags to cleanup, but no flow (%d) yet\n",
2672				__FUNCTION__, flow_id));
2673			*pkt_count = 1;
2674			return 0;
2675		}
2676
2677		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2678			ptr->exp_idx, ptr->exp_idx);
2679		/* set it to the last packet */
2680		if (plast) {
2681			PKTSETNEXT(dhd->osh, plast, cur_pkt);
2682			cnt++;
2683		}
2684		else {
2685			if (cnt != 0) {
2686				DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n",
2687					__FUNCTION__, cnt));
2688			}
2689			*pkt = cur_pkt;
2690			cnt = 1;
2691		}
2692		buf_size += ((ptr->max_idx + 1) * sizeof(void *));
2693		MFREE(dhd->osh, ptr, buf_size);
2694		dhd->reorder_bufs[flow_id] = NULL;
2695		*pkt_count = cnt;
2696		return 0;
2697	}
2698	/* all the other cases depend on the existance of the reorder struct for that flow id */
2699	if (ptr == NULL) {
2700		uint32 buf_size_alloc = sizeof(reorder_info_t);
2701		max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
2702
2703		buf_size_alloc += ((max_idx + 1) * sizeof(void*));
2704		/* allocate space to hold the buffers, index etc */
2705
2706		DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
2707			__FUNCTION__, buf_size_alloc, flow_id, max_idx));
2708		ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
2709		if (ptr == NULL) {
2710			DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
2711			*pkt_count = 1;
2712			return 0;
2713		}
2714		bzero(ptr, buf_size_alloc);
2715		dhd->reorder_bufs[flow_id] = ptr;
2716		ptr->p = (void *)(ptr+1);
2717		ptr->max_idx = max_idx;
2718	}
2719	if (flags & WLHOST_REORDERDATA_NEW_HOLE)  {
2720		DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
2721		if (ptr->pend_pkts) {
2722			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2723				ptr->exp_idx, ptr->exp_idx);
2724			ptr->pend_pkts = 0;
2725		}
2726		ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
2727		ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2728		ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
2729		ptr->p[ptr->cur_idx] = cur_pkt;
2730		ptr->pend_pkts++;
2731		*pkt_count = cnt;
2732	}
2733	else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
2734		cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
2735		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2736
2737
2738		if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
2739			/* still in the current hole */
2740			/* enqueue the current on the buffer chain */
2741			if (ptr->p[cur_idx] != NULL) {
2742				DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
2743					__FUNCTION__));
2744				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
2745				ptr->p[cur_idx] = NULL;
2746			}
2747			ptr->p[cur_idx] = cur_pkt;
2748			ptr->pend_pkts++;
2749			ptr->cur_idx = cur_idx;
2750			DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
2751				__FUNCTION__, ptr->pend_pkts));
2752			*pkt_count = 0;
2753			*pkt = NULL;
2754		}
2755		else if (ptr->exp_idx == cur_idx) {
2756			/* got the right one ..flush from cur to exp and update exp */
2757			DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
2758				__FUNCTION__, cur_idx));
2759			if (ptr->p[cur_idx] != NULL) {
2760				DHD_REORDER(("%s: Error buffer pending..free it\n",
2761					__FUNCTION__));
2762				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
2763				ptr->p[cur_idx] = NULL;
2764			}
2765			ptr->p[cur_idx] = cur_pkt;
2766			ptr->pend_pkts++;
2767
2768			ptr->cur_idx = cur_idx;
2769			ptr->exp_idx = exp_idx;
2770
2771			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2772				cur_idx, exp_idx);
2773			ptr->pend_pkts -= (uint8)cnt;
2774			*pkt_count = cnt;
2775			DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
2776				__FUNCTION__, cnt, ptr->pend_pkts));
2777		}
2778		else {
2779			uint8 end_idx;
2780			bool flush_current = FALSE;
2781			/* both cur and exp are moved now .. */
2782			DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
2783				__FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
2784				ptr->exp_idx, exp_idx));
2785			if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
2786				end_idx = ptr->exp_idx;
2787			else
2788				end_idx = exp_idx;
2789
2790			/* flush pkts first */
2791			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
2792				ptr->exp_idx, end_idx);
2793
2794			if (cur_idx == ptr->max_idx) {
2795				if (exp_idx == 0)
2796					flush_current = TRUE;
2797			} else {
2798				if (exp_idx == cur_idx + 1)
2799					flush_current = TRUE;
2800			}
2801			if (flush_current) {
2802				if (plast)
2803					PKTSETNEXT(dhd->osh, plast, cur_pkt);
2804				else
2805					*pkt = cur_pkt;
2806				cnt++;
2807			}
2808			else {
2809				ptr->p[cur_idx] = cur_pkt;
2810				ptr->pend_pkts++;
2811			}
2812			ptr->exp_idx = exp_idx;
2813			ptr->cur_idx = cur_idx;
2814			*pkt_count = cnt;
2815		}
2816	}
2817	else {
2818		uint8 end_idx;
2819		/* no real packet but update to exp_seq...that means explicit window move */
2820		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
2821
2822		DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
2823			__FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
2824		if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
2825			end_idx =  ptr->exp_idx;
2826		else
2827			end_idx =  exp_idx;
2828
2829		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx);
2830		ptr->pend_pkts -= (uint8)cnt;
2831		if (plast)
2832			PKTSETNEXT(dhd->osh, plast, cur_pkt);
2833		else
2834			*pkt = cur_pkt;
2835		cnt++;
2836		*pkt_count = cnt;
2837		/* set the new expected idx */
2838		ptr->exp_idx = exp_idx;
2839	}
2840	return 0;
2841}
2842