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