• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * DHD Protocol Module for CDC and BDC.
3  *
4  * Copyright (C) 1999-2019, Broadcom.
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
16  * of the license of that module.  An independent module is a module which is
17  * not 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  *
25  * <<Broadcom-WL-IPTag/Open:>>
26  *
27  * $Id: dhd_cdc.c 752794 2018-03-19 04:00:31Z $
28  *
29  * BDC is like CDC, except it includes a header for data packets to convey
30  * packet priority over the bus, and flags (e.g. to indicate checksum status
31  * for dongle offload.)
32  */
33 
34 #include <typedefs.h>
35 #include <osl.h>
36 
37 #include <bcmutils.h>
38 #include <bcmcdc.h>
39 #include <bcmendian.h>
40 
41 #include <dngl_stats.h>
42 #include <dhd.h>
43 #include <dhd_proto.h>
44 #include <dhd_bus.h>
45 #include <dhd_dbg.h>
46 
47 #ifdef PROP_TXSTATUS
48 #include <wlfc_proto.h>
49 #include <dhd_wlfc.h>
50 #endif // endif
51 #ifdef BCMDBUS
52 #include <dhd_config.h>
53 #endif /* BCMDBUS */
54 
55 #ifdef DHD_ULP
56 #include <dhd_ulp.h>
57 #endif /* DHD_ULP */
58 
59 #define RETRIES 2 /* # of retries to retrieve matching ioctl response */
60 #define BUS_HEADER_LEN                                                         \
61     (24 + DHD_SDALIGN) /* Must be at least SDPCM_RESERVE                       \
62                         * defined in dhd_sdio.c (amount of header tha might be \
63                         * added) plus any space that might be needed for                                       \
64                         * alignment padding.                                   \
65                         */
66 #define ROUND_UP_MARGIN                                                        \
67     2048 /* Biggest SDIO block size possible for                               \
68           * round off at the end of buffer                                     \
69           */
70 
71 typedef struct dhd_prot {
72     uint16 reqid;
73     uint8 pending;
74     uint32 lastcmd;
75 #ifdef BCMDBUS
76     uint ctl_completed;
77 #endif /* BCMDBUS */
78     uint8 bus_header[BUS_HEADER_LEN];
79     cdc_ioctl_t msg;
80     unsigned char buf[WLC_IOCTL_MAXLEN + ROUND_UP_MARGIN];
81 } dhd_prot_t;
82 
dhd_prot_get_ioctl_trans_id(dhd_pub_t * dhdp)83 uint16 dhd_prot_get_ioctl_trans_id(dhd_pub_t *dhdp)
84 {
85     /* SDIO does not have ioctl_trans_id yet, so return -1 */
86     return -1;
87 }
88 
dhdcdc_msg(dhd_pub_t * dhd)89 static int dhdcdc_msg(dhd_pub_t *dhd)
90 {
91 #ifdef BCMDBUS
92     int timeout = 0;
93 #endif /* BCMDBUS */
94     int err = 0;
95     dhd_prot_t *prot = dhd->prot;
96     int len = ltoh32(prot->msg.len) + sizeof(cdc_ioctl_t);
97 
98     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
99 
100     DHD_OS_WAKE_LOCK(dhd);
101 
102     /* NOTE : cdc->msg.len holds the desired length of the buffer to be
103      *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
104      *	  is actually sent to the dongle
105      */
106     if (len > CDC_MAX_MSG_SIZE) {
107         len = CDC_MAX_MSG_SIZE;
108     }
109 
110     /* Send request */
111 #ifdef BCMDBUS
112     DHD_OS_IOCTL_RESP_LOCK(dhd);
113     prot->ctl_completed = FALSE;
114     err = dbus_send_ctl(dhd->bus, (void *)&prot->msg, len);
115     if (err) {
116         DHD_ERROR(("dbus_send_ctl error=%d\n", err));
117         DHD_OS_IOCTL_RESP_UNLOCK(dhd);
118         DHD_OS_WAKE_UNLOCK(dhd);
119         return err;
120     }
121 #else
122     err = dhd_bus_txctl(dhd->bus, (uchar *)&prot->msg, len);
123 #endif /* BCMDBUS */
124 
125 #ifdef BCMDBUS
126     timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed);
127     if ((!timeout) || (!prot->ctl_completed)) {
128         DHD_ERROR(("Txctl timeout %d ctl_completed %d\n", timeout,
129                    prot->ctl_completed));
130         DHD_ERROR(("Txctl wait timed out\n"));
131         err = -1;
132     }
133     DHD_OS_IOCTL_RESP_UNLOCK(dhd);
134 #endif /* BCMDBUS */
135 #if defined(BCMDBUS) && defined(INTR_EP_ENABLE)
136     /* If the ctl write is successfully completed, wait for an acknowledgement
137      * that indicates that it is now ok to do ctl read from the dongle
138      */
139     if (err != -1) {
140         DHD_OS_IOCTL_RESP_LOCK(dhd);
141         prot->ctl_completed = FALSE;
142         if (dbus_poll_intr(dhd->dbus)) {
143             DHD_ERROR(("dbus_poll_intr not submitted\n"));
144         } else {
145             /* interrupt polling is sucessfully submitted. Wait for dongle to
146              * send interrupt
147              */
148             timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed);
149             if (!timeout) {
150                 DHD_ERROR(("intr poll wait timed out\n"));
151             }
152         }
153         DHD_OS_IOCTL_RESP_UNLOCK(dhd);
154     }
155 #endif /* defined(BCMDBUS) && defined(INTR_EP_ENABLE) */
156     DHD_OS_WAKE_UNLOCK(dhd);
157     return err;
158 }
159 
dhdcdc_cmplt(dhd_pub_t * dhd,uint32 id,uint32 len)160 static int dhdcdc_cmplt(dhd_pub_t *dhd, uint32 id, uint32 len)
161 {
162 #ifdef BCMDBUS
163     int timeout = 0;
164 #endif /* BCMDBUS */
165     int ret;
166     int cdc_len = len + sizeof(cdc_ioctl_t);
167     dhd_prot_t *prot = dhd->prot;
168 
169     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
170 
171     do {
172 #ifdef BCMDBUS
173         DHD_OS_IOCTL_RESP_LOCK(dhd);
174         prot->ctl_completed = FALSE;
175         ret = dbus_recv_ctl(dhd->bus, (uchar *)&prot->msg, cdc_len);
176         if (ret) {
177             DHD_ERROR(("dbus_recv_ctl error=0x%x(%d)\n", ret, ret));
178             DHD_OS_IOCTL_RESP_UNLOCK(dhd);
179             goto done;
180         }
181         timeout = dhd_os_ioctl_resp_wait(dhd, &prot->ctl_completed);
182         if ((!timeout) || (!prot->ctl_completed)) {
183             DHD_ERROR(("Rxctl timeout %d ctl_completed %d\n", timeout,
184                        prot->ctl_completed));
185             ret = -1;
186             DHD_OS_IOCTL_RESP_UNLOCK(dhd);
187 
188             goto done;
189         }
190         DHD_OS_IOCTL_RESP_UNLOCK(dhd);
191 
192         ret = cdc_len;
193 #else
194         ret = dhd_bus_rxctl(dhd->bus, (uchar *)&prot->msg, cdc_len);
195 #endif /* BCMDBUS */
196         if (ret < 0) {
197             break;
198         }
199     } while (CDC_IOC_ID(ltoh32(prot->msg.flags)) != id);
200 
201 #ifdef BCMDBUS
202 done:
203 #endif /* BCMDBUS */
204     return ret;
205 }
206 
dhdcdc_query_ioctl(dhd_pub_t * dhd,int ifidx,uint cmd,void * buf,uint len,uint8 action)207 static int dhdcdc_query_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf,
208                               uint len, uint8 action)
209 {
210     dhd_prot_t *prot = dhd->prot;
211     cdc_ioctl_t *msg = &prot->msg;
212     int ret = 0, retries = 0;
213     uint32 id, flags = 0;
214 
215     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
216     DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
217 
218     /* Respond "bcmerror" and "bcmerrorstr" with local cache */
219     if (cmd == WLC_GET_VAR && buf) {
220         if (!strcmp((char *)buf, "bcmerrorstr")) {
221             strncpy((char *)buf, bcmerrorstr(dhd->dongle_error), BCME_STRLEN);
222             goto done;
223         } else if (!strcmp((char *)buf, "bcmerror")) {
224             *(int *)buf = dhd->dongle_error;
225             goto done;
226         }
227     }
228 
229     memset(msg, 0, sizeof(cdc_ioctl_t));
230 
231 #ifdef BCMSPI
232     /* 11bit gSPI bus allows 2048bytes of max-data.  We restrict 'len'
233      * value which is 8Kbytes for various 'get' commands to 2000.  48 bytes are
234      * left for sw headers and misc.
235      */
236     if (len > 0x7D0) {
237         DHD_ERROR(("dhdcdc_query_ioctl: len is truncated to 2000 bytes\n"));
238         len = 0x7D0;
239     }
240 #endif /* BCMSPI */
241     msg->cmd = htol32(cmd);
242     msg->len = htol32(len);
243     msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
244     CDC_SET_IF_IDX(msg, ifidx);
245     /* add additional action bits */
246     action &= WL_IOCTL_ACTION_MASK;
247     msg->flags |= (action << CDCF_IOC_ACTION_SHIFT);
248     msg->flags = htol32(msg->flags);
249 
250     if (buf) {
251         memcpy(prot->buf, buf, len);
252     }
253 
254     if ((ret = dhdcdc_msg(dhd)) < 0) {
255         if (!dhd->hang_was_sent) {
256             DHD_ERROR(
257                 ("dhdcdc_query_ioctl: dhdcdc_msg failed w/status %d\n", ret));
258         }
259         goto done;
260     }
261 
262 retry:
263     /* wait for interrupt and get first fragment */
264     if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) {
265         goto done;
266     }
267 
268     flags = ltoh32(msg->flags);
269     id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
270 
271     if ((id < prot->reqid) && (++retries < RETRIES)) {
272         goto retry;
273     }
274     if (id != prot->reqid) {
275         DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
276                    dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
277         ret = -EINVAL;
278         goto done;
279     }
280 
281     /* Copy info buffer */
282     if (buf) {
283         if (ret < (int)len) {
284             len = ret;
285         }
286         memcpy(buf, (void *)prot->buf, len);
287     }
288 
289     /* Check the ERROR flag */
290     if (flags & CDCF_IOC_ERROR) {
291         ret = ltoh32(msg->status);
292         /* Cache error from dongle */
293         dhd->dongle_error = ret;
294     }
295 
296 done:
297     return ret;
298 }
299 
300 #ifdef DHD_PM_CONTROL_FROM_FILE
301 extern bool g_pm_control;
302 #endif /* DHD_PM_CONTROL_FROM_FILE */
303 
dhdcdc_set_ioctl(dhd_pub_t * dhd,int ifidx,uint cmd,void * buf,uint len,uint8 action)304 static int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf,
305                             uint len, uint8 action)
306 {
307     dhd_prot_t *prot = dhd->prot;
308     cdc_ioctl_t *msg = &prot->msg;
309     int ret = 0;
310     uint32 flags, id;
311 
312     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
313     DHD_CTL(("%s: cmd %d len %d\n", __FUNCTION__, cmd, len));
314 
315     if (dhd->busstate == DHD_BUS_DOWN) {
316         DHD_ERROR(("%s : bus is down. we have nothing to do\n", __FUNCTION__));
317         return -EIO;
318     }
319 
320     /* don't talk to the dongle if fw is about to be reloaded */
321     if (dhd->hang_was_sent) {
322         DHD_ERROR(("%s: HANG was sent up earlier. Not talking to the chip\n",
323                    __FUNCTION__));
324         return -EIO;
325     }
326 
327     if (cmd == WLC_SET_PM) {
328 #ifdef DHD_PM_CONTROL_FROM_FILE
329         if (g_pm_control == TRUE) {
330             DHD_ERROR(("%s: SET PM ignored!(Requested:%d)\n", __FUNCTION__,
331                        buf ? *(char *)buf : 0));
332             goto done;
333         }
334 #endif /* DHD_PM_CONTROL_FROM_FILE */
335         DHD_TRACE_HW4(
336             ("%s: SET PM to %d\n", __FUNCTION__, buf ? *(char *)buf : 0));
337     }
338 
339     memset(msg, 0, sizeof(cdc_ioctl_t));
340 
341     msg->cmd = htol32(cmd);
342     msg->len = htol32(len);
343     msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
344     CDC_SET_IF_IDX(msg, ifidx);
345     /* add additional action bits */
346     action &= WL_IOCTL_ACTION_MASK;
347     msg->flags |= (action << CDCF_IOC_ACTION_SHIFT) | CDCF_IOC_SET;
348     msg->flags = htol32(msg->flags);
349 
350     if (buf) {
351         memcpy(prot->buf, buf, len);
352     }
353 
354 #ifdef DHD_ULP
355     if (buf && (!strncmp(buf, "ulp", sizeof("ulp")))) {
356         /* force all the writes after this point to NOT to use cached sbwad
357          * value */
358         dhd_ulp_disable_cached_sbwad(dhd);
359     }
360 #endif /* DHD_ULP */
361 
362     if ((ret = dhdcdc_msg(dhd)) < 0) {
363         DHD_ERROR(("%s: dhdcdc_msg failed w/status %d\n", __FUNCTION__, ret));
364         goto done;
365     }
366 
367     if ((ret = dhdcdc_cmplt(dhd, prot->reqid, len)) < 0) {
368         goto done;
369     }
370 
371     flags = ltoh32(msg->flags);
372     id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
373 
374     if (id != prot->reqid) {
375         DHD_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
376                    dhd_ifname(dhd, ifidx), __FUNCTION__, id, prot->reqid));
377         ret = -EINVAL;
378         goto done;
379     }
380 
381 #ifdef DHD_ULP
382     /* For ulp prototyping temporary */
383     if ((ret = dhd_ulp_check_ulp_request(dhd, buf)) < 0) {
384         goto done;
385     }
386 #endif /* DHD_ULP */
387 
388     /* Check the ERROR flag */
389     if (flags & CDCF_IOC_ERROR) {
390         ret = ltoh32(msg->status);
391         /* Cache error from dongle */
392         dhd->dongle_error = ret;
393     }
394 
395 done:
396     return ret;
397 }
398 
399 #ifdef BCMDBUS
dhd_prot_ctl_complete(dhd_pub_t * dhd)400 int dhd_prot_ctl_complete(dhd_pub_t *dhd)
401 {
402     dhd_prot_t *prot;
403 
404     if (dhd == NULL) {
405         return BCME_ERROR;
406     }
407 
408     prot = dhd->prot;
409 
410     ASSERT(prot);
411     DHD_OS_IOCTL_RESP_LOCK(dhd);
412     prot->ctl_completed = TRUE;
413     dhd_os_ioctl_resp_wake(dhd);
414     DHD_OS_IOCTL_RESP_UNLOCK(dhd);
415     return 0;
416 }
417 #endif /* BCMDBUS */
418 
dhd_prot_ioctl(dhd_pub_t * dhd,int ifidx,wl_ioctl_t * ioc,void * buf,int len)419 int dhd_prot_ioctl(dhd_pub_t *dhd, int ifidx, wl_ioctl_t *ioc, void *buf,
420                    int len)
421 {
422     dhd_prot_t *prot = dhd->prot;
423     int ret = -1;
424     uint8 action;
425     static int error_cnt = 0;
426 
427     if ((dhd->busstate == DHD_BUS_DOWN) || dhd->hang_was_sent) {
428         DHD_ERROR(
429             ("%s : bus is down. we have nothing to do - bs: %d, has: %d\n",
430              __FUNCTION__, dhd->busstate, dhd->hang_was_sent));
431         goto done;
432     }
433 
434     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
435 
436     ASSERT(len <= WLC_IOCTL_MAXLEN);
437 
438     if (len > WLC_IOCTL_MAXLEN) {
439         goto done;
440     }
441 
442     if (prot->pending == TRUE) {
443         DHD_ERROR(
444             ("CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n",
445              ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
446              (unsigned long)prot->lastcmd));
447         if ((ioc->cmd == WLC_SET_VAR) || (ioc->cmd == WLC_GET_VAR)) {
448             DHD_TRACE(("iovar cmd=%s\n", buf ? (char *)buf : "\0"));
449         }
450         goto done;
451     }
452 
453     prot->pending = TRUE;
454     prot->lastcmd = ioc->cmd;
455     action = ioc->set;
456     if (action & WL_IOCTL_ACTION_SET) {
457         ret = dhdcdc_set_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
458     } else {
459         ret = dhdcdc_query_ioctl(dhd, ifidx, ioc->cmd, buf, len, action);
460         if (ret > 0) {
461             ioc->used = ret - sizeof(cdc_ioctl_t);
462         }
463     }
464     // terence 20130805: send hang event to wpa_supplicant
465     if (ret == -EIO) {
466         error_cnt++;
467         if (error_cnt > 0x2) {
468             ret = -ETIMEDOUT;
469         }
470     } else {
471         error_cnt = 0;
472     }
473 
474     /* Too many programs assume ioctl() returns 0 on success */
475     if (ret >= 0) {
476         ret = 0;
477     } else {
478         cdc_ioctl_t *msg = &prot->msg;
479         ioc->needed = ltoh32(
480             msg->len); /* len == needed when set/query fails from dongle */
481     }
482 
483     /* Intercept the wme_dp ioctl here */
484     if ((!ret) && (ioc->cmd == WLC_SET_VAR) && (!strcmp(buf, "wme_dp"))) {
485         int slen, val = 0;
486 
487         slen = strlen("wme_dp") + 1;
488         if (len >= (int)(slen + sizeof(int))) {
489             bcopy(((char *)buf + slen), &val, sizeof(int));
490         }
491         dhd->wme_dp = (uint8)ltoh32(val);
492     }
493 
494     prot->pending = FALSE;
495 
496 done:
497 
498     return ret;
499 }
500 
dhd_prot_iovar_op(dhd_pub_t * dhdp,const char * name,void * params,int plen,void * arg,int len,bool set)501 int dhd_prot_iovar_op(dhd_pub_t *dhdp, const char *name, void *params, int plen,
502                       void *arg, int len, bool set)
503 {
504     return BCME_UNSUPPORTED;
505 }
506 
dhd_prot_dump(dhd_pub_t * dhdp,struct bcmstrbuf * strbuf)507 void dhd_prot_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
508 {
509     if (!dhdp || !dhdp->prot) {
510         return;
511     }
512 
513     bcm_bprintf(strbuf, "Protocol CDC: reqid %d\n", dhdp->prot->reqid);
514 #ifdef PROP_TXSTATUS
515     dhd_wlfc_dump(dhdp, strbuf);
516 #endif // endif
517 }
518 
519 /*	The FreeBSD PKTPUSH could change the packet buf pinter
520     so we need to make it changable
521 */
522 #define PKTBUF pktbuf
dhd_prot_hdrpush(dhd_pub_t * dhd,int ifidx,void * PKTBUF)523 void dhd_prot_hdrpush(dhd_pub_t *dhd, int ifidx, void *PKTBUF)
524 {
525 #ifdef BDC
526     struct bdc_header *h;
527 #endif /* BDC */
528 
529     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
530 
531 #ifdef BDC
532     /* Push BDC header used to convey priority for buses that don't */
533 
534     PKTPUSH(dhd->osh, PKTBUF, BDC_HEADER_LEN);
535 
536     h = (struct bdc_header *)PKTDATA(dhd->osh, PKTBUF);
537 
538     h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
539     if (PKTSUMNEEDED(PKTBUF)) {
540         h->flags |= BDC_FLAG_SUM_NEEDED;
541     }
542 
543     h->priority = (PKTPRIO(PKTBUF) & BDC_PRIORITY_MASK);
544     h->flags2 = 0;
545     h->dataOffset = 0;
546 #endif /* BDC */
547     BDC_SET_IF_IDX(h, ifidx);
548 }
549 #undef PKTBUF /* Only defined in the above routine */
550 
dhd_prot_hdrlen(dhd_pub_t * dhd,void * PKTBUF)551 uint dhd_prot_hdrlen(dhd_pub_t *dhd, void *PKTBUF)
552 {
553     uint hdrlen = 0;
554 #ifdef BDC
555     /* Length of BDC(+WLFC) headers pushed */
556     hdrlen = BDC_HEADER_LEN + (((struct bdc_header *)PKTBUF)->dataOffset * 0x4);
557 #endif // endif
558     return hdrlen;
559 }
560 
dhd_prot_hdrpull(dhd_pub_t * dhd,int * ifidx,void * pktbuf,uchar * reorder_buf_info,uint * reorder_info_len)561 int dhd_prot_hdrpull(dhd_pub_t *dhd, int *ifidx, void *pktbuf,
562                      uchar *reorder_buf_info, uint *reorder_info_len)
563 {
564 #ifdef BDC
565     struct bdc_header *h;
566 #endif // endif
567     uint8 data_offset = 0;
568 
569     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
570 
571 #ifdef BDC
572     if (reorder_info_len) {
573         *reorder_info_len = 0;
574     }
575     /* Pop BDC header used to convey priority for buses that don't */
576 
577     if (PKTLEN(dhd->osh, pktbuf) < BDC_HEADER_LEN) {
578         DHD_ERROR(("%s: rx data too short (%d < %d)\n", __FUNCTION__,
579                    PKTLEN(dhd->osh, pktbuf), BDC_HEADER_LEN));
580         return BCME_ERROR;
581     }
582 
583     h = (struct bdc_header *)PKTDATA(dhd->osh, pktbuf);
584 
585     if (!ifidx) {
586         /* for tx packet, skip the analysis */
587         data_offset = h->dataOffset;
588         PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
589         goto exit;
590     }
591 
592     *ifidx = BDC_GET_IF_IDX(h);
593 
594     if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
595         BDC_PROTO_VER) {
596         DHD_ERROR(("%s: non-BDC packet received, flags = 0x%x\n",
597                    dhd_ifname(dhd, *ifidx), h->flags));
598         if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) ==
599             BDC_PROTO_VER_1) {
600             h->dataOffset = 0;
601         } else {
602             return BCME_ERROR;
603         }
604     }
605 
606     if (h->flags & BDC_FLAG_SUM_GOOD) {
607         DHD_INFO(("%s: BDC packet received with good rx-csum, flags 0x%x\n",
608                   dhd_ifname(dhd, *ifidx), h->flags));
609         PKTSETSUMGOOD(pktbuf, TRUE);
610     }
611 
612     PKTSETPRIO(pktbuf, (h->priority & BDC_PRIORITY_MASK));
613     data_offset = h->dataOffset;
614     PKTPULL(dhd->osh, pktbuf, BDC_HEADER_LEN);
615 #endif /* BDC */
616 
617 #ifdef PROP_TXSTATUS
618     if (!DHD_PKTTAG_PKTDIR(PKTTAG(pktbuf))) {
619         /*
620         - parse txstatus only for packets that came from the firmware
621         */
622         dhd_wlfc_parse_header_info(dhd, pktbuf, (data_offset << 0x2),
623                                    reorder_buf_info, reorder_info_len);
624 
625 #ifdef BCMDBUS
626 #ifndef DHD_WLFC_THREAD
627         dhd_wlfc_commit_packets(dhd, (f_commitpkt_t)dhd_bus_txdata, dhd->bus,
628                                 NULL, FALSE);
629 #endif /* DHD_WLFC_THREAD */
630 #endif /* BCMDBUS */
631     }
632 #endif /* PROP_TXSTATUS */
633 
634 exit:
635     PKTPULL(dhd->osh, pktbuf, (data_offset << 0x2));
636     return 0;
637 }
638 
dhd_prot_attach(dhd_pub_t * dhd)639 int dhd_prot_attach(dhd_pub_t *dhd)
640 {
641     dhd_prot_t *cdc;
642 
643     if (!(cdc = (dhd_prot_t *)DHD_OS_PREALLOC(dhd, DHD_PREALLOC_PROT,
644                                               sizeof(dhd_prot_t)))) {
645         DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
646         goto fail;
647     }
648     memset(cdc, 0, sizeof(dhd_prot_t));
649 
650     /* ensure that the msg buf directly follows the cdc msg struct */
651     if ((uintptr)(&cdc->msg + 1) != (uintptr)cdc->buf) {
652         DHD_ERROR(("dhd_prot_t is not correctly defined\n"));
653         goto fail;
654     }
655 
656     dhd->prot = cdc;
657 #ifdef BDC
658     dhd->hdrlen += BDC_HEADER_LEN;
659 #endif // endif
660     dhd->maxctl = WLC_IOCTL_MAXLEN + sizeof(cdc_ioctl_t) + ROUND_UP_MARGIN;
661     return 0;
662 
663 fail:
664     if (cdc != NULL) {
665         DHD_OS_PREFREE(dhd, cdc, sizeof(dhd_prot_t));
666     }
667     return BCME_NOMEM;
668 }
669 
670 /* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
dhd_prot_detach(dhd_pub_t * dhd)671 void dhd_prot_detach(dhd_pub_t *dhd)
672 {
673 #ifdef PROP_TXSTATUS
674     dhd_wlfc_deinit(dhd);
675 #endif // endif
676     DHD_OS_PREFREE(dhd, dhd->prot, sizeof(dhd_prot_t));
677     dhd->prot = NULL;
678 }
679 
dhd_prot_dstats(dhd_pub_t * dhd)680 void dhd_prot_dstats(dhd_pub_t *dhd)
681 {
682     /*  copy bus stats */
683 
684     dhd->dstats.tx_packets = dhd->tx_packets;
685     dhd->dstats.tx_errors = dhd->tx_errors;
686     dhd->dstats.rx_packets = dhd->rx_packets;
687     dhd->dstats.rx_errors = dhd->rx_errors;
688     dhd->dstats.rx_dropped = dhd->rx_dropped;
689     dhd->dstats.multicast = dhd->rx_multicast;
690     return;
691 }
692 
dhd_sync_with_dongle(dhd_pub_t * dhd)693 int dhd_sync_with_dongle(dhd_pub_t *dhd)
694 {
695     int ret = 0;
696     wlc_rev_info_t revinfo;
697     DHD_TRACE(("%s: Enter\n", __FUNCTION__));
698 
699 #ifdef DHD_FW_COREDUMP
700     /* Check the memdump capability */
701     dhd_get_memdump_info(dhd);
702 #endif /* DHD_FW_COREDUMP */
703 
704 #ifdef BCMASSERT_LOG
705     dhd_get_assert_info(dhd);
706 #endif /* BCMASSERT_LOG */
707 
708     /* Get the device rev info */
709     memset(&revinfo, 0, sizeof(revinfo));
710     ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_REVINFO, &revinfo, sizeof(revinfo),
711                            FALSE, 0);
712     if (ret < 0) {
713         goto done;
714     }
715 #if defined(BCMDBUS)
716     if (dhd_download_fw_on_driverload) {
717         dhd_conf_reset(dhd);
718         dhd_conf_set_chiprev(dhd, revinfo.chipnum, revinfo.chiprev);
719         dhd_conf_preinit(dhd);
720         dhd_conf_read_config(dhd, dhd->conf_path);
721     }
722 #endif /* BCMDBUS */
723 
724     DHD_SSSR_DUMP_INIT(dhd);
725 
726     dhd_process_cid_mac(dhd, TRUE);
727     ret = dhd_preinit_ioctls(dhd);
728     dhd_process_cid_mac(dhd, FALSE);
729 
730     /* Always assumes wl for now */
731     dhd->iswl = TRUE;
732 
733 done:
734     return ret;
735 }
736 
dhd_prot_init(dhd_pub_t * dhd)737 int dhd_prot_init(dhd_pub_t *dhd)
738 {
739     return BCME_OK;
740 }
741 
dhd_prot_stop(dhd_pub_t * dhd)742 void dhd_prot_stop(dhd_pub_t *dhd)
743 {
744     /* Nothing to do for CDC */
745 }
746 
dhd_get_hostreorder_pkts(void * osh,struct reorder_info * ptr,void ** pkt,uint32 * pkt_count,void ** pplast,uint8 start,uint8 end)747 static void dhd_get_hostreorder_pkts(void *osh, struct reorder_info *ptr,
748                                      void **pkt, uint32 *pkt_count,
749                                      void **pplast, uint8 start, uint8 end)
750 {
751     void *plast = NULL, *p;
752     uint32 pkt_cnt = 0;
753 
754     if (ptr->pend_pkts == 0) {
755         DHD_REORDER(("%s: no packets in reorder queue \n", __FUNCTION__));
756         *pplast = NULL;
757         *pkt_count = 0;
758         *pkt = NULL;
759         return;
760     }
761     do {
762         p = (void *)(ptr->p[start]);
763         ptr->p[start] = NULL;
764 
765         if (p != NULL) {
766             if (plast == NULL) {
767                 *pkt = p;
768             } else {
769                 PKTSETNEXT(osh, plast, p);
770             }
771 
772             plast = p;
773             pkt_cnt++;
774         }
775         start++;
776         if (start > ptr->max_idx) {
777             start = 0;
778         }
779     } while (start != end);
780     *pplast = plast;
781     *pkt_count = pkt_cnt;
782     ptr->pend_pkts -= (uint8)pkt_cnt;
783 }
784 
dhd_process_pkt_reorder_info(dhd_pub_t * dhd,uchar * reorder_info_buf,uint reorder_info_len,void ** pkt,uint32 * pkt_count)785 int dhd_process_pkt_reorder_info(dhd_pub_t *dhd, uchar *reorder_info_buf,
786                                  uint reorder_info_len, void **pkt,
787                                  uint32 *pkt_count)
788 {
789     uint8 flow_id, max_idx, cur_idx, exp_idx;
790     struct reorder_info *ptr;
791     uint8 flags;
792     void *cur_pkt, *plast = NULL;
793     uint32 cnt = 0;
794 
795     if (pkt == NULL) {
796         if (pkt_count != NULL) {
797             *pkt_count = 0;
798         }
799         return 0;
800     }
801 
802     flow_id = reorder_info_buf[WLHOST_REORDERDATA_FLOWID_OFFSET];
803     flags = reorder_info_buf[WLHOST_REORDERDATA_FLAGS_OFFSET];
804 
805     DHD_REORDER(("flow_id %d, flags 0x%02x, idx(%d, %d, %d)\n", flow_id, flags,
806                  reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET],
807                  reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET],
808                  reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET]));
809 
810     /* validate flags and flow id */
811     if (flags == 0xFF) {
812         DHD_ERROR(
813             ("%s: invalid flags...so ignore this packet\n", __FUNCTION__));
814         *pkt_count = 1;
815         return 0;
816     }
817 
818     cur_pkt = *pkt;
819     *pkt = NULL;
820 
821     ptr = dhd->reorder_bufs[flow_id];
822     if (flags & WLHOST_REORDERDATA_DEL_FLOW) {
823         uint32 buf_size = sizeof(struct reorder_info);
824 
825         DHD_REORDER(("%s: Flags indicating to delete a flow id %d\n",
826                      __FUNCTION__, flow_id));
827 
828         if (ptr == NULL) {
829             DHD_REORDER(
830                 ("%s: received flags to cleanup, but no flow (%d) yet\n",
831                  __FUNCTION__, flow_id));
832             *pkt_count = 1;
833             *pkt = cur_pkt;
834             return 0;
835         }
836 
837         dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx,
838                                  ptr->exp_idx);
839         /* set it to the last packet */
840         if (plast) {
841             PKTSETNEXT(dhd->osh, plast, cur_pkt);
842             cnt++;
843         } else {
844             if (cnt != 0) {
845                 DHD_ERROR(
846                     ("%s: del flow: something fishy, pending packets %d\n",
847                      __FUNCTION__, cnt));
848             }
849             *pkt = cur_pkt;
850             cnt = 1;
851         }
852         buf_size += ((ptr->max_idx + 1) * sizeof(void *));
853         MFREE(dhd->osh, ptr, buf_size);
854         dhd->reorder_bufs[flow_id] = NULL;
855         *pkt_count = cnt;
856         return 0;
857     }
858     /* all the other cases depend on the existance of the reorder struct for
859      * that flow id */
860     if (ptr == NULL) {
861         uint32 buf_size_alloc = sizeof(reorder_info_t);
862         max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
863 
864         buf_size_alloc += ((max_idx + 1) * sizeof(void *));
865         /* allocate space to hold the buffers, index etc */
866 
867         DHD_REORDER((
868             "%s: alloc buffer of size %d size, reorder info id %d, maxidx %d\n",
869             __FUNCTION__, buf_size_alloc, flow_id, max_idx));
870         ptr = (struct reorder_info *)MALLOC(dhd->osh, buf_size_alloc);
871         if (ptr == NULL) {
872             DHD_ERROR(("%s: Malloc failed to alloc buffer\n", __FUNCTION__));
873             *pkt_count = 1;
874             return 0;
875         }
876         bzero(ptr, buf_size_alloc);
877         dhd->reorder_bufs[flow_id] = ptr;
878         ptr->p = (void *)(ptr + 1);
879         ptr->max_idx = max_idx;
880     }
881     if (flags & WLHOST_REORDERDATA_NEW_HOLE) {
882         DHD_REORDER(
883             ("%s: new hole, so cleanup pending buffers\n", __FUNCTION__));
884         if (ptr->pend_pkts) {
885             dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
886                                      ptr->exp_idx, ptr->exp_idx);
887             ptr->pend_pkts = 0;
888         }
889         ptr->cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
890         ptr->exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
891         ptr->max_idx = reorder_info_buf[WLHOST_REORDERDATA_MAXIDX_OFFSET];
892         ptr->p[ptr->cur_idx] = cur_pkt;
893         ptr->pend_pkts++;
894         *pkt_count = cnt;
895     } else if (flags & WLHOST_REORDERDATA_CURIDX_VALID) {
896         cur_idx = reorder_info_buf[WLHOST_REORDERDATA_CURIDX_OFFSET];
897         exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
898 
899         if ((exp_idx == ptr->exp_idx) && (cur_idx != ptr->exp_idx)) {
900             /* still in the current hole */
901             /* enqueue the current on the buffer chain */
902             if (ptr->p[cur_idx] != NULL) {
903                 DHD_REORDER(("%s: HOLE: ERROR buffer pending..free it\n",
904                              __FUNCTION__));
905                 PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
906                 ptr->p[cur_idx] = NULL;
907             }
908             ptr->p[cur_idx] = cur_pkt;
909             ptr->pend_pkts++;
910             ptr->cur_idx = cur_idx;
911             DHD_REORDER(("%s: fill up a hole..pending packets is %d\n",
912                          __FUNCTION__, ptr->pend_pkts));
913             *pkt_count = 0;
914             *pkt = NULL;
915         } else if (ptr->exp_idx == cur_idx) {
916             /* got the right one ..flush from cur to exp and update exp */
917             DHD_REORDER(("%s: got the right one now, cur_idx is %d\n",
918                          __FUNCTION__, cur_idx));
919             if (ptr->p[cur_idx] != NULL) {
920                 DHD_REORDER(
921                     ("%s: Error buffer pending..free it\n", __FUNCTION__));
922                 PKTFREE(dhd->osh, ptr->p[cur_idx], TRUE);
923                 ptr->p[cur_idx] = NULL;
924             }
925             ptr->p[cur_idx] = cur_pkt;
926             ptr->pend_pkts++;
927 
928             ptr->cur_idx = cur_idx;
929             ptr->exp_idx = exp_idx;
930 
931             dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, cur_idx,
932                                      exp_idx);
933             *pkt_count = cnt;
934             DHD_REORDER(("%s: freeing up buffers %d, still pending %d\n",
935                          __FUNCTION__, cnt, ptr->pend_pkts));
936         } else {
937             uint8 end_idx;
938             bool flush_current = FALSE;
939             /* both cur and exp are moved now .. */
940             DHD_REORDER(("%s:, flow %d, both moved, cur %d(%d), exp %d(%d)\n",
941                          __FUNCTION__, flow_id, ptr->cur_idx, cur_idx,
942                          ptr->exp_idx, exp_idx));
943             if (flags & WLHOST_REORDERDATA_FLUSH_ALL) {
944                 end_idx = ptr->exp_idx;
945             } else {
946                 end_idx = exp_idx;
947             }
948 
949             /* flush pkts first */
950             dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast,
951                                      ptr->exp_idx, end_idx);
952 
953             if (cur_idx == ptr->max_idx) {
954                 if (exp_idx == 0) {
955                     flush_current = TRUE;
956                 }
957             } else {
958                 if (exp_idx == cur_idx + 1) {
959                     flush_current = TRUE;
960                 }
961             }
962             if (flush_current) {
963                 if (plast) {
964                     PKTSETNEXT(dhd->osh, plast, cur_pkt);
965                 } else {
966                     *pkt = cur_pkt;
967                 }
968                 cnt++;
969             } else {
970                 ptr->p[cur_idx] = cur_pkt;
971                 ptr->pend_pkts++;
972             }
973             ptr->exp_idx = exp_idx;
974             ptr->cur_idx = cur_idx;
975             *pkt_count = cnt;
976         }
977     } else {
978         uint8 end_idx;
979         /* no real packet but update to exp_seq...that means explicit window
980          * move */
981         exp_idx = reorder_info_buf[WLHOST_REORDERDATA_EXPIDX_OFFSET];
982 
983         DHD_REORDER(
984             ("%s: move the window, cur_idx is %d, exp is %d, new exp is %d\n",
985              __FUNCTION__, ptr->cur_idx, ptr->exp_idx, exp_idx));
986         if (flags & WLHOST_REORDERDATA_FLUSH_ALL) {
987             end_idx = ptr->exp_idx;
988         } else {
989             end_idx = exp_idx;
990         }
991 
992         dhd_get_hostreorder_pkts(dhd->osh, ptr, pkt, &cnt, &plast, ptr->exp_idx,
993                                  end_idx);
994         if (plast) {
995             PKTSETNEXT(dhd->osh, plast, cur_pkt);
996         } else {
997             *pkt = cur_pkt;
998         }
999         cnt++;
1000         *pkt_count = cnt;
1001         /* set the new expected idx */
1002         ptr->exp_idx = exp_idx;
1003     }
1004     return 0;
1005 }
1006