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