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