1 /*
2 * Broadcom Dongle Host Driver (DHD), common DHD core.
3 *
4 * Copyright (C) 1999-2013, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhd_common.c 419132 2013-08-19 21:33:05Z $
25 */
26 #include <typedefs.h>
27 #include <osl.h>
28
29 #include <epivers.h>
30 #include <bcmutils.h>
31
32 #include <bcmendian.h>
33 #include <dngl_stats.h>
34 #include <wlioctl.h>
35 #include <dhd.h>
36 #include <dhd_ip.h>
37
38 #include <proto/bcmevent.h>
39 #include <proto/bcmip.h>
40
41 #include <dhd_bus.h>
42 #include <dhd_proto.h>
43 #include <dhd_dbg.h>
44 #include <msgtrace.h>
45
46 #ifdef WL_CFG80211
47 #include <wl_cfg80211.h>
48 #endif
49 #ifdef PNO_SUPPORT
50 #include <dhd_pno.h>
51 #endif
52 #ifdef SET_RANDOM_MAC_SOFTAP
53 #include <linux/random.h>
54 #include <linux/jiffies.h>
55 #endif
56
57 #define htod32(i) (i)
58 #define htod16(i) (i)
59 #define dtoh32(i) (i)
60 #define dtoh16(i) (i)
61 #define htodchanspec(i) (i)
62 #define dtohchanspec(i) (i)
63
64 #ifdef PROP_TXSTATUS
65 #include <wlfc_proto.h>
66 #include <dhd_wlfc.h>
67 #endif
68
69 #ifdef WLMEDIA_HTSF
70 extern void htsf_update(struct dhd_info *dhd, void *data);
71 #endif
72 int dhd_msg_level = DHD_ERROR_VAL;
73
74
75 #include <wl_iw.h>
76
77 char fw_path[MOD_PARAM_PATHLEN];
78 char nv_path[MOD_PARAM_PATHLEN];
79
80 #ifdef SOFTAP
81 char fw_path2[MOD_PARAM_PATHLEN];
82 extern bool softap_enabled;
83 #endif
84
85 /* Last connection success/failure status */
86 uint32 dhd_conn_event;
87 uint32 dhd_conn_status;
88 uint32 dhd_conn_reason;
89
90 extern int dhd_iscan_request(void * dhdp, uint16 action);
91 extern void dhd_ind_scan_confirm(void *h, bool status);
92 extern int dhd_iscan_in_progress(void *h);
93 void dhd_iscan_lock(void);
94 void dhd_iscan_unlock(void);
95 extern int dhd_change_mtu(dhd_pub_t *dhd, int new_mtu, int ifidx);
96 #if !defined(AP) && defined(WLP2P)
97 extern int dhd_get_concurrent_capabilites(dhd_pub_t *dhd);
98 #endif
99 bool ap_cfg_running = FALSE;
100 bool ap_fw_loaded = FALSE;
101
102
103 #ifdef DHD_DEBUG
104 const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR "\nCompiled on "
105 __DATE__ " at " __TIME__;
106 #else
107 const char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR;
108 #endif
109
110 void dhd_set_timer(void *bus, uint wdtick);
111
112 /* IOVar table */
113 enum {
114 IOV_VERSION = 1,
115 IOV_MSGLEVEL,
116 IOV_BCMERRORSTR,
117 IOV_BCMERROR,
118 IOV_WDTICK,
119 IOV_DUMP,
120 IOV_CLEARCOUNTS,
121 IOV_LOGDUMP,
122 IOV_LOGCAL,
123 IOV_LOGSTAMP,
124 IOV_GPIOOB,
125 IOV_IOCTLTIMEOUT,
126 #if defined(DHD_DEBUG)
127 IOV_CONS,
128 IOV_DCONSOLE_POLL,
129 #endif /* defined(DHD_DEBUG) */
130 #ifdef PROP_TXSTATUS
131 IOV_PROPTXSTATUS_ENABLE,
132 IOV_PROPTXSTATUS_MODE,
133 IOV_PROPTXSTATUS_OPT,
134 #endif /* PROP_TXSTATUS */
135 IOV_BUS_TYPE,
136 #ifdef WLMEDIA_HTSF
137 IOV_WLPKTDLYSTAT_SZ,
138 #endif
139 IOV_CHANGEMTU,
140 IOV_HOSTREORDER_FLOWS,
141 IOV_LAST
142 };
143
144 const bcm_iovar_t dhd_iovars[] = {
145 {"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(dhd_version) },
146 #ifdef DHD_DEBUG
147 {"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 },
148 #endif /* DHD_DEBUG */
149 {"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN },
150 {"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0 },
151 {"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0 },
152 {"dump", IOV_DUMP, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN },
153 #ifdef DHD_DEBUG
154 {"cons", IOV_CONS, 0, IOVT_BUFFER, 0 },
155 {"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0 },
156 #endif
157 {"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0 },
158 {"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0 },
159 {"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0 },
160 #ifdef PROP_TXSTATUS
161 {"proptx", IOV_PROPTXSTATUS_ENABLE, 0, IOVT_UINT32, 0 },
162 /*
163 set the proptxtstatus operation mode:
164 0 - Do not do any proptxtstatus flow control
165 1 - Use implied credit from a packet status
166 2 - Use explicit credit
167 */
168 {"ptxmode", IOV_PROPTXSTATUS_MODE, 0, IOVT_UINT32, 0 },
169 {"proptx_opt", IOV_PROPTXSTATUS_OPT, 0, IOVT_UINT32, 0 },
170 #endif /* PROP_TXSTATUS */
171 {"bustype", IOV_BUS_TYPE, 0, IOVT_UINT32, 0},
172 #ifdef WLMEDIA_HTSF
173 {"pktdlystatsz", IOV_WLPKTDLYSTAT_SZ, 0, IOVT_UINT8, 0 },
174 #endif
175 {"changemtu", IOV_CHANGEMTU, 0, IOVT_UINT32, 0 },
176 {"host_reorder_flows", IOV_HOSTREORDER_FLOWS, 0, IOVT_BUFFER,
177 (WLHOST_REORDERDATA_MAXFLOWS + 1) },
178 {NULL, 0, 0, 0, 0 }
179 };
180 #define DHD_IOVAR_BUF_SIZE 128
181
182 void
dhd_common_init(osl_t * osh)183 dhd_common_init(osl_t *osh)
184 {
185 #ifdef CONFIG_BCMDHD_FW_PATH
186 bcm_strncpy_s(fw_path, sizeof(fw_path), CONFIG_BCMDHD_FW_PATH, MOD_PARAM_PATHLEN-1);
187 #else /* CONFIG_BCMDHD_FW_PATH */
188 fw_path[0] = '\0';
189 #endif /* CONFIG_BCMDHD_FW_PATH */
190 #ifdef CONFIG_BCMDHD_NVRAM_PATH
191 bcm_strncpy_s(nv_path, sizeof(nv_path), CONFIG_BCMDHD_NVRAM_PATH, MOD_PARAM_PATHLEN-1);
192 #else /* CONFIG_BCMDHD_NVRAM_PATH */
193 nv_path[0] = '\0';
194 #endif /* CONFIG_BCMDHD_NVRAM_PATH */
195 #ifdef SOFTAP
196 fw_path2[0] = '\0';
197 #endif
198 }
199
200 void
dhd_common_deinit(dhd_pub_t * dhd_pub,dhd_cmn_t * sa_cmn)201 dhd_common_deinit(dhd_pub_t *dhd_pub, dhd_cmn_t *sa_cmn)
202 {
203 osl_t *osh;
204 dhd_cmn_t *cmn;
205
206 if (dhd_pub != NULL)
207 cmn = dhd_pub->cmn;
208 else
209 cmn = sa_cmn;
210
211 if (!cmn)
212 return;
213
214 osh = cmn->osh;
215
216 if (dhd_pub != NULL)
217 dhd_pub->cmn = NULL;
218
219 MFREE(osh, cmn, sizeof(dhd_cmn_t));
220 }
221
222 static int
dhd_dump(dhd_pub_t * dhdp,char * buf,int buflen)223 dhd_dump(dhd_pub_t *dhdp, char *buf, int buflen)
224 {
225 char eabuf[ETHER_ADDR_STR_LEN];
226
227 struct bcmstrbuf b;
228 struct bcmstrbuf *strbuf = &b;
229
230 bcm_binit(strbuf, buf, buflen);
231
232 /* Base DHD info */
233 bcm_bprintf(strbuf, "%s\n", dhd_version);
234 bcm_bprintf(strbuf, "\n");
235 bcm_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
236 dhdp->up, dhdp->txoff, dhdp->busstate);
237 bcm_bprintf(strbuf, "pub.hdrlen %u pub.maxctl %u pub.rxsz %u\n",
238 dhdp->hdrlen, dhdp->maxctl, dhdp->rxsz);
239 bcm_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %s\n",
240 dhdp->iswl, dhdp->drv_version, bcm_ether_ntoa(&dhdp->mac, eabuf));
241 bcm_bprintf(strbuf, "pub.bcmerror %d tickcnt %u\n", dhdp->bcmerror, dhdp->tickcnt);
242
243 bcm_bprintf(strbuf, "dongle stats:\n");
244 bcm_bprintf(strbuf, "tx_packets %lu tx_bytes %lu tx_errors %lu tx_dropped %lu\n",
245 dhdp->dstats.tx_packets, dhdp->dstats.tx_bytes,
246 dhdp->dstats.tx_errors, dhdp->dstats.tx_dropped);
247 bcm_bprintf(strbuf, "rx_packets %lu rx_bytes %lu rx_errors %lu rx_dropped %lu\n",
248 dhdp->dstats.rx_packets, dhdp->dstats.rx_bytes,
249 dhdp->dstats.rx_errors, dhdp->dstats.rx_dropped);
250 bcm_bprintf(strbuf, "multicast %lu\n", dhdp->dstats.multicast);
251
252 bcm_bprintf(strbuf, "bus stats:\n");
253 bcm_bprintf(strbuf, "tx_packets %lu tx_multicast %lu tx_errors %lu\n",
254 dhdp->tx_packets, dhdp->tx_multicast, dhdp->tx_errors);
255 bcm_bprintf(strbuf, "tx_ctlpkts %lu tx_ctlerrs %lu\n",
256 dhdp->tx_ctlpkts, dhdp->tx_ctlerrs);
257 bcm_bprintf(strbuf, "rx_packets %lu rx_multicast %lu rx_errors %lu \n",
258 dhdp->rx_packets, dhdp->rx_multicast, dhdp->rx_errors);
259 bcm_bprintf(strbuf, "rx_ctlpkts %lu rx_ctlerrs %lu rx_dropped %lu\n",
260 dhdp->rx_ctlpkts, dhdp->rx_ctlerrs, dhdp->rx_dropped);
261 bcm_bprintf(strbuf, "rx_readahead_cnt %lu tx_realloc %lu\n",
262 dhdp->rx_readahead_cnt, dhdp->tx_realloc);
263 bcm_bprintf(strbuf, "\n");
264
265 /* Add any prot info */
266 dhd_prot_dump(dhdp, strbuf);
267 bcm_bprintf(strbuf, "\n");
268
269 /* Add any bus info */
270 dhd_bus_dump(dhdp, strbuf);
271
272 return (!strbuf->size ? BCME_BUFTOOSHORT : 0);
273 }
274
275 int
dhd_wl_ioctl_cmd(dhd_pub_t * dhd_pub,int cmd,void * arg,int len,uint8 set,int ifindex)276 dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, int ifindex)
277 {
278 wl_ioctl_t ioc;
279
280 ioc.cmd = cmd;
281 ioc.buf = arg;
282 ioc.len = len;
283 ioc.set = set;
284
285 return dhd_wl_ioctl(dhd_pub, ifindex, &ioc, arg, len);
286 }
287
288
289 int
dhd_wl_ioctl(dhd_pub_t * dhd_pub,int ifindex,wl_ioctl_t * ioc,void * buf,int len)290 dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len)
291 {
292 int ret = 0;
293
294 if (dhd_os_proto_block(dhd_pub))
295 {
296
297 ret = dhd_prot_ioctl(dhd_pub, ifindex, ioc, buf, len);
298 if ((ret) && (dhd_pub->up))
299 /* Send hang event only if dhd_open() was success */
300 dhd_os_check_hang(dhd_pub, ifindex, ret);
301
302 if (ret == -ETIMEDOUT && !dhd_pub->up) {
303 DHD_ERROR(("%s: 'resumed on timeout' error is "
304 "occurred before the interface does not"
305 " bring up\n", __FUNCTION__));
306 dhd_pub->busstate = DHD_BUS_DOWN;
307 }
308
309 dhd_os_proto_unblock(dhd_pub);
310
311
312 }
313 return ret;
314 }
315
316 static int
dhd_doiovar(dhd_pub_t * dhd_pub,const bcm_iovar_t * vi,uint32 actionid,const char * name,void * params,int plen,void * arg,int len,int val_size)317 dhd_doiovar(dhd_pub_t *dhd_pub, const bcm_iovar_t *vi, uint32 actionid, const char *name,
318 void *params, int plen, void *arg, int len, int val_size)
319 {
320 int bcmerror = 0;
321 int32 int_val = 0;
322
323 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
324 DHD_TRACE(("%s: actionid = %d; name %s\n", __FUNCTION__, actionid, name));
325
326 if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid))) != 0)
327 goto exit;
328
329 if (plen >= (int)sizeof(int_val))
330 bcopy(params, &int_val, sizeof(int_val));
331
332 switch (actionid) {
333 case IOV_GVAL(IOV_VERSION):
334 /* Need to have checked buffer length */
335 bcm_strncpy_s((char*)arg, len, dhd_version, len);
336 break;
337
338 case IOV_GVAL(IOV_MSGLEVEL):
339 int_val = (int32)dhd_msg_level;
340 bcopy(&int_val, arg, val_size);
341 break;
342
343 case IOV_SVAL(IOV_MSGLEVEL):
344 #ifdef WL_CFG80211
345 /* Enable DHD and WL logs in oneshot */
346 if (int_val & DHD_WL_VAL2)
347 wl_cfg80211_enable_trace(TRUE, int_val & (~DHD_WL_VAL2));
348 else if (int_val & DHD_WL_VAL)
349 wl_cfg80211_enable_trace(FALSE, WL_DBG_DBG);
350 if (!(int_val & DHD_WL_VAL2))
351 #endif /* WL_CFG80211 */
352 dhd_msg_level = int_val;
353 break;
354 case IOV_GVAL(IOV_BCMERRORSTR):
355 bcm_strncpy_s((char *)arg, len, bcmerrorstr(dhd_pub->bcmerror), BCME_STRLEN);
356 ((char *)arg)[BCME_STRLEN - 1] = 0x00;
357 break;
358
359 case IOV_GVAL(IOV_BCMERROR):
360 int_val = (int32)dhd_pub->bcmerror;
361 bcopy(&int_val, arg, val_size);
362 break;
363
364 case IOV_GVAL(IOV_WDTICK):
365 int_val = (int32)dhd_watchdog_ms;
366 bcopy(&int_val, arg, val_size);
367 break;
368
369 case IOV_SVAL(IOV_WDTICK):
370 if (!dhd_pub->up) {
371 bcmerror = BCME_NOTUP;
372 break;
373 }
374 dhd_os_wd_timer(dhd_pub, (uint)int_val);
375 break;
376
377 case IOV_GVAL(IOV_DUMP):
378 bcmerror = dhd_dump(dhd_pub, arg, len);
379 break;
380
381 #ifdef DHD_DEBUG
382 case IOV_GVAL(IOV_DCONSOLE_POLL):
383 int_val = (int32)dhd_console_ms;
384 bcopy(&int_val, arg, val_size);
385 break;
386
387 case IOV_SVAL(IOV_DCONSOLE_POLL):
388 dhd_console_ms = (uint)int_val;
389 break;
390
391 case IOV_SVAL(IOV_CONS):
392 if (len > 0)
393 bcmerror = dhd_bus_console_in(dhd_pub, arg, len - 1);
394 break;
395 #endif /* DHD_DEBUG */
396
397 case IOV_SVAL(IOV_CLEARCOUNTS):
398 dhd_pub->tx_packets = dhd_pub->rx_packets = 0;
399 dhd_pub->tx_errors = dhd_pub->rx_errors = 0;
400 dhd_pub->tx_ctlpkts = dhd_pub->rx_ctlpkts = 0;
401 dhd_pub->tx_ctlerrs = dhd_pub->rx_ctlerrs = 0;
402 dhd_pub->rx_dropped = 0;
403 dhd_pub->rx_readahead_cnt = 0;
404 dhd_pub->tx_realloc = 0;
405 dhd_pub->wd_dpc_sched = 0;
406 memset(&dhd_pub->dstats, 0, sizeof(dhd_pub->dstats));
407 dhd_bus_clearcounts(dhd_pub);
408 #ifdef PROP_TXSTATUS
409 /* clear proptxstatus related counters */
410 if (dhd_pub->wlfc_state) {
411 athost_wl_status_info_t *wlfc =
412 (athost_wl_status_info_t*)dhd_pub->wlfc_state;
413 wlfc_hanger_t* hanger;
414
415 memset(&wlfc->stats, 0, sizeof(athost_wl_stat_counters_t));
416
417 hanger = (wlfc_hanger_t*)wlfc->hanger;
418 hanger->pushed = 0;
419 hanger->popped = 0;
420 hanger->failed_slotfind = 0;
421 hanger->failed_to_pop = 0;
422 hanger->failed_to_push = 0;
423 }
424 #endif /* PROP_TXSTATUS */
425 break;
426
427
428 case IOV_GVAL(IOV_IOCTLTIMEOUT): {
429 int_val = (int32)dhd_os_get_ioctl_resp_timeout();
430 bcopy(&int_val, arg, sizeof(int_val));
431 break;
432 }
433
434 case IOV_SVAL(IOV_IOCTLTIMEOUT): {
435 if (int_val <= 0)
436 bcmerror = BCME_BADARG;
437 else
438 dhd_os_set_ioctl_resp_timeout((unsigned int)int_val);
439 break;
440 }
441
442
443 #ifdef PROP_TXSTATUS
444 case IOV_GVAL(IOV_PROPTXSTATUS_ENABLE):
445 int_val = dhd_pub->wlfc_enabled? 1 : 0;
446 bcopy(&int_val, arg, val_size);
447 break;
448
449 case IOV_SVAL(IOV_PROPTXSTATUS_ENABLE):
450 dhd_pub->wlfc_enabled = int_val? 1 : 0;
451 break;
452
453 case IOV_GVAL(IOV_PROPTXSTATUS_MODE): {
454 athost_wl_status_info_t *wlfc =
455 (athost_wl_status_info_t*)dhd_pub->wlfc_state;
456 int_val = dhd_pub->wlfc_state ? (int32)wlfc->proptxstatus_mode : 0;
457 bcopy(&int_val, arg, val_size);
458 break;
459 }
460
461 case IOV_SVAL(IOV_PROPTXSTATUS_MODE):
462 if (dhd_pub->wlfc_state) {
463 athost_wl_status_info_t *wlfc =
464 (athost_wl_status_info_t*)dhd_pub->wlfc_state;
465 wlfc->proptxstatus_mode = int_val & 0xff;
466 }
467 break;
468 #endif /* PROP_TXSTATUS */
469
470 case IOV_GVAL(IOV_BUS_TYPE):
471 /* The dhd application queries the driver to check if its usb or sdio. */
472 #ifdef BCMDHDUSB
473 int_val = BUS_TYPE_USB;
474 #endif
475 int_val = BUS_TYPE_SDIO;
476 bcopy(&int_val, arg, val_size);
477 break;
478
479
480 #ifdef WLMEDIA_HTSF
481 case IOV_GVAL(IOV_WLPKTDLYSTAT_SZ):
482 int_val = dhd_pub->htsfdlystat_sz;
483 bcopy(&int_val, arg, val_size);
484 break;
485
486 case IOV_SVAL(IOV_WLPKTDLYSTAT_SZ):
487 dhd_pub->htsfdlystat_sz = int_val & 0xff;
488 printf("Setting tsfdlystat_sz:%d\n", dhd_pub->htsfdlystat_sz);
489 break;
490 #endif
491 case IOV_SVAL(IOV_CHANGEMTU):
492 int_val &= 0xffff;
493 bcmerror = dhd_change_mtu(dhd_pub, int_val, 0);
494 break;
495
496 case IOV_GVAL(IOV_HOSTREORDER_FLOWS):
497 {
498 uint i = 0;
499 uint8 *ptr = (uint8 *)arg;
500 uint8 count = 0;
501
502 ptr++;
503 for (i = 0; i < WLHOST_REORDERDATA_MAXFLOWS; i++) {
504 if (dhd_pub->reorder_bufs[i] != NULL) {
505 *ptr = dhd_pub->reorder_bufs[i]->flow_id;
506 ptr++;
507 count++;
508 }
509 }
510 ptr = (uint8 *)arg;
511 *ptr = count;
512 break;
513 }
514
515 default:
516 bcmerror = BCME_UNSUPPORTED;
517 break;
518 }
519
520 exit:
521 DHD_TRACE(("%s: actionid %d, bcmerror %d\n", __FUNCTION__, actionid, bcmerror));
522 return bcmerror;
523 }
524
525 /* Store the status of a connection attempt for later retrieval by an iovar */
526 void
dhd_store_conn_status(uint32 event,uint32 status,uint32 reason)527 dhd_store_conn_status(uint32 event, uint32 status, uint32 reason)
528 {
529 /* Do not overwrite a WLC_E_PRUNE with a WLC_E_SET_SSID
530 * because an encryption/rsn mismatch results in both events, and
531 * the important information is in the WLC_E_PRUNE.
532 */
533 if (!(event == WLC_E_SET_SSID && status == WLC_E_STATUS_FAIL &&
534 dhd_conn_event == WLC_E_PRUNE)) {
535 dhd_conn_event = event;
536 dhd_conn_status = status;
537 dhd_conn_reason = reason;
538 }
539 }
540
541 bool
dhd_prec_enq(dhd_pub_t * dhdp,struct pktq * q,void * pkt,int prec)542 dhd_prec_enq(dhd_pub_t *dhdp, struct pktq *q, void *pkt, int prec)
543 {
544 void *p;
545 int eprec = -1; /* precedence to evict from */
546 bool discard_oldest;
547
548 /* Fast case, precedence queue is not full and we are also not
549 * exceeding total queue length
550 */
551 if (!pktq_pfull(q, prec) && !pktq_full(q)) {
552 pktq_penq(q, prec, pkt);
553 return TRUE;
554 }
555
556 /* Determine precedence from which to evict packet, if any */
557 if (pktq_pfull(q, prec))
558 eprec = prec;
559 else if (pktq_full(q)) {
560 p = pktq_peek_tail(q, &eprec);
561 ASSERT(p);
562 if (eprec > prec || eprec < 0)
563 return FALSE;
564 }
565
566 /* Evict if needed */
567 if (eprec >= 0) {
568 /* Detect queueing to unconfigured precedence */
569 ASSERT(!pktq_pempty(q, eprec));
570 discard_oldest = AC_BITMAP_TST(dhdp->wme_dp, eprec);
571 if (eprec == prec && !discard_oldest)
572 return FALSE; /* refuse newer (incoming) packet */
573 /* Evict packet according to discard policy */
574 p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q, eprec);
575 ASSERT(p);
576
577 PKTFREE(dhdp->osh, p, TRUE);
578 }
579
580 /* Enqueue */
581 p = pktq_penq(q, prec, pkt);
582 ASSERT(p);
583
584 return TRUE;
585 }
586
587 /*
588 * Functions to drop proper pkts from queue:
589 * If one pkt in queue is non-fragmented, drop first non-fragmented pkt only
590 * If all pkts in queue are all fragmented, find and drop one whole set fragmented pkts
591 * If can't find pkts matching upper 2 cases, drop first pkt anyway
592 */
593 bool
dhd_prec_drop_pkts(osl_t * osh,struct pktq * pq,int prec)594 dhd_prec_drop_pkts(osl_t *osh, struct pktq *pq, int prec)
595 {
596 struct pktq_prec *q = NULL;
597 void *p, *prev = NULL, *next = NULL, *first = NULL, *last = NULL, *prev_first = NULL;
598 pkt_frag_t frag_info;
599
600 ASSERT(osh && pq);
601 ASSERT(prec >= 0 && prec < pq->num_prec);
602
603 q = &pq->q[prec];
604 p = q->head;
605
606 if (p == NULL)
607 return FALSE;
608
609 while (p) {
610 frag_info = pkt_frag_info(osh, p);
611 if (frag_info == DHD_PKT_FRAG_NONE) {
612 break;
613 } else if (frag_info == DHD_PKT_FRAG_FIRST) {
614 if (first) {
615 /* No last frag pkt, use prev as last */
616 last = prev;
617 } else {
618 first = p;
619 prev_first = prev;
620 }
621 } else if (frag_info == DHD_PKT_FRAG_LAST) {
622 if (first) {
623 last = p;
624 break;
625 }
626 }
627
628 prev = p;
629 p = PKTLINK(p);
630 }
631
632 if ((p == NULL) || ((frag_info != DHD_PKT_FRAG_NONE) && !(first && last))) {
633 /* Not found matching pkts, use oldest */
634 prev = NULL;
635 p = q->head;
636 frag_info = 0;
637 }
638
639 if (frag_info == DHD_PKT_FRAG_NONE) {
640 first = last = p;
641 prev_first = prev;
642 }
643
644 p = first;
645 while (p) {
646 next = PKTLINK(p);
647 q->len--;
648 pq->len--;
649
650 PKTSETLINK(p, NULL);
651
652 PKTFREE(osh, p, TRUE);
653
654 if (p == last)
655 break;
656
657 p = next;
658 }
659
660 if (prev_first == NULL) {
661 if ((q->head = next) == NULL)
662 q->tail = NULL;
663 } else {
664 PKTSETLINK(prev_first, next);
665 }
666
667 return TRUE;
668 }
669
670 static int
dhd_iovar_op(dhd_pub_t * dhd_pub,const char * name,void * params,int plen,void * arg,int len,bool set)671 dhd_iovar_op(dhd_pub_t *dhd_pub, const char *name,
672 void *params, int plen, void *arg, int len, bool set)
673 {
674 int bcmerror = 0;
675 int val_size;
676 const bcm_iovar_t *vi = NULL;
677 uint32 actionid;
678
679 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
680
681 ASSERT(name);
682 ASSERT(len >= 0);
683
684 /* Get MUST have return space */
685 ASSERT(set || (arg && len));
686
687 /* Set does NOT take qualifiers */
688 ASSERT(!set || (!params && !plen));
689
690 if ((vi = bcm_iovar_lookup(dhd_iovars, name)) == NULL) {
691 bcmerror = BCME_UNSUPPORTED;
692 goto exit;
693 }
694
695 DHD_CTL(("%s: %s %s, len %d plen %d\n", __FUNCTION__,
696 name, (set ? "set" : "get"), len, plen));
697
698 /* set up 'params' pointer in case this is a set command so that
699 * the convenience int and bool code can be common to set and get
700 */
701 if (params == NULL) {
702 params = arg;
703 plen = len;
704 }
705
706 if (vi->type == IOVT_VOID)
707 val_size = 0;
708 else if (vi->type == IOVT_BUFFER)
709 val_size = len;
710 else
711 /* all other types are integer sized */
712 val_size = sizeof(int);
713
714 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
715
716 bcmerror = dhd_doiovar(dhd_pub, vi, actionid, name, params, plen, arg, len, val_size);
717
718 exit:
719 return bcmerror;
720 }
721
722 int
dhd_ioctl(dhd_pub_t * dhd_pub,dhd_ioctl_t * ioc,void * buf,uint buflen)723 dhd_ioctl(dhd_pub_t * dhd_pub, dhd_ioctl_t *ioc, void * buf, uint buflen)
724 {
725 int bcmerror = 0;
726
727 DHD_TRACE(("%s: Enter\n", __FUNCTION__));
728
729 if (!buf) {
730 return BCME_BADARG;
731 }
732
733 switch (ioc->cmd) {
734 case DHD_GET_MAGIC:
735 if (buflen < sizeof(int))
736 bcmerror = BCME_BUFTOOSHORT;
737 else
738 *(int*)buf = DHD_IOCTL_MAGIC;
739 break;
740
741 case DHD_GET_VERSION:
742 if (buflen < sizeof(int))
743 bcmerror = BCME_BUFTOOSHORT;
744 else
745 *(int*)buf = DHD_IOCTL_VERSION;
746 break;
747
748 case DHD_GET_VAR:
749 case DHD_SET_VAR: {
750 char *arg;
751 uint arglen;
752
753 /* scan past the name to any arguments */
754 for (arg = buf, arglen = buflen; *arg && arglen; arg++, arglen--)
755 ;
756
757 if (*arg) {
758 bcmerror = BCME_BUFTOOSHORT;
759 break;
760 }
761
762 /* account for the NUL terminator */
763 arg++, arglen--;
764
765 /* call with the appropriate arguments */
766 if (ioc->cmd == DHD_GET_VAR)
767 bcmerror = dhd_iovar_op(dhd_pub, buf, arg, arglen,
768 buf, buflen, IOV_GET);
769 else
770 bcmerror = dhd_iovar_op(dhd_pub, buf, NULL, 0, arg, arglen, IOV_SET);
771 if (bcmerror != BCME_UNSUPPORTED)
772 break;
773
774 /* not in generic table, try protocol module */
775 if (ioc->cmd == DHD_GET_VAR)
776 bcmerror = dhd_prot_iovar_op(dhd_pub, buf, arg,
777 arglen, buf, buflen, IOV_GET);
778 else
779 bcmerror = dhd_prot_iovar_op(dhd_pub, buf,
780 NULL, 0, arg, arglen, IOV_SET);
781 if (bcmerror != BCME_UNSUPPORTED)
782 break;
783
784 /* if still not found, try bus module */
785 if (ioc->cmd == DHD_GET_VAR) {
786 bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
787 arg, arglen, buf, buflen, IOV_GET);
788 } else {
789 bcmerror = dhd_bus_iovar_op(dhd_pub, buf,
790 NULL, 0, arg, arglen, IOV_SET);
791 }
792
793 break;
794 }
795
796 default:
797 bcmerror = BCME_UNSUPPORTED;
798 }
799
800 return bcmerror;
801 }
802
803 #ifdef SHOW_EVENTS
804 static void
wl_show_host_event(wl_event_msg_t * event,void * event_data)805 wl_show_host_event(wl_event_msg_t *event, void *event_data)
806 {
807 uint i, status, reason;
808 bool group = FALSE, flush_txq = FALSE, link = FALSE;
809 const char *auth_str;
810 const char *event_name;
811 uchar *buf;
812 char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
813 uint event_type, flags, auth_type, datalen;
814
815 event_type = ntoh32(event->event_type);
816 flags = ntoh16(event->flags);
817 status = ntoh32(event->status);
818 reason = ntoh32(event->reason);
819 BCM_REFERENCE(reason);
820 auth_type = ntoh32(event->auth_type);
821 datalen = ntoh32(event->datalen);
822
823 /* debug dump of event messages */
824 snprintf(eabuf, sizeof(eabuf), "%02x:%02x:%02x:%02x:%02x:%02x",
825 (uchar)event->addr.octet[0]&0xff,
826 (uchar)event->addr.octet[1]&0xff,
827 (uchar)event->addr.octet[2]&0xff,
828 (uchar)event->addr.octet[3]&0xff,
829 (uchar)event->addr.octet[4]&0xff,
830 (uchar)event->addr.octet[5]&0xff);
831
832 event_name = "UNKNOWN";
833 for (i = 0; i < (uint)bcmevent_names_size; i++)
834 if (bcmevent_names[i].event == event_type)
835 event_name = bcmevent_names[i].name;
836
837 if (flags & WLC_EVENT_MSG_LINK)
838 link = TRUE;
839 if (flags & WLC_EVENT_MSG_GROUP)
840 group = TRUE;
841 if (flags & WLC_EVENT_MSG_FLUSHTXQ)
842 flush_txq = TRUE;
843
844 switch (event_type) {
845 case WLC_E_START:
846 case WLC_E_DEAUTH:
847 case WLC_E_DISASSOC:
848 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
849 break;
850
851 case WLC_E_ASSOC_IND:
852 case WLC_E_REASSOC_IND:
853
854 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
855 break;
856
857 case WLC_E_ASSOC:
858 case WLC_E_REASSOC:
859 if (status == WLC_E_STATUS_SUCCESS) {
860 DHD_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n", event_name, eabuf));
861 } else if (status == WLC_E_STATUS_TIMEOUT) {
862 DHD_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n", event_name, eabuf));
863 } else if (status == WLC_E_STATUS_FAIL) {
864 DHD_EVENT(("MACEVENT: %s, MAC %s, FAILURE, reason %d\n",
865 event_name, eabuf, (int)reason));
866 } else {
867 DHD_EVENT(("MACEVENT: %s, MAC %s, unexpected status %d\n",
868 event_name, eabuf, (int)status));
869 }
870 break;
871
872 case WLC_E_DEAUTH_IND:
873 case WLC_E_DISASSOC_IND:
874 DHD_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name, eabuf, (int)reason));
875 break;
876
877 case WLC_E_AUTH:
878 case WLC_E_AUTH_IND:
879 if (auth_type == DOT11_OPEN_SYSTEM)
880 auth_str = "Open System";
881 else if (auth_type == DOT11_SHARED_KEY)
882 auth_str = "Shared Key";
883 else {
884 snprintf(err_msg, sizeof(err_msg), "AUTH unknown: %d", (int)auth_type);
885 auth_str = err_msg;
886 }
887 if (event_type == WLC_E_AUTH_IND) {
888 DHD_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name, eabuf, auth_str));
889 } else if (status == WLC_E_STATUS_SUCCESS) {
890 DHD_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
891 event_name, eabuf, auth_str));
892 } else if (status == WLC_E_STATUS_TIMEOUT) {
893 DHD_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
894 event_name, eabuf, auth_str));
895 } else if (status == WLC_E_STATUS_FAIL) {
896 DHD_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n",
897 event_name, eabuf, auth_str, (int)reason));
898 }
899 BCM_REFERENCE(auth_str);
900
901 break;
902
903 case WLC_E_JOIN:
904 case WLC_E_ROAM:
905 case WLC_E_SET_SSID:
906 if (status == WLC_E_STATUS_SUCCESS) {
907 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
908 } else if (status == WLC_E_STATUS_FAIL) {
909 DHD_EVENT(("MACEVENT: %s, failed\n", event_name));
910 } else if (status == WLC_E_STATUS_NO_NETWORKS) {
911 DHD_EVENT(("MACEVENT: %s, no networks found\n", event_name));
912 } else {
913 DHD_EVENT(("MACEVENT: %s, unexpected status %d\n",
914 event_name, (int)status));
915 }
916 break;
917
918 case WLC_E_BEACON_RX:
919 if (status == WLC_E_STATUS_SUCCESS) {
920 DHD_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
921 } else if (status == WLC_E_STATUS_FAIL) {
922 DHD_EVENT(("MACEVENT: %s, FAIL\n", event_name));
923 } else {
924 DHD_EVENT(("MACEVENT: %s, status %d\n", event_name, status));
925 }
926 break;
927
928 case WLC_E_LINK:
929 DHD_EVENT(("MACEVENT: %s %s\n", event_name, link?"UP":"DOWN"));
930 BCM_REFERENCE(link);
931 break;
932
933 case WLC_E_MIC_ERROR:
934 DHD_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
935 event_name, eabuf, group, flush_txq));
936 BCM_REFERENCE(group);
937 BCM_REFERENCE(flush_txq);
938 break;
939
940 case WLC_E_ICV_ERROR:
941 case WLC_E_UNICAST_DECODE_ERROR:
942 case WLC_E_MULTICAST_DECODE_ERROR:
943 DHD_EVENT(("MACEVENT: %s, MAC %s\n",
944 event_name, eabuf));
945 break;
946
947 case WLC_E_TXFAIL:
948 DHD_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
949 break;
950
951 case WLC_E_SCAN_COMPLETE:
952 case WLC_E_ASSOC_REQ_IE:
953 case WLC_E_ASSOC_RESP_IE:
954 case WLC_E_PMKID_CACHE:
955 DHD_EVENT(("MACEVENT: %s\n", event_name));
956 break;
957
958 case WLC_E_PFN_NET_FOUND:
959 case WLC_E_PFN_NET_LOST:
960 case WLC_E_PFN_SCAN_COMPLETE:
961 case WLC_E_PFN_SCAN_NONE:
962 case WLC_E_PFN_SCAN_ALLGONE:
963 DHD_EVENT(("PNOEVENT: %s\n", event_name));
964 break;
965
966 case WLC_E_PSK_SUP:
967 case WLC_E_PRUNE:
968 DHD_EVENT(("MACEVENT: %s, status %d, reason %d\n",
969 event_name, (int)status, (int)reason));
970 break;
971
972 #ifdef WIFI_ACT_FRAME
973 case WLC_E_ACTION_FRAME:
974 DHD_TRACE(("MACEVENT: %s Bssid %s\n", event_name, eabuf));
975 break;
976 #endif /* WIFI_ACT_FRAME */
977
978 case WLC_E_TRACE: {
979 static uint32 seqnum_prev = 0;
980 static uint32 logtrace_seqnum_prev = 0;
981 msgtrace_hdr_t hdr;
982 uint32 nblost;
983 char *s, *p;
984
985 buf = (uchar *) event_data;
986 memcpy(&hdr, buf, MSGTRACE_HDRLEN);
987
988 if (hdr.version != MSGTRACE_VERSION) {
989 printf("\nMACEVENT: %s [unsupported version --> "
990 "dhd version:%d dongle version:%d]\n",
991 event_name, MSGTRACE_VERSION, hdr.version);
992 /* Reset datalen to avoid display below */
993 datalen = 0;
994 break;
995 }
996
997 if (hdr.trace_type == MSGTRACE_HDR_TYPE_MSG) {
998 /* There are 2 bytes available at the end of data */
999 buf[MSGTRACE_HDRLEN + ntoh16(hdr.len)] = '\0';
1000
1001 if (ntoh32(hdr.discarded_bytes) || ntoh32(hdr.discarded_printf)) {
1002 printf("\nWLC_E_TRACE: [Discarded traces in dongle -->"
1003 "discarded_bytes %d discarded_printf %d]\n",
1004 ntoh32(hdr.discarded_bytes), ntoh32(hdr.discarded_printf));
1005 }
1006
1007 nblost = ntoh32(hdr.seqnum) - seqnum_prev - 1;
1008 if (nblost > 0) {
1009 printf("\nWLC_E_TRACE: [Event lost (msg) --> seqnum %d nblost %d\n",
1010 ntoh32(hdr.seqnum), nblost);
1011 }
1012 seqnum_prev = ntoh32(hdr.seqnum);
1013
1014 /* Display the trace buffer. Advance from \n to \n to avoid display big
1015 * printf (issue with Linux printk )
1016 */
1017 p = (char *)&buf[MSGTRACE_HDRLEN];
1018 while (*p != '\0' && (s = strstr(p, "\n")) != NULL) {
1019 *s = '\0';
1020 printf("%s\n", p);
1021 p = s+1;
1022 }
1023 if (*p) printf("%s", p);
1024
1025 /* Reset datalen to avoid display below */
1026 datalen = 0;
1027
1028 } else if (hdr.trace_type == MSGTRACE_HDR_TYPE_LOG) {
1029 /* Let the standard event printing work for now */
1030 uint32 timestamp, w;
1031 if (ntoh32(hdr.seqnum) == logtrace_seqnum_prev) {
1032 printf("\nWLC_E_TRACE: [Event duplicate (log) %d",
1033 logtrace_seqnum_prev);
1034 } else {
1035 nblost = ntoh32(hdr.seqnum) - logtrace_seqnum_prev - 1;
1036 if (nblost > 0) {
1037 printf("\nWLC_E_TRACE: [Event lost (log)"
1038 " --> seqnum %d nblost %d\n",
1039 ntoh32(hdr.seqnum), nblost);
1040 }
1041 logtrace_seqnum_prev = ntoh32(hdr.seqnum);
1042
1043 p = (char *)&buf[MSGTRACE_HDRLEN];
1044 datalen -= MSGTRACE_HDRLEN;
1045 w = ntoh32((uint32) *p);
1046 p += 4;
1047 datalen -= 4;
1048 timestamp = ntoh32((uint32) *p);
1049 printf("Logtrace %x timestamp %x %x",
1050 logtrace_seqnum_prev, timestamp, w);
1051
1052 while (datalen > 4) {
1053 p += 4;
1054 datalen -= 4;
1055 /* Print each word. DO NOT ntoh it. */
1056 printf(" %8.8x", *((uint32 *) p));
1057 }
1058 printf("\n");
1059 }
1060 datalen = 0;
1061 }
1062
1063 break;
1064 }
1065
1066
1067 case WLC_E_RSSI:
1068 DHD_EVENT(("MACEVENT: %s %d\n", event_name, ntoh32(*((int *)event_data))));
1069 break;
1070
1071 case WLC_E_SERVICE_FOUND:
1072 case WLC_E_P2PO_ADD_DEVICE:
1073 case WLC_E_P2PO_DEL_DEVICE:
1074 DHD_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
1075 break;
1076
1077 default:
1078 DHD_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, auth %d\n",
1079 event_name, event_type, eabuf, (int)status, (int)reason,
1080 (int)auth_type));
1081 break;
1082 }
1083
1084 /* show any appended data */
1085 if (DHD_BYTES_ON() && DHD_EVENT_ON() && datalen) {
1086 buf = (uchar *) event_data;
1087 DHD_EVENT((" data (%d) : ", datalen));
1088 for (i = 0; i < datalen; i++)
1089 DHD_EVENT((" 0x%02x ", *buf++));
1090 DHD_EVENT(("\n"));
1091 }
1092 }
1093 #endif /* SHOW_EVENTS */
1094
1095 int
wl_host_event(dhd_pub_t * dhd_pub,int * ifidx,void * pktdata,wl_event_msg_t * event,void ** data_ptr)1096 wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata,
1097 wl_event_msg_t *event, void **data_ptr)
1098 {
1099 /* check whether packet is a BRCM event pkt */
1100 bcm_event_t *pvt_data = (bcm_event_t *)pktdata;
1101 uint8 *event_data;
1102 uint32 type, status, datalen;
1103 uint16 flags;
1104 int evlen;
1105
1106 if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
1107 DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__));
1108 return (BCME_ERROR);
1109 }
1110
1111 /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
1112 if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) {
1113 DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__));
1114 return (BCME_ERROR);
1115 }
1116
1117 *data_ptr = &pvt_data[1];
1118 event_data = *data_ptr;
1119
1120 /* memcpy since BRCM event pkt may be unaligned. */
1121 memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t));
1122
1123 type = ntoh32_ua((void *)&event->event_type);
1124 flags = ntoh16_ua((void *)&event->flags);
1125 status = ntoh32_ua((void *)&event->status);
1126 datalen = ntoh32_ua((void *)&event->datalen);
1127 evlen = datalen + sizeof(bcm_event_t);
1128
1129 switch (type) {
1130 #ifdef PROP_TXSTATUS
1131 case WLC_E_FIFO_CREDIT_MAP:
1132 dhd_os_wlfc_block(dhd_pub);
1133 dhd_wlfc_event(dhd_pub->info);
1134 dhd_wlfc_FIFOcreditmap_event(dhd_pub->info, event_data);
1135 dhd_os_wlfc_unblock(dhd_pub);
1136 WLFC_DBGMESG(("WLC_E_FIFO_CREDIT_MAP:(AC0,AC1,AC2,AC3),(BC_MC),(OTHER): "
1137 "(%d,%d,%d,%d),(%d),(%d)\n", event_data[0], event_data[1],
1138 event_data[2],
1139 event_data[3], event_data[4], event_data[5]));
1140 break;
1141 #endif
1142
1143 case WLC_E_IF:
1144 {
1145 dhd_if_event_t *ifevent = (dhd_if_event_t *)event_data;
1146
1147 /* Ignore the event if NOIF is set */
1148 if (ifevent->flags & WLC_E_IF_FLAGS_BSSCFG_NOIF) {
1149 WLFC_DBGMESG(("WLC_E_IF: NO_IF set, event Ignored\r\n"));
1150 return (BCME_OK);
1151 }
1152
1153 #ifdef PROP_TXSTATUS
1154 {
1155 uint8* ea = pvt_data->eth.ether_dhost;
1156 WLFC_DBGMESG(("WLC_E_IF: idx:%d, action:%s, iftype:%s, "
1157 "[%02x:%02x:%02x:%02x:%02x:%02x]\n",
1158 ifevent->ifidx,
1159 ((ifevent->action == WLC_E_IF_ADD) ? "ADD":"DEL"),
1160 ((ifevent->is_AP == 0) ? "STA":"AP "),
1161 ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]));
1162 (void)ea;
1163
1164 dhd_os_wlfc_block(dhd_pub);
1165 if (ifevent->action == WLC_E_IF_CHANGE)
1166 dhd_wlfc_interface_event(dhd_pub->info,
1167 eWLFC_MAC_ENTRY_ACTION_UPDATE,
1168 ifevent->ifidx, ifevent->is_AP, ea);
1169 else
1170 dhd_wlfc_interface_event(dhd_pub->info,
1171 ((ifevent->action == WLC_E_IF_ADD) ?
1172 eWLFC_MAC_ENTRY_ACTION_ADD : eWLFC_MAC_ENTRY_ACTION_DEL),
1173 ifevent->ifidx, ifevent->is_AP, ea);
1174 dhd_os_wlfc_unblock(dhd_pub);
1175
1176 /* dhd already has created an interface by default, for 0 */
1177 if (ifevent->ifidx == 0)
1178 break;
1179 }
1180 #endif /* PROP_TXSTATUS */
1181
1182 #ifdef WL_CFG80211
1183 if (wl_cfg80211_is_progress_ifchange()) {
1184 DHD_ERROR(("%s: ifidx %d for %s action %d\n",
1185 __FUNCTION__, ifevent->ifidx,
1186 event->ifname, ifevent->action));
1187 if (ifevent->action == WLC_E_IF_ADD ||
1188 ifevent->action == WLC_E_IF_CHANGE)
1189 wl_cfg80211_notify_ifchange();
1190 return (BCME_OK);
1191 }
1192 #endif /* WL_CFG80211 */
1193 if (ifevent->ifidx > 0 && ifevent->ifidx < DHD_MAX_IFS) {
1194 if (ifevent->action == WLC_E_IF_ADD) {
1195 if (dhd_add_if(dhd_pub->info, ifevent->ifidx,
1196 NULL, event->ifname,
1197 event->addr.octet,
1198 ifevent->flags, ifevent->bssidx)) {
1199 DHD_ERROR(("%s: dhd_add_if failed!!"
1200 " ifidx: %d for %s\n",
1201 __FUNCTION__,
1202 ifevent->ifidx,
1203 event->ifname));
1204 return (BCME_ERROR);
1205 }
1206 }
1207 else if (ifevent->action == WLC_E_IF_DEL)
1208 dhd_del_if(dhd_pub->info, ifevent->ifidx);
1209 } else {
1210 #ifndef PROP_TXSTATUS
1211 DHD_ERROR(("%s: Invalid ifidx %d for %s\n",
1212 __FUNCTION__, ifevent->ifidx, event->ifname));
1213 #endif /* !PROP_TXSTATUS */
1214 }
1215 }
1216 /* send up the if event: btamp user needs it */
1217 *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname);
1218 /* push up to external supp/auth */
1219 dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx);
1220 break;
1221
1222
1223 #ifdef WLMEDIA_HTSF
1224 case WLC_E_HTSFSYNC:
1225 htsf_update(dhd_pub->info, event_data);
1226 break;
1227 #endif /* WLMEDIA_HTSF */
1228 case WLC_E_NDIS_LINK: {
1229 uint32 temp = hton32(WLC_E_LINK);
1230
1231 memcpy((void *)(&pvt_data->event.event_type), &temp,
1232 sizeof(pvt_data->event.event_type));
1233 }
1234 case WLC_E_PFN_NET_FOUND:
1235 case WLC_E_PFN_NET_LOST:
1236 break;
1237 case WLC_E_PFN_BSSID_NET_FOUND:
1238 case WLC_E_PFN_BSSID_NET_LOST:
1239 case WLC_E_PFN_BEST_BATCHING:
1240 #ifdef PNO_SUPPORT
1241 dhd_pno_event_handler(dhd_pub, event, (void *)event_data);
1242 #endif
1243 break;
1244 /* These are what external supplicant/authenticator wants */
1245 /* fall through */
1246 case WLC_E_LINK:
1247 case WLC_E_DEAUTH:
1248 case WLC_E_DEAUTH_IND:
1249 case WLC_E_DISASSOC:
1250 case WLC_E_DISASSOC_IND:
1251 DHD_EVENT(("%s: Link event %d, flags %x, status %x\n",
1252 __FUNCTION__, type, flags, status));
1253 /* fall through */
1254 default:
1255 *ifidx = dhd_ifname2idx(dhd_pub->info, event->ifname);
1256 /* push up to external supp/auth */
1257 dhd_event(dhd_pub->info, (char *)pvt_data, evlen, *ifidx);
1258 DHD_TRACE(("%s: MAC event %d, flags %x, status %x\n",
1259 __FUNCTION__, type, flags, status));
1260 BCM_REFERENCE(flags);
1261 BCM_REFERENCE(status);
1262
1263 /* put it back to WLC_E_NDIS_LINK */
1264 if (type == WLC_E_NDIS_LINK) {
1265 uint32 temp;
1266
1267 temp = ntoh32_ua((void *)&event->event_type);
1268 DHD_TRACE(("Converted to WLC_E_LINK type %d\n", temp));
1269
1270 temp = ntoh32(WLC_E_NDIS_LINK);
1271 memcpy((void *)(&pvt_data->event.event_type), &temp,
1272 sizeof(pvt_data->event.event_type));
1273 }
1274 break;
1275 }
1276
1277 #ifdef SHOW_EVENTS
1278 wl_show_host_event(event, (void *)event_data);
1279 #endif /* SHOW_EVENTS */
1280
1281 return (BCME_OK);
1282 }
1283
1284 void
wl_event_to_host_order(wl_event_msg_t * evt)1285 wl_event_to_host_order(wl_event_msg_t * evt)
1286 {
1287 /* Event struct members passed from dongle to host are stored in network
1288 * byte order. Convert all members to host-order.
1289 */
1290 evt->event_type = ntoh32(evt->event_type);
1291 evt->flags = ntoh16(evt->flags);
1292 evt->status = ntoh32(evt->status);
1293 evt->reason = ntoh32(evt->reason);
1294 evt->auth_type = ntoh32(evt->auth_type);
1295 evt->datalen = ntoh32(evt->datalen);
1296 evt->version = ntoh16(evt->version);
1297 }
1298
1299 void
dhd_print_buf(void * pbuf,int len,int bytes_per_line)1300 dhd_print_buf(void *pbuf, int len, int bytes_per_line)
1301 {
1302 #ifdef DHD_DEBUG
1303 int i, j = 0;
1304 unsigned char *buf = pbuf;
1305
1306 if (bytes_per_line == 0) {
1307 bytes_per_line = len;
1308 }
1309
1310 for (i = 0; i < len; i++) {
1311 printf("%2.2x", *buf++);
1312 j++;
1313 if (j == bytes_per_line) {
1314 printf("\n");
1315 j = 0;
1316 } else {
1317 printf(":");
1318 }
1319 }
1320 printf("\n");
1321 #endif /* DHD_DEBUG */
1322 }
1323
1324 #ifndef strtoul
1325 #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
1326 #endif
1327
1328 #ifdef PKT_FILTER_SUPPORT
1329 /* Convert user's input in hex pattern to byte-size mask */
1330 static int
wl_pattern_atoh(char * src,char * dst)1331 wl_pattern_atoh(char *src, char *dst)
1332 {
1333 int i;
1334 if (strncmp(src, "0x", 2) != 0 &&
1335 strncmp(src, "0X", 2) != 0) {
1336 DHD_ERROR(("Mask invalid format. Needs to start with 0x\n"));
1337 return -1;
1338 }
1339 src = src + 2; /* Skip past 0x */
1340 if (strlen(src) % 2 != 0) {
1341 DHD_ERROR(("Mask invalid format. Needs to be of even length\n"));
1342 return -1;
1343 }
1344 for (i = 0; *src != '\0'; i++) {
1345 char num[3];
1346 bcm_strncpy_s(num, sizeof(num), src, 2);
1347 num[2] = '\0';
1348 dst[i] = (uint8)strtoul(num, NULL, 16);
1349 src += 2;
1350 }
1351 return i;
1352 }
1353
1354 void
dhd_pktfilter_offload_enable(dhd_pub_t * dhd,char * arg,int enable,int master_mode)1355 dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode)
1356 {
1357 char *argv[8];
1358 int i = 0;
1359 const char *str;
1360 int buf_len;
1361 int str_len;
1362 char *arg_save = 0, *arg_org = 0;
1363 int rc;
1364 char buf[128];
1365 wl_pkt_filter_enable_t enable_parm;
1366 wl_pkt_filter_enable_t * pkt_filterp;
1367
1368 if (!arg)
1369 return;
1370
1371 if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
1372 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
1373 goto fail;
1374 }
1375 arg_org = arg_save;
1376 memcpy(arg_save, arg, strlen(arg) + 1);
1377
1378 argv[i] = bcmstrtok(&arg_save, " ", 0);
1379
1380 i = 0;
1381 if (argv[i] == NULL) {
1382 DHD_ERROR(("No args provided\n"));
1383 goto fail;
1384 }
1385
1386 str = "pkt_filter_enable";
1387 str_len = strlen(str);
1388 bcm_strncpy_s(buf, sizeof(buf), str, str_len);
1389 buf[str_len] = '\0';
1390 buf_len = str_len + 1;
1391
1392 pkt_filterp = (wl_pkt_filter_enable_t *)(buf + str_len + 1);
1393
1394 /* Parse packet filter id. */
1395 enable_parm.id = htod32(strtoul(argv[i], NULL, 0));
1396
1397 /* Parse enable/disable value. */
1398 enable_parm.enable = htod32(enable);
1399
1400 buf_len += sizeof(enable_parm);
1401 memcpy((char *)pkt_filterp,
1402 &enable_parm,
1403 sizeof(enable_parm));
1404
1405 /* Enable/disable the specified filter. */
1406 rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0);
1407 rc = rc >= 0 ? 0 : rc;
1408 if (rc)
1409 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1410 __FUNCTION__, arg, rc));
1411 else
1412 DHD_TRACE(("%s: successfully added pktfilter %s\n",
1413 __FUNCTION__, arg));
1414
1415 /* Contorl the master mode */
1416 bcm_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf, sizeof(buf));
1417 rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0);
1418 rc = rc >= 0 ? 0 : rc;
1419 if (rc)
1420 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1421 __FUNCTION__, arg, rc));
1422
1423 fail:
1424 if (arg_org)
1425 MFREE(dhd->osh, arg_org, strlen(arg) + 1);
1426 }
1427
1428 void
dhd_pktfilter_offload_set(dhd_pub_t * dhd,char * arg)1429 dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg)
1430 {
1431 const char *str;
1432 wl_pkt_filter_t pkt_filter;
1433 wl_pkt_filter_t *pkt_filterp;
1434 int buf_len;
1435 int str_len;
1436 int rc;
1437 uint32 mask_size;
1438 uint32 pattern_size;
1439 char *argv[8], * buf = 0;
1440 int i = 0;
1441 char *arg_save = 0, *arg_org = 0;
1442 #define BUF_SIZE 2048
1443
1444 if (!arg)
1445 return;
1446
1447 if (!(arg_save = MALLOC(dhd->osh, strlen(arg) + 1))) {
1448 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
1449 goto fail;
1450 }
1451
1452 arg_org = arg_save;
1453
1454 if (!(buf = MALLOC(dhd->osh, BUF_SIZE))) {
1455 DHD_ERROR(("%s: kmalloc failed\n", __FUNCTION__));
1456 goto fail;
1457 }
1458
1459 memcpy(arg_save, arg, strlen(arg) + 1);
1460
1461 if (strlen(arg) > BUF_SIZE) {
1462 DHD_ERROR(("Not enough buffer %d < %d\n", (int)strlen(arg), (int)sizeof(buf)));
1463 goto fail;
1464 }
1465
1466 argv[i] = bcmstrtok(&arg_save, " ", 0);
1467 while (argv[i++])
1468 argv[i] = bcmstrtok(&arg_save, " ", 0);
1469
1470 i = 0;
1471 if (argv[i] == NULL) {
1472 DHD_ERROR(("No args provided\n"));
1473 goto fail;
1474 }
1475
1476 str = "pkt_filter_add";
1477 str_len = strlen(str);
1478 bcm_strncpy_s(buf, BUF_SIZE, str, str_len);
1479 buf[ str_len ] = '\0';
1480 buf_len = str_len + 1;
1481
1482 pkt_filterp = (wl_pkt_filter_t *) (buf + str_len + 1);
1483
1484 /* Parse packet filter id. */
1485 pkt_filter.id = htod32(strtoul(argv[i], NULL, 0));
1486
1487 if (argv[++i] == NULL) {
1488 DHD_ERROR(("Polarity not provided\n"));
1489 goto fail;
1490 }
1491
1492 /* Parse filter polarity. */
1493 pkt_filter.negate_match = htod32(strtoul(argv[i], NULL, 0));
1494
1495 if (argv[++i] == NULL) {
1496 DHD_ERROR(("Filter type not provided\n"));
1497 goto fail;
1498 }
1499
1500 /* Parse filter type. */
1501 pkt_filter.type = htod32(strtoul(argv[i], NULL, 0));
1502
1503 if (argv[++i] == NULL) {
1504 DHD_ERROR(("Offset not provided\n"));
1505 goto fail;
1506 }
1507
1508 /* Parse pattern filter offset. */
1509 pkt_filter.u.pattern.offset = htod32(strtoul(argv[i], NULL, 0));
1510
1511 if (argv[++i] == NULL) {
1512 DHD_ERROR(("Bitmask not provided\n"));
1513 goto fail;
1514 }
1515
1516 /* Parse pattern filter mask. */
1517 mask_size =
1518 htod32(wl_pattern_atoh(argv[i], (char *) pkt_filterp->u.pattern.mask_and_pattern));
1519
1520 if (argv[++i] == NULL) {
1521 DHD_ERROR(("Pattern not provided\n"));
1522 goto fail;
1523 }
1524
1525 /* Parse pattern filter pattern. */
1526 pattern_size =
1527 htod32(wl_pattern_atoh(argv[i],
1528 (char *) &pkt_filterp->u.pattern.mask_and_pattern[mask_size]));
1529
1530 if (mask_size != pattern_size) {
1531 DHD_ERROR(("Mask and pattern not the same size\n"));
1532 goto fail;
1533 }
1534
1535 pkt_filter.u.pattern.size_bytes = mask_size;
1536 buf_len += WL_PKT_FILTER_FIXED_LEN;
1537 buf_len += (WL_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
1538
1539 /* Keep-alive attributes are set in local variable (keep_alive_pkt), and
1540 ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
1541 ** guarantee that the buffer is properly aligned.
1542 */
1543 memcpy((char *)pkt_filterp,
1544 &pkt_filter,
1545 WL_PKT_FILTER_FIXED_LEN + WL_PKT_FILTER_PATTERN_FIXED_LEN);
1546
1547 rc = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0);
1548 rc = rc >= 0 ? 0 : rc;
1549
1550 if (rc)
1551 DHD_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
1552 __FUNCTION__, arg, rc));
1553 else
1554 DHD_TRACE(("%s: successfully added pktfilter %s\n",
1555 __FUNCTION__, arg));
1556
1557 fail:
1558 if (arg_org)
1559 MFREE(dhd->osh, arg_org, strlen(arg) + 1);
1560
1561 if (buf)
1562 MFREE(dhd->osh, buf, BUF_SIZE);
1563 }
1564
dhd_pktfilter_offload_delete(dhd_pub_t * dhd,int id)1565 void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id)
1566 {
1567 char iovbuf[32];
1568 int ret;
1569
1570 bcm_mkiovar("pkt_filter_delete", (char *)&id, 4, iovbuf, sizeof(iovbuf));
1571 ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
1572 if (ret < 0) {
1573 DHD_ERROR(("%s: Failed to delete filter ID:%d, ret=%d\n",
1574 __FUNCTION__, id, ret));
1575 }
1576 }
1577 #endif /* PKT_FILTER_SUPPORT */
1578
1579 /* ========================== */
1580 /* ==== ARP OFFLOAD SUPPORT = */
1581 /* ========================== */
1582 #ifdef ARP_OFFLOAD_SUPPORT
1583 void
dhd_arp_offload_set(dhd_pub_t * dhd,int arp_mode)1584 dhd_arp_offload_set(dhd_pub_t * dhd, int arp_mode)
1585 {
1586 char iovbuf[32];
1587 int retcode;
1588
1589 bcm_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
1590 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
1591 retcode = retcode >= 0 ? 0 : retcode;
1592 if (retcode)
1593 DHD_TRACE(("%s: failed to set ARP offload mode to 0x%x, retcode = %d\n",
1594 __FUNCTION__, arp_mode, retcode));
1595 else
1596 DHD_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
1597 __FUNCTION__, arp_mode));
1598 }
1599
1600 void
dhd_arp_offload_enable(dhd_pub_t * dhd,int arp_enable)1601 dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable)
1602 {
1603 char iovbuf[32];
1604 int retcode;
1605
1606 bcm_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
1607 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
1608 retcode = retcode >= 0 ? 0 : retcode;
1609 if (retcode)
1610 DHD_TRACE(("%s: failed to enabe ARP offload to %d, retcode = %d\n",
1611 __FUNCTION__, arp_enable, retcode));
1612 else
1613 DHD_TRACE(("%s: successfully enabed ARP offload to %d\n",
1614 __FUNCTION__, arp_enable));
1615 if (arp_enable) {
1616 uint32 version;
1617 bcm_mkiovar("arp_version", 0, 0, iovbuf, sizeof(iovbuf));
1618 retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0);
1619 if (retcode) {
1620 DHD_INFO(("%s: fail to get version (maybe version 1:retcode = %d\n",
1621 __FUNCTION__, retcode));
1622 dhd->arp_version = 1;
1623 }
1624 else {
1625 memcpy(&version, iovbuf, sizeof(version));
1626 DHD_INFO(("%s: ARP Version= %x\n", __FUNCTION__, version));
1627 dhd->arp_version = version;
1628 }
1629 }
1630 }
1631
1632 void
dhd_aoe_arp_clr(dhd_pub_t * dhd,int idx)1633 dhd_aoe_arp_clr(dhd_pub_t *dhd, int idx)
1634 {
1635 int ret = 0;
1636 int iov_len = 0;
1637 char iovbuf[DHD_IOVAR_BUF_SIZE];
1638
1639 if (dhd == NULL) return;
1640 if (dhd->arp_version == 1)
1641 idx = 0;
1642
1643 iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf));
1644 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0)
1645 DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
1646 }
1647
1648 void
dhd_aoe_hostip_clr(dhd_pub_t * dhd,int idx)1649 dhd_aoe_hostip_clr(dhd_pub_t *dhd, int idx)
1650 {
1651 int ret = 0;
1652 int iov_len = 0;
1653 char iovbuf[DHD_IOVAR_BUF_SIZE];
1654
1655 if (dhd == NULL) return;
1656 if (dhd->arp_version == 1)
1657 idx = 0;
1658
1659 iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf));
1660 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx)) < 0)
1661 DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
1662 }
1663
1664 void
dhd_arp_offload_add_ip(dhd_pub_t * dhd,uint32 ipaddr,int idx)1665 dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr, int idx)
1666 {
1667 int iov_len = 0;
1668 char iovbuf[DHD_IOVAR_BUF_SIZE];
1669 int retcode;
1670
1671
1672 if (dhd == NULL) return;
1673 if (dhd->arp_version == 1)
1674 idx = 0;
1675 iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr,
1676 sizeof(ipaddr), iovbuf, sizeof(iovbuf));
1677 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx);
1678
1679 if (retcode)
1680 DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n",
1681 __FUNCTION__, retcode));
1682 else
1683 DHD_TRACE(("%s: sARP H ipaddr entry added \n",
1684 __FUNCTION__));
1685 }
1686
1687 int
dhd_arp_get_arp_hostip_table(dhd_pub_t * dhd,void * buf,int buflen,int idx)1688 dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen, int idx)
1689 {
1690 int retcode, i;
1691 int iov_len;
1692 uint32 *ptr32 = buf;
1693 bool clr_bottom = FALSE;
1694
1695 if (!buf)
1696 return -1;
1697 if (dhd == NULL) return -1;
1698 if (dhd->arp_version == 1)
1699 idx = 0;
1700
1701 iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen);
1702 BCM_REFERENCE(iov_len);
1703 retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, FALSE, idx);
1704
1705 if (retcode) {
1706 DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n",
1707 __FUNCTION__, retcode));
1708
1709 return -1;
1710 }
1711
1712 /* clean up the buf, ascii reminder */
1713 for (i = 0; i < MAX_IPV4_ENTRIES; i++) {
1714 if (!clr_bottom) {
1715 if (*ptr32 == 0)
1716 clr_bottom = TRUE;
1717 } else {
1718 *ptr32 = 0;
1719 }
1720 ptr32++;
1721 }
1722
1723 return 0;
1724 }
1725 #endif /* ARP_OFFLOAD_SUPPORT */
1726
1727 /*
1728 * Neighbor Discovery Offload: enable NDO feature
1729 * Called by ipv6 event handler when interface comes up/goes down
1730 */
1731 int
dhd_ndo_enable(dhd_pub_t * dhd,int ndo_enable)1732 dhd_ndo_enable(dhd_pub_t *dhd, int ndo_enable)
1733 {
1734 char iovbuf[DHD_IOVAR_BUF_SIZE];
1735 int retcode;
1736
1737 if (dhd == NULL)
1738 return -1;
1739
1740 bcm_mkiovar("ndoe", (char *)&ndo_enable, 4, iovbuf, sizeof(iovbuf));
1741 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0);
1742 if (retcode)
1743 DHD_ERROR(("%s: failed to enabe ndo to %d, retcode = %d\n",
1744 __FUNCTION__, ndo_enable, retcode));
1745 else
1746 DHD_TRACE(("%s: successfully enabed ndo offload to %d\n",
1747 __FUNCTION__, ndo_enable));
1748
1749 return retcode;
1750 }
1751
1752 /*
1753 * Neighbor Discover Offload: add host ipv6 ip into firmware
1754 * Called by ipv6 event handler when interface comes up
1755 */
1756 int
dhd_ndo_add_ip(dhd_pub_t * dhd,char * ipv6addr,int idx)1757 dhd_ndo_add_ip(dhd_pub_t *dhd, char *ipv6addr, int idx)
1758 {
1759 int iov_len = 0;
1760 char iovbuf[DHD_IOVAR_BUF_SIZE];
1761 int retcode;
1762
1763 if (dhd == NULL || ipv6addr == NULL)
1764 return -1;
1765
1766 iov_len = bcm_mkiovar("nd_hostip", ipv6addr,
1767 IPV6_ADDR_LEN, iovbuf, sizeof(iovbuf));
1768 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx);
1769
1770 if (retcode)
1771 DHD_ERROR(("%s: ndo ip addr add failed, retcode = %d\n",
1772 __FUNCTION__, retcode));
1773 else
1774 DHD_ERROR(("%s: ndo ipaddr entry added \n",
1775 __FUNCTION__));
1776 return retcode;
1777 }
1778
1779 /*
1780 * Neighbor Discover Offload: disable NDO feature
1781 * Called by ipv6 event handler when interface goes down
1782 */
1783 int
dhd_ndo_remove_ip(dhd_pub_t * dhd,int idx)1784 dhd_ndo_remove_ip(dhd_pub_t *dhd, int idx)
1785 {
1786 int iov_len = 0;
1787 char iovbuf[DHD_IOVAR_BUF_SIZE];
1788 int retcode;
1789
1790 if (dhd == NULL)
1791 return -1;
1792
1793 iov_len = bcm_mkiovar("nd_hostip_clear", (char *)NULL,
1794 0, iovbuf, sizeof(iovbuf));
1795 retcode = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, iov_len, TRUE, idx);
1796
1797 if (retcode)
1798 DHD_ERROR(("%s: ndo ip addr remove failed, retcode = %d\n",
1799 __FUNCTION__, retcode));
1800 else
1801 DHD_TRACE(("%s: ndo ipaddr entry removed \n",
1802 __FUNCTION__));
1803
1804 return retcode;
1805 }
1806
1807 /* send up locally generated event */
1808 void
dhd_sendup_event_common(dhd_pub_t * dhdp,wl_event_msg_t * event,void * data)1809 dhd_sendup_event_common(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data)
1810 {
1811 switch (ntoh32(event->event_type)) {
1812 default:
1813 break;
1814 }
1815
1816 /* Call per-port handler. */
1817 dhd_sendup_event(dhdp, event, data);
1818 }
1819
1820
1821 /*
1822 * returns = TRUE if associated, FALSE if not associated
1823 */
dhd_is_associated(dhd_pub_t * dhd,void * bss_buf,int * retval)1824 bool dhd_is_associated(dhd_pub_t *dhd, void *bss_buf, int *retval)
1825 {
1826 char bssid[6], zbuf[6];
1827 int ret = -1;
1828
1829 bzero(bssid, 6);
1830 bzero(zbuf, 6);
1831
1832 ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BSSID, (char *)&bssid, ETHER_ADDR_LEN, FALSE, 0);
1833 DHD_TRACE((" %s WLC_GET_BSSID ioctl res = %d\n", __FUNCTION__, ret));
1834
1835 if (ret == BCME_NOTASSOCIATED) {
1836 DHD_TRACE(("%s: not associated! res:%d\n", __FUNCTION__, ret));
1837 }
1838
1839 if (retval)
1840 *retval = ret;
1841
1842 if (ret < 0)
1843 return FALSE;
1844
1845 if ((memcmp(bssid, zbuf, ETHER_ADDR_LEN) != 0)) {
1846 /* STA is assocoated BSSID is non zero */
1847
1848 if (bss_buf) {
1849 /* return bss if caller provided buf */
1850 memcpy(bss_buf, bssid, ETHER_ADDR_LEN);
1851 }
1852 return TRUE;
1853 } else {
1854 DHD_TRACE(("%s: WLC_GET_BSSID ioctl returned zero bssid\n", __FUNCTION__));
1855 return FALSE;
1856 }
1857 }
1858
1859
1860 /* Function to estimate possible DTIM_SKIP value */
1861 int
dhd_get_suspend_bcn_li_dtim(dhd_pub_t * dhd)1862 dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd)
1863 {
1864 int bcn_li_dtim = 1; /* deafult no dtim skip setting */
1865 int ret = -1;
1866 int dtim_period = 0;
1867 int ap_beacon = 0;
1868 int allowed_skip_dtim_cnt = 0;
1869 /* Check if associated */
1870 if (dhd_is_associated(dhd, NULL, NULL) == FALSE) {
1871 DHD_TRACE(("%s NOT assoc ret %d\n", __FUNCTION__, ret));
1872 goto exit;
1873 }
1874
1875 /* read associated AP beacon interval */
1876 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_BCNPRD,
1877 &ap_beacon, sizeof(ap_beacon), FALSE, 0)) < 0) {
1878 DHD_ERROR(("%s get beacon failed code %d\n", __FUNCTION__, ret));
1879 goto exit;
1880 }
1881
1882 /* if associated APs Beacon more that 100msec do no dtim skip */
1883 if (ap_beacon > MAX_DTIM_SKIP_BEACON_INTERVAL) {
1884 DHD_ERROR(("%s NO dtim skip for AP with beacon %d ms\n", __FUNCTION__, ap_beacon));
1885 goto exit;
1886 }
1887
1888 /* read associated ap's dtim setup */
1889 if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_DTIMPRD,
1890 &dtim_period, sizeof(dtim_period), FALSE, 0)) < 0) {
1891 DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
1892 goto exit;
1893 }
1894
1895 /* if not assocated just eixt */
1896 if (dtim_period == 0) {
1897 goto exit;
1898 }
1899
1900 /* attemp to use platform defined dtim skip interval */
1901 bcn_li_dtim = dhd->suspend_bcn_li_dtim;
1902
1903 /* check if sta listen interval fits into AP dtim */
1904 if (dtim_period > CUSTOM_LISTEN_INTERVAL) {
1905 /* AP DTIM to big for our Listen Interval : no dtim skiping */
1906 bcn_li_dtim = NO_DTIM_SKIP;
1907 DHD_ERROR(("%s DTIM=%d > Listen=%d : too big ...\n",
1908 __FUNCTION__, dtim_period, CUSTOM_LISTEN_INTERVAL));
1909 goto exit;
1910 }
1911
1912 if ((dtim_period * ap_beacon * bcn_li_dtim) > MAX_DTIM_ALLOWED_INTERVAL) {
1913 allowed_skip_dtim_cnt = MAX_DTIM_ALLOWED_INTERVAL / (dtim_period * ap_beacon);
1914 bcn_li_dtim = (allowed_skip_dtim_cnt != 0) ? allowed_skip_dtim_cnt : NO_DTIM_SKIP;
1915 }
1916
1917 if ((bcn_li_dtim * dtim_period) > CUSTOM_LISTEN_INTERVAL) {
1918 /* Round up dtim_skip to fit into STAs Listen Interval */
1919 bcn_li_dtim = (int)(CUSTOM_LISTEN_INTERVAL / dtim_period);
1920 DHD_TRACE(("%s agjust dtim_skip as %d\n", __FUNCTION__, bcn_li_dtim));
1921 }
1922
1923 DHD_ERROR(("%s beacon=%d bcn_li_dtim=%d DTIM=%d Listen=%d\n",
1924 __FUNCTION__, ap_beacon, bcn_li_dtim, dtim_period, CUSTOM_LISTEN_INTERVAL));
1925
1926 exit:
1927 return bcn_li_dtim;
1928 }
1929
1930 /* Check if the mode supports STA MODE */
dhd_support_sta_mode(dhd_pub_t * dhd)1931 bool dhd_support_sta_mode(dhd_pub_t *dhd)
1932 {
1933
1934 #ifdef WL_CFG80211
1935 if (!(dhd->op_mode & DHD_FLAG_STA_MODE))
1936 return FALSE;
1937 else
1938 #endif /* WL_CFG80211 */
1939 return TRUE;
1940 }
1941
1942 #if defined(KEEP_ALIVE)
dhd_keep_alive_onoff(dhd_pub_t * dhd)1943 int dhd_keep_alive_onoff(dhd_pub_t *dhd)
1944 {
1945 char buf[256];
1946 const char *str;
1947 wl_mkeep_alive_pkt_t mkeep_alive_pkt = {0};
1948 wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
1949 int buf_len;
1950 int str_len;
1951 int res = -1;
1952
1953 if (!dhd_support_sta_mode(dhd))
1954 return res;
1955
1956 DHD_TRACE(("%s execution\n", __FUNCTION__));
1957
1958 str = "mkeep_alive";
1959 str_len = strlen(str);
1960 strncpy(buf, str, str_len);
1961 buf[ str_len ] = '\0';
1962 mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1);
1963 mkeep_alive_pkt.period_msec = CUSTOM_KEEP_ALIVE_SETTING;
1964 buf_len = str_len + 1;
1965 mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION);
1966 mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN);
1967 /* Setup keep alive zero for null packet generation */
1968 mkeep_alive_pkt.keep_alive_id = 0;
1969 mkeep_alive_pkt.len_bytes = 0;
1970 buf_len += WL_MKEEP_ALIVE_FIXED_LEN;
1971 bzero(mkeep_alive_pkt.data, sizeof(mkeep_alive_pkt.data));
1972 /* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and
1973 * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no
1974 * guarantee that the buffer is properly aligned.
1975 */
1976 memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN);
1977
1978 res = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0);
1979
1980 return res;
1981 }
1982 #endif /* defined(KEEP_ALIVE) */
1983 /* Android ComboSCAN support */
1984
1985 /*
1986 * data parsing from ComboScan tlv list
1987 */
1988 int
wl_iw_parse_data_tlv(char ** list_str,void * dst,int dst_size,const char token,int input_size,int * bytes_left)1989 wl_iw_parse_data_tlv(char** list_str, void *dst, int dst_size, const char token,
1990 int input_size, int *bytes_left)
1991 {
1992 char* str;
1993 uint16 short_temp;
1994 uint32 int_temp;
1995
1996 if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
1997 DHD_ERROR(("%s error paramters\n", __FUNCTION__));
1998 return -1;
1999 }
2000 str = *list_str;
2001
2002 /* Clean all dest bytes */
2003 memset(dst, 0, dst_size);
2004 while (*bytes_left > 0) {
2005
2006 if (str[0] != token) {
2007 DHD_TRACE(("%s NOT Type=%d get=%d left_parse=%d \n",
2008 __FUNCTION__, token, str[0], *bytes_left));
2009 return -1;
2010 }
2011
2012 *bytes_left -= 1;
2013 str += 1;
2014
2015 if (input_size == 1) {
2016 memcpy(dst, str, input_size);
2017 }
2018 else if (input_size == 2) {
2019 memcpy(dst, (char *)htod16(memcpy(&short_temp, str, input_size)),
2020 input_size);
2021 }
2022 else if (input_size == 4) {
2023 memcpy(dst, (char *)htod32(memcpy(&int_temp, str, input_size)),
2024 input_size);
2025 }
2026
2027 *bytes_left -= input_size;
2028 str += input_size;
2029 *list_str = str;
2030 return 1;
2031 }
2032 return 1;
2033 }
2034
2035 /*
2036 * channel list parsing from cscan tlv list
2037 */
2038 int
wl_iw_parse_channel_list_tlv(char ** list_str,uint16 * channel_list,int channel_num,int * bytes_left)2039 wl_iw_parse_channel_list_tlv(char** list_str, uint16* channel_list,
2040 int channel_num, int *bytes_left)
2041 {
2042 char* str;
2043 int idx = 0;
2044
2045 if ((list_str == NULL) || (*list_str == NULL) ||(bytes_left == NULL) || (*bytes_left < 0)) {
2046 DHD_ERROR(("%s error paramters\n", __FUNCTION__));
2047 return -1;
2048 }
2049 str = *list_str;
2050
2051 while (*bytes_left > 0) {
2052
2053 if (str[0] != CSCAN_TLV_TYPE_CHANNEL_IE) {
2054 *list_str = str;
2055 DHD_TRACE(("End channel=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
2056 return idx;
2057 }
2058 /* Get proper CSCAN_TLV_TYPE_CHANNEL_IE */
2059 *bytes_left -= 1;
2060 str += 1;
2061
2062 if (str[0] == 0) {
2063 /* All channels */
2064 channel_list[idx] = 0x0;
2065 }
2066 else {
2067 channel_list[idx] = (uint16)str[0];
2068 DHD_TRACE(("%s channel=%d \n", __FUNCTION__, channel_list[idx]));
2069 }
2070 *bytes_left -= 1;
2071 str += 1;
2072
2073 if (idx++ > 255) {
2074 DHD_ERROR(("%s Too many channels \n", __FUNCTION__));
2075 return -1;
2076 }
2077 }
2078
2079 *list_str = str;
2080 return idx;
2081 }
2082
2083 /*
2084 * SSIDs list parsing from cscan tlv list
2085 */
2086 int
wl_iw_parse_ssid_list_tlv(char ** list_str,wlc_ssid_ext_t * ssid,int max,int * bytes_left)2087 wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_ext_t* ssid, int max, int *bytes_left)
2088 {
2089 char* str;
2090 int idx = 0;
2091
2092 if ((list_str == NULL) || (*list_str == NULL) || (*bytes_left < 0)) {
2093 DHD_ERROR(("%s error paramters\n", __FUNCTION__));
2094 return -1;
2095 }
2096 str = *list_str;
2097 while (*bytes_left > 0) {
2098
2099 if (str[0] != CSCAN_TLV_TYPE_SSID_IE) {
2100 *list_str = str;
2101 DHD_TRACE(("nssid=%d left_parse=%d %d\n", idx, *bytes_left, str[0]));
2102 return idx;
2103 }
2104
2105 /* Get proper CSCAN_TLV_TYPE_SSID_IE */
2106 *bytes_left -= 1;
2107 str += 1;
2108
2109 if (str[0] == 0) {
2110 /* Broadcast SSID */
2111 ssid[idx].SSID_len = 0;
2112 memset((char*)ssid[idx].SSID, 0x0, DOT11_MAX_SSID_LEN);
2113 *bytes_left -= 1;
2114 str += 1;
2115
2116 DHD_TRACE(("BROADCAST SCAN left=%d\n", *bytes_left));
2117 }
2118 else if (str[0] <= DOT11_MAX_SSID_LEN) {
2119 /* Get proper SSID size */
2120 ssid[idx].SSID_len = str[0];
2121 *bytes_left -= 1;
2122 str += 1;
2123
2124 /* Get SSID */
2125 if (ssid[idx].SSID_len > *bytes_left) {
2126 DHD_ERROR(("%s out of memory range len=%d but left=%d\n",
2127 __FUNCTION__, ssid[idx].SSID_len, *bytes_left));
2128 return -1;
2129 }
2130
2131 memcpy((char*)ssid[idx].SSID, str, ssid[idx].SSID_len);
2132
2133 *bytes_left -= ssid[idx].SSID_len;
2134 str += ssid[idx].SSID_len;
2135 ssid[idx].hidden = TRUE;
2136
2137 DHD_TRACE(("%s :size=%d left=%d\n",
2138 (char*)ssid[idx].SSID, ssid[idx].SSID_len, *bytes_left));
2139 }
2140 else {
2141 DHD_ERROR(("### SSID size more that %d\n", str[0]));
2142 return -1;
2143 }
2144
2145 if (idx++ > max) {
2146 DHD_ERROR(("%s number of SSIDs more that %d\n", __FUNCTION__, idx));
2147 return -1;
2148 }
2149 }
2150
2151 *list_str = str;
2152 return idx;
2153 }
2154
2155 /* Parse a comma-separated list from list_str into ssid array, starting
2156 * at index idx. Max specifies size of the ssid array. Parses ssids
2157 * and returns updated idx; if idx >= max not all fit, the excess have
2158 * not been copied. Returns -1 on empty string, or on ssid too long.
2159 */
2160 int
wl_iw_parse_ssid_list(char ** list_str,wlc_ssid_t * ssid,int idx,int max)2161 wl_iw_parse_ssid_list(char** list_str, wlc_ssid_t* ssid, int idx, int max)
2162 {
2163 char* str, *ptr;
2164
2165 if ((list_str == NULL) || (*list_str == NULL))
2166 return -1;
2167
2168 for (str = *list_str; str != NULL; str = ptr) {
2169
2170 /* check for next TAG */
2171 if (!strncmp(str, GET_CHANNEL, strlen(GET_CHANNEL))) {
2172 *list_str = str + strlen(GET_CHANNEL);
2173 return idx;
2174 }
2175
2176 if ((ptr = strchr(str, ',')) != NULL) {
2177 *ptr++ = '\0';
2178 }
2179
2180 if (strlen(str) > DOT11_MAX_SSID_LEN) {
2181 DHD_ERROR(("ssid <%s> exceeds %d\n", str, DOT11_MAX_SSID_LEN));
2182 return -1;
2183 }
2184
2185 if (strlen(str) == 0)
2186 ssid[idx].SSID_len = 0;
2187
2188 if (idx < max) {
2189 bzero(ssid[idx].SSID, sizeof(ssid[idx].SSID));
2190 strncpy((char*)ssid[idx].SSID, str, sizeof(ssid[idx].SSID) - 1);
2191 ssid[idx].SSID_len = strlen(str);
2192 }
2193 idx++;
2194 }
2195 return idx;
2196 }
2197
2198 /*
2199 * Parse channel list from iwpriv CSCAN
2200 */
2201 int
wl_iw_parse_channel_list(char ** list_str,uint16 * channel_list,int channel_num)2202 wl_iw_parse_channel_list(char** list_str, uint16* channel_list, int channel_num)
2203 {
2204 int num;
2205 int val;
2206 char* str;
2207 char* endptr = NULL;
2208
2209 if ((list_str == NULL)||(*list_str == NULL))
2210 return -1;
2211
2212 str = *list_str;
2213 num = 0;
2214 while (strncmp(str, GET_NPROBE, strlen(GET_NPROBE))) {
2215 val = (int)strtoul(str, &endptr, 0);
2216 if (endptr == str) {
2217 printf("could not parse channel number starting at"
2218 " substring \"%s\" in list:\n%s\n",
2219 str, *list_str);
2220 return -1;
2221 }
2222 str = endptr + strspn(endptr, " ,");
2223
2224 if (num == channel_num) {
2225 DHD_ERROR(("too many channels (more than %d) in channel list:\n%s\n",
2226 channel_num, *list_str));
2227 return -1;
2228 }
2229
2230 channel_list[num++] = (uint16)val;
2231 }
2232 *list_str = str;
2233 return num;
2234 }
2235