• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * DHD Protocol Module for CDC and BDC.
3  *
4  * Copyright (C) 1999-2013, Broadcom Corporation
5  *
6  *      Unless you and Broadcom execute a separate written software license
7  * agreement governing use of this software, this software is licensed to you
8  * under the terms of the GNU General Public License version 2 (the "GPL"),
9  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10  * following added to such license:
11  *
12  *      As a special exception, the copyright holders of this software give you
13  * permission to link this software with independent modules, and to copy and
14  * distribute the resulting executable under terms of your choice, provided that
15  * you also meet, for each linked independent module, the terms and conditions of
16  * the license of that module.  An independent module is a module which is not
17  * derived from this software.  The special exception does not apply to any
18  * modifications of the software.
19  *
20  *      Notwithstanding the above, under no circumstances may you combine this
21  * software in any way with any other Broadcom software provided under a license
22  * other than the GPL, without Broadcom's express prior written consent.
23  *
24  * $Id: dhd_cdc.c 416698 2013-08-06 07:53:34Z $
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	(24+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 typedef struct dhd_prot {
61 	uint16 reqid;
62 	uint8 pending;
63 	uint32 lastcmd;
64 	uint8 bus_header[BUS_HEADER_LEN];
65 	cdc_ioctl_t msg;
66 	unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
67 } dhd_prot_t;
68 
69 
70 static int
dhdcdc_msg(dhd_pub_t * dhd)71 dhdcdc_msg(dhd_pub_t *dhd)
72 {
73 	int err = 0;
74 	dhd_prot_t *prot = dhd->prot;
75 	int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
76 
77 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
78 
79 	DHD_OS_WAKE_LOCK(dhd);
80 
81 	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
82 	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
83 	 *	  is actually sent to the dongle
84 	 */
85 	if (len > CDC_MAX_MSG_SIZE)
86 		len = CDC_MAX_MSG_SIZE;
87 
88 	/* Send request */
89 	err = dhd_bus_txctl(dhd->bus, (uchar*)&prot->msg, len);
90 
91 	DHD_OS_WAKE_UNLOCK(dhd);
92 	return err;
93 }
94 
95 static int
dhdcdc_cmplt(dhd_pub_t * dhd,uint32 id,uint32 len)96 dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
97 {
98 	int ret;
99 	int cdc_len = len + sizeof(cdc_ioctl_t);
100 	dhd_prot_t *prot = dhd->prot;
101 
102 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
103 
104 
105 	do {
106 		ret = dhd_bus_rxctl(dhd->bus, (uchar*)&prot->msg, cdc_len);
107 		if (ret < 0)
108 			break;
109 	} while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
110 
111 
112 	return ret;
113 }
114 
115 static int
dhdcdc_query_ioctl(dhd_pub_t * dhd,int ifidx,uint cmd,void * buf,uint len,uint8 action)116 dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
117 {
118 	dhd_prot_t *prot = dhd->prot;
119 	cdc_ioctl_t *msg = &prot->msg;
120 	int ret = 0, retries = 0;
121 	uint32 id, flags = 0;
122 
123 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
124 	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
125 
126 
127 	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
128 	if (cmd == WLC_GET_VAR && buf)
129 	{
130 		if (!strcmp((char *)buf, "bcmerrorstr"))
131 		{
132 			strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
133 			goto done;
134 		}
135 		else if (!strcmp((char *)buf, "bcmerror"))
136 		{
137 			*(int *)buf = dhd->dongle_error;
138 			goto done;
139 		}
140 	}
141 
142 	memset(msg, 0, sizeof(cdc_ioctl_t));
143 
144 	msg->cmd = htol32(cmd);
145 	msg->len = htol32(len);
146 	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
147 	CDC_SET_IF_IDX(msg, ifidx);
148 	/* add additional action bits */
149 	action &= WL_IOCTL_ACTION_MASK;
150 	msg->flags |= (action << CDCF_IOC_ACTION_SHIFT);
151 	msg->flags = htol32(msg->flags);
152 
153 	if (buf)
154 		memcpy(prot->buf, buf, len);
155 
156 	if ((ret = dhdcdc_msg(dhd)) < 0) {
157 		if (!dhd->hang_was_sent)
158 		DHD_ERROR(("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
159 		goto done;
160 	}
161 
162 retry:
163 	/* wait for interrupt and get first fragment */
164 	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
165 		goto done;
166 
167 	flags = ltoh32(msg->flags);
168 	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
169 
170 	if ((id < prot->reqid) && (++retries < RETRIES))
171 		goto retry;
172 	if (id != prot->reqid) {
173 		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
174 		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
175 		ret = -EINVAL;
176 		goto done;
177 	}
178 
179 	/* Copy info buffer */
180 	if (buf)
181 	{
182 		if (ret < (int)len)
183 			len = ret;
184 		memcpy(buf, (void*) prot->buf, len);
185 	}
186 
187 	/* Check the ERROR flag */
188 	if (flags & CDCF_IOC_ERROR)
189 	{
190 		ret = ltoh32(msg->status);
191 		/* Cache error from dongle */
192 		dhd->dongle_error = ret;
193 	}
194 
195 done:
196 	return ret;
197 }
198 
199 
200 static int
dhdcdc_set_ioctl(dhd_pub_t * dhd,int ifidx,uint cmd,void * buf,uint len,uint8 action)201 dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len, uint8 action)
202 {
203 	dhd_prot_t *prot = dhd->prot;
204 	cdc_ioctl_t *msg = &prot->msg;
205 	int ret = 0;
206 	uint32 flags, id;
207 
208 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
209 	DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
210 
211 	if (dhd->busstate == DHD_BUS_DOWN) {
212 		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
213 		return -EIO;
214 	}
215 
216 	/* don't talk to the dongle if fw is about to be reloaded */
217 	if (dhd->hang_was_sent) {
218 		DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
219 			__FUNCTION__));
220 		return -EIO;
221 	}
222 
223 
224 	memset(msg, 0, sizeof(cdc_ioctl_t));
225 
226 	msg->cmd = htol32(cmd);
227 	msg->len = htol32(len);
228 	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
229 	CDC_SET_IF_IDX(msg, ifidx);
230 	/* add additional action bits */
231 	action &= WL_IOCTL_ACTION_MASK;
232 	msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET;
233 	msg->flags = htol32(msg->flags);
234 
235 	if (buf)
236 		memcpy(prot->buf, buf, len);
237 
238 	if ((ret = dhdcdc_msg(dhd)) < 0) {
239 		DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret));
240 		goto done;
241 	}
242 
243 	if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0)
244 		goto done;
245 
246 	flags = ltoh32(msg->flags);
247 	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
248 
249 	if (id != prot->reqid) {
250 		DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
251 		           dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
252 		ret = -EINVAL;
253 		goto done;
254 	}
255 
256 	/* Check the ERROR flag */
257 	if (flags & CDCF_IOC_ERROR)
258 	{
259 		ret = ltoh32(msg->status);
260 		/* Cache error from dongle */
261 		dhd->dongle_error = ret;
262 	}
263 
264 done:
265 	return ret;
266 }
267 
268 
269 int
dhd_prot_ioctl(dhd_pub_t * dhd,int ifidx,wl_ioctl_t * ioc,void * buf,int len)270 dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t * ioc, void * buf, int len)
271 {
272 	dhd_prot_t *prot = dhd->prot;
273 	int ret = -1;
274 	uint8 action;
275 
276 	if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
277 		DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
278 		goto done;
279 	}
280 
281 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
282 
283 	ASSERT(len <= WLC_IOCTL_MAXLEN);
284 
285 	if (len > WLC_IOCTL_MAXLEN)
286 		goto done;
287 
288 	if (prot->pending == TRUE) {
289 		DHD_ERROR(("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
290 			ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
291 			(unsigned long)prot->lastcmd));
292 		if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
293 			DHD_TRACE(("iovar cmd=%s\n", (char*)buf));
294 		}
295 		goto done;
296 	}
297 
298 	prot->pending = TRUE;
299 	prot->lastcmd = ioc->cmd;
300 	action = ioc->set;
301 	if (action & WL_IOCTL_ACTION_SET)
302 		ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
303 	else {
304 		ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
305 		if (ret > 0)
306 			ioc->used = ret - sizeof(cdc_ioctl_t);
307 	}
308 
309 	/* Too many programs assume ioctl() returns 0 on success */
310 	if (ret >= 0)
311 		ret = 0;
312 	else {
313 		cdc_ioctl_t *msg = &prot->msg;
314 		ioc->needed = ltoh32(msg->len); /* len == needed when set/query fails from dongle */
315 	}
316 
317 	/* Intercept the wme_dp ioctl here */
318 	if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
319 		int slen, val = 0;
320 
321 		slen = strlen("wme_dp") + 1;
322 		if (len >= (int)(slen + sizeof(int)))
323 			bcopy(((char *)buf + slen), &val, sizeof(int));
324 		dhd->wme_dp = (uint8) ltoh32(val);
325 	}
326 
327 	prot->pending = FALSE;
328 
329 done:
330 
331 	return ret;
332 }
333 
334 int
dhd_prot_iovar_op(dhd_pub_t * dhdp,const char * name,void * params,int plen,void * arg,int len,bool set)335 dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name,
336                   void *params, int plen, void *arg, int len, bool set)
337 {
338 	return BCME_UNSUPPORTED;
339 }
340 
341 void
dhd_prot_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)342 dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
343 {
344 	bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
345 #ifdef PROP_TXSTATUS
346 	if (dhdp->wlfc_state)
347 		dhd_wlfc_dump(dhdp, strbuf);
348 #endif
349 }
350 
351 /*	The FreeBSD PKTPUSH could change the packet buf pinter
352 	so we need to make it changable
353 */
354 #define PKTBUF pktbuf
355 void
dhd_prot_hdrpush(dhd_pub_t * dhd,int ifidx,void * PKTBUF)356 dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *PKTBUF)
357 {
358 #ifdef BDC
359 	struct bdc_header *h;
360 #endif /* BDC */
361 
362 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
363 
364 #ifdef BDC
365 	/* Push BDC header used to convey priority for buses that don't */
366 
367 	PKTPUSH(dhd->osh, PKTBUF, BDC_HEADER_LEN);
368 
369 	h = (struct bdc_header *)PKTDATA(dhd->osh, PKTBUF);
370 
371 	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
372 	if (PKTSUMNEEDED(PKTBUF))
373 		h->flags |= BDC_FLAG_SUM_NEEDED;
374 
375 
376 	h->priority = (PKTPRIO(PKTBUF) & BDC_PRIORITY_MASK);
377 	h->flags2 = 0;
378 	h->dataOffset = 0;
379 #endif /* BDC */
380 	BDC_SET_IF_IDX(h, ifidx);
381 }
382 #undef PKTBUF	/* Only defined in the above routine */
383 
384 int
dhd_prot_hdrpull(dhd_pub_t * dhd,int * ifidx,void * pktbuf,uchar * reorder_buf_info,uint * reorder_info_len)385 dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf, uchar *reorder_buf_info,
386 	uint *reorder_info_len)
387 {
388 #ifdef BDC
389 	struct bdc_header *h;
390 #endif
391 	uint8 data_offset = 0;
392 
393 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
394 
395 #ifdef BDC
396 	if (reorder_info_len)
397 		*reorder_info_len = 0;
398 	/* Pop BDC header used to convey priority for buses that don't */
399 
400 	if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
401 		DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
402 		           PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
403 		return BCME_ERROR;
404 	}
405 
406 	h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
407 
408 	if (!ifidx) {
409 		/* for tx packet, skip the analysis */
410 		data_offset = h->dataOffset;
411 		PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
412 		goto exit;
413 	}
414 
415 	if ((*ifidx = BDC_GET_IF_IDX(h)) >= DHD_MAX_IFS) {
416 		DHD_ERROR(("%s: rx data ifnum out of range (%d)\n",
417 		           __FUNCTION__, *ifidx));
418 		return BCME_ERROR;
419 	}
420 
421 	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != BDC_PROTO_VER) {
422 		DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
423 		           dhd_ifname(dhd, *ifidx), h->flags));
424 		if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) == BDC_PROTO_VER_1)
425 			h->dataOffset = 0;
426 		else
427 		return BCME_ERROR;
428 	}
429 
430 	if (h->flags & BDC_FLAG_SUM_GOOD) {
431 		DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
432 		          dhd_ifname(dhd, *ifidx), h->flags));
433 		PKTSETSUMGOOD(pktbuf, TRUE);
434 	}
435 
436 	PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
437 	data_offset = h->dataOffset;
438 	PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
439 #endif /* BDC */
440 
441 #ifdef PROP_TXSTATUS
442 	dhd_os_wlfc_block(dhd);
443 	if (dhd->wlfc_state &&
444 		((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode
445 		!= WLFC_FCMODE_NONE &&
446 		(!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf)))) {
447 		/*
448 		- parse txstatus only for packets that came from the firmware
449 		*/
450 		dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 2),
451 			reorder_buf_info, reorder_info_len);
452 		((athost_wl_status_info_t*)dhd->wlfc_state)->stats.dhd_hdrpulls++;
453 
454 	}
455 	dhd_os_wlfc_unblock(dhd);
456 #endif /* PROP_TXSTATUS */
457 
458 exit:
459 		PKTPULL(dhd->osh, pktbuf, (data_offset << 2));
460 	return 0;
461 }
462 
463 #if defined(PROP_TXSTATUS)
464 void
dhd_wlfc_trigger_pktcommit(dhd_pub_t * dhd)465 dhd_wlfc_trigger_pktcommit(dhd_pub_t *dhd)
466 {
467 	dhd_os_wlfc_block(dhd);
468 	if (dhd->wlfc_state &&
469 		(((athost_wl_status_info_t*)dhd->wlfc_state)->proptxstatus_mode
470 		!= WLFC_FCMODE_NONE)) {
471 		dhd_wlfc_commit_packets(dhd->wlfc_state, (f_commitpkt_t)dhd_bus_txdata,
472 			(void *)dhd->bus, NULL);
473 	}
474 	dhd_os_wlfc_unblock(dhd);
475 }
476 #endif
477 
478 
479 int
dhd_prot_attach(dhd_pub_t * dhd)480 dhd_prot_attach(dhd_pub_t *dhd)
481 {
482 	dhd_prot_t *cdc;
483 
484 	if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd->osh, DHD_PREALLOC_PROT,
485 		sizeof(dhd_prot_t)))) {
486 			DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
487 			goto fail;
488 		}
489 	memset(cdc, 0, sizeof(dhd_prot_t));
490 
491 	/* ensure that the msg buf directly follows the cdc msg struct */
492 	if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
493 		DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
494 		goto fail;
495 	}
496 
497 	dhd->prot = cdc;
498 #ifdef BDC
499 	dhd->hdrlen += BDC_HEADER_LEN;
500 #endif
501 	dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
502 	return 0;
503 
504 fail:
505 #ifndef CONFIG_DHD_USE_STATIC_BUF
506 	if (cdc != NULL)
507 		MFREE(dhd->osh, cdc, sizeof(dhd_prot_t));
508 #endif /* CONFIG_DHD_USE_STATIC_BUF */
509 	return BCME_NOMEM;
510 }
511 
512 /* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
513 void
dhd_prot_detach(dhd_pub_t * dhd)514 dhd_prot_detach(dhd_pub_t *dhd)
515 {
516 #ifdef PROP_TXSTATUS
517 	dhd_wlfc_deinit(dhd);
518 	if (dhd->plat_deinit)
519 		dhd->plat_deinit((void *)dhd);
520 #endif
521 #ifndef CONFIG_DHD_USE_STATIC_BUF
522 	MFREE(dhd->osh, dhd->prot, sizeof(dhd_prot_t));
523 #endif /* CONFIG_DHD_USE_STATIC_BUF */
524 	dhd->prot = NULL;
525 }
526 
527 void
dhd_prot_dstats(dhd_pub_t * dhd)528 dhd_prot_dstats(dhd_pub_t *dhd)
529 {
530 	/* No stats from dongle added yet, copy bus stats */
531 	dhd->dstats.tx_packets = dhd->tx_packets;
532 	dhd->dstats.tx_errors = dhd->tx_errors;
533 	dhd->dstats.rx_packets = dhd->rx_packets;
534 	dhd->dstats.rx_errors = dhd->rx_errors;
535 	dhd->dstats.rx_dropped = dhd->rx_dropped;
536 	dhd->dstats.multicast = dhd->rx_multicast;
537 	return;
538 }
539 
540 int
dhd_prot_init(dhd_pub_t * dhd)541 dhd_prot_init(dhd_pub_t *dhd)
542 {
543 	int ret = 0;
544 	wlc_rev_info_t revinfo;
545 	DHD_TRACE(("%s: Enter\n", __FUNCTION__));
546 
547 
548 	/* Get the device rev info */
549 	memset(&revinfo, 0, sizeof(revinfo));
550 	ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo), FALSE, 0);
551 	if (ret < 0)
552 		goto done;
553 
554 
555 #if defined(WL_CFG80211)
556 	if (dhd_download_fw_on_driverload)
557 #endif /* defined(WL_CFG80211) */
558 		ret = dhd_preinit_ioctls(dhd);
559 	/* Always assumes wl for now */
560 	dhd->iswl = TRUE;
561 
562 done:
563 	return ret;
564 }
565 
566 void
dhd_prot_stop(dhd_pub_t * dhd)567 dhd_prot_stop(dhd_pub_t *dhd)
568 {
569 	/* Nothing to do for CDC */
570 }
571 
572 
573 static void
dhd_get_hostreorder_pkts(void * osh,struct reorder_info * ptr,void ** pkt,uint32 * pkt_count,void ** pplast,uint8 start,uint8 end)574 dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr, void **pkt,
575 	uint32 *pkt_count, void **pplast, uint8 start, uint8 end)
576 {
577 	uint i;
578 	void *plast = NULL, *p;
579 	uint32 pkt_cnt = 0;
580 
581 	if (ptr->pend_pkts == 0) {
582 		DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
583 		*pplast = NULL;
584 		*pkt_count = 0;
585 		*pkt = NULL;
586 		return;
587 	}
588 	if (start == end)
589 		i = ptr->max_idx + 1;
590 	else {
591 		if (start > end)
592 			i = ((ptr->max_idx + 1) - start) + end;
593 		else
594 			i = end - start;
595 	}
596 	while (i) {
597 		p = (void *)(ptr->p[start]);
598 		ptr->p[start] = NULL;
599 
600 		if (p != NULL) {
601 			if (plast == NULL)
602 				*pkt = p;
603 			else
604 				PKTSETNEXT(osh, plast, p);
605 
606 			plast = p;
607 			pkt_cnt++;
608 		}
609 		i--;
610 		if (start++ == ptr->max_idx)
611 			start = 0;
612 	}
613 	*pplast = plast;
614 	*pkt_count = (uint32)pkt_cnt;
615 }
616 
617 int
dhd_process_pkt_reorder_info(dhd_pub_t * dhd,uchar * reorder_info_buf,uint reorder_info_len,void ** pkt,uint32 * pkt_count)618 dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf, uint reorder_info_len,
619 	void **pkt, uint32 *pkt_count)
620 {
621 	uint8 flow_id, max_idx, cur_idx, exp_idx;
622 	struct reorder_info *ptr;
623 	uint8 flags;
624 	void *cur_pkt, *plast = NULL;
625 	uint32 cnt = 0;
626 
627 	if (pkt == NULL) {
628 		if (pkt_count != NULL)
629 			*pkt_count = 0;
630 		return 0;
631 	}
632 
633 	flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
634 	flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
635 
636 	DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
637 		reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
638 		reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
639 		reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
640 
641 	/* validate flags and flow id */
642 	if (flags == 0xFF) {
643 		DHD_ERROR(("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
644 		*pkt_count = 1;
645 		return 0;
646 	}
647 
648 	cur_pkt = *pkt;
649 	*pkt = NULL;
650 
651 	ptr = dhd->reorder_bufs[flow_id];
652 	if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
653 		uint32 buf_size = sizeof(struct reorder_info);
654 
655 		DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
656 			__FUNCTION__, flow_id));
657 
658 		if (ptr == NULL) {
659 			DHD_REORDER(("%s: received flags to cleanup, but no flow (%d) yet\n",
660 				__FUNCTION__, flow_id));
661 			*pkt_count = 1;
662 			*pkt = cur_pkt;
663 			return 0;
664 		}
665 
666 		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
667 			ptr->exp_idx, ptr->exp_idx);
668 		/* set it to the last packet */
669 		if (plast) {
670 			PKTSETNEXT(dhd->osh, plast, cur_pkt);
671 			cnt++;
672 		}
673 		else {
674 			if (cnt != 0) {
675 				DHD_ERROR(("%s: del flow: something fishy, pending packets %d\n",
676 					__FUNCTION__, cnt));
677 			}
678 			*pkt = cur_pkt;
679 			cnt = 1;
680 		}
681 		buf_size += ((ptr->max_idx + 1) * sizeof(void *));
682 		MFREE(dhd->osh, ptr, buf_size);
683 		dhd->reorder_bufs[flow_id] = NULL;
684 		*pkt_count = cnt;
685 		return 0;
686 	}
687 	/* all the other cases depend on the existance of the reorder struct for that flow id */
688 	if (ptr == NULL) {
689 		uint32 buf_size_alloc = sizeof(reorder_info_t);
690 		max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
691 
692 		buf_size_alloc += ((max_idx + 1) * sizeof(void*));
693 		/* allocate space to hold the buffers, index etc */
694 
695 		DHD_REORDER(("%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
696 			__FUNCTION__, buf_size_alloc, flow_id, max_idx));
697 		ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
698 		if (ptr == NULL) {
699 			DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
700 			*pkt_count = 1;
701 			return 0;
702 		}
703 		bzero(ptr, buf_size_alloc);
704 		dhd->reorder_bufs[flow_id] = ptr;
705 		ptr->p = (void *)(ptr+1);
706 		ptr->max_idx = max_idx;
707 	}
708 	if (flags & WLHOST_REORDERDATA_NEW_HOLE)  {
709 		DHD_REORDER(("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
710 		if (ptr->pend_pkts) {
711 			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
712 				ptr->exp_idx, ptr->exp_idx);
713 			ptr->pend_pkts = 0;
714 		}
715 		ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
716 		ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
717 		ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
718 		ptr->p[ptr->cur_idx] = cur_pkt;
719 		ptr->pend_pkts++;
720 		*pkt_count = cnt;
721 	}
722 	else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
723 		cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
724 		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
725 
726 
727 		if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
728 			/* still in the current hole */
729 			/* enqueue the current on the buffer chain */
730 			if (ptr->p[cur_idx] != NULL) {
731 				DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
732 					__FUNCTION__));
733 				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
734 				ptr->p[cur_idx] = NULL;
735 			}
736 			ptr->p[cur_idx] = cur_pkt;
737 			ptr->pend_pkts++;
738 			ptr->cur_idx = cur_idx;
739 			DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
740 				__FUNCTION__, ptr->pend_pkts));
741 			*pkt_count = 0;
742 			*pkt = NULL;
743 		}
744 		else if (ptr->exp_idx == cur_idx) {
745 			/* got the right one ..flush from cur to exp and update exp */
746 			DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
747 				__FUNCTION__, cur_idx));
748 			if (ptr->p[cur_idx] != NULL) {
749 				DHD_REORDER(("%s: Error buffer pending..free it\n",
750 					__FUNCTION__));
751 				PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
752 				ptr->p[cur_idx] = NULL;
753 			}
754 			ptr->p[cur_idx] = cur_pkt;
755 			ptr->pend_pkts++;
756 
757 			ptr->cur_idx = cur_idx;
758 			ptr->exp_idx = exp_idx;
759 
760 			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
761 				cur_idx, exp_idx);
762 			ptr->pend_pkts -= (uint8)cnt;
763 			*pkt_count = cnt;
764 			DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
765 				__FUNCTION__, cnt, ptr->pend_pkts));
766 		}
767 		else {
768 			uint8 end_idx;
769 			bool flush_current = FALSE;
770 			/* both cur and exp are moved now .. */
771 			DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
772 				__FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
773 				ptr->exp_idx, exp_idx));
774 			if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
775 				end_idx = ptr->exp_idx;
776 			else
777 				end_idx = exp_idx;
778 
779 			/* flush pkts first */
780 			dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
781 				ptr->exp_idx, end_idx);
782 
783 			if (cur_idx == ptr->max_idx) {
784 				if (exp_idx == 0)
785 					flush_current = TRUE;
786 			} else {
787 				if (exp_idx == cur_idx + 1)
788 					flush_current = TRUE;
789 			}
790 			if (flush_current) {
791 				if (plast)
792 					PKTSETNEXT(dhd->osh, plast, cur_pkt);
793 				else
794 					*pkt = cur_pkt;
795 				cnt++;
796 			}
797 			else {
798 				ptr->p[cur_idx] = cur_pkt;
799 				ptr->pend_pkts++;
800 			}
801 			ptr->exp_idx = exp_idx;
802 			ptr->cur_idx = cur_idx;
803 			*pkt_count = cnt;
804 		}
805 	}
806 	else {
807 		uint8 end_idx;
808 		/* no real packet but update to exp_seq...that means explicit window move */
809 		exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
810 
811 		DHD_REORDER(("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
812 			__FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
813 		if (flags & WLHOST_REORDERDATA_FLUSH_ALL)
814 			end_idx =  ptr->exp_idx;
815 		else
816 			end_idx =  exp_idx;
817 
818 		dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx, end_idx);
819 		ptr->pend_pkts -= (uint8)cnt;
820 		if (plast)
821 			PKTSETNEXT(dhd->osh, plast, cur_pkt);
822 		else
823 			*pkt = cur_pkt;
824 		cnt++;
825 		*pkt_count = cnt;
826 		/* set the new expected idx */
827 		ptr->exp_idx = exp_idx;
828 	}
829 	return 0;
830 }
831