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