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