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