1 /*
2 * Linux Wireless Extensions support
3 *
4 * Copyright (C) 1999-2017, 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 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id: wl_iw.c 616333 2016-02-01 05:30:29Z $
28 */
29
30 #if defined(USE_IW)
31 #define LINUX_PORT
32
33 #include <typedefs.h>
34 #include <linuxver.h>
35 #include <osl.h>
36
37 #include <bcmutils.h>
38 #include <bcmendian.h>
39 #include <ethernet.h>
40
41 #include <linux/if_arp.h>
42 #include <asm/uaccess.h>
43 #include <wlioctl.h>
44 #ifdef WL_NAN
45 #include <wlioctl_utils.h>
46 #endif
47 #include <wl_android.h>
48 #ifdef WL_ESCAN
49 #include <wl_escan.h>
50 #endif
51 #include <dhd_config.h>
52
53 typedef const struct si_pub si_t;
54
55 /* message levels */
56 #define WL_ERROR_LEVEL 0x0001
57 #define WL_SCAN_LEVEL 0x0002
58 #define WL_ASSOC_LEVEL 0x0004
59 #define WL_INFORM_LEVEL 0x0008
60 #define WL_WSEC_LEVEL 0x0010
61 #define WL_PNO_LEVEL 0x0020
62 #define WL_COEX_LEVEL 0x0040
63 #define WL_SOFTAP_LEVEL 0x0080
64 #define WL_TRACE_LEVEL 0x0100
65
66 uint iw_msg_level = WL_ERROR_LEVEL;
67
68 #define WL_ERROR(x) do {if (iw_msg_level & WL_ERROR_LEVEL) printf x;} while (0)
69 #define WL_SCAN(x) do {if (iw_msg_level & WL_SCAN_LEVEL) printf x;} while (0)
70 #define WL_ASSOC(x) do {if (iw_msg_level & WL_ASSOC_LEVEL) printf x;} while (0)
71 #define WL_INFORM(x) do {if (iw_msg_level & WL_INFORM_LEVEL) printf x;} while (0)
72 #define WL_WSEC(x) do {if (iw_msg_level & WL_WSEC_LEVEL) printf x;} while (0)
73 #define WL_PNO(x) do {if (iw_msg_level & WL_PNO_LEVEL) printf x;} while (0)
74 #define WL_COEX(x) do {if (iw_msg_level & WL_COEX_LEVEL) printf x;} while (0)
75 #define WL_SOFTAP(x) do {if (iw_msg_level & WL_SOFTAP_LEVEL) printf x;} while (0)
76 #define WL_TRACE(x) do {if (iw_msg_level & WL_TRACE_LEVEL) printf x;} while (0)
77
78 #include <wl_iw.h>
79
80 #ifdef BCMWAPI_WPI
81 /* these items should evetually go into wireless.h of the linux system headfile dir */
82 #ifndef IW_ENCODE_ALG_SM4
83 #define IW_ENCODE_ALG_SM4 0x20
84 #endif
85
86 #ifndef IW_AUTH_WAPI_ENABLED
87 #define IW_AUTH_WAPI_ENABLED 0x20
88 #endif
89
90 #ifndef IW_AUTH_WAPI_VERSION_1
91 #define IW_AUTH_WAPI_VERSION_1 0x00000008
92 #endif
93
94 #ifndef IW_AUTH_CIPHER_SMS4
95 #define IW_AUTH_CIPHER_SMS4 0x00000020
96 #endif
97
98 #ifndef IW_AUTH_KEY_MGMT_WAPI_PSK
99 #define IW_AUTH_KEY_MGMT_WAPI_PSK 4
100 #endif
101
102 #ifndef IW_AUTH_KEY_MGMT_WAPI_CERT
103 #define IW_AUTH_KEY_MGMT_WAPI_CERT 8
104 #endif
105 #endif /* BCMWAPI_WPI */
106
107 /* Broadcom extensions to WEXT, linux upstream has obsoleted WEXT */
108 #ifndef IW_AUTH_KEY_MGMT_FT_802_1X
109 #define IW_AUTH_KEY_MGMT_FT_802_1X 0x04
110 #endif
111
112 #ifndef IW_AUTH_KEY_MGMT_FT_PSK
113 #define IW_AUTH_KEY_MGMT_FT_PSK 0x08
114 #endif
115
116 #ifndef IW_ENC_CAPA_FW_ROAM_ENABLE
117 #define IW_ENC_CAPA_FW_ROAM_ENABLE 0x00000020
118 #endif
119
120
121 /* FC9: wireless.h 2.6.25-14.fc9.i686 is missing these, even though WIRELESS_EXT is set to latest
122 * version 22.
123 */
124 #ifndef IW_ENCODE_ALG_PMK
125 #define IW_ENCODE_ALG_PMK 4
126 #endif
127 #ifndef IW_ENC_CAPA_4WAY_HANDSHAKE
128 #define IW_ENC_CAPA_4WAY_HANDSHAKE 0x00000010
129 #endif
130 /* End FC9. */
131
132 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
133 #include <linux/rtnetlink.h>
134 #endif
135
136 extern bool wl_iw_conn_status_str(uint32 event_type, uint32 status,
137 uint32 reason, char* stringBuf, uint buflen);
138
139 uint wl_msg_level = WL_ERROR_VAL;
140
141 #define MAX_WLIW_IOCTL_LEN WLC_IOCTL_MEDLEN
142
143 /* IOCTL swapping mode for Big Endian host with Little Endian dongle. Default to off */
144 #define htod32(i) (i)
145 #define htod16(i) (i)
146 #define dtoh32(i) (i)
147 #define dtoh16(i) (i)
148 #define htodchanspec(i) (i)
149 #define dtohchanspec(i) (i)
150
151 extern struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev);
152 extern int dhd_wait_pend8021x(struct net_device *dev);
153
154 #if WIRELESS_EXT < 19
155 #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
156 #define IW_EVENT_IDX(cmd) ((cmd) - IWEVFIRST)
157 #endif /* WIRELESS_EXT < 19 */
158
159
160 #ifndef WL_ESCAN
161 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0))
162 #define DAEMONIZE(a) do { \
163 allow_signal(SIGKILL); \
164 allow_signal(SIGTERM); \
165 } while (0)
166 #elif ((LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)) && \
167 (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)))
168 #define DAEMONIZE(a) daemonize(a); \
169 allow_signal(SIGKILL); \
170 allow_signal(SIGTERM);
171 #else /* Linux 2.4 (w/o preemption patch) */
172 #define RAISE_RX_SOFTIRQ() \
173 cpu_raise_softirq(smp_processor_id(), NET_RX_SOFTIRQ)
174 #define DAEMONIZE(a) daemonize(); \
175 do { if (a) \
176 strncpy(current->comm, a, MIN(sizeof(current->comm), (strlen(a) + 1))); \
177 } while (0);
178 #endif /* LINUX_VERSION_CODE */
179
180 #define ISCAN_STATE_IDLE 0
181 #define ISCAN_STATE_SCANING 1
182
183 /* the buf lengh can be WLC_IOCTL_MAXLEN (8K) to reduce iteration */
184 #define WLC_IW_ISCAN_MAXLEN 2048
185 typedef struct iscan_buf {
186 struct iscan_buf * next;
187 char iscan_buf[WLC_IW_ISCAN_MAXLEN];
188 } iscan_buf_t;
189
190 struct pmk_list {
191 pmkid_list_t pmkids;
192 pmkid_t foo[MAXPMKID - 1];
193 };
194
195 typedef struct iscan_info {
196 struct net_device *dev;
197 struct timer_list timer;
198 uint32 timer_ms;
199 uint32 timer_on;
200 int iscan_state;
201 iscan_buf_t * list_hdr;
202 iscan_buf_t * list_cur;
203
204 /* Thread to work on iscan */
205 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
206 struct task_struct *kthread;
207 #endif
208 long sysioc_pid;
209 struct semaphore sysioc_sem;
210 struct completion sysioc_exited;
211 char ioctlbuf[WLC_IOCTL_SMLEN];
212 struct pmk_list pmk_list;
213 wl_conn_info_t conn_info;
214 } iscan_info_t;
215 static void wl_iw_timerfunc(ulong data);
216 static void wl_iw_set_event_mask(struct net_device *dev);
217 static int wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action);
218 #endif /* WL_ESCAN */
219
220 /* priv_link becomes netdev->priv and is the link between netdev and wlif struct */
221 typedef struct priv_link {
222 wl_iw_t *wliw;
223 } priv_link_t;
224
225 /* dev to priv_link */
226 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
227 #define WL_DEV_LINK(dev) (priv_link_t*)(dev->priv)
228 #else
229 #define WL_DEV_LINK(dev) (priv_link_t*)netdev_priv(dev)
230 #endif
231
232 /* dev to wl_iw_t */
233 #define IW_DEV_IF(dev) ((wl_iw_t*)(WL_DEV_LINK(dev))->wliw)
234
swap_key_from_BE(wl_wsec_key_t * key)235 static void swap_key_from_BE(
236 wl_wsec_key_t *key
237 )
238 {
239 key->index = htod32(key->index);
240 key->len = htod32(key->len);
241 key->algo = htod32(key->algo);
242 key->flags = htod32(key->flags);
243 key->rxiv.hi = htod32(key->rxiv.hi);
244 key->rxiv.lo = htod16(key->rxiv.lo);
245 key->iv_initialized = htod32(key->iv_initialized);
246 }
247
swap_key_to_BE(wl_wsec_key_t * key)248 static void swap_key_to_BE(
249 wl_wsec_key_t *key
250 )
251 {
252 key->index = dtoh32(key->index);
253 key->len = dtoh32(key->len);
254 key->algo = dtoh32(key->algo);
255 key->flags = dtoh32(key->flags);
256 key->rxiv.hi = dtoh32(key->rxiv.hi);
257 key->rxiv.lo = dtoh16(key->rxiv.lo);
258 key->iv_initialized = dtoh32(key->iv_initialized);
259 }
260
261 static int
dev_wlc_ioctl(struct net_device * dev,int cmd,void * arg,int len)262 dev_wlc_ioctl(
263 struct net_device *dev,
264 int cmd,
265 void *arg,
266 int len
267 )
268 {
269 struct ifreq ifr;
270 wl_ioctl_t ioc;
271 mm_segment_t fs;
272 int ret;
273
274 memset(&ioc, 0, sizeof(ioc));
275 #ifdef CONFIG_COMPAT
276 ioc.cmd = cmd | WLC_SPEC_FLAG;
277 #else
278 ioc.cmd = cmd;
279 #endif
280 ioc.buf = arg;
281 ioc.len = len;
282
283 strncpy(ifr.ifr_name, dev->name, sizeof(ifr.ifr_name));
284 ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
285 ifr.ifr_data = (caddr_t) &ioc;
286
287 fs = get_fs();
288 set_fs(KERNEL_DS);
289 #if defined(WL_USE_NETDEV_OPS)
290 ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
291 #else
292 ret = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
293 #endif
294 set_fs(fs);
295
296 return ret;
297 }
298
299 /*
300 set named driver variable to int value and return error indication
301 calling example: dev_wlc_intvar_set(dev, "arate", rate)
302 */
303
304 static int
dev_wlc_intvar_set(struct net_device * dev,char * name,int val)305 dev_wlc_intvar_set(
306 struct net_device *dev,
307 char *name,
308 int val)
309 {
310 char buf[WLC_IOCTL_SMLEN];
311 uint len;
312
313 val = htod32(val);
314 len = bcm_mkiovar(name, (char *)(&val), sizeof(val), buf, sizeof(buf));
315 ASSERT(len);
316
317 return (dev_wlc_ioctl(dev, WLC_SET_VAR, buf, len));
318 }
319
320 #ifndef WL_ESCAN
321 static int
dev_iw_iovar_setbuf(struct net_device * dev,char * iovar,void * param,int paramlen,void * bufptr,int buflen)322 dev_iw_iovar_setbuf(
323 struct net_device *dev,
324 char *iovar,
325 void *param,
326 int paramlen,
327 void *bufptr,
328 int buflen)
329 {
330 int iolen;
331
332 iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
333 ASSERT(iolen);
334 BCM_REFERENCE(iolen);
335
336 return (dev_wlc_ioctl(dev, WLC_SET_VAR, bufptr, iolen));
337 }
338
339 static int
dev_iw_iovar_getbuf(struct net_device * dev,char * iovar,void * param,int paramlen,void * bufptr,int buflen)340 dev_iw_iovar_getbuf(
341 struct net_device *dev,
342 char *iovar,
343 void *param,
344 int paramlen,
345 void *bufptr,
346 int buflen)
347 {
348 int iolen;
349
350 iolen = bcm_mkiovar(iovar, param, paramlen, bufptr, buflen);
351 ASSERT(iolen);
352 BCM_REFERENCE(iolen);
353
354 return (dev_wlc_ioctl(dev, WLC_GET_VAR, bufptr, buflen));
355 }
356 #endif
357
358 #if WIRELESS_EXT > 17
359 static int
dev_wlc_bufvar_set(struct net_device * dev,char * name,char * buf,int len)360 dev_wlc_bufvar_set(
361 struct net_device *dev,
362 char *name,
363 char *buf, int len)
364 {
365 char *ioctlbuf;
366 uint buflen;
367 int error;
368
369 ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
370 if (!ioctlbuf)
371 return -ENOMEM;
372
373 buflen = bcm_mkiovar(name, buf, len, ioctlbuf, MAX_WLIW_IOCTL_LEN);
374 ASSERT(buflen);
375 error = dev_wlc_ioctl(dev, WLC_SET_VAR, ioctlbuf, buflen);
376
377 kfree(ioctlbuf);
378 return error;
379 }
380 #endif /* WIRELESS_EXT > 17 */
381
382 /*
383 get named driver variable to int value and return error indication
384 calling example: dev_wlc_bufvar_get(dev, "arate", &rate)
385 */
386
387 static int
dev_wlc_bufvar_get(struct net_device * dev,char * name,char * buf,int buflen)388 dev_wlc_bufvar_get(
389 struct net_device *dev,
390 char *name,
391 char *buf, int buflen)
392 {
393 char *ioctlbuf;
394 int error;
395
396 uint len;
397
398 ioctlbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
399 if (!ioctlbuf)
400 return -ENOMEM;
401 len = bcm_mkiovar(name, NULL, 0, ioctlbuf, MAX_WLIW_IOCTL_LEN);
402 ASSERT(len);
403 BCM_REFERENCE(len);
404 error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)ioctlbuf, MAX_WLIW_IOCTL_LEN);
405 if (!error)
406 bcopy(ioctlbuf, buf, buflen);
407
408 kfree(ioctlbuf);
409 return (error);
410 }
411
412 /*
413 get named driver variable to int value and return error indication
414 calling example: dev_wlc_intvar_get(dev, "arate", &rate)
415 */
416
417 static int
dev_wlc_intvar_get(struct net_device * dev,char * name,int * retval)418 dev_wlc_intvar_get(
419 struct net_device *dev,
420 char *name,
421 int *retval)
422 {
423 union {
424 char buf[WLC_IOCTL_SMLEN];
425 int val;
426 } var;
427 int error;
428
429 uint len;
430 uint data_null;
431
432 len = bcm_mkiovar(name, (char *)(&data_null), 0, (char *)(&var), sizeof(var.buf));
433 ASSERT(len);
434 error = dev_wlc_ioctl(dev, WLC_GET_VAR, (void *)&var, len);
435
436 *retval = dtoh32(var.val);
437
438 return (error);
439 }
440
441 /* Maintain backward compatibility */
442 #if WIRELESS_EXT < 13
443 struct iw_request_info
444 {
445 __u16 cmd; /* Wireless Extension command */
446 __u16 flags; /* More to come ;-) */
447 };
448
449 typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info,
450 void *wrqu, char *extra);
451 #endif /* WIRELESS_EXT < 13 */
452
453 #if WIRELESS_EXT > 12
454 static int
wl_iw_set_leddc(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)455 wl_iw_set_leddc(
456 struct net_device *dev,
457 struct iw_request_info *info,
458 union iwreq_data *wrqu,
459 char *extra
460 )
461 {
462 int dc = *(int *)extra;
463 int error;
464
465 error = dev_wlc_intvar_set(dev, "leddc", dc);
466 return error;
467 }
468
469 static int
wl_iw_set_vlanmode(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)470 wl_iw_set_vlanmode(
471 struct net_device *dev,
472 struct iw_request_info *info,
473 union iwreq_data *wrqu,
474 char *extra
475 )
476 {
477 int mode = *(int *)extra;
478 int error;
479
480 mode = htod32(mode);
481 error = dev_wlc_intvar_set(dev, "vlan_mode", mode);
482 return error;
483 }
484
485 static int
wl_iw_set_pm(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)486 wl_iw_set_pm(
487 struct net_device *dev,
488 struct iw_request_info *info,
489 union iwreq_data *wrqu,
490 char *extra
491 )
492 {
493 int pm = *(int *)extra;
494 int error;
495
496 pm = htod32(pm);
497 error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm));
498 return error;
499 }
500 #endif /* WIRELESS_EXT > 12 */
501
502 static void
wl_iw_update_connect_status(struct net_device * dev,enum wl_ext_status status)503 wl_iw_update_connect_status(struct net_device *dev, enum wl_ext_status status)
504 {
505 #ifndef WL_CFG80211
506 struct dhd_pub *dhd = dhd_get_pub(dev);
507 int cur_eapol_status = 0;
508 int wpa_auth = 0;
509 int error = -EINVAL;
510 wl_conn_info_t *conn_info = NULL;
511 #ifdef WL_ESCAN
512 wl_escan_info_t *escan;
513 if (dhd && dhd->escan) {
514 escan = (wl_escan_info_t *)dhd->escan;
515 conn_info = &escan->conn_info;
516 }
517 #else
518 iscan_info_t *iscan;
519 if (dhd && dhd->iscan) {
520 iscan = (iscan_info_t *)dhd->iscan;
521 conn_info = &iscan->conn_info;
522 }
523 #endif
524
525 if (!dhd || !dhd->conf || !conn_info)
526 return;
527
528 cur_eapol_status = dhd->conf->eapol_status;
529
530 if (status == WL_EXT_STATUS_CONNECTING) {
531 wl_ext_add_remove_pm_enable_work(conn_info, TRUE);
532 error = dev_wlc_intvar_get(dev, "wpa_auth", &wpa_auth);
533 if (error) {
534 WL_ERROR(("%s: wpa_auth get error %d\n", __FUNCTION__, error));
535 return;
536 }
537 if (wpa_auth & (WPA_AUTH_PSK|WPA2_AUTH_PSK))
538 dhd->conf->eapol_status = EAPOL_STATUS_WPA_START;
539 else
540 dhd->conf->eapol_status = EAPOL_STATUS_NONE;
541 } else if (status == WL_EXT_STATUS_ADD_KEY) {
542 dhd->conf->eapol_status = EAPOL_STATUS_WPA_END;
543 } else if (status == WL_EXT_STATUS_DISCONNECTING) {
544 wl_ext_add_remove_pm_enable_work(conn_info, FALSE);
545 if (cur_eapol_status >= EAPOL_STATUS_WPA_START &&
546 cur_eapol_status < EAPOL_STATUS_WPA_END) {
547 WL_ERROR(("%s: WPA failed at %d\n", __FUNCTION__, cur_eapol_status));
548 dhd->conf->eapol_status = EAPOL_STATUS_NONE;
549 } else if (cur_eapol_status >= EAPOL_STATUS_WPS_WSC_START &&
550 cur_eapol_status < EAPOL_STATUS_WPS_DONE) {
551 WL_ERROR(("%s: WPS failed at %d\n", __FUNCTION__, cur_eapol_status));
552 dhd->conf->eapol_status = EAPOL_STATUS_NONE;
553 }
554 } else if (status == WL_EXT_STATUS_DISCONNECTED) {
555 if (cur_eapol_status >= EAPOL_STATUS_WPA_START &&
556 cur_eapol_status < EAPOL_STATUS_WPA_END) {
557 WL_ERROR(("%s: WPA failed at %d\n", __FUNCTION__, cur_eapol_status));
558 dhd->conf->eapol_status = EAPOL_STATUS_NONE;
559 } else if (cur_eapol_status >= EAPOL_STATUS_WPS_WSC_START &&
560 cur_eapol_status < EAPOL_STATUS_WPS_DONE) {
561 WL_ERROR(("%s: WPS failed at %d\n", __FUNCTION__, cur_eapol_status));
562 dhd->conf->eapol_status = EAPOL_STATUS_NONE;
563 }
564 }
565 #endif
566 return;
567 }
568
569 int
wl_iw_send_priv_event(struct net_device * dev,char * flag)570 wl_iw_send_priv_event(
571 struct net_device *dev,
572 char *flag
573 )
574 {
575 union iwreq_data wrqu;
576 char extra[IW_CUSTOM_MAX + 1];
577 int cmd;
578
579 cmd = IWEVCUSTOM;
580 memset(&wrqu, 0, sizeof(wrqu));
581 if (strlen(flag) > sizeof(extra))
582 return -1;
583
584 strncpy(extra, flag, sizeof(extra));
585 extra[sizeof(extra) - 1] = '\0';
586 wrqu.data.length = strlen(extra);
587 wireless_send_event(dev, cmd, &wrqu, extra);
588 WL_TRACE(("Send IWEVCUSTOM Event as %s\n", extra));
589
590 return 0;
591 }
592
593 static int
wl_iw_config_commit(struct net_device * dev,struct iw_request_info * info,void * zwrq,char * extra)594 wl_iw_config_commit(
595 struct net_device *dev,
596 struct iw_request_info *info,
597 void *zwrq,
598 char *extra
599 )
600 {
601 wlc_ssid_t ssid;
602 int error;
603 struct sockaddr bssid;
604
605 WL_TRACE(("%s: SIOCSIWCOMMIT\n", dev->name));
606
607 if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid))))
608 return error;
609
610 ssid.SSID_len = dtoh32(ssid.SSID_len);
611
612 if (!ssid.SSID_len)
613 return 0;
614
615 bzero(&bssid, sizeof(struct sockaddr));
616 if ((error = dev_wlc_ioctl(dev, WLC_REASSOC, &bssid, ETHER_ADDR_LEN))) {
617 WL_ERROR(("%s: WLC_REASSOC failed (%d)\n", __FUNCTION__, error));
618 return error;
619 }
620
621 return 0;
622 }
623
624 static int
wl_iw_get_name(struct net_device * dev,struct iw_request_info * info,union iwreq_data * cwrq,char * extra)625 wl_iw_get_name(
626 struct net_device *dev,
627 struct iw_request_info *info,
628 union iwreq_data *cwrq,
629 char *extra
630 )
631 {
632 int phytype, err;
633 uint band[3];
634 char cap[5];
635
636 WL_TRACE(("%s: SIOCGIWNAME\n", dev->name));
637
638 cap[0] = 0;
639 if ((err = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))) < 0)
640 goto done;
641 if ((err = dev_wlc_ioctl(dev, WLC_GET_BANDLIST, band, sizeof(band))) < 0)
642 goto done;
643
644 band[0] = dtoh32(band[0]);
645 switch (phytype) {
646 case WLC_PHY_TYPE_A:
647 strncpy(cap, "a", sizeof(cap));
648 break;
649 case WLC_PHY_TYPE_B:
650 strncpy(cap, "b", sizeof(cap));
651 break;
652 case WLC_PHY_TYPE_G:
653 if (band[0] >= 2)
654 strncpy(cap, "abg", sizeof(cap));
655 else
656 strncpy(cap, "bg", sizeof(cap));
657 break;
658 case WLC_PHY_TYPE_N:
659 if (band[0] >= 2)
660 strncpy(cap, "abgn", sizeof(cap));
661 else
662 strncpy(cap, "bgn", sizeof(cap));
663 break;
664 }
665 done:
666 (void)snprintf(cwrq->name, IFNAMSIZ, "IEEE 802.11%s", cap);
667
668 return 0;
669 }
670
671 static int
wl_iw_set_freq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * fwrq,char * extra)672 wl_iw_set_freq(
673 struct net_device *dev,
674 struct iw_request_info *info,
675 struct iw_freq *fwrq,
676 char *extra
677 )
678 {
679 int error, chan;
680 uint sf = 0;
681 struct dhd_pub *dhd = dhd_get_pub(dev);
682 wl_conn_info_t *conn_info = NULL;
683 #ifdef WL_ESCAN
684 wl_escan_info_t *escan;
685 if (dhd && dhd->escan) {
686 escan = (wl_escan_info_t *)dhd->escan;
687 conn_info = &escan->conn_info;
688 }
689 #else
690 iscan_info_t *iscan;
691 if (dhd && dhd->iscan) {
692 iscan = (iscan_info_t *)dhd->iscan;
693 conn_info = &iscan->conn_info;
694 }
695 #endif
696
697 WL_TRACE(("%s: SIOCSIWFREQ\n", dev->name));
698
699 /* Setting by channel number */
700 if (fwrq->e == 0 && fwrq->m < MAXCHANNEL) {
701 chan = fwrq->m;
702 }
703
704 /* Setting by frequency */
705 else {
706 /* Convert to MHz as best we can */
707 if (fwrq->e >= 6) {
708 fwrq->e -= 6;
709 while (fwrq->e--)
710 fwrq->m *= 10;
711 } else if (fwrq->e < 6) {
712 while (fwrq->e++ < 6)
713 fwrq->m /= 10;
714 }
715 /* handle 4.9GHz frequencies as Japan 4 GHz based channelization */
716 if (fwrq->m > 4000 && fwrq->m < 5000) {
717 sf = WF_CHAN_FACTOR_4_G; /* start factor for 4 GHz */
718 }
719 chan = wf_mhz2channel(fwrq->m, sf);
720 }
721 if (conn_info)
722 conn_info->channel = chan;
723 WL_ERROR(("%s: chan=%d\n", __FUNCTION__, chan));
724 chan = htod32(chan);
725 if ((error = dev_wlc_ioctl(dev, WLC_SET_CHANNEL, &chan, sizeof(chan)))) {
726 WL_ERROR(("%s: WLC_SET_CHANNEL failed (%d).\n", __FUNCTION__, error));
727 return error;
728 }
729
730 /* -EINPROGRESS: Call commit handler */
731 return -EINPROGRESS;
732 }
733
734 static int
wl_iw_get_freq(struct net_device * dev,struct iw_request_info * info,struct iw_freq * fwrq,char * extra)735 wl_iw_get_freq(
736 struct net_device *dev,
737 struct iw_request_info *info,
738 struct iw_freq *fwrq,
739 char *extra
740 )
741 {
742 int error;
743 u32 chanspec = 0;
744 int ctl_chan;
745
746 WL_TRACE(("%s: SIOCGIWFREQ\n", dev->name));
747
748 error = dev_wlc_intvar_get(dev, "chanspec", &chanspec);
749 if (error)
750 return error;
751 ctl_chan = wf_chspec_ctlchan(chanspec);
752
753 /* Return radio channel in channel form */
754 fwrq->m = ctl_chan;
755 fwrq->e = dtoh32(0);
756 return 0;
757 }
758
759 static int
wl_iw_set_mode(struct net_device * dev,struct iw_request_info * info,__u32 * uwrq,char * extra)760 wl_iw_set_mode(
761 struct net_device *dev,
762 struct iw_request_info *info,
763 __u32 *uwrq,
764 char *extra
765 )
766 {
767 int infra = 0, ap = 0, error = 0;
768 struct dhd_pub *dhd = dhd_get_pub(dev);
769 wl_conn_info_t *conn_info = NULL;
770 #ifdef WL_ESCAN
771 wl_escan_info_t *escan;
772 if (dhd && dhd->escan) {
773 escan = (wl_escan_info_t *)dhd->escan;
774 conn_info = &escan->conn_info;
775 }
776 #else
777 iscan_info_t *iscan;
778 if (dhd && dhd->iscan) {
779 iscan = (iscan_info_t *)dhd->iscan;
780 conn_info = &iscan->conn_info;
781 }
782 #endif
783
784 WL_TRACE(("%s: SIOCSIWMODE\n", dev->name));
785 if (conn_info) {
786 memset(&conn_info->ssid, 0, sizeof(wlc_ssid_t));
787 memset(&conn_info->bssid, 0, sizeof(struct ether_addr));
788 conn_info->channel = 0;
789 }
790
791 switch (*uwrq) {
792 case IW_MODE_MASTER:
793 infra = ap = 1;
794 break;
795 case IW_MODE_ADHOC:
796 case IW_MODE_AUTO:
797 break;
798 case IW_MODE_INFRA:
799 infra = 1;
800 break;
801 default:
802 return -EINVAL;
803 }
804 infra = htod32(infra);
805 ap = htod32(ap);
806
807 if ((error = dev_wlc_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra))) ||
808 (error = dev_wlc_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap))))
809 return error;
810
811 /* -EINPROGRESS: Call commit handler */
812 return -EINPROGRESS;
813 }
814
815 static int
wl_iw_get_mode(struct net_device * dev,struct iw_request_info * info,__u32 * uwrq,char * extra)816 wl_iw_get_mode(
817 struct net_device *dev,
818 struct iw_request_info *info,
819 __u32 *uwrq,
820 char *extra
821 )
822 {
823 int error, infra = 0, ap = 0;
824
825 WL_TRACE(("%s: SIOCGIWMODE\n", dev->name));
826
827 if ((error = dev_wlc_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra))) ||
828 (error = dev_wlc_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap))))
829 return error;
830
831 infra = dtoh32(infra);
832 ap = dtoh32(ap);
833 *uwrq = infra ? ap ? IW_MODE_MASTER : IW_MODE_INFRA : IW_MODE_ADHOC;
834
835 return 0;
836 }
837
838 static int
wl_iw_get_range(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)839 wl_iw_get_range(
840 struct net_device *dev,
841 struct iw_request_info *info,
842 struct iw_point *dwrq,
843 char *extra
844 )
845 {
846 struct iw_range *range = (struct iw_range *) extra;
847 static int channels[MAXCHANNEL+1];
848 wl_uint32_list_t *list = (wl_uint32_list_t *) channels;
849 wl_rateset_t rateset;
850 int error, i, k;
851 uint sf, ch;
852
853 int phytype;
854 int bw_cap = 0, sgi_tx = 0, nmode = 0;
855 channel_info_t ci;
856 uint8 nrate_list2copy = 0;
857 uint16 nrate_list[4][8] = { {13, 26, 39, 52, 78, 104, 117, 130},
858 {14, 29, 43, 58, 87, 116, 130, 144},
859 {27, 54, 81, 108, 162, 216, 243, 270},
860 {30, 60, 90, 120, 180, 240, 270, 300}};
861 int fbt_cap = 0;
862
863 WL_TRACE(("%s: SIOCGIWRANGE\n", dev->name));
864
865 if (!extra)
866 return -EINVAL;
867
868 dwrq->length = sizeof(struct iw_range);
869 memset(range, 0, sizeof(*range));
870
871 /* We don't use nwids */
872 range->min_nwid = range->max_nwid = 0;
873
874 /* Set available channels/frequencies */
875 list->count = htod32(MAXCHANNEL);
876 if ((error = dev_wlc_ioctl(dev, WLC_GET_VALID_CHANNELS, channels, sizeof(channels))))
877 return error;
878 for (i = 0; i < dtoh32(list->count) && i < IW_MAX_FREQUENCIES; i++) {
879 range->freq[i].i = dtoh32(list->element[i]);
880
881 ch = dtoh32(list->element[i]);
882 if (ch <= CH_MAX_2G_CHANNEL)
883 sf = WF_CHAN_FACTOR_2_4_G;
884 else
885 sf = WF_CHAN_FACTOR_5_G;
886
887 range->freq[i].m = wf_channel2mhz(ch, sf);
888 range->freq[i].e = 6;
889 }
890 range->num_frequency = range->num_channels = i;
891
892 /* Link quality (use NDIS cutoffs) */
893 range->max_qual.qual = 5;
894 /* Signal level (use RSSI) */
895 range->max_qual.level = 0x100 - 200; /* -200 dBm */
896 /* Noise level (use noise) */
897 range->max_qual.noise = 0x100 - 200; /* -200 dBm */
898 /* Signal level threshold range (?) */
899 range->sensitivity = 65535;
900
901 #if WIRELESS_EXT > 11
902 /* Link quality (use NDIS cutoffs) */
903 range->avg_qual.qual = 3;
904 /* Signal level (use RSSI) */
905 range->avg_qual.level = 0x100 + WL_IW_RSSI_GOOD;
906 /* Noise level (use noise) */
907 range->avg_qual.noise = 0x100 - 75; /* -75 dBm */
908 #endif /* WIRELESS_EXT > 11 */
909
910 /* Set available bitrates */
911 if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
912 return error;
913 rateset.count = dtoh32(rateset.count);
914 range->num_bitrates = rateset.count;
915 for (i = 0; i < rateset.count && i < IW_MAX_BITRATES; i++)
916 range->bitrate[i] = (rateset.rates[i] & 0x7f) * 500000; /* convert to bps */
917 if ((error = dev_wlc_intvar_get(dev, "nmode", &nmode)))
918 return error;
919 if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &phytype, sizeof(phytype))))
920 return error;
921 if (nmode == 1 && (((phytype == WLC_PHY_TYPE_LCN) ||
922 (phytype == WLC_PHY_TYPE_LCN40)))) {
923 if ((error = dev_wlc_intvar_get(dev, "mimo_bw_cap", &bw_cap)))
924 return error;
925 if ((error = dev_wlc_intvar_get(dev, "sgi_tx", &sgi_tx)))
926 return error;
927 if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(channel_info_t))))
928 return error;
929 ci.hw_channel = dtoh32(ci.hw_channel);
930
931 if (bw_cap == 0 ||
932 (bw_cap == 2 && ci.hw_channel <= 14)) {
933 if (sgi_tx == 0)
934 nrate_list2copy = 0;
935 else
936 nrate_list2copy = 1;
937 }
938 if (bw_cap == 1 ||
939 (bw_cap == 2 && ci.hw_channel >= 36)) {
940 if (sgi_tx == 0)
941 nrate_list2copy = 2;
942 else
943 nrate_list2copy = 3;
944 }
945 range->num_bitrates += 8;
946 ASSERT(range->num_bitrates < IW_MAX_BITRATES);
947 for (k = 0; i < range->num_bitrates; k++, i++) {
948 /* convert to bps */
949 range->bitrate[i] = (nrate_list[nrate_list2copy][k]) * 500000;
950 }
951 }
952
953 /* Set an indication of the max TCP throughput
954 * in bit/s that we can expect using this interface.
955 * May be use for QoS stuff... Jean II
956 */
957 if ((error = dev_wlc_ioctl(dev, WLC_GET_PHYTYPE, &i, sizeof(i))))
958 return error;
959 i = dtoh32(i);
960 if (i == WLC_PHY_TYPE_A)
961 range->throughput = 24000000; /* 24 Mbits/s */
962 else
963 range->throughput = 1500000; /* 1.5 Mbits/s */
964
965 /* RTS and fragmentation thresholds */
966 range->min_rts = 0;
967 range->max_rts = 2347;
968 range->min_frag = 256;
969 range->max_frag = 2346;
970
971 range->max_encoding_tokens = DOT11_MAX_DEFAULT_KEYS;
972 range->num_encoding_sizes = 4;
973 range->encoding_size[0] = WEP1_KEY_SIZE;
974 range->encoding_size[1] = WEP128_KEY_SIZE;
975 #if WIRELESS_EXT > 17
976 range->encoding_size[2] = TKIP_KEY_SIZE;
977 #else
978 range->encoding_size[2] = 0;
979 #endif
980 range->encoding_size[3] = AES_KEY_SIZE;
981
982 /* Do not support power micro-management */
983 range->min_pmp = 0;
984 range->max_pmp = 0;
985 range->min_pmt = 0;
986 range->max_pmt = 0;
987 range->pmp_flags = 0;
988 range->pm_capa = 0;
989
990 /* Transmit Power - values are in mW */
991 range->num_txpower = 2;
992 range->txpower[0] = 1;
993 range->txpower[1] = 255;
994 range->txpower_capa = IW_TXPOW_MWATT;
995
996 #if WIRELESS_EXT > 10
997 range->we_version_compiled = WIRELESS_EXT;
998 range->we_version_source = 19;
999
1000 /* Only support retry limits */
1001 range->retry_capa = IW_RETRY_LIMIT;
1002 range->retry_flags = IW_RETRY_LIMIT;
1003 range->r_time_flags = 0;
1004 /* SRL and LRL limits */
1005 range->min_retry = 1;
1006 range->max_retry = 255;
1007 /* Retry lifetime limits unsupported */
1008 range->min_r_time = 0;
1009 range->max_r_time = 0;
1010 #endif /* WIRELESS_EXT > 10 */
1011
1012 #if WIRELESS_EXT > 17
1013 range->enc_capa = IW_ENC_CAPA_WPA;
1014 range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP;
1015 range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP;
1016 range->enc_capa |= IW_ENC_CAPA_WPA2;
1017
1018 /* Determine driver FBT capability. */
1019 if (dev_wlc_intvar_get(dev, "fbt_cap", &fbt_cap) == 0) {
1020 if (fbt_cap == WLC_FBT_CAP_DRV_4WAY_AND_REASSOC) {
1021 /* Tell the host (e.g. wpa_supplicant) to let driver do the handshake */
1022 range->enc_capa |= IW_ENC_CAPA_4WAY_HANDSHAKE;
1023 }
1024 }
1025
1026 #ifdef BCMFW_ROAM_ENABLE_WEXT
1027 /* Advertise firmware roam capability to the external supplicant */
1028 range->enc_capa |= IW_ENC_CAPA_FW_ROAM_ENABLE;
1029 #endif /* BCMFW_ROAM_ENABLE_WEXT */
1030
1031 /* Event capability (kernel) */
1032 IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
1033 /* Event capability (driver) */
1034 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWAP);
1035 IW_EVENT_CAPA_SET(range->event_capa, SIOCGIWSCAN);
1036 IW_EVENT_CAPA_SET(range->event_capa, IWEVTXDROP);
1037 IW_EVENT_CAPA_SET(range->event_capa, IWEVMICHAELMICFAILURE);
1038 IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCREQIE);
1039 IW_EVENT_CAPA_SET(range->event_capa, IWEVASSOCRESPIE);
1040 IW_EVENT_CAPA_SET(range->event_capa, IWEVPMKIDCAND);
1041
1042 #if WIRELESS_EXT >= 22 && defined(IW_SCAN_CAPA_ESSID)
1043 /* FC7 wireless.h defines EXT 22 but doesn't define scan_capa bits */
1044 range->scan_capa = IW_SCAN_CAPA_ESSID;
1045 #endif
1046 #endif /* WIRELESS_EXT > 17 */
1047
1048 return 0;
1049 }
1050
1051 #ifndef WL_ESCAN
1052 static int
rssi_to_qual(int rssi)1053 rssi_to_qual(int rssi)
1054 {
1055 if (rssi <= WL_IW_RSSI_NO_SIGNAL)
1056 return 0;
1057 else if (rssi <= WL_IW_RSSI_VERY_LOW)
1058 return 1;
1059 else if (rssi <= WL_IW_RSSI_LOW)
1060 return 2;
1061 else if (rssi <= WL_IW_RSSI_GOOD)
1062 return 3;
1063 else if (rssi <= WL_IW_RSSI_VERY_GOOD)
1064 return 4;
1065 else
1066 return 5;
1067 }
1068 #endif /* WL_ESCAN */
1069
1070 static int
wl_iw_set_spy(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1071 wl_iw_set_spy(
1072 struct net_device *dev,
1073 struct iw_request_info *info,
1074 struct iw_point *dwrq,
1075 char *extra
1076 )
1077 {
1078 wl_iw_t *iw = IW_DEV_IF(dev);
1079 struct sockaddr *addr = (struct sockaddr *) extra;
1080 int i;
1081
1082 WL_TRACE(("%s: SIOCSIWSPY\n", dev->name));
1083
1084 if (!extra)
1085 return -EINVAL;
1086
1087 iw->spy_num = MIN(ARRAYSIZE(iw->spy_addr), dwrq->length);
1088 for (i = 0; i < iw->spy_num; i++)
1089 memcpy(&iw->spy_addr[i], addr[i].sa_data, ETHER_ADDR_LEN);
1090 memset(iw->spy_qual, 0, sizeof(iw->spy_qual));
1091
1092 return 0;
1093 }
1094
1095 static int
wl_iw_get_spy(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1096 wl_iw_get_spy(
1097 struct net_device *dev,
1098 struct iw_request_info *info,
1099 struct iw_point *dwrq,
1100 char *extra
1101 )
1102 {
1103 wl_iw_t *iw = IW_DEV_IF(dev);
1104 struct sockaddr *addr = (struct sockaddr *) extra;
1105 struct iw_quality *qual = (struct iw_quality *) &addr[iw->spy_num];
1106 int i;
1107
1108 WL_TRACE(("%s: SIOCGIWSPY\n", dev->name));
1109
1110 if (!extra)
1111 return -EINVAL;
1112
1113 dwrq->length = iw->spy_num;
1114 for (i = 0; i < iw->spy_num; i++) {
1115 memcpy(addr[i].sa_data, &iw->spy_addr[i], ETHER_ADDR_LEN);
1116 addr[i].sa_family = AF_UNIX;
1117 memcpy(&qual[i], &iw->spy_qual[i], sizeof(struct iw_quality));
1118 iw->spy_qual[i].updated = 0;
1119 }
1120
1121 return 0;
1122 }
1123
1124 static int
wl_iw_set_wap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)1125 wl_iw_set_wap(
1126 struct net_device *dev,
1127 struct iw_request_info *info,
1128 struct sockaddr *awrq,
1129 char *extra
1130 )
1131 {
1132 int error = -EINVAL;
1133 struct dhd_pub *dhd = dhd_get_pub(dev);
1134 wl_conn_info_t *conn_info = NULL;
1135 #ifdef WL_ESCAN
1136 wl_escan_info_t *escan;
1137 if (dhd && dhd->escan) {
1138 escan = (wl_escan_info_t *)dhd->escan;
1139 conn_info = &escan->conn_info;
1140 }
1141 #else
1142 iscan_info_t *iscan;
1143 if (dhd && dhd->iscan) {
1144 iscan = (iscan_info_t *)dhd->iscan;
1145 conn_info = &iscan->conn_info;
1146 }
1147 #endif
1148
1149 WL_TRACE(("%s: SIOCSIWAP\n", dev->name));
1150
1151 if (awrq->sa_family != ARPHRD_ETHER) {
1152 WL_ERROR(("%s: Invalid Header...sa_family\n", __FUNCTION__));
1153 return -EINVAL;
1154 }
1155
1156 /* Ignore "auto" or "off" */
1157 if (ETHER_ISBCAST(awrq->sa_data) || ETHER_ISNULLADDR(awrq->sa_data)) {
1158 scb_val_t scbval;
1159 bzero(&scbval, sizeof(scb_val_t));
1160 WL_ERROR(("%s: WLC_DISASSOC\n", __FUNCTION__));
1161 if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) {
1162 WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error));
1163 }
1164 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTING);
1165 return 0;
1166 }
1167
1168 /* Reassociate to the specified AP */
1169 if (conn_info)
1170 memcpy(&conn_info->bssid, awrq->sa_data, ETHER_ADDR_LEN);
1171 if (conn_info && conn_info->ssid.SSID_len) {
1172 error = wl_ext_connect(dev, conn_info);
1173 if (error)
1174 return error;
1175 } else {
1176 error = dev_wlc_ioctl(dev, WLC_REASSOC, awrq->sa_data, ETHER_ADDR_LEN);
1177 if (error) {
1178 WL_ERROR(("%s: WLC_REASSOC failed (%d).\n", __FUNCTION__, error));
1179 return error;
1180 }
1181 WL_ERROR(("%s: join BSSID="MACSTR"\n", __FUNCTION__, MAC2STR((u8 *)awrq->sa_data)));
1182 }
1183 wl_iw_update_connect_status(dev, WL_EXT_STATUS_CONNECTING);
1184
1185 return 0;
1186 }
1187
1188 static int
wl_iw_get_wap(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)1189 wl_iw_get_wap(
1190 struct net_device *dev,
1191 struct iw_request_info *info,
1192 struct sockaddr *awrq,
1193 char *extra
1194 )
1195 {
1196 WL_TRACE(("%s: SIOCGIWAP\n", dev->name));
1197
1198 awrq->sa_family = ARPHRD_ETHER;
1199 memset(awrq->sa_data, 0, ETHER_ADDR_LEN);
1200
1201 /* Ignore error (may be down or disassociated) */
1202 (void) dev_wlc_ioctl(dev, WLC_GET_BSSID, awrq->sa_data, ETHER_ADDR_LEN);
1203
1204 return 0;
1205 }
1206
1207 #if WIRELESS_EXT > 17
1208 static int
wl_iw_mlme(struct net_device * dev,struct iw_request_info * info,struct sockaddr * awrq,char * extra)1209 wl_iw_mlme(
1210 struct net_device *dev,
1211 struct iw_request_info *info,
1212 struct sockaddr *awrq,
1213 char *extra
1214 )
1215 {
1216 struct iw_mlme *mlme;
1217 scb_val_t scbval;
1218 int error = -EINVAL;
1219
1220 WL_TRACE(("%s: SIOCSIWMLME\n", dev->name));
1221
1222 mlme = (struct iw_mlme *)extra;
1223 if (mlme == NULL) {
1224 WL_ERROR(("Invalid ioctl data.\n"));
1225 return error;
1226 }
1227
1228 scbval.val = mlme->reason_code;
1229 bcopy(&mlme->addr.sa_data, &scbval.ea, ETHER_ADDR_LEN);
1230
1231 if (mlme->cmd == IW_MLME_DISASSOC) {
1232 scbval.val = htod32(scbval.val);
1233 error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
1234 }
1235 else if (mlme->cmd == IW_MLME_DEAUTH) {
1236 scbval.val = htod32(scbval.val);
1237 error = dev_wlc_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scbval,
1238 sizeof(scb_val_t));
1239 }
1240 else {
1241 WL_ERROR(("%s: Invalid ioctl data.\n", __FUNCTION__));
1242 return error;
1243 }
1244 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTING);
1245
1246 return error;
1247 }
1248 #endif /* WIRELESS_EXT > 17 */
1249
1250 #ifndef WL_ESCAN
1251 static int
wl_iw_get_aplist(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1252 wl_iw_get_aplist(
1253 struct net_device *dev,
1254 struct iw_request_info *info,
1255 struct iw_point *dwrq,
1256 char *extra
1257 )
1258 {
1259 wl_scan_results_t *list;
1260 struct sockaddr *addr = (struct sockaddr *) extra;
1261 struct iw_quality qual[IW_MAX_AP];
1262 wl_bss_info_t *bi = NULL;
1263 int error, i;
1264 uint buflen = dwrq->length;
1265 int16 rssi;
1266
1267 WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
1268
1269 if (!extra)
1270 return -EINVAL;
1271
1272 /* Get scan results (too large to put on the stack) */
1273 list = kmalloc(buflen, GFP_KERNEL);
1274 if (!list)
1275 return -ENOMEM;
1276 memset(list, 0, buflen);
1277 list->buflen = htod32(buflen);
1278 if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
1279 WL_ERROR(("%d: Scan results error %d\n", __LINE__, error));
1280 kfree(list);
1281 return error;
1282 }
1283 list->buflen = dtoh32(list->buflen);
1284 list->version = dtoh32(list->version);
1285 list->count = dtoh32(list->count);
1286 ASSERT(list->version == WL_BSS_INFO_VERSION);
1287
1288 for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
1289 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1290 ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1291 buflen));
1292
1293 /* Infrastructure only */
1294 if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
1295 continue;
1296
1297 /* BSSID */
1298 memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1299 addr[dwrq->length].sa_family = ARPHRD_ETHER;
1300 // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
1301 rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
1302 qual[dwrq->length].qual = rssi_to_qual(rssi);
1303 qual[dwrq->length].level = 0x100 + rssi;
1304 qual[dwrq->length].noise = 0x100 + bi->phy_noise;
1305
1306 /* Updated qual, level, and noise */
1307 #if WIRELESS_EXT > 18
1308 qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1309 #else
1310 qual[dwrq->length].updated = 7;
1311 #endif /* WIRELESS_EXT > 18 */
1312
1313 dwrq->length++;
1314 }
1315
1316 kfree(list);
1317
1318 if (dwrq->length) {
1319 memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
1320 /* Provided qual */
1321 dwrq->flags = 1;
1322 }
1323
1324 return 0;
1325 }
1326
1327 static int
wl_iw_iscan_get_aplist(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1328 wl_iw_iscan_get_aplist(
1329 struct net_device *dev,
1330 struct iw_request_info *info,
1331 struct iw_point *dwrq,
1332 char *extra
1333 )
1334 {
1335 wl_scan_results_t *list;
1336 iscan_buf_t * buf;
1337 iscan_info_t *iscan;
1338
1339 struct sockaddr *addr = (struct sockaddr *) extra;
1340 struct iw_quality qual[IW_MAX_AP];
1341 wl_bss_info_t *bi = NULL;
1342 int i;
1343 int16 rssi;
1344 struct dhd_pub *dhd = dhd_get_pub(dev);
1345 if (dhd && dhd->iscan)
1346 iscan = (iscan_info_t *)dhd->iscan;
1347
1348 WL_TRACE(("%s: SIOCGIWAPLIST\n", dev->name));
1349
1350 if (!extra)
1351 return -EINVAL;
1352
1353 if ((!iscan) || (iscan->sysioc_pid < 0)) {
1354 return wl_iw_get_aplist(dev, info, dwrq, extra);
1355 }
1356
1357 buf = iscan->list_hdr;
1358 /* Get scan results (too large to put on the stack) */
1359 while (buf) {
1360 list = &((wl_iscan_results_t*)buf->iscan_buf)->results;
1361 ASSERT(list->version == WL_BSS_INFO_VERSION);
1362
1363 bi = NULL;
1364 for (i = 0, dwrq->length = 0; i < list->count && dwrq->length < IW_MAX_AP; i++) {
1365 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1366 ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1367 WLC_IW_ISCAN_MAXLEN));
1368
1369 /* Infrastructure only */
1370 if (!(dtoh16(bi->capability) & DOT11_CAP_ESS))
1371 continue;
1372
1373 /* BSSID */
1374 memcpy(addr[dwrq->length].sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1375 addr[dwrq->length].sa_family = ARPHRD_ETHER;
1376 // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
1377 rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
1378 qual[dwrq->length].qual = rssi_to_qual(rssi);
1379 qual[dwrq->length].level = 0x100 + rssi;
1380 qual[dwrq->length].noise = 0x100 + bi->phy_noise;
1381
1382 /* Updated qual, level, and noise */
1383 #if WIRELESS_EXT > 18
1384 qual[dwrq->length].updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
1385 #else
1386 qual[dwrq->length].updated = 7;
1387 #endif /* WIRELESS_EXT > 18 */
1388
1389 dwrq->length++;
1390 }
1391 buf = buf->next;
1392 }
1393 if (dwrq->length) {
1394 memcpy(&addr[dwrq->length], qual, sizeof(struct iw_quality) * dwrq->length);
1395 /* Provided qual */
1396 dwrq->flags = 1;
1397 }
1398
1399 return 0;
1400 }
1401 #endif
1402
1403 #if WIRELESS_EXT > 13
1404 #ifndef WL_ESCAN
1405 static int
wl_iw_set_scan(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1406 wl_iw_set_scan(
1407 struct net_device *dev,
1408 struct iw_request_info *info,
1409 union iwreq_data *wrqu,
1410 char *extra
1411 )
1412 {
1413 wlc_ssid_t ssid;
1414
1415 WL_TRACE(("%s: SIOCSIWSCAN\n", dev->name));
1416
1417 /* default Broadcast scan */
1418 memset(&ssid, 0, sizeof(ssid));
1419
1420 #if WIRELESS_EXT > 17
1421 /* check for given essid */
1422 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1423 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1424 struct iw_scan_req *req = (struct iw_scan_req *)extra;
1425 ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
1426 memcpy(ssid.SSID, req->essid, ssid.SSID_len);
1427 ssid.SSID_len = htod32(ssid.SSID_len);
1428 }
1429 }
1430 #endif
1431 /* Ignore error (most likely scan in progress) */
1432 (void) dev_wlc_ioctl(dev, WLC_SCAN, &ssid, sizeof(ssid));
1433
1434 return 0;
1435 }
1436
1437 static int
wl_iw_iscan_set_scan(struct net_device * dev,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)1438 wl_iw_iscan_set_scan(
1439 struct net_device *dev,
1440 struct iw_request_info *info,
1441 union iwreq_data *wrqu,
1442 char *extra
1443 )
1444 {
1445 wlc_ssid_t ssid;
1446 struct dhd_pub *dhd = dhd_get_pub(dev);
1447 iscan_info_t *iscan;
1448 if (dhd && dhd->iscan)
1449 iscan = (iscan_info_t *)dhd->iscan;
1450
1451 WL_TRACE(("%s: SIOCSIWSCAN iscan=%p\n", dev->name, iscan));
1452
1453 /* use backup if our thread is not successful */
1454 if ((!iscan) || (iscan->sysioc_pid < 0)) {
1455 return wl_iw_set_scan(dev, info, wrqu, extra);
1456 }
1457 if (iscan->iscan_state == ISCAN_STATE_SCANING) {
1458 return 0;
1459 }
1460
1461 /* default Broadcast scan */
1462 memset(&ssid, 0, sizeof(ssid));
1463
1464 #if WIRELESS_EXT > 17
1465 /* check for given essid */
1466 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
1467 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
1468 struct iw_scan_req *req = (struct iw_scan_req *)extra;
1469 ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
1470 memcpy(ssid.SSID, req->essid, ssid.SSID_len);
1471 ssid.SSID_len = htod32(ssid.SSID_len);
1472 }
1473 }
1474 #endif
1475
1476 iscan->list_cur = iscan->list_hdr;
1477 iscan->iscan_state = ISCAN_STATE_SCANING;
1478
1479
1480 wl_iw_set_event_mask(dev);
1481 wl_iw_iscan(iscan, &ssid, WL_SCAN_ACTION_START);
1482
1483 iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
1484 add_timer(&iscan->timer);
1485 iscan->timer_on = 1;
1486
1487 return 0;
1488 }
1489 #endif /* WL_ESCAN */
1490
1491 #if WIRELESS_EXT > 17
1492 static bool
ie_is_wpa_ie(uint8 ** wpaie,uint8 ** tlvs,int * tlvs_len)1493 ie_is_wpa_ie(uint8 **wpaie, uint8 **tlvs, int *tlvs_len)
1494 {
1495 /* Is this body of this tlvs entry a WPA entry? If */
1496 /* not update the tlvs buffer pointer/length */
1497 uint8 *ie = *wpaie;
1498
1499 /* If the contents match the WPA_OUI and type=1 */
1500 if ((ie[1] >= 6) &&
1501 !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x01"), 4)) {
1502 return TRUE;
1503 }
1504
1505 /* point to the next ie */
1506 ie += ie[1] + 2;
1507 /* calculate the length of the rest of the buffer */
1508 *tlvs_len -= (int)(ie - *tlvs);
1509 /* update the pointer to the start of the buffer */
1510 *tlvs = ie;
1511 return FALSE;
1512 }
1513
1514 static bool
ie_is_wps_ie(uint8 ** wpsie,uint8 ** tlvs,int * tlvs_len)1515 ie_is_wps_ie(uint8 **wpsie, uint8 **tlvs, int *tlvs_len)
1516 {
1517 /* Is this body of this tlvs entry a WPS entry? If */
1518 /* not update the tlvs buffer pointer/length */
1519 uint8 *ie = *wpsie;
1520
1521 /* If the contents match the WPA_OUI and type=4 */
1522 if ((ie[1] >= 4) &&
1523 !bcmp((const void *)&ie[2], (const void *)(WPA_OUI "\x04"), 4)) {
1524 return TRUE;
1525 }
1526
1527 /* point to the next ie */
1528 ie += ie[1] + 2;
1529 /* calculate the length of the rest of the buffer */
1530 *tlvs_len -= (int)(ie - *tlvs);
1531 /* update the pointer to the start of the buffer */
1532 *tlvs = ie;
1533 return FALSE;
1534 }
1535 #endif /* WIRELESS_EXT > 17 */
1536
1537 #ifdef BCMWAPI_WPI
_wpa_snprintf_hex(char * buf,size_t buf_size,const u8 * data,size_t len,int uppercase)1538 static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
1539 size_t len, int uppercase)
1540 {
1541 size_t i;
1542 char *pos = buf, *end = buf + buf_size;
1543 int ret;
1544 if (buf_size == 0)
1545 return 0;
1546 for (i = 0; i < len; i++) {
1547 ret = snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
1548 data[i]);
1549 if (ret < 0 || ret >= end - pos) {
1550 end[-1] = '\0';
1551 return pos - buf;
1552 }
1553 pos += ret;
1554 }
1555 end[-1] = '\0';
1556 return pos - buf;
1557 }
1558
1559 /**
1560 * wpa_snprintf_hex - Print data as a hex string into a buffer
1561 * @buf: Memory area to use as the output buffer
1562 * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
1563 * @data: Data to be printed
1564 * @len: Length of data in bytes
1565 * Returns: Number of bytes written
1566 */
1567 static int
wpa_snprintf_hex(char * buf,size_t buf_size,const u8 * data,size_t len)1568 wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
1569 {
1570 return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
1571 }
1572 #endif /* BCMWAPI_WPI */
1573
1574 #ifndef WL_ESCAN
1575 static
1576 #endif
1577 int
wl_iw_handle_scanresults_ies(char ** event_p,char * end,struct iw_request_info * info,wl_bss_info_t * bi)1578 wl_iw_handle_scanresults_ies(char **event_p, char *end,
1579 struct iw_request_info *info, wl_bss_info_t *bi)
1580 {
1581 #if WIRELESS_EXT > 17
1582 struct iw_event iwe;
1583 char *event;
1584 #ifdef BCMWAPI_WPI
1585 char *buf;
1586 int custom_event_len;
1587 #endif
1588
1589 event = *event_p;
1590 if (bi->ie_length) {
1591 /* look for wpa/rsn ies in the ie list... */
1592 bcm_tlv_t *ie;
1593 uint8 *ptr = ((uint8 *)bi) + bi->ie_offset;
1594 int ptr_len = bi->ie_length;
1595
1596 /* OSEN IE */
1597 if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_VS_ID)) &&
1598 ie->len > WFA_OUI_LEN + 1 &&
1599 !bcmp((const void *)&ie->data[0], (const void *)WFA_OUI, WFA_OUI_LEN) &&
1600 ie->data[WFA_OUI_LEN] == WFA_OUI_TYPE_OSEN) {
1601 iwe.cmd = IWEVGENIE;
1602 iwe.u.data.length = ie->len + 2;
1603 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1604 }
1605 ptr = ((uint8 *)bi) + bi->ie_offset;
1606
1607 if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_RSN_ID))) {
1608 iwe.cmd = IWEVGENIE;
1609 iwe.u.data.length = ie->len + 2;
1610 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1611 }
1612 ptr = ((uint8 *)bi) + bi->ie_offset;
1613
1614 if ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_MDIE_ID))) {
1615 iwe.cmd = IWEVGENIE;
1616 iwe.u.data.length = ie->len + 2;
1617 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1618 }
1619 ptr = ((uint8 *)bi) + bi->ie_offset;
1620
1621 while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1622 /* look for WPS IE */
1623 if (ie_is_wps_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
1624 iwe.cmd = IWEVGENIE;
1625 iwe.u.data.length = ie->len + 2;
1626 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1627 break;
1628 }
1629 }
1630
1631 ptr = ((uint8 *)bi) + bi->ie_offset;
1632 ptr_len = bi->ie_length;
1633 while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WPA_ID))) {
1634 if (ie_is_wpa_ie(((uint8 **)&ie), &ptr, &ptr_len)) {
1635 iwe.cmd = IWEVGENIE;
1636 iwe.u.data.length = ie->len + 2;
1637 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1638 break;
1639 }
1640 }
1641
1642 #ifdef BCMWAPI_WPI
1643 ptr = ((uint8 *)bi) + sizeof(wl_bss_info_t);
1644 ptr_len = bi->ie_length;
1645
1646 while ((ie = bcm_parse_tlvs(ptr, ptr_len, DOT11_MNG_WAPI_ID))) {
1647 WL_TRACE(("%s: found a WAPI IE...\n", __FUNCTION__));
1648 #ifdef WAPI_IE_USE_GENIE
1649 iwe.cmd = IWEVGENIE;
1650 iwe.u.data.length = ie->len + 2;
1651 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)ie);
1652 #else /* using CUSTOM event */
1653 iwe.cmd = IWEVCUSTOM;
1654 custom_event_len = strlen("wapi_ie=") + 2*(ie->len + 2);
1655 iwe.u.data.length = custom_event_len;
1656
1657 buf = kmalloc(custom_event_len+1, GFP_KERNEL);
1658 if (buf == NULL) {
1659 WL_ERROR(("malloc(%d) returned NULL...\n", custom_event_len));
1660 break;
1661 }
1662
1663 memcpy(buf, "wapi_ie=", 8);
1664 wpa_snprintf_hex(buf + 8, 2+1, &(ie->id), 1);
1665 wpa_snprintf_hex(buf + 10, 2+1, &(ie->len), 1);
1666 wpa_snprintf_hex(buf + 12, 2*ie->len+1, ie->data, ie->len);
1667 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, buf);
1668 kfree(buf);
1669 #endif /* WAPI_IE_USE_GENIE */
1670 break;
1671 }
1672 #endif /* BCMWAPI_WPI */
1673 *event_p = event;
1674 }
1675
1676 #endif /* WIRELESS_EXT > 17 */
1677 return 0;
1678 }
1679
1680 #ifndef WL_ESCAN
1681 static int
wl_iw_get_scan(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1682 wl_iw_get_scan(
1683 struct net_device *dev,
1684 struct iw_request_info *info,
1685 struct iw_point *dwrq,
1686 char *extra
1687 )
1688 {
1689 channel_info_t ci;
1690 wl_scan_results_t *list;
1691 struct iw_event iwe;
1692 wl_bss_info_t *bi = NULL;
1693 int error, i, j;
1694 char *event = extra, *end = extra + dwrq->length, *value;
1695 uint buflen = dwrq->length;
1696 int16 rssi;
1697 int channel;
1698
1699 WL_TRACE(("%s: %s SIOCGIWSCAN\n", __FUNCTION__, dev->name));
1700
1701 if (!extra)
1702 return -EINVAL;
1703
1704 /* Check for scan in progress */
1705 if ((error = dev_wlc_ioctl(dev, WLC_GET_CHANNEL, &ci, sizeof(ci))))
1706 return error;
1707 ci.scan_channel = dtoh32(ci.scan_channel);
1708 if (ci.scan_channel)
1709 return -EAGAIN;
1710
1711 /* Get scan results (too large to put on the stack) */
1712 list = kmalloc(buflen, GFP_KERNEL);
1713 if (!list)
1714 return -ENOMEM;
1715 memset(list, 0, buflen);
1716 list->buflen = htod32(buflen);
1717 if ((error = dev_wlc_ioctl(dev, WLC_SCAN_RESULTS, list, buflen))) {
1718 kfree(list);
1719 return error;
1720 }
1721 list->buflen = dtoh32(list->buflen);
1722 list->version = dtoh32(list->version);
1723 list->count = dtoh32(list->count);
1724
1725 ASSERT(list->version == WL_BSS_INFO_VERSION);
1726
1727 for (i = 0; i < list->count && i < IW_MAX_AP; i++) {
1728 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1729 ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1730 buflen));
1731
1732 // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
1733 rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
1734 channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
1735 WL_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
1736 __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID));
1737
1738 /* First entry must be the BSSID */
1739 iwe.cmd = SIOCGIWAP;
1740 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1741 memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1742 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
1743
1744 /* SSID */
1745 iwe.u.data.length = dtoh32(bi->SSID_len);
1746 iwe.cmd = SIOCGIWESSID;
1747 iwe.u.data.flags = 1;
1748 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
1749
1750 /* Mode */
1751 if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1752 iwe.cmd = SIOCGIWMODE;
1753 if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1754 iwe.u.mode = IW_MODE_INFRA;
1755 else
1756 iwe.u.mode = IW_MODE_ADHOC;
1757 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
1758 }
1759
1760 /* Channel */
1761 iwe.cmd = SIOCGIWFREQ;
1762
1763 iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
1764 (CHSPEC_IS2G(bi->chanspec)) ?
1765 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
1766 iwe.u.freq.e = 6;
1767 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
1768
1769 /* Channel quality */
1770 iwe.cmd = IWEVQUAL;
1771 iwe.u.qual.qual = rssi_to_qual(rssi);
1772 iwe.u.qual.level = 0x100 + rssi;
1773 iwe.u.qual.noise = 0x100 + bi->phy_noise;
1774 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
1775
1776 wl_iw_handle_scanresults_ies(&event, end, info, bi);
1777
1778 /* Encryption */
1779 iwe.cmd = SIOCGIWENCODE;
1780 if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1781 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1782 else
1783 iwe.u.data.flags = IW_ENCODE_DISABLED;
1784 iwe.u.data.length = 0;
1785 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
1786
1787 /* Rates */
1788 if (bi->rateset.count) {
1789 value = event + IW_EV_LCP_LEN;
1790 iwe.cmd = SIOCGIWRATE;
1791 /* Those two flags are ignored... */
1792 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
1793 for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
1794 iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
1795 value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
1796 IW_EV_PARAM_LEN);
1797 }
1798 event = value;
1799 }
1800 }
1801
1802 kfree(list);
1803
1804 dwrq->length = event - extra;
1805 dwrq->flags = 0; /* todo */
1806
1807 return 0;
1808 }
1809
1810 static int
wl_iw_iscan_get_scan(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1811 wl_iw_iscan_get_scan(
1812 struct net_device *dev,
1813 struct iw_request_info *info,
1814 struct iw_point *dwrq,
1815 char *extra
1816 )
1817 {
1818 wl_scan_results_t *list;
1819 struct iw_event iwe;
1820 wl_bss_info_t *bi = NULL;
1821 int ii, j;
1822 int apcnt;
1823 char *event = extra, *end = extra + dwrq->length, *value;
1824 iscan_buf_t * p_buf;
1825 int16 rssi;
1826 int channel;
1827 struct dhd_pub *dhd = dhd_get_pub(dev);
1828 iscan_info_t *iscan;
1829 if (dhd && dhd->iscan)
1830 iscan = (iscan_info_t *)dhd->iscan;
1831
1832 WL_TRACE(("%s: %s SIOCGIWSCAN\n", __FUNCTION__, dev->name));
1833
1834 if (!extra)
1835 return -EINVAL;
1836
1837 /* use backup if our thread is not successful */
1838 if ((!iscan) || (iscan->sysioc_pid < 0)) {
1839 return wl_iw_get_scan(dev, info, dwrq, extra);
1840 }
1841
1842 /* Check for scan in progress */
1843 if (iscan->iscan_state == ISCAN_STATE_SCANING) {
1844 WL_TRACE(("%s: SIOCGIWSCAN GET still scanning\n", dev->name));
1845 return -EAGAIN;
1846 }
1847
1848 apcnt = 0;
1849 p_buf = iscan->list_hdr;
1850 /* Get scan results */
1851 while (p_buf != iscan->list_cur) {
1852 list = &((wl_iscan_results_t*)p_buf->iscan_buf)->results;
1853
1854 if (list->version != WL_BSS_INFO_VERSION) {
1855 WL_ERROR(("list->version %d != WL_BSS_INFO_VERSION\n", list->version));
1856 }
1857
1858 bi = NULL;
1859 for (ii = 0; ii < list->count && apcnt < IW_MAX_AP; apcnt++, ii++) {
1860 bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : list->bss_info;
1861 ASSERT(((uintptr)bi + dtoh32(bi->length)) <= ((uintptr)list +
1862 WLC_IW_ISCAN_MAXLEN));
1863
1864 /* overflow check cover fields before wpa IEs */
1865 if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
1866 IW_EV_QUAL_LEN >= end)
1867 return -E2BIG;
1868
1869 // terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
1870 rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
1871 channel = (bi->ctl_ch == 0) ? CHSPEC_CHANNEL(bi->chanspec) : bi->ctl_ch;
1872 WL_SCAN(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
1873 __FUNCTION__, MAC2STR(bi->BSSID.octet), channel, rssi, bi->SSID));
1874
1875 /* First entry must be the BSSID */
1876 iwe.cmd = SIOCGIWAP;
1877 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1878 memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
1879 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
1880
1881 /* SSID */
1882 iwe.u.data.length = dtoh32(bi->SSID_len);
1883 iwe.cmd = SIOCGIWESSID;
1884 iwe.u.data.flags = 1;
1885 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
1886
1887 /* Mode */
1888 if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
1889 iwe.cmd = SIOCGIWMODE;
1890 if (dtoh16(bi->capability) & DOT11_CAP_ESS)
1891 iwe.u.mode = IW_MODE_INFRA;
1892 else
1893 iwe.u.mode = IW_MODE_ADHOC;
1894 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
1895 }
1896
1897 /* Channel */
1898 iwe.cmd = SIOCGIWFREQ;
1899 iwe.u.freq.m = wf_channel2mhz(CHSPEC_CHANNEL(bi->chanspec),
1900 (CHSPEC_IS2G(bi->chanspec)) ?
1901 WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
1902 iwe.u.freq.e = 6;
1903 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
1904
1905 /* Channel quality */
1906 iwe.cmd = IWEVQUAL;
1907 iwe.u.qual.qual = rssi_to_qual(rssi);
1908 iwe.u.qual.level = 0x100 + rssi;
1909 iwe.u.qual.noise = 0x100 + bi->phy_noise;
1910 event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
1911
1912 wl_iw_handle_scanresults_ies(&event, end, info, bi);
1913
1914 /* Encryption */
1915 iwe.cmd = SIOCGIWENCODE;
1916 if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
1917 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1918 else
1919 iwe.u.data.flags = IW_ENCODE_DISABLED;
1920 iwe.u.data.length = 0;
1921 event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
1922
1923 /* Rates */
1924 if (bi->rateset.count <= sizeof(bi->rateset.rates)) {
1925 if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end)
1926 return -E2BIG;
1927
1928 value = event + IW_EV_LCP_LEN;
1929 iwe.cmd = SIOCGIWRATE;
1930 /* Those two flags are ignored... */
1931 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
1932 for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
1933 iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
1934 value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
1935 IW_EV_PARAM_LEN);
1936 }
1937 event = value;
1938 }
1939 }
1940 p_buf = p_buf->next;
1941 } /* while (p_buf) */
1942
1943 dwrq->length = event - extra;
1944 dwrq->flags = 0; /* todo */
1945 WL_SCAN(("%s: apcnt=%d\n", __FUNCTION__, apcnt));
1946
1947 return 0;
1948 }
1949 #endif /* WL_ESCAN */
1950 #endif /* WIRELESS_EXT > 13 */
1951
1952
1953 static int
wl_iw_set_essid(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)1954 wl_iw_set_essid(
1955 struct net_device *dev,
1956 struct iw_request_info *info,
1957 struct iw_point *dwrq,
1958 char *extra
1959 )
1960 {
1961 wlc_ssid_t ssid;
1962 int error;
1963 struct dhd_pub *dhd = dhd_get_pub(dev);
1964 wl_conn_info_t *conn_info = NULL;
1965 #ifdef WL_ESCAN
1966 wl_escan_info_t *escan;
1967 if (dhd && dhd->escan) {
1968 escan = (wl_escan_info_t *)dhd->escan;
1969 conn_info = &escan->conn_info;
1970 }
1971 #else
1972 iscan_info_t *iscan;
1973 if (dhd && dhd->iscan) {
1974 iscan = (iscan_info_t *)dhd->iscan;
1975 conn_info = &iscan->conn_info;
1976 }
1977 #endif
1978
1979 WL_TRACE(("%s: SIOCSIWESSID\n", dev->name));
1980
1981 /* default Broadcast SSID */
1982 memset(&ssid, 0, sizeof(ssid));
1983 if (dwrq->length && extra) {
1984 #if WIRELESS_EXT > 20
1985 ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length);
1986 #else
1987 ssid.SSID_len = MIN(sizeof(ssid.SSID), dwrq->length-1);
1988 #endif
1989 memcpy(ssid.SSID, extra, ssid.SSID_len);
1990 ssid.SSID_len = htod32(ssid.SSID_len);
1991
1992 if (conn_info) {
1993 memcpy(conn_info->ssid.SSID, ssid.SSID, ssid.SSID_len);
1994 conn_info->ssid.SSID_len = ssid.SSID_len;
1995 }
1996 if (conn_info && memcmp(ðer_null, &conn_info->bssid, ETHER_ADDR_LEN)) {
1997 error = wl_ext_connect(dev, conn_info);
1998 if (error)
1999 return error;
2000 } else {
2001 error = dev_wlc_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid));
2002 if (error) {
2003 WL_ERROR(("%s: WLC_SET_SSID failed (%d).\n", __FUNCTION__, error));
2004 return error;
2005 }
2006 WL_ERROR(("%s: join SSID=%s\n", __FUNCTION__, ssid.SSID));
2007 }
2008 wl_iw_update_connect_status(dev, WL_EXT_STATUS_CONNECTING);
2009 }
2010 /* If essid null then it is "iwconfig <interface> essid off" command */
2011 else {
2012 scb_val_t scbval;
2013 bzero(&scbval, sizeof(scb_val_t));
2014 WL_ERROR(("%s: WLC_DISASSOC\n", __FUNCTION__));
2015 if ((error = dev_wlc_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t)))) {
2016 WL_ERROR(("%s: WLC_DISASSOC failed (%d).\n", __FUNCTION__, error));
2017 return error;
2018 }
2019 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTING);
2020 }
2021 return 0;
2022 }
2023
2024 static int
wl_iw_get_essid(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2025 wl_iw_get_essid(
2026 struct net_device *dev,
2027 struct iw_request_info *info,
2028 struct iw_point *dwrq,
2029 char *extra
2030 )
2031 {
2032 wlc_ssid_t ssid;
2033 int error;
2034
2035 WL_TRACE(("%s: SIOCGIWESSID\n", dev->name));
2036
2037 if (!extra)
2038 return -EINVAL;
2039
2040 if ((error = dev_wlc_ioctl(dev, WLC_GET_SSID, &ssid, sizeof(ssid)))) {
2041 WL_ERROR(("Error getting the SSID\n"));
2042 return error;
2043 }
2044
2045 ssid.SSID_len = dtoh32(ssid.SSID_len);
2046
2047 /* Max SSID length check */
2048 if (ssid.SSID_len > IW_ESSID_MAX_SIZE) {
2049 ssid.SSID_len = IW_ESSID_MAX_SIZE;
2050 }
2051
2052 /* Get the current SSID */
2053 memcpy(extra, ssid.SSID, ssid.SSID_len);
2054
2055 /* NULL terminating as length of extra buffer is IW_ESSID_MAX_SIZE ie 32 */
2056 extra[IW_ESSID_MAX_SIZE] = '\0';
2057
2058 dwrq->length = ssid.SSID_len;
2059
2060 dwrq->flags = 1; /* active */
2061
2062 return 0;
2063 }
2064
2065 static int
wl_iw_set_nick(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2066 wl_iw_set_nick(
2067 struct net_device *dev,
2068 struct iw_request_info *info,
2069 struct iw_point *dwrq,
2070 char *extra
2071 )
2072 {
2073 wl_iw_t *iw = IW_DEV_IF(dev);
2074 WL_TRACE(("%s: SIOCSIWNICKN\n", dev->name));
2075
2076 if (!extra)
2077 return -EINVAL;
2078
2079 /* Check the size of the string */
2080 if (dwrq->length > sizeof(iw->nickname))
2081 return -E2BIG;
2082
2083 memcpy(iw->nickname, extra, dwrq->length);
2084 iw->nickname[dwrq->length - 1] = '\0';
2085
2086 return 0;
2087 }
2088
2089 static int
wl_iw_get_nick(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2090 wl_iw_get_nick(
2091 struct net_device *dev,
2092 struct iw_request_info *info,
2093 struct iw_point *dwrq,
2094 char *extra
2095 )
2096 {
2097 wl_iw_t *iw = IW_DEV_IF(dev);
2098 WL_TRACE(("%s: SIOCGIWNICKN\n", dev->name));
2099
2100 if (!extra)
2101 return -EINVAL;
2102
2103 strcpy(extra, iw->nickname);
2104 dwrq->length = strlen(extra) + 1;
2105
2106 return 0;
2107 }
2108
wl_iw_set_rate(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2109 static int wl_iw_set_rate(
2110 struct net_device *dev,
2111 struct iw_request_info *info,
2112 struct iw_param *vwrq,
2113 char *extra
2114 )
2115 {
2116 wl_rateset_t rateset;
2117 int error, rate, i, error_bg, error_a;
2118
2119 WL_TRACE(("%s: SIOCSIWRATE\n", dev->name));
2120
2121 /* Get current rateset */
2122 if ((error = dev_wlc_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, sizeof(rateset))))
2123 return error;
2124
2125 rateset.count = dtoh32(rateset.count);
2126
2127 if (vwrq->value < 0) {
2128 /* Select maximum rate */
2129 rate = rateset.rates[rateset.count - 1] & 0x7f;
2130 } else if (vwrq->value < rateset.count) {
2131 /* Select rate by rateset index */
2132 rate = rateset.rates[vwrq->value] & 0x7f;
2133 } else {
2134 /* Specified rate in bps */
2135 rate = vwrq->value / 500000;
2136 }
2137
2138 if (vwrq->fixed) {
2139 /*
2140 Set rate override,
2141 Since the is a/b/g-blind, both a/bg_rate are enforced.
2142 */
2143 error_bg = dev_wlc_intvar_set(dev, "bg_rate", rate);
2144 error_a = dev_wlc_intvar_set(dev, "a_rate", rate);
2145
2146 if (error_bg && error_a)
2147 return (error_bg | error_a);
2148 } else {
2149 /*
2150 clear rate override
2151 Since the is a/b/g-blind, both a/bg_rate are enforced.
2152 */
2153 /* 0 is for clearing rate override */
2154 error_bg = dev_wlc_intvar_set(dev, "bg_rate", 0);
2155 /* 0 is for clearing rate override */
2156 error_a = dev_wlc_intvar_set(dev, "a_rate", 0);
2157
2158 if (error_bg && error_a)
2159 return (error_bg | error_a);
2160
2161 /* Remove rates above selected rate */
2162 for (i = 0; i < rateset.count; i++)
2163 if ((rateset.rates[i] & 0x7f) > rate)
2164 break;
2165 rateset.count = htod32(i);
2166
2167 /* Set current rateset */
2168 if ((error = dev_wlc_ioctl(dev, WLC_SET_RATESET, &rateset, sizeof(rateset))))
2169 return error;
2170 }
2171
2172 return 0;
2173 }
2174
wl_iw_get_rate(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2175 static int wl_iw_get_rate(
2176 struct net_device *dev,
2177 struct iw_request_info *info,
2178 struct iw_param *vwrq,
2179 char *extra
2180 )
2181 {
2182 int error, rate;
2183
2184 WL_TRACE(("%s: SIOCGIWRATE\n", dev->name));
2185
2186 /* Report the current tx rate */
2187 if ((error = dev_wlc_ioctl(dev, WLC_GET_RATE, &rate, sizeof(rate))))
2188 return error;
2189 rate = dtoh32(rate);
2190 vwrq->value = rate * 500000;
2191
2192 return 0;
2193 }
2194
2195 static int
wl_iw_set_rts(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2196 wl_iw_set_rts(
2197 struct net_device *dev,
2198 struct iw_request_info *info,
2199 struct iw_param *vwrq,
2200 char *extra
2201 )
2202 {
2203 int error, rts;
2204
2205 WL_TRACE(("%s: SIOCSIWRTS\n", dev->name));
2206
2207 if (vwrq->disabled)
2208 rts = DOT11_DEFAULT_RTS_LEN;
2209 else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_RTS_LEN)
2210 return -EINVAL;
2211 else
2212 rts = vwrq->value;
2213
2214 if ((error = dev_wlc_intvar_set(dev, "rtsthresh", rts)))
2215 return error;
2216
2217 return 0;
2218 }
2219
2220 static int
wl_iw_get_rts(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2221 wl_iw_get_rts(
2222 struct net_device *dev,
2223 struct iw_request_info *info,
2224 struct iw_param *vwrq,
2225 char *extra
2226 )
2227 {
2228 int error, rts;
2229
2230 WL_TRACE(("%s: SIOCGIWRTS\n", dev->name));
2231
2232 if ((error = dev_wlc_intvar_get(dev, "rtsthresh", &rts)))
2233 return error;
2234
2235 vwrq->value = rts;
2236 vwrq->disabled = (rts >= DOT11_DEFAULT_RTS_LEN);
2237 vwrq->fixed = 1;
2238
2239 return 0;
2240 }
2241
2242 static int
wl_iw_set_frag(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2243 wl_iw_set_frag(
2244 struct net_device *dev,
2245 struct iw_request_info *info,
2246 struct iw_param *vwrq,
2247 char *extra
2248 )
2249 {
2250 int error, frag;
2251
2252 WL_TRACE(("%s: SIOCSIWFRAG\n", dev->name));
2253
2254 if (vwrq->disabled)
2255 frag = DOT11_DEFAULT_FRAG_LEN;
2256 else if (vwrq->value < 0 || vwrq->value > DOT11_DEFAULT_FRAG_LEN)
2257 return -EINVAL;
2258 else
2259 frag = vwrq->value;
2260
2261 if ((error = dev_wlc_intvar_set(dev, "fragthresh", frag)))
2262 return error;
2263
2264 return 0;
2265 }
2266
2267 static int
wl_iw_get_frag(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2268 wl_iw_get_frag(
2269 struct net_device *dev,
2270 struct iw_request_info *info,
2271 struct iw_param *vwrq,
2272 char *extra
2273 )
2274 {
2275 int error, fragthreshold;
2276
2277 WL_TRACE(("%s: SIOCGIWFRAG\n", dev->name));
2278
2279 if ((error = dev_wlc_intvar_get(dev, "fragthresh", &fragthreshold)))
2280 return error;
2281
2282 vwrq->value = fragthreshold;
2283 vwrq->disabled = (fragthreshold >= DOT11_DEFAULT_FRAG_LEN);
2284 vwrq->fixed = 1;
2285
2286 return 0;
2287 }
2288
2289 static int
wl_iw_set_txpow(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2290 wl_iw_set_txpow(
2291 struct net_device *dev,
2292 struct iw_request_info *info,
2293 struct iw_param *vwrq,
2294 char *extra
2295 )
2296 {
2297 int error, disable;
2298 uint16 txpwrmw;
2299 WL_TRACE(("%s: SIOCSIWTXPOW\n", dev->name));
2300
2301 /* Make sure radio is off or on as far as software is concerned */
2302 disable = vwrq->disabled ? WL_RADIO_SW_DISABLE : 0;
2303 disable += WL_RADIO_SW_DISABLE << 16;
2304
2305 disable = htod32(disable);
2306 if ((error = dev_wlc_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable))))
2307 return error;
2308
2309 /* If Radio is off, nothing more to do */
2310 if (disable & WL_RADIO_SW_DISABLE)
2311 return 0;
2312
2313 /* Only handle mW */
2314 if (!(vwrq->flags & IW_TXPOW_MWATT))
2315 return -EINVAL;
2316
2317 /* Value < 0 means just "on" or "off" */
2318 if (vwrq->value < 0)
2319 return 0;
2320
2321 if (vwrq->value > 0xffff) txpwrmw = 0xffff;
2322 else txpwrmw = (uint16)vwrq->value;
2323
2324
2325 error = dev_wlc_intvar_set(dev, "qtxpower", (int)(bcm_mw_to_qdbm(txpwrmw)));
2326 return error;
2327 }
2328
2329 static int
wl_iw_get_txpow(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2330 wl_iw_get_txpow(
2331 struct net_device *dev,
2332 struct iw_request_info *info,
2333 struct iw_param *vwrq,
2334 char *extra
2335 )
2336 {
2337 int error, disable, txpwrdbm;
2338 uint8 result;
2339
2340 WL_TRACE(("%s: SIOCGIWTXPOW\n", dev->name));
2341
2342 if ((error = dev_wlc_ioctl(dev, WLC_GET_RADIO, &disable, sizeof(disable))) ||
2343 (error = dev_wlc_intvar_get(dev, "qtxpower", &txpwrdbm)))
2344 return error;
2345
2346 disable = dtoh32(disable);
2347 result = (uint8)(txpwrdbm & ~WL_TXPWR_OVERRIDE);
2348 vwrq->value = (int32)bcm_qdbm_to_mw(result);
2349 vwrq->fixed = 0;
2350 vwrq->disabled = (disable & (WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE)) ? 1 : 0;
2351 vwrq->flags = IW_TXPOW_MWATT;
2352
2353 return 0;
2354 }
2355
2356 #if WIRELESS_EXT > 10
2357 static int
wl_iw_set_retry(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2358 wl_iw_set_retry(
2359 struct net_device *dev,
2360 struct iw_request_info *info,
2361 struct iw_param *vwrq,
2362 char *extra
2363 )
2364 {
2365 int error, lrl, srl;
2366
2367 WL_TRACE(("%s: SIOCSIWRETRY\n", dev->name));
2368
2369 /* Do not handle "off" or "lifetime" */
2370 if (vwrq->disabled || (vwrq->flags & IW_RETRY_LIFETIME))
2371 return -EINVAL;
2372
2373 /* Handle "[min|max] limit" */
2374 if (vwrq->flags & IW_RETRY_LIMIT) {
2375 /* "max limit" or just "limit" */
2376 #if WIRELESS_EXT > 20
2377 if ((vwrq->flags & IW_RETRY_LONG) ||(vwrq->flags & IW_RETRY_MAX) ||
2378 !((vwrq->flags & IW_RETRY_SHORT) || (vwrq->flags & IW_RETRY_MIN)))
2379 #else
2380 if ((vwrq->flags & IW_RETRY_MAX) || !(vwrq->flags & IW_RETRY_MIN))
2381 #endif /* WIRELESS_EXT > 20 */
2382 {
2383 lrl = htod32(vwrq->value);
2384 if ((error = dev_wlc_ioctl(dev, WLC_SET_LRL, &lrl, sizeof(lrl))))
2385 return error;
2386 }
2387 /* "min limit" or just "limit" */
2388 #if WIRELESS_EXT > 20
2389 if ((vwrq->flags & IW_RETRY_SHORT) ||(vwrq->flags & IW_RETRY_MIN) ||
2390 !((vwrq->flags & IW_RETRY_LONG) || (vwrq->flags & IW_RETRY_MAX)))
2391 #else
2392 if ((vwrq->flags & IW_RETRY_MIN) || !(vwrq->flags & IW_RETRY_MAX))
2393 #endif /* WIRELESS_EXT > 20 */
2394 {
2395 srl = htod32(vwrq->value);
2396 if ((error = dev_wlc_ioctl(dev, WLC_SET_SRL, &srl, sizeof(srl))))
2397 return error;
2398 }
2399 }
2400
2401 return 0;
2402 }
2403
2404 static int
wl_iw_get_retry(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2405 wl_iw_get_retry(
2406 struct net_device *dev,
2407 struct iw_request_info *info,
2408 struct iw_param *vwrq,
2409 char *extra
2410 )
2411 {
2412 int error, lrl, srl;
2413
2414 WL_TRACE(("%s: SIOCGIWRETRY\n", dev->name));
2415
2416 vwrq->disabled = 0; /* Can't be disabled */
2417
2418 /* Do not handle lifetime queries */
2419 if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
2420 return -EINVAL;
2421
2422 /* Get retry limits */
2423 if ((error = dev_wlc_ioctl(dev, WLC_GET_LRL, &lrl, sizeof(lrl))) ||
2424 (error = dev_wlc_ioctl(dev, WLC_GET_SRL, &srl, sizeof(srl))))
2425 return error;
2426
2427 lrl = dtoh32(lrl);
2428 srl = dtoh32(srl);
2429
2430 /* Note : by default, display the min retry number */
2431 if (vwrq->flags & IW_RETRY_MAX) {
2432 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
2433 vwrq->value = lrl;
2434 } else {
2435 vwrq->flags = IW_RETRY_LIMIT;
2436 vwrq->value = srl;
2437 if (srl != lrl)
2438 vwrq->flags |= IW_RETRY_MIN;
2439 }
2440
2441 return 0;
2442 }
2443 #endif /* WIRELESS_EXT > 10 */
2444
2445 static int
wl_iw_set_encode(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2446 wl_iw_set_encode(
2447 struct net_device *dev,
2448 struct iw_request_info *info,
2449 struct iw_point *dwrq,
2450 char *extra
2451 )
2452 {
2453 wl_wsec_key_t key;
2454 int error, val, wsec;
2455
2456 WL_TRACE(("%s: SIOCSIWENCODE\n", dev->name));
2457
2458 memset(&key, 0, sizeof(key));
2459
2460 if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2461 /* Find the current key */
2462 for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
2463 val = htod32(key.index);
2464 if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
2465 return error;
2466 val = dtoh32(val);
2467 if (val)
2468 break;
2469 }
2470 /* Default to 0 */
2471 if (key.index == DOT11_MAX_DEFAULT_KEYS)
2472 key.index = 0;
2473 } else {
2474 key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2475 if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2476 return -EINVAL;
2477 }
2478
2479 /* Interpret "off" to mean no encryption */
2480 wsec = (dwrq->flags & IW_ENCODE_DISABLED) ? 0 : WEP_ENABLED;
2481
2482 if ((error = dev_wlc_intvar_set(dev, "wsec", wsec)))
2483 return error;
2484
2485 /* Old API used to pass a NULL pointer instead of IW_ENCODE_NOKEY */
2486 if (!extra || !dwrq->length || (dwrq->flags & IW_ENCODE_NOKEY)) {
2487 /* Just select a new current key */
2488 val = htod32(key.index);
2489 if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY, &val, sizeof(val))))
2490 return error;
2491 } else {
2492 key.len = dwrq->length;
2493
2494 if (dwrq->length > sizeof(key.data))
2495 return -EINVAL;
2496
2497 memcpy(key.data, extra, dwrq->length);
2498
2499 key.flags = WL_PRIMARY_KEY;
2500 switch (key.len) {
2501 case WEP1_KEY_SIZE:
2502 key.algo = CRYPTO_ALGO_WEP1;
2503 break;
2504 case WEP128_KEY_SIZE:
2505 key.algo = CRYPTO_ALGO_WEP128;
2506 break;
2507 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
2508 case TKIP_KEY_SIZE:
2509 key.algo = CRYPTO_ALGO_TKIP;
2510 break;
2511 #endif
2512 case AES_KEY_SIZE:
2513 key.algo = CRYPTO_ALGO_AES_CCM;
2514 break;
2515 default:
2516 return -EINVAL;
2517 }
2518
2519 /* Set the new key/index */
2520 swap_key_from_BE(&key);
2521 if ((error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key))))
2522 return error;
2523 }
2524
2525 /* Interpret "restricted" to mean shared key authentication */
2526 val = (dwrq->flags & IW_ENCODE_RESTRICTED) ? 1 : 0;
2527 val = htod32(val);
2528 if ((error = dev_wlc_ioctl(dev, WLC_SET_AUTH, &val, sizeof(val))))
2529 return error;
2530
2531 return 0;
2532 }
2533
2534 static int
wl_iw_get_encode(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2535 wl_iw_get_encode(
2536 struct net_device *dev,
2537 struct iw_request_info *info,
2538 struct iw_point *dwrq,
2539 char *extra
2540 )
2541 {
2542 wl_wsec_key_t key;
2543 int error, val, wsec, auth;
2544
2545 WL_TRACE(("%s: SIOCGIWENCODE\n", dev->name));
2546
2547 /* assure default values of zero for things we don't touch */
2548 bzero(&key, sizeof(wl_wsec_key_t));
2549
2550 if ((dwrq->flags & IW_ENCODE_INDEX) == 0) {
2551 /* Find the current key */
2552 for (key.index = 0; key.index < DOT11_MAX_DEFAULT_KEYS; key.index++) {
2553 val = key.index;
2554 if ((error = dev_wlc_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val))))
2555 return error;
2556 val = dtoh32(val);
2557 if (val)
2558 break;
2559 }
2560 } else
2561 key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2562
2563 if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2564 key.index = 0;
2565
2566 /* Get info */
2567
2568 if ((error = dev_wlc_ioctl(dev, WLC_GET_WSEC, &wsec, sizeof(wsec))) ||
2569 (error = dev_wlc_ioctl(dev, WLC_GET_AUTH, &auth, sizeof(auth))))
2570 return error;
2571
2572 swap_key_to_BE(&key);
2573
2574 wsec = dtoh32(wsec);
2575 auth = dtoh32(auth);
2576 /* Get key length */
2577 dwrq->length = MIN(IW_ENCODING_TOKEN_MAX, key.len);
2578
2579 /* Get flags */
2580 dwrq->flags = key.index + 1;
2581 if (!(wsec & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))) {
2582 /* Interpret "off" to mean no encryption */
2583 dwrq->flags |= IW_ENCODE_DISABLED;
2584 }
2585 if (auth) {
2586 /* Interpret "restricted" to mean shared key authentication */
2587 dwrq->flags |= IW_ENCODE_RESTRICTED;
2588 }
2589
2590 /* Get key */
2591 if (dwrq->length && extra)
2592 memcpy(extra, key.data, dwrq->length);
2593
2594 return 0;
2595 }
2596
2597 static int
wl_iw_set_power(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2598 wl_iw_set_power(
2599 struct net_device *dev,
2600 struct iw_request_info *info,
2601 struct iw_param *vwrq,
2602 char *extra
2603 )
2604 {
2605 int error, pm;
2606
2607 WL_TRACE(("%s: SIOCSIWPOWER\n", dev->name));
2608
2609 pm = vwrq->disabled ? PM_OFF : PM_MAX;
2610
2611 pm = htod32(pm);
2612 if ((error = dev_wlc_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm))))
2613 return error;
2614
2615 return 0;
2616 }
2617
2618 static int
wl_iw_get_power(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2619 wl_iw_get_power(
2620 struct net_device *dev,
2621 struct iw_request_info *info,
2622 struct iw_param *vwrq,
2623 char *extra
2624 )
2625 {
2626 int error, pm;
2627
2628 WL_TRACE(("%s: SIOCGIWPOWER\n", dev->name));
2629
2630 if ((error = dev_wlc_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm))))
2631 return error;
2632
2633 pm = dtoh32(pm);
2634 vwrq->disabled = pm ? 0 : 1;
2635 vwrq->flags = IW_POWER_ALL_R;
2636
2637 return 0;
2638 }
2639
2640 #if WIRELESS_EXT > 17
2641 static int
wl_iw_set_wpaie(struct net_device * dev,struct iw_request_info * info,struct iw_point * iwp,char * extra)2642 wl_iw_set_wpaie(
2643 struct net_device *dev,
2644 struct iw_request_info *info,
2645 struct iw_point *iwp,
2646 char *extra
2647 )
2648 {
2649 #if defined(BCMWAPI_WPI)
2650 uchar buf[WLC_IOCTL_SMLEN] = {0};
2651 uchar *p = buf;
2652 int wapi_ie_size;
2653
2654 WL_TRACE(("%s: SIOCSIWGENIE\n", dev->name));
2655
2656 if (extra[0] == DOT11_MNG_WAPI_ID) {
2657 wapi_ie_size = iwp->length;
2658 memcpy(p, extra, iwp->length);
2659 dev_wlc_bufvar_set(dev, "wapiie", buf, wapi_ie_size);
2660 } else
2661 #endif
2662 dev_wlc_bufvar_set(dev, "wpaie", extra, iwp->length);
2663
2664 return 0;
2665 }
2666
2667 static int
wl_iw_get_wpaie(struct net_device * dev,struct iw_request_info * info,struct iw_point * iwp,char * extra)2668 wl_iw_get_wpaie(
2669 struct net_device *dev,
2670 struct iw_request_info *info,
2671 struct iw_point *iwp,
2672 char *extra
2673 )
2674 {
2675 WL_TRACE(("%s: SIOCGIWGENIE\n", dev->name));
2676 iwp->length = 64;
2677 dev_wlc_bufvar_get(dev, "wpaie", extra, iwp->length);
2678 return 0;
2679 }
2680
2681 static int
wl_iw_set_encodeext(struct net_device * dev,struct iw_request_info * info,struct iw_point * dwrq,char * extra)2682 wl_iw_set_encodeext(
2683 struct net_device *dev,
2684 struct iw_request_info *info,
2685 struct iw_point *dwrq,
2686 char *extra
2687 )
2688 {
2689 wl_wsec_key_t key;
2690 int error;
2691 struct iw_encode_ext *iwe;
2692
2693 WL_TRACE(("%s: SIOCSIWENCODEEXT\n", dev->name));
2694
2695 memset(&key, 0, sizeof(key));
2696 iwe = (struct iw_encode_ext *)extra;
2697
2698 /* disable encryption completely */
2699 if (dwrq->flags & IW_ENCODE_DISABLED) {
2700 }
2701
2702 /* get the key index */
2703 key.index = 0;
2704 if (dwrq->flags & IW_ENCODE_INDEX)
2705 key.index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
2706
2707 key.len = iwe->key_len;
2708
2709 /* Instead of bcast for ea address for default wep keys, driver needs it to be Null */
2710 if (!ETHER_ISMULTI(iwe->addr.sa_data))
2711 bcopy((void *)&iwe->addr.sa_data, (char *)&key.ea, ETHER_ADDR_LEN);
2712
2713 /* check for key index change */
2714 if (key.len == 0) {
2715 if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2716 WL_WSEC(("Changing the the primary Key to %d\n", key.index));
2717 /* change the key index .... */
2718 key.index = htod32(key.index);
2719 error = dev_wlc_ioctl(dev, WLC_SET_KEY_PRIMARY,
2720 &key.index, sizeof(key.index));
2721 if (error)
2722 return error;
2723 }
2724 /* key delete */
2725 else {
2726 swap_key_from_BE(&key);
2727 error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2728 if (error)
2729 return error;
2730 }
2731 }
2732 /* This case is used to allow an external 802.1x supplicant
2733 * to pass the PMK to the in-driver supplicant for use in
2734 * the 4-way handshake.
2735 */
2736 else if (iwe->alg == IW_ENCODE_ALG_PMK) {
2737 int j;
2738 wsec_pmk_t pmk;
2739 char keystring[WSEC_MAX_PSK_LEN + 1];
2740 char* charptr = keystring;
2741 uint len;
2742
2743 /* copy the raw hex key to the appropriate format */
2744 for (j = 0; j < (WSEC_MAX_PSK_LEN / 2); j++) {
2745 (void)snprintf(charptr, 3, "%02x", iwe->key[j]);
2746 charptr += 2;
2747 }
2748 len = strlen(keystring);
2749 pmk.key_len = htod16(len);
2750 bcopy(keystring, pmk.key, len);
2751 pmk.flags = htod16(WSEC_PASSPHRASE);
2752
2753 WL_WSEC(("%s: set key %s\n", __FUNCTION__, keystring));
2754 error = dev_wlc_ioctl(dev, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk));
2755 if (error) {
2756 WL_ERROR(("%s: WLC_SET_WSEC_PMK error %d\n", __FUNCTION__, error));
2757 return error;
2758 }
2759 }
2760
2761 else {
2762 if (iwe->key_len > sizeof(key.data))
2763 return -EINVAL;
2764
2765 WL_WSEC(("Setting the key index %d\n", key.index));
2766 if (iwe->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
2767 WL_WSEC(("key is a Primary Key\n"));
2768 key.flags = WL_PRIMARY_KEY;
2769 }
2770
2771 bcopy((void *)iwe->key, key.data, iwe->key_len);
2772
2773 if (iwe->alg == IW_ENCODE_ALG_TKIP) {
2774 uint8 keybuf[8];
2775 bcopy(&key.data[24], keybuf, sizeof(keybuf));
2776 bcopy(&key.data[16], &key.data[24], sizeof(keybuf));
2777 bcopy(keybuf, &key.data[16], sizeof(keybuf));
2778 }
2779
2780 /* rx iv */
2781 if (iwe->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
2782 uchar *ivptr;
2783 ivptr = (uchar *)iwe->rx_seq;
2784 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2785 (ivptr[3] << 8) | ivptr[2];
2786 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2787 key.iv_initialized = TRUE;
2788 }
2789
2790 switch (iwe->alg) {
2791 case IW_ENCODE_ALG_NONE:
2792 key.algo = CRYPTO_ALGO_OFF;
2793 break;
2794 case IW_ENCODE_ALG_WEP:
2795 if (iwe->key_len == WEP1_KEY_SIZE)
2796 key.algo = CRYPTO_ALGO_WEP1;
2797 else
2798 key.algo = CRYPTO_ALGO_WEP128;
2799 break;
2800 case IW_ENCODE_ALG_TKIP:
2801 key.algo = CRYPTO_ALGO_TKIP;
2802 break;
2803 case IW_ENCODE_ALG_CCMP:
2804 key.algo = CRYPTO_ALGO_AES_CCM;
2805 break;
2806 #ifdef BCMWAPI_WPI
2807 case IW_ENCODE_ALG_SM4:
2808 key.algo = CRYPTO_ALGO_SMS4;
2809 if (iwe->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
2810 key.flags &= ~WL_PRIMARY_KEY;
2811 }
2812 break;
2813 #endif
2814 default:
2815 break;
2816 }
2817 swap_key_from_BE(&key);
2818
2819 dhd_wait_pend8021x(dev);
2820
2821 error = dev_wlc_ioctl(dev, WLC_SET_KEY, &key, sizeof(key));
2822 if (error)
2823 return error;
2824 wl_iw_update_connect_status(dev, WL_EXT_STATUS_ADD_KEY);
2825 }
2826 return 0;
2827 }
2828
2829 /* wpa2 pmk list */
2830 static int
wl_iw_set_pmksa(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2831 wl_iw_set_pmksa(
2832 struct net_device *dev,
2833 struct iw_request_info *info,
2834 struct iw_param *vwrq,
2835 char *extra
2836 )
2837 {
2838 struct pmk_list *pmk_list = NULL;
2839 struct iw_pmksa *iwpmksa;
2840 uint i;
2841 char eabuf[ETHER_ADDR_STR_LEN];
2842 pmkid_t *pmkid_array = NULL;
2843 struct dhd_pub *dhd = dhd_get_pub(dev);
2844 #ifdef WL_ESCAN
2845 wl_escan_info_t *escan;
2846 if (dhd && dhd->escan) {
2847 escan = (wl_escan_info_t *)dhd->escan;
2848 pmk_list = &escan->pmk_list;
2849 }
2850 #else
2851 iscan_info_t *iscan;
2852 if (dhd && dhd->iscan) {
2853 iscan = (iscan_info_t *)dhd->iscan;
2854 pmk_list = &iscan->pmk_list;
2855 }
2856 #endif
2857
2858 WL_TRACE(("%s: SIOCSIWPMKSA\n", dev->name));
2859 if (pmk_list)
2860 pmkid_array = pmk_list->pmkids.pmkid;
2861 iwpmksa = (struct iw_pmksa *)extra;
2862 bzero((char *)eabuf, ETHER_ADDR_STR_LEN);
2863 if (iwpmksa->cmd == IW_PMKSA_FLUSH) {
2864 WL_TRACE(("wl_iw_set_pmksa - IW_PMKSA_FLUSH\n"));
2865 bzero((char *)pmk_list, sizeof(struct pmk_list));
2866 }
2867 if (iwpmksa->cmd == IW_PMKSA_REMOVE) {
2868 pmkid_list_t pmkid, *pmkidptr;
2869 pmkidptr = &pmkid;
2870 bcopy(&iwpmksa->bssid.sa_data[0], &pmkidptr->pmkid[0].BSSID, ETHER_ADDR_LEN);
2871 bcopy(&iwpmksa->pmkid[0], &pmkidptr->pmkid[0].PMKID, WPA2_PMKID_LEN);
2872 {
2873 uint j;
2874 WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_REMOVE - PMKID: %s = ",
2875 bcm_ether_ntoa(&pmkidptr->pmkid[0].BSSID,
2876 eabuf)));
2877 for (j = 0; j < WPA2_PMKID_LEN; j++)
2878 WL_TRACE(("%02x ", pmkidptr->pmkid[0].PMKID[j]));
2879 WL_TRACE(("\n"));
2880 }
2881 for (i = 0; i < pmk_list->pmkids.npmkid; i++)
2882 if (!bcmp(&iwpmksa->bssid.sa_data[0], &pmkid_array[i].BSSID,
2883 ETHER_ADDR_LEN))
2884 break;
2885 for (; i < pmk_list->pmkids.npmkid; i++) {
2886 bcopy(&pmkid_array[i+1].BSSID,
2887 &pmkid_array[i].BSSID,
2888 ETHER_ADDR_LEN);
2889 bcopy(&pmkid_array[i+1].PMKID,
2890 &pmkid_array[i].PMKID,
2891 WPA2_PMKID_LEN);
2892 }
2893 pmk_list->pmkids.npmkid--;
2894 }
2895 if (iwpmksa->cmd == IW_PMKSA_ADD) {
2896 bcopy(&iwpmksa->bssid.sa_data[0],
2897 &pmkid_array[pmk_list->pmkids.npmkid].BSSID,
2898 ETHER_ADDR_LEN);
2899 bcopy(&iwpmksa->pmkid[0], &pmkid_array[pmk_list->pmkids.npmkid].PMKID,
2900 WPA2_PMKID_LEN);
2901 {
2902 uint j;
2903 uint k;
2904 k = pmk_list->pmkids.npmkid;
2905 BCM_REFERENCE(k);
2906 WL_TRACE(("wl_iw_set_pmksa,IW_PMKSA_ADD - PMKID: %s = ",
2907 bcm_ether_ntoa(&pmkid_array[k].BSSID,
2908 eabuf)));
2909 for (j = 0; j < WPA2_PMKID_LEN; j++)
2910 WL_TRACE(("%02x ", pmkid_array[k].PMKID[j]));
2911 WL_TRACE(("\n"));
2912 }
2913 pmk_list->pmkids.npmkid++;
2914 }
2915 WL_TRACE(("PRINTING pmkid LIST - No of elements %d\n", pmk_list->pmkids.npmkid));
2916 for (i = 0; i < pmk_list->pmkids.npmkid; i++) {
2917 uint j;
2918 WL_TRACE(("PMKID[%d]: %s = ", i,
2919 bcm_ether_ntoa(&pmkid_array[i].BSSID,
2920 eabuf)));
2921 for (j = 0; j < WPA2_PMKID_LEN; j++)
2922 WL_TRACE(("%02x ", pmkid_array[i].PMKID[j]));
2923 printf("\n");
2924 }
2925 WL_TRACE(("\n"));
2926 dev_wlc_bufvar_set(dev, "pmkid_info", (char *)pmk_list, sizeof(struct pmk_list));
2927 return 0;
2928 }
2929
2930 static int
wl_iw_get_encodeext(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2931 wl_iw_get_encodeext(
2932 struct net_device *dev,
2933 struct iw_request_info *info,
2934 struct iw_param *vwrq,
2935 char *extra
2936 )
2937 {
2938 WL_TRACE(("%s: SIOCGIWENCODEEXT\n", dev->name));
2939 return 0;
2940 }
2941
2942 static int
wl_iw_set_wpaauth(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)2943 wl_iw_set_wpaauth(
2944 struct net_device *dev,
2945 struct iw_request_info *info,
2946 struct iw_param *vwrq,
2947 char *extra
2948 )
2949 {
2950 int error = 0;
2951 int paramid;
2952 int paramval;
2953 uint32 cipher_combined;
2954 int val = 0;
2955 wl_iw_t *iw = IW_DEV_IF(dev);
2956
2957 WL_TRACE(("%s: SIOCSIWAUTH\n", dev->name));
2958
2959 paramid = vwrq->flags & IW_AUTH_INDEX;
2960 paramval = vwrq->value;
2961
2962 WL_TRACE(("%s: SIOCSIWAUTH, paramid = 0x%0x, paramval = 0x%0x\n",
2963 dev->name, paramid, paramval));
2964
2965 switch (paramid) {
2966 case IW_AUTH_WPA_VERSION:
2967 /* supported wpa version disabled or wpa or wpa2 */
2968 if (paramval & IW_AUTH_WPA_VERSION_DISABLED)
2969 val = WPA_AUTH_DISABLED;
2970 else if (paramval & (IW_AUTH_WPA_VERSION_WPA))
2971 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
2972 else if (paramval & IW_AUTH_WPA_VERSION_WPA2)
2973 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
2974 #ifdef BCMWAPI_WPI
2975 else if (paramval & IW_AUTH_WAPI_VERSION_1)
2976 val = WAPI_AUTH_UNSPECIFIED;
2977 #endif
2978 WL_TRACE(("%s: %d: setting wpa_auth to 0x%0x\n", __FUNCTION__, __LINE__, val));
2979 if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
2980 return error;
2981 break;
2982
2983 case IW_AUTH_CIPHER_PAIRWISE:
2984 case IW_AUTH_CIPHER_GROUP: {
2985 int fbt_cap = 0;
2986
2987 if (paramid == IW_AUTH_CIPHER_PAIRWISE) {
2988 iw->pwsec = paramval;
2989 }
2990 else {
2991 iw->gwsec = paramval;
2992 }
2993
2994 if ((error = dev_wlc_intvar_get(dev, "wsec", &val))) {
2995 WL_ERROR(("%s: wsec error %d\n", __FUNCTION__, error));
2996 return error;
2997 }
2998 WL_WSEC(("%s: get wsec=0x%x\n", __FUNCTION__, val));
2999
3000 cipher_combined = iw->gwsec | iw->pwsec;
3001 val &= ~(WEP_ENABLED | TKIP_ENABLED | AES_ENABLED);
3002 if (cipher_combined & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104))
3003 val |= WEP_ENABLED;
3004 if (cipher_combined & IW_AUTH_CIPHER_TKIP)
3005 val |= TKIP_ENABLED;
3006 if (cipher_combined & IW_AUTH_CIPHER_CCMP)
3007 val |= AES_ENABLED;
3008 #ifdef BCMWAPI_WPI
3009 val &= ~SMS4_ENABLED;
3010 if (cipher_combined & IW_AUTH_CIPHER_SMS4)
3011 val |= SMS4_ENABLED;
3012 #endif
3013
3014 if (iw->privacy_invoked && !val) {
3015 WL_WSEC(("%s: %s: 'Privacy invoked' TRUE but clearing wsec, assuming "
3016 "we're a WPS enrollee\n", dev->name, __FUNCTION__));
3017 if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
3018 WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
3019 return error;
3020 }
3021 } else if (val) {
3022 if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
3023 WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
3024 return error;
3025 }
3026 }
3027
3028 WL_WSEC(("%s: set wsec=0x%x\n", __FUNCTION__, val));
3029 if ((error = dev_wlc_intvar_set(dev, "wsec", val))) {
3030 WL_ERROR(("%s: wsec error %d\n", __FUNCTION__, error));
3031 return error;
3032 }
3033
3034 /* Ensure in-dongle supplicant is turned on when FBT wants to do the 4-way
3035 * handshake.
3036 */
3037 if (dev_wlc_intvar_get(dev, "fbt_cap", &fbt_cap) == 0) {
3038 WL_WSEC(("%s: get fbt_cap=0x%x\n", __FUNCTION__, fbt_cap));
3039 if (fbt_cap == WLC_FBT_CAP_DRV_4WAY_AND_REASSOC) {
3040 if ((paramid == IW_AUTH_CIPHER_PAIRWISE) && (val & AES_ENABLED)) {
3041 if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 1))) {
3042 WL_ERROR(("%s: sup_wpa 1 error %d\n", __FUNCTION__, error));
3043 return error;
3044 }
3045 }
3046 else if (val == 0) {
3047 if ((error = dev_wlc_intvar_set(dev, "sup_wpa", 0))) {
3048 WL_ERROR(("%s: sup_wpa 0 error %d\n", __FUNCTION__, error));
3049 return error;
3050 }
3051 }
3052 }
3053 }
3054 break;
3055 }
3056
3057 case IW_AUTH_KEY_MGMT:
3058 if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val))) {
3059 WL_ERROR(("%s: wpa_auth error %d\n", __FUNCTION__, error));
3060 return error;
3061 }
3062 WL_WSEC(("%s: get wpa_auth to %d\n", __FUNCTION__, val));
3063
3064 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
3065 if (paramval & (IW_AUTH_KEY_MGMT_FT_PSK | IW_AUTH_KEY_MGMT_PSK))
3066 val = WPA_AUTH_PSK;
3067 else
3068 val = WPA_AUTH_UNSPECIFIED;
3069 if (paramval & (IW_AUTH_KEY_MGMT_FT_802_1X | IW_AUTH_KEY_MGMT_FT_PSK))
3070 val |= WPA2_AUTH_FT;
3071 }
3072 else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
3073 if (paramval & (IW_AUTH_KEY_MGMT_FT_PSK | IW_AUTH_KEY_MGMT_PSK))
3074 val = WPA2_AUTH_PSK;
3075 else
3076 val = WPA2_AUTH_UNSPECIFIED;
3077 if (paramval & (IW_AUTH_KEY_MGMT_FT_802_1X | IW_AUTH_KEY_MGMT_FT_PSK))
3078 val |= WPA2_AUTH_FT;
3079 }
3080 #ifdef BCMWAPI_WPI
3081 if (paramval & (IW_AUTH_KEY_MGMT_WAPI_PSK | IW_AUTH_KEY_MGMT_WAPI_CERT))
3082 val = WAPI_AUTH_UNSPECIFIED;
3083 #endif
3084 WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
3085 if ((error = dev_wlc_intvar_set(dev, "wpa_auth", val)))
3086 return error;
3087 break;
3088
3089 case IW_AUTH_TKIP_COUNTERMEASURES:
3090 dev_wlc_bufvar_set(dev, "tkip_countermeasures", (char *)¶mval, 1);
3091 break;
3092
3093 case IW_AUTH_80211_AUTH_ALG:
3094 /* open shared */
3095 WL_ERROR(("Setting the D11auth %d\n", paramval));
3096 if (paramval & IW_AUTH_ALG_OPEN_SYSTEM)
3097 val = 0;
3098 else if (paramval & IW_AUTH_ALG_SHARED_KEY)
3099 val = 1;
3100 else
3101 error = 1;
3102 if (!error && (error = dev_wlc_intvar_set(dev, "auth", val)))
3103 return error;
3104 break;
3105
3106 case IW_AUTH_WPA_ENABLED:
3107 if (paramval == 0) {
3108 val = 0;
3109 WL_TRACE(("%s: %d: setting wpa_auth to %d\n", __FUNCTION__, __LINE__, val));
3110 error = dev_wlc_intvar_set(dev, "wpa_auth", val);
3111 return error;
3112 }
3113 else {
3114 /* If WPA is enabled, wpa_auth is set elsewhere */
3115 }
3116 break;
3117
3118 case IW_AUTH_DROP_UNENCRYPTED:
3119 dev_wlc_bufvar_set(dev, "wsec_restrict", (char *)¶mval, 1);
3120 break;
3121
3122 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
3123 dev_wlc_bufvar_set(dev, "rx_unencrypted_eapol", (char *)¶mval, 1);
3124 break;
3125
3126 #if WIRELESS_EXT > 17
3127
3128 case IW_AUTH_ROAMING_CONTROL:
3129 WL_TRACE(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
3130 /* driver control or user space app control */
3131 break;
3132
3133 case IW_AUTH_PRIVACY_INVOKED: {
3134 int wsec;
3135
3136 if (paramval == 0) {
3137 iw->privacy_invoked = FALSE;
3138 if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
3139 WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
3140 return error;
3141 }
3142 } else {
3143 iw->privacy_invoked = TRUE;
3144 if ((error = dev_wlc_intvar_get(dev, "wsec", &wsec)))
3145 return error;
3146
3147 if (!WSEC_ENABLED(wsec)) {
3148 /* if privacy is true, but wsec is false, we are a WPS enrollee */
3149 if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", TRUE))) {
3150 WL_WSEC(("Failed to set iovar is_WPS_enrollee\n"));
3151 return error;
3152 }
3153 } else {
3154 if ((error = dev_wlc_intvar_set(dev, "is_WPS_enrollee", FALSE))) {
3155 WL_WSEC(("Failed to clear iovar is_WPS_enrollee\n"));
3156 return error;
3157 }
3158 }
3159 }
3160 break;
3161 }
3162
3163
3164 #endif /* WIRELESS_EXT > 17 */
3165
3166 #ifdef BCMWAPI_WPI
3167
3168 case IW_AUTH_WAPI_ENABLED:
3169 error = dev_wlc_intvar_get(dev, "wsec", &val);
3170 if (error)
3171 return error;
3172 if (paramval) {
3173 val |= SMS4_ENABLED;
3174 error = dev_wlc_intvar_set(dev, "wsec", val);
3175 if (error) {
3176 WL_ERROR(("%s: setting wsec to 0x%0x returned error %d\n",
3177 __FUNCTION__, val, error));
3178 return error;
3179 }
3180 error = dev_wlc_intvar_set(dev, "wpa_auth", WAPI_AUTH_UNSPECIFIED);
3181 if (error) {
3182 WL_ERROR(("%s: setting wpa_auth(%d) returned %d\n",
3183 __FUNCTION__, WAPI_AUTH_UNSPECIFIED,
3184 error));
3185 return error;
3186 }
3187 }
3188
3189 break;
3190
3191 #endif /* BCMWAPI_WPI */
3192
3193 default:
3194 break;
3195 }
3196 return 0;
3197 }
3198 #define VAL_PSK(_val) (((_val) & WPA_AUTH_PSK) || ((_val) & WPA2_AUTH_PSK))
3199
3200 static int
wl_iw_get_wpaauth(struct net_device * dev,struct iw_request_info * info,struct iw_param * vwrq,char * extra)3201 wl_iw_get_wpaauth(
3202 struct net_device *dev,
3203 struct iw_request_info *info,
3204 struct iw_param *vwrq,
3205 char *extra
3206 )
3207 {
3208 int error;
3209 int paramid;
3210 int paramval = 0;
3211 int val;
3212 wl_iw_t *iw = IW_DEV_IF(dev);
3213
3214 WL_TRACE(("%s: SIOCGIWAUTH\n", dev->name));
3215
3216 paramid = vwrq->flags & IW_AUTH_INDEX;
3217
3218 switch (paramid) {
3219 case IW_AUTH_WPA_VERSION:
3220 /* supported wpa version disabled or wpa or wpa2 */
3221 if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
3222 return error;
3223 if (val & (WPA_AUTH_NONE | WPA_AUTH_DISABLED))
3224 paramval = IW_AUTH_WPA_VERSION_DISABLED;
3225 else if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED))
3226 paramval = IW_AUTH_WPA_VERSION_WPA;
3227 else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED))
3228 paramval = IW_AUTH_WPA_VERSION_WPA2;
3229 break;
3230
3231 case IW_AUTH_CIPHER_PAIRWISE:
3232 paramval = iw->pwsec;
3233 break;
3234
3235 case IW_AUTH_CIPHER_GROUP:
3236 paramval = iw->gwsec;
3237 break;
3238
3239 case IW_AUTH_KEY_MGMT:
3240 /* psk, 1x */
3241 if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
3242 return error;
3243 if (VAL_PSK(val))
3244 paramval = IW_AUTH_KEY_MGMT_PSK;
3245 else
3246 paramval = IW_AUTH_KEY_MGMT_802_1X;
3247
3248 break;
3249 case IW_AUTH_TKIP_COUNTERMEASURES:
3250 dev_wlc_bufvar_get(dev, "tkip_countermeasures", (char *)¶mval, 1);
3251 break;
3252
3253 case IW_AUTH_DROP_UNENCRYPTED:
3254 dev_wlc_bufvar_get(dev, "wsec_restrict", (char *)¶mval, 1);
3255 break;
3256
3257 case IW_AUTH_RX_UNENCRYPTED_EAPOL:
3258 dev_wlc_bufvar_get(dev, "rx_unencrypted_eapol", (char *)¶mval, 1);
3259 break;
3260
3261 case IW_AUTH_80211_AUTH_ALG:
3262 /* open, shared, leap */
3263 if ((error = dev_wlc_intvar_get(dev, "auth", &val)))
3264 return error;
3265 if (!val)
3266 paramval = IW_AUTH_ALG_OPEN_SYSTEM;
3267 else
3268 paramval = IW_AUTH_ALG_SHARED_KEY;
3269 break;
3270 case IW_AUTH_WPA_ENABLED:
3271 if ((error = dev_wlc_intvar_get(dev, "wpa_auth", &val)))
3272 return error;
3273 if (val)
3274 paramval = TRUE;
3275 else
3276 paramval = FALSE;
3277 break;
3278
3279 #if WIRELESS_EXT > 17
3280
3281 case IW_AUTH_ROAMING_CONTROL:
3282 WL_ERROR(("%s: IW_AUTH_ROAMING_CONTROL\n", __FUNCTION__));
3283 /* driver control or user space app control */
3284 break;
3285
3286 case IW_AUTH_PRIVACY_INVOKED:
3287 paramval = iw->privacy_invoked;
3288 break;
3289
3290 #endif /* WIRELESS_EXT > 17 */
3291 }
3292 vwrq->value = paramval;
3293 return 0;
3294 }
3295 #endif /* WIRELESS_EXT > 17 */
3296
3297 static const iw_handler wl_iw_handler[] =
3298 {
3299 (iw_handler) wl_iw_config_commit, /* SIOCSIWCOMMIT */
3300 (iw_handler) wl_iw_get_name, /* SIOCGIWNAME */
3301 (iw_handler) NULL, /* SIOCSIWNWID */
3302 (iw_handler) NULL, /* SIOCGIWNWID */
3303 (iw_handler) wl_iw_set_freq, /* SIOCSIWFREQ */
3304 (iw_handler) wl_iw_get_freq, /* SIOCGIWFREQ */
3305 (iw_handler) wl_iw_set_mode, /* SIOCSIWMODE */
3306 (iw_handler) wl_iw_get_mode, /* SIOCGIWMODE */
3307 (iw_handler) NULL, /* SIOCSIWSENS */
3308 (iw_handler) NULL, /* SIOCGIWSENS */
3309 (iw_handler) NULL, /* SIOCSIWRANGE */
3310 (iw_handler) wl_iw_get_range, /* SIOCGIWRANGE */
3311 (iw_handler) NULL, /* SIOCSIWPRIV */
3312 (iw_handler) NULL, /* SIOCGIWPRIV */
3313 (iw_handler) NULL, /* SIOCSIWSTATS */
3314 (iw_handler) NULL, /* SIOCGIWSTATS */
3315 (iw_handler) wl_iw_set_spy, /* SIOCSIWSPY */
3316 (iw_handler) wl_iw_get_spy, /* SIOCGIWSPY */
3317 (iw_handler) NULL, /* -- hole -- */
3318 (iw_handler) NULL, /* -- hole -- */
3319 (iw_handler) wl_iw_set_wap, /* SIOCSIWAP */
3320 (iw_handler) wl_iw_get_wap, /* SIOCGIWAP */
3321 #if WIRELESS_EXT > 17
3322 (iw_handler) wl_iw_mlme, /* SIOCSIWMLME */
3323 #else
3324 (iw_handler) NULL, /* -- hole -- */
3325 #endif
3326 #ifdef WL_ESCAN
3327 (iw_handler) NULL, /* SIOCGIWAPLIST */
3328 #else
3329 (iw_handler) wl_iw_iscan_get_aplist, /* SIOCGIWAPLIST */
3330 #endif
3331 #if WIRELESS_EXT > 13
3332 #ifdef WL_ESCAN
3333 (iw_handler) wl_escan_set_scan, /* SIOCSIWSCAN */
3334 (iw_handler) wl_escan_get_scan, /* SIOCGIWSCAN */
3335 #else
3336 (iw_handler) wl_iw_iscan_set_scan, /* SIOCSIWSCAN */
3337 (iw_handler) wl_iw_iscan_get_scan, /* SIOCGIWSCAN */
3338 #endif
3339 #else /* WIRELESS_EXT > 13 */
3340 (iw_handler) NULL, /* SIOCSIWSCAN */
3341 (iw_handler) NULL, /* SIOCGIWSCAN */
3342 #endif /* WIRELESS_EXT > 13 */
3343 (iw_handler) wl_iw_set_essid, /* SIOCSIWESSID */
3344 (iw_handler) wl_iw_get_essid, /* SIOCGIWESSID */
3345 (iw_handler) wl_iw_set_nick, /* SIOCSIWNICKN */
3346 (iw_handler) wl_iw_get_nick, /* SIOCGIWNICKN */
3347 (iw_handler) NULL, /* -- hole -- */
3348 (iw_handler) NULL, /* -- hole -- */
3349 (iw_handler) wl_iw_set_rate, /* SIOCSIWRATE */
3350 (iw_handler) wl_iw_get_rate, /* SIOCGIWRATE */
3351 (iw_handler) wl_iw_set_rts, /* SIOCSIWRTS */
3352 (iw_handler) wl_iw_get_rts, /* SIOCGIWRTS */
3353 (iw_handler) wl_iw_set_frag, /* SIOCSIWFRAG */
3354 (iw_handler) wl_iw_get_frag, /* SIOCGIWFRAG */
3355 (iw_handler) wl_iw_set_txpow, /* SIOCSIWTXPOW */
3356 (iw_handler) wl_iw_get_txpow, /* SIOCGIWTXPOW */
3357 #if WIRELESS_EXT > 10
3358 (iw_handler) wl_iw_set_retry, /* SIOCSIWRETRY */
3359 (iw_handler) wl_iw_get_retry, /* SIOCGIWRETRY */
3360 #endif /* WIRELESS_EXT > 10 */
3361 (iw_handler) wl_iw_set_encode, /* SIOCSIWENCODE */
3362 (iw_handler) wl_iw_get_encode, /* SIOCGIWENCODE */
3363 (iw_handler) wl_iw_set_power, /* SIOCSIWPOWER */
3364 (iw_handler) wl_iw_get_power, /* SIOCGIWPOWER */
3365 #if WIRELESS_EXT > 17
3366 (iw_handler) NULL, /* -- hole -- */
3367 (iw_handler) NULL, /* -- hole -- */
3368 (iw_handler) wl_iw_set_wpaie, /* SIOCSIWGENIE */
3369 (iw_handler) wl_iw_get_wpaie, /* SIOCGIWGENIE */
3370 (iw_handler) wl_iw_set_wpaauth, /* SIOCSIWAUTH */
3371 (iw_handler) wl_iw_get_wpaauth, /* SIOCGIWAUTH */
3372 (iw_handler) wl_iw_set_encodeext, /* SIOCSIWENCODEEXT */
3373 (iw_handler) wl_iw_get_encodeext, /* SIOCGIWENCODEEXT */
3374 (iw_handler) wl_iw_set_pmksa, /* SIOCSIWPMKSA */
3375 #endif /* WIRELESS_EXT > 17 */
3376 };
3377
3378 #if WIRELESS_EXT > 12
3379 enum {
3380 WL_IW_SET_LEDDC = SIOCIWFIRSTPRIV,
3381 WL_IW_SET_VLANMODE,
3382 WL_IW_SET_PM,
3383 WL_IW_SET_LAST
3384 };
3385
3386 static iw_handler wl_iw_priv_handler[] = {
3387 wl_iw_set_leddc,
3388 wl_iw_set_vlanmode,
3389 wl_iw_set_pm,
3390 NULL
3391 };
3392
3393 static struct iw_priv_args wl_iw_priv_args[] = {
3394 {
3395 WL_IW_SET_LEDDC,
3396 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3397 0,
3398 "set_leddc"
3399 },
3400 {
3401 WL_IW_SET_VLANMODE,
3402 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3403 0,
3404 "set_vlanmode"
3405 },
3406 {
3407 WL_IW_SET_PM,
3408 IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
3409 0,
3410 "set_pm"
3411 },
3412 { 0, 0, 0, { 0 } }
3413 };
3414
3415 const struct iw_handler_def wl_iw_handler_def =
3416 {
3417 .num_standard = ARRAYSIZE(wl_iw_handler),
3418 .num_private = ARRAY_SIZE(wl_iw_priv_handler),
3419 .num_private_args = ARRAY_SIZE(wl_iw_priv_args),
3420 .standard = (const iw_handler *) wl_iw_handler,
3421 .private = wl_iw_priv_handler,
3422 .private_args = wl_iw_priv_args,
3423 #if WIRELESS_EXT >= 19
3424 get_wireless_stats: dhd_get_wireless_stats,
3425 #endif /* WIRELESS_EXT >= 19 */
3426 };
3427 #endif /* WIRELESS_EXT > 12 */
3428
3429 int
wl_iw_ioctl(struct net_device * dev,struct ifreq * rq,int cmd)3430 wl_iw_ioctl(
3431 struct net_device *dev,
3432 struct ifreq *rq,
3433 int cmd
3434 )
3435 {
3436 struct iwreq *wrq = (struct iwreq *) rq;
3437 struct iw_request_info info;
3438 iw_handler handler;
3439 char *extra = NULL;
3440 size_t token_size = 1;
3441 int max_tokens = 0, ret = 0;
3442 #ifndef WL_ESCAN
3443 struct dhd_pub *dhd = dhd_get_pub(dev);
3444 iscan_info_t *iscan;
3445 if (dhd && dhd->iscan)
3446 iscan = (iscan_info_t *)dhd->iscan;
3447 #endif
3448
3449 if (cmd < SIOCIWFIRST ||
3450 IW_IOCTL_IDX(cmd) >= ARRAYSIZE(wl_iw_handler) ||
3451 !(handler = wl_iw_handler[IW_IOCTL_IDX(cmd)]))
3452 return -EOPNOTSUPP;
3453
3454 switch (cmd) {
3455 case SIOCSIWESSID:
3456 case SIOCGIWESSID:
3457 case SIOCSIWNICKN:
3458 case SIOCGIWNICKN:
3459 max_tokens = IW_ESSID_MAX_SIZE + 1;
3460 break;
3461
3462 case SIOCSIWENCODE:
3463 case SIOCGIWENCODE:
3464 #if WIRELESS_EXT > 17
3465 case SIOCSIWENCODEEXT:
3466 case SIOCGIWENCODEEXT:
3467 #endif
3468 max_tokens = IW_ENCODING_TOKEN_MAX;
3469 break;
3470
3471 case SIOCGIWRANGE:
3472 max_tokens = sizeof(struct iw_range);
3473 break;
3474
3475 case SIOCGIWAPLIST:
3476 token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
3477 max_tokens = IW_MAX_AP;
3478 break;
3479
3480 #if WIRELESS_EXT > 13
3481 case SIOCGIWSCAN:
3482 #ifndef WL_ESCAN
3483 if (iscan)
3484 max_tokens = wrq->u.data.length;
3485 else
3486 #endif
3487 max_tokens = IW_SCAN_MAX_DATA;
3488 break;
3489 #endif /* WIRELESS_EXT > 13 */
3490
3491 case SIOCSIWSPY:
3492 token_size = sizeof(struct sockaddr);
3493 max_tokens = IW_MAX_SPY;
3494 break;
3495
3496 case SIOCGIWSPY:
3497 token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality);
3498 max_tokens = IW_MAX_SPY;
3499 break;
3500 default:
3501 break;
3502 }
3503
3504 if (max_tokens && wrq->u.data.pointer) {
3505 if (wrq->u.data.length > max_tokens)
3506 return -E2BIG;
3507
3508 if (!(extra = kmalloc(max_tokens * token_size, GFP_KERNEL)))
3509 return -ENOMEM;
3510
3511 if (copy_from_user(extra, wrq->u.data.pointer, wrq->u.data.length * token_size)) {
3512 kfree(extra);
3513 return -EFAULT;
3514 }
3515 }
3516
3517 info.cmd = cmd;
3518 info.flags = 0;
3519
3520 ret = handler(dev, &info, &wrq->u, extra);
3521
3522 if (extra) {
3523 if (copy_to_user(wrq->u.data.pointer, extra, wrq->u.data.length * token_size)) {
3524 kfree(extra);
3525 return -EFAULT;
3526 }
3527
3528 kfree(extra);
3529 }
3530
3531 return ret;
3532 }
3533
3534 /* Convert a connection status event into a connection status string.
3535 * Returns TRUE if a matching connection status string was found.
3536 */
3537 bool
wl_iw_conn_status_str(uint32 event_type,uint32 status,uint32 reason,char * stringBuf,uint buflen)3538 wl_iw_conn_status_str(uint32 event_type, uint32 status, uint32 reason,
3539 char* stringBuf, uint buflen)
3540 {
3541 typedef struct conn_fail_event_map_t {
3542 uint32 inEvent; /* input: event type to match */
3543 uint32 inStatus; /* input: event status code to match */
3544 uint32 inReason; /* input: event reason code to match */
3545 const char* outName; /* output: failure type */
3546 const char* outCause; /* output: failure cause */
3547 } conn_fail_event_map_t;
3548
3549 /* Map of WLC_E events to connection failure strings */
3550 # define WL_IW_DONT_CARE 9999
3551 const conn_fail_event_map_t event_map [] = {
3552 /* inEvent inStatus inReason */
3553 /* outName outCause */
3554 {WLC_E_SET_SSID, WLC_E_STATUS_SUCCESS, WL_IW_DONT_CARE,
3555 "Conn", "Success"},
3556 {WLC_E_SET_SSID, WLC_E_STATUS_NO_NETWORKS, WL_IW_DONT_CARE,
3557 "Conn", "NoNetworks"},
3558 {WLC_E_SET_SSID, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3559 "Conn", "ConfigMismatch"},
3560 {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_PRUNE_ENCR_MISMATCH,
3561 "Conn", "EncrypMismatch"},
3562 {WLC_E_PRUNE, WL_IW_DONT_CARE, WLC_E_RSN_MISMATCH,
3563 "Conn", "RsnMismatch"},
3564 {WLC_E_AUTH, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
3565 "Conn", "AuthTimeout"},
3566 {WLC_E_AUTH, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3567 "Conn", "AuthFail"},
3568 {WLC_E_AUTH, WLC_E_STATUS_NO_ACK, WL_IW_DONT_CARE,
3569 "Conn", "AuthNoAck"},
3570 {WLC_E_REASSOC, WLC_E_STATUS_FAIL, WL_IW_DONT_CARE,
3571 "Conn", "ReassocFail"},
3572 {WLC_E_REASSOC, WLC_E_STATUS_TIMEOUT, WL_IW_DONT_CARE,
3573 "Conn", "ReassocTimeout"},
3574 {WLC_E_REASSOC, WLC_E_STATUS_ABORT, WL_IW_DONT_CARE,
3575 "Conn", "ReassocAbort"},
3576 {WLC_E_PSK_SUP, WLC_SUP_KEYED, WL_IW_DONT_CARE,
3577 "Sup", "ConnSuccess"},
3578 {WLC_E_PSK_SUP, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3579 "Sup", "WpaHandshakeFail"},
3580 {WLC_E_DEAUTH_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3581 "Conn", "Deauth"},
3582 {WLC_E_DISASSOC_IND, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3583 "Conn", "DisassocInd"},
3584 {WLC_E_DISASSOC, WL_IW_DONT_CARE, WL_IW_DONT_CARE,
3585 "Conn", "Disassoc"}
3586 };
3587
3588 const char* name = "";
3589 const char* cause = NULL;
3590 int i;
3591
3592 /* Search the event map table for a matching event */
3593 for (i = 0; i < sizeof(event_map)/sizeof(event_map[0]); i++) {
3594 const conn_fail_event_map_t* row = &event_map[i];
3595 if (row->inEvent == event_type &&
3596 (row->inStatus == status || row->inStatus == WL_IW_DONT_CARE) &&
3597 (row->inReason == reason || row->inReason == WL_IW_DONT_CARE)) {
3598 name = row->outName;
3599 cause = row->outCause;
3600 break;
3601 }
3602 }
3603
3604 /* If found, generate a connection failure string and return TRUE */
3605 if (cause) {
3606 memset(stringBuf, 0, buflen);
3607 (void)snprintf(stringBuf, buflen, "%s %s %02d %02d", name, cause, status, reason);
3608 WL_TRACE(("Connection status: %s\n", stringBuf));
3609 return TRUE;
3610 } else {
3611 return FALSE;
3612 }
3613 }
3614
3615 #if (WIRELESS_EXT > 14)
3616 /* Check if we have received an event that indicates connection failure
3617 * If so, generate a connection failure report string.
3618 * The caller supplies a buffer to hold the generated string.
3619 */
3620 static bool
wl_iw_check_conn_fail(wl_event_msg_t * e,char * stringBuf,uint buflen)3621 wl_iw_check_conn_fail(wl_event_msg_t *e, char* stringBuf, uint buflen)
3622 {
3623 uint32 event = ntoh32(e->event_type);
3624 uint32 status = ntoh32(e->status);
3625 uint32 reason = ntoh32(e->reason);
3626
3627 if (wl_iw_conn_status_str(event, status, reason, stringBuf, buflen)) {
3628 return TRUE;
3629 } else
3630 {
3631 return FALSE;
3632 }
3633 }
3634 #endif /* WIRELESS_EXT > 14 */
3635
3636 #ifndef IW_CUSTOM_MAX
3637 #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
3638 #endif /* IW_CUSTOM_MAX */
3639
3640 void
wl_iw_event(struct net_device * dev,wl_event_msg_t * e,void * data)3641 wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data)
3642 {
3643 #if WIRELESS_EXT > 13
3644 union iwreq_data wrqu;
3645 char extra[IW_CUSTOM_MAX + 1];
3646 int cmd = 0;
3647 uint32 event_type = ntoh32(e->event_type);
3648 uint16 flags = ntoh16(e->flags);
3649 uint32 datalen = ntoh32(e->datalen);
3650 uint32 status = ntoh32(e->status);
3651 uint32 reason = ntoh32(e->reason);
3652 struct dhd_pub *dhd = dhd_get_pub(dev);
3653 wl_conn_info_t *conn_info = NULL;
3654 #ifdef WL_ESCAN
3655 wl_escan_info_t *escan;
3656 if (dhd && dhd->escan) {
3657 escan = (wl_escan_info_t *)dhd->escan;
3658 conn_info = &escan->conn_info;
3659 }
3660 #else
3661 iscan_info_t *iscan;
3662 if (dhd && dhd->iscan) {
3663 iscan = (iscan_info_t *)dhd->iscan;
3664 conn_info = &iscan->conn_info;
3665 }
3666 #endif
3667
3668 memset(&wrqu, 0, sizeof(wrqu));
3669 memset(extra, 0, sizeof(extra));
3670
3671 memcpy(wrqu.addr.sa_data, &e->addr, ETHER_ADDR_LEN);
3672 wrqu.addr.sa_family = ARPHRD_ETHER;
3673
3674 switch (event_type) {
3675 case WLC_E_TXFAIL:
3676 cmd = IWEVTXDROP;
3677 break;
3678 #if WIRELESS_EXT > 14
3679 case WLC_E_JOIN:
3680 case WLC_E_ASSOC_IND:
3681 case WLC_E_REASSOC_IND:
3682 cmd = IWEVREGISTERED;
3683 break;
3684 case WLC_E_DEAUTH:
3685 case WLC_E_DISASSOC:
3686 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTED);
3687 printf("%s: disconnected with "MACSTR", event %d, reason %d\n",
3688 __FUNCTION__, MAC2STR((u8 *)wrqu.addr.sa_data), event_type, reason);
3689 break;
3690 case WLC_E_DEAUTH_IND:
3691 case WLC_E_DISASSOC_IND:
3692 cmd = SIOCGIWAP;
3693 wrqu.data.length = strlen(extra);
3694 bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
3695 bzero(&extra, ETHER_ADDR_LEN);
3696 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTED);
3697 printf("%s: disconnected with "MACSTR", event %d, reason %d\n",
3698 __FUNCTION__, MAC2STR((u8 *)wrqu.addr.sa_data), event_type, reason);
3699 break;
3700
3701 case WLC_E_LINK:
3702 cmd = SIOCGIWAP;
3703 if (!(flags & WLC_EVENT_MSG_LINK)) {
3704 printf("%s: Link Down with "MACSTR", reason=%d\n", __FUNCTION__,
3705 MAC2STR((u8 *)wrqu.addr.sa_data), reason);
3706 bzero(wrqu.addr.sa_data, ETHER_ADDR_LEN);
3707 bzero(&extra, ETHER_ADDR_LEN);
3708 wl_iw_update_connect_status(dev, WL_EXT_STATUS_DISCONNECTED);
3709 } else {
3710 printf("%s: Link UP with "MACSTR"\n", __FUNCTION__,
3711 MAC2STR((u8 *)wrqu.addr.sa_data));
3712 }
3713 break;
3714 case WLC_E_ACTION_FRAME:
3715 cmd = IWEVCUSTOM;
3716 if (datalen + 1 <= sizeof(extra)) {
3717 wrqu.data.length = datalen + 1;
3718 extra[0] = WLC_E_ACTION_FRAME;
3719 memcpy(&extra[1], data, datalen);
3720 WL_TRACE(("WLC_E_ACTION_FRAME len %d \n", wrqu.data.length));
3721 }
3722 break;
3723
3724 case WLC_E_ACTION_FRAME_COMPLETE:
3725 cmd = IWEVCUSTOM;
3726 if (sizeof(status) + 1 <= sizeof(extra)) {
3727 wrqu.data.length = sizeof(status) + 1;
3728 extra[0] = WLC_E_ACTION_FRAME_COMPLETE;
3729 memcpy(&extra[1], &status, sizeof(status));
3730 WL_TRACE(("wl_iw_event status %d \n", status));
3731 }
3732 break;
3733 #endif /* WIRELESS_EXT > 14 */
3734 #if WIRELESS_EXT > 17
3735 case WLC_E_MIC_ERROR: {
3736 struct iw_michaelmicfailure *micerrevt = (struct iw_michaelmicfailure *)&extra;
3737 cmd = IWEVMICHAELMICFAILURE;
3738 wrqu.data.length = sizeof(struct iw_michaelmicfailure);
3739 if (flags & WLC_EVENT_MSG_GROUP)
3740 micerrevt->flags |= IW_MICFAILURE_GROUP;
3741 else
3742 micerrevt->flags |= IW_MICFAILURE_PAIRWISE;
3743 memcpy(micerrevt->src_addr.sa_data, &e->addr, ETHER_ADDR_LEN);
3744 micerrevt->src_addr.sa_family = ARPHRD_ETHER;
3745
3746 break;
3747 }
3748
3749 case WLC_E_ASSOC_REQ_IE:
3750 cmd = IWEVASSOCREQIE;
3751 wrqu.data.length = datalen;
3752 if (datalen < sizeof(extra))
3753 memcpy(extra, data, datalen);
3754 break;
3755
3756 case WLC_E_ASSOC_RESP_IE:
3757 cmd = IWEVASSOCRESPIE;
3758 wrqu.data.length = datalen;
3759 if (datalen < sizeof(extra))
3760 memcpy(extra, data, datalen);
3761 break;
3762
3763 case WLC_E_PMKID_CACHE: {
3764 struct iw_pmkid_cand *iwpmkidcand = (struct iw_pmkid_cand *)&extra;
3765 pmkid_cand_list_t *pmkcandlist;
3766 pmkid_cand_t *pmkidcand;
3767 int count;
3768
3769 if (data == NULL)
3770 break;
3771
3772 cmd = IWEVPMKIDCAND;
3773 pmkcandlist = data;
3774 count = ntoh32_ua((uint8 *)&pmkcandlist->npmkid_cand);
3775 wrqu.data.length = sizeof(struct iw_pmkid_cand);
3776 pmkidcand = pmkcandlist->pmkid_cand;
3777 while (count) {
3778 bzero(iwpmkidcand, sizeof(struct iw_pmkid_cand));
3779 if (pmkidcand->preauth)
3780 iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH;
3781 bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data,
3782 ETHER_ADDR_LEN);
3783 wireless_send_event(dev, cmd, &wrqu, extra);
3784 pmkidcand++;
3785 count--;
3786 }
3787 break;
3788 }
3789 #endif /* WIRELESS_EXT > 17 */
3790
3791 #ifdef WL_ESCAN
3792 case WLC_E_ESCAN_RESULT:
3793 WL_TRACE(("event WLC_E_ESCAN_RESULT\n"));
3794 wl_escan_event(dev, e, data);
3795 break;
3796 #else
3797
3798 case WLC_E_SCAN_COMPLETE:
3799 #if WIRELESS_EXT > 14
3800 cmd = SIOCGIWSCAN;
3801 #endif
3802 WL_TRACE(("event WLC_E_SCAN_COMPLETE\n"));
3803 // terence 20150224: fix "wlan0: (WE) : Wireless Event too big (65306)"
3804 memset(&wrqu, 0, sizeof(wrqu));
3805 if ((iscan) && (iscan->sysioc_pid >= 0) &&
3806 (iscan->iscan_state != ISCAN_STATE_IDLE))
3807 up(&iscan->sysioc_sem);
3808 break;
3809 #endif
3810
3811 default:
3812 /* Cannot translate event */
3813 break;
3814 }
3815
3816 if (cmd) {
3817 #ifndef WL_ESCAN
3818 if (cmd == SIOCGIWSCAN) {
3819 if ((!iscan) || (iscan->sysioc_pid < 0)) {
3820 wireless_send_event(dev, cmd, &wrqu, NULL);
3821 };
3822 } else
3823 #endif
3824 wireless_send_event(dev, cmd, &wrqu, extra);
3825 }
3826
3827 #if WIRELESS_EXT > 14
3828 /* Look for WLC events that indicate a connection failure.
3829 * If found, generate an IWEVCUSTOM event.
3830 */
3831 memset(extra, 0, sizeof(extra));
3832 if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) {
3833 cmd = IWEVCUSTOM;
3834 wrqu.data.length = strlen(extra);
3835 wireless_send_event(dev, cmd, &wrqu, extra);
3836 }
3837 #endif /* WIRELESS_EXT > 14 */
3838
3839 #endif /* WIRELESS_EXT > 13 */
3840 }
3841
3842 #ifdef WL_NAN
wl_iw_get_wireless_stats_cbfn(void * ctx,uint8 * data,uint16 type,uint16 len)3843 static int wl_iw_get_wireless_stats_cbfn(void *ctx, uint8 *data, uint16 type, uint16 len)
3844 {
3845 struct iw_statistics *wstats = ctx;
3846 int res = BCME_OK;
3847
3848 switch (type) {
3849 case WL_CNT_XTLV_WLC: {
3850 wl_cnt_wlc_t *cnt = (wl_cnt_wlc_t *)data;
3851 if (len > sizeof(wl_cnt_wlc_t)) {
3852 printf("counter structure length invalid! %d > %d\n",
3853 len, (int)sizeof(wl_cnt_wlc_t));
3854 }
3855 wstats->discard.nwid = 0;
3856 wstats->discard.code = dtoh32(cnt->rxundec);
3857 wstats->discard.fragment = dtoh32(cnt->rxfragerr);
3858 wstats->discard.retries = dtoh32(cnt->txfail);
3859 wstats->discard.misc = dtoh32(cnt->rxrunt) + dtoh32(cnt->rxgiant);
3860 wstats->miss.beacon = 0;
3861 WL_TRACE(("wl_iw_get_wireless_stats counters txframe=%d txbyte=%d\n",
3862 dtoh32(cnt->txframe), dtoh32(cnt->txbyte)));
3863 WL_TRACE(("wl_iw_get_wireless_stats counters rxundec=%d\n",
3864 dtoh32(cnt->rxundec)));
3865 WL_TRACE(("wl_iw_get_wireless_stats counters txfail=%d\n",
3866 dtoh32(cnt->txfail)));
3867 WL_TRACE(("wl_iw_get_wireless_stats counters rxfragerr=%d\n",
3868 dtoh32(cnt->rxfragerr)));
3869 WL_TRACE(("wl_iw_get_wireless_stats counters rxrunt=%d\n",
3870 dtoh32(cnt->rxrunt)));
3871 WL_TRACE(("wl_iw_get_wireless_stats counters rxgiant=%d\n",
3872 dtoh32(cnt->rxgiant)));
3873 break;
3874 }
3875 case WL_CNT_XTLV_CNTV_LE10_UCODE:
3876 case WL_CNT_XTLV_LT40_UCODE_V1:
3877 case WL_CNT_XTLV_GE40_UCODE_V1:
3878 {
3879 /* Offsets of rxfrmtoolong and rxbadplcp are the same in
3880 * wl_cnt_v_le10_mcst_t, wl_cnt_lt40mcst_v1_t, and wl_cnt_ge40mcst_v1_t.
3881 * So we can just cast to wl_cnt_v_le10_mcst_t here.
3882 */
3883 wl_cnt_v_le10_mcst_t *cnt = (wl_cnt_v_le10_mcst_t *)data;
3884 if (len != WL_CNT_MCST_STRUCT_SZ) {
3885 printf("counter structure length mismatch! %d != %d\n",
3886 len, WL_CNT_MCST_STRUCT_SZ);
3887 }
3888 WL_TRACE(("wl_iw_get_wireless_stats counters rxfrmtoolong=%d\n",
3889 dtoh32(cnt->rxfrmtoolong)));
3890 WL_TRACE(("wl_iw_get_wireless_stats counters rxbadplcp=%d\n",
3891 dtoh32(cnt->rxbadplcp)));
3892 BCM_REFERENCE(cnt);
3893 break;
3894 }
3895 default:
3896 WL_ERROR(("%s %d: Unsupported type %d\n", __FUNCTION__, __LINE__, type));
3897 break;
3898 }
3899 return res;
3900 }
3901 #endif
3902
wl_iw_get_wireless_stats(struct net_device * dev,struct iw_statistics * wstats)3903 int wl_iw_get_wireless_stats(struct net_device *dev, struct iw_statistics *wstats)
3904 {
3905 int res = 0;
3906 int phy_noise;
3907 int rssi;
3908 scb_val_t scb_val;
3909 #if WIRELESS_EXT > 11
3910 char *cntbuf = NULL;
3911 wl_cnt_info_t *cntinfo;
3912 uint16 ver;
3913 uint32 corerev = 0;
3914 #endif /* WIRELESS_EXT > 11 */
3915
3916 phy_noise = 0;
3917 res = dev_wlc_ioctl(dev, WLC_GET_PHY_NOISE, &phy_noise, sizeof(phy_noise));
3918 if (res) {
3919 WL_ERROR(("%s: WLC_GET_PHY_NOISE error=%d\n", __FUNCTION__, res));
3920 goto done;
3921 }
3922
3923 phy_noise = dtoh32(phy_noise);
3924 WL_TRACE(("wl_iw_get_wireless_stats phy noise=%d\n *****", phy_noise));
3925
3926 memset(&scb_val, 0, sizeof(scb_val));
3927 res = dev_wlc_ioctl(dev, WLC_GET_RSSI, &scb_val, sizeof(scb_val_t));
3928 if (res) {
3929 WL_ERROR(("%s: WLC_GET_RSSI error=%d\n", __FUNCTION__, res));
3930 goto done;
3931 }
3932
3933 rssi = dtoh32(scb_val.val);
3934 rssi = MIN(rssi, RSSI_MAXVAL);
3935 WL_TRACE(("wl_iw_get_wireless_stats rssi=%d ****** \n", rssi));
3936 if (rssi <= WL_IW_RSSI_NO_SIGNAL)
3937 wstats->qual.qual = 0;
3938 else if (rssi <= WL_IW_RSSI_VERY_LOW)
3939 wstats->qual.qual = 1;
3940 else if (rssi <= WL_IW_RSSI_LOW)
3941 wstats->qual.qual = 2;
3942 else if (rssi <= WL_IW_RSSI_GOOD)
3943 wstats->qual.qual = 3;
3944 else if (rssi <= WL_IW_RSSI_VERY_GOOD)
3945 wstats->qual.qual = 4;
3946 else
3947 wstats->qual.qual = 5;
3948
3949 /* Wraps to 0 if RSSI is 0 */
3950 wstats->qual.level = 0x100 + rssi;
3951 wstats->qual.noise = 0x100 + phy_noise;
3952 #if WIRELESS_EXT > 18
3953 wstats->qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM);
3954 #else
3955 wstats->qual.updated |= 7;
3956 #endif /* WIRELESS_EXT > 18 */
3957
3958 #if WIRELESS_EXT > 11
3959 WL_TRACE(("wl_iw_get_wireless_stats counters\n *****"));
3960
3961 cntbuf = kmalloc(MAX_WLIW_IOCTL_LEN, GFP_KERNEL);
3962 if (!cntbuf) {
3963 res = BCME_NOMEM;
3964 goto done;
3965 }
3966
3967 memset(cntbuf, 0, MAX_WLIW_IOCTL_LEN);
3968 res = dev_wlc_bufvar_get(dev, "counters", cntbuf, MAX_WLIW_IOCTL_LEN);
3969 if (res)
3970 {
3971 WL_ERROR(("wl_iw_get_wireless_stats counters failed error=%d ****** \n", res));
3972 goto done;
3973 }
3974
3975 cntinfo = (wl_cnt_info_t *)cntbuf;
3976 cntinfo->version = dtoh16(cntinfo->version);
3977 cntinfo->datalen = dtoh16(cntinfo->datalen);
3978 ver = cntinfo->version;
3979 #ifdef WL_NAN
3980 CHK_CNTBUF_DATALEN(cntbuf, MAX_WLIW_IOCTL_LEN);
3981 #endif
3982 if (ver > WL_CNT_T_VERSION) {
3983 WL_TRACE(("\tIncorrect version of counters struct: expected %d; got %d\n",
3984 WL_CNT_T_VERSION, ver));
3985 res = BCME_VERSION;
3986 goto done;
3987 }
3988
3989 if (ver == WL_CNT_VERSION_11) {
3990 wlc_rev_info_t revinfo;
3991 memset(&revinfo, 0, sizeof(revinfo));
3992 res = dev_wlc_ioctl(dev, WLC_GET_REVINFO, &revinfo, sizeof(revinfo));
3993 if (res) {
3994 WL_ERROR(("%s: WLC_GET_REVINFO failed %d\n", __FUNCTION__, res));
3995 goto done;
3996 }
3997 corerev = dtoh32(revinfo.corerev);
3998 }
3999
4000 #ifdef WL_NAN
4001 res = wl_cntbuf_to_xtlv_format(NULL, cntinfo, MAX_WLIW_IOCTL_LEN, corerev);
4002 if (res) {
4003 WL_ERROR(("%s: wl_cntbuf_to_xtlv_format failed %d\n", __FUNCTION__, res));
4004 goto done;
4005 }
4006
4007 if ((res = bcm_unpack_xtlv_buf(wstats, cntinfo->data, cntinfo->datalen,
4008 BCM_XTLV_OPTION_ALIGN32, wl_iw_get_wireless_stats_cbfn))) {
4009 goto done;
4010 }
4011 #endif
4012 #endif /* WIRELESS_EXT > 11 */
4013
4014 done:
4015 #if WIRELESS_EXT > 11
4016 if (cntbuf) {
4017 kfree(cntbuf);
4018 }
4019 #endif /* WIRELESS_EXT > 11 */
4020 return res;
4021 }
4022
4023 #ifndef WL_ESCAN
4024 static void
wl_iw_timerfunc(struct timer_list * t)4025 wl_iw_timerfunc(
4026 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
4027 struct timer_list *t
4028 #else
4029 unsigned long data
4030 #endif
4031 )
4032 {
4033 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
4034 iscan_info_t *iscan = from_timer(iscan, t, timer);
4035 #else
4036 iscan_info_t *iscan = (iscan_info_t *)data;
4037 #endif
4038 iscan->timer_on = 0;
4039 if (iscan->iscan_state != ISCAN_STATE_IDLE) {
4040 WL_TRACE(("timer trigger\n"));
4041 up(&iscan->sysioc_sem);
4042 }
4043 }
4044
4045 static void
wl_iw_set_event_mask(struct net_device * dev)4046 wl_iw_set_event_mask(struct net_device *dev)
4047 {
4048 char eventmask[WL_EVENTING_MASK_LEN];
4049 char iovbuf[WL_EVENTING_MASK_LEN + 12]; /* Room for "event_msgs" + '\0' + bitvec */
4050
4051 dev_iw_iovar_getbuf(dev, "event_msgs", "", 0, iovbuf, sizeof(iovbuf));
4052 bcopy(iovbuf, eventmask, WL_EVENTING_MASK_LEN);
4053 setbit(eventmask, WLC_E_SCAN_COMPLETE);
4054 dev_iw_iovar_setbuf(dev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN,
4055 iovbuf, sizeof(iovbuf));
4056 }
4057
4058 static int
wl_iw_iscan_prep(wl_scan_params_t * params,wlc_ssid_t * ssid)4059 wl_iw_iscan_prep(wl_scan_params_t *params, wlc_ssid_t *ssid)
4060 {
4061 int err = 0;
4062
4063 memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN);
4064 params->bss_type = DOT11_BSSTYPE_ANY;
4065 params->scan_type = 0;
4066 params->nprobes = -1;
4067 params->active_time = -1;
4068 params->passive_time = -1;
4069 params->home_time = -1;
4070 params->channel_num = 0;
4071
4072 params->nprobes = htod32(params->nprobes);
4073 params->active_time = htod32(params->active_time);
4074 params->passive_time = htod32(params->passive_time);
4075 params->home_time = htod32(params->home_time);
4076 if (ssid && ssid->SSID_len)
4077 memcpy(¶ms->ssid, ssid, sizeof(wlc_ssid_t));
4078
4079 return err;
4080 }
4081
4082 static int
wl_iw_iscan(iscan_info_t * iscan,wlc_ssid_t * ssid,uint16 action)4083 wl_iw_iscan(iscan_info_t *iscan, wlc_ssid_t *ssid, uint16 action)
4084 {
4085 int params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_iscan_params_t, params));
4086 wl_iscan_params_t *params;
4087 int err = 0;
4088
4089 if (ssid && ssid->SSID_len) {
4090 params_size += sizeof(wlc_ssid_t);
4091 }
4092 params = (wl_iscan_params_t*)kmalloc(params_size, GFP_KERNEL);
4093 if (params == NULL) {
4094 return -ENOMEM;
4095 }
4096 memset(params, 0, params_size);
4097 ASSERT(params_size < WLC_IOCTL_SMLEN);
4098
4099 err = wl_iw_iscan_prep(¶ms->params, ssid);
4100
4101 if (!err) {
4102 params->version = htod32(ISCAN_REQ_VERSION);
4103 params->action = htod16(action);
4104 params->scan_duration = htod16(0);
4105
4106 (void) dev_iw_iovar_setbuf(iscan->dev, "iscan", params, params_size,
4107 iscan->ioctlbuf, WLC_IOCTL_SMLEN);
4108 }
4109
4110 kfree(params);
4111 return err;
4112 }
4113
4114 static uint32
wl_iw_iscan_get(iscan_info_t * iscan)4115 wl_iw_iscan_get(iscan_info_t *iscan)
4116 {
4117 iscan_buf_t * buf;
4118 iscan_buf_t * ptr;
4119 wl_iscan_results_t * list_buf;
4120 wl_iscan_results_t list;
4121 wl_scan_results_t *results;
4122 uint32 status;
4123
4124 /* buffers are allocated on demand */
4125 if (iscan->list_cur) {
4126 buf = iscan->list_cur;
4127 iscan->list_cur = buf->next;
4128 }
4129 else {
4130 buf = kmalloc(sizeof(iscan_buf_t), GFP_KERNEL);
4131 if (!buf)
4132 return WL_SCAN_RESULTS_ABORTED;
4133 buf->next = NULL;
4134 if (!iscan->list_hdr)
4135 iscan->list_hdr = buf;
4136 else {
4137 ptr = iscan->list_hdr;
4138 while (ptr->next) {
4139 ptr = ptr->next;
4140 }
4141 ptr->next = buf;
4142 }
4143 }
4144 memset(buf->iscan_buf, 0, WLC_IW_ISCAN_MAXLEN);
4145 list_buf = (wl_iscan_results_t*)buf->iscan_buf;
4146 results = &list_buf->results;
4147 results->buflen = WL_ISCAN_RESULTS_FIXED_SIZE;
4148 results->version = 0;
4149 results->count = 0;
4150
4151 memset(&list, 0, sizeof(list));
4152 list.results.buflen = htod32(WLC_IW_ISCAN_MAXLEN);
4153 (void) dev_iw_iovar_getbuf(
4154 iscan->dev,
4155 "iscanresults",
4156 &list,
4157 WL_ISCAN_RESULTS_FIXED_SIZE,
4158 buf->iscan_buf,
4159 WLC_IW_ISCAN_MAXLEN);
4160 results->buflen = dtoh32(results->buflen);
4161 results->version = dtoh32(results->version);
4162 results->count = dtoh32(results->count);
4163 WL_TRACE(("results->count = %d\n", results->count));
4164
4165 WL_TRACE(("results->buflen = %d\n", results->buflen));
4166 status = dtoh32(list_buf->status);
4167 return status;
4168 }
4169
wl_iw_send_scan_complete(iscan_info_t * iscan)4170 static void wl_iw_send_scan_complete(iscan_info_t *iscan)
4171 {
4172 union iwreq_data wrqu;
4173
4174 memset(&wrqu, 0, sizeof(wrqu));
4175
4176 /* wext expects to get no data for SIOCGIWSCAN Event */
4177 wireless_send_event(iscan->dev, SIOCGIWSCAN, &wrqu, NULL);
4178 }
4179
4180 static int
_iscan_sysioc_thread(void * data)4181 _iscan_sysioc_thread(void *data)
4182 {
4183 uint32 status;
4184 iscan_info_t *iscan = (iscan_info_t *)data;
4185
4186 printf("%s: thread Enter\n", __FUNCTION__);
4187 DAEMONIZE("iscan_sysioc");
4188
4189 status = WL_SCAN_RESULTS_PARTIAL;
4190 while (down_interruptible(&iscan->sysioc_sem) == 0) {
4191 if (iscan->timer_on) {
4192 del_timer(&iscan->timer);
4193 iscan->timer_on = 0;
4194 }
4195
4196 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4197 rtnl_lock();
4198 #endif
4199 status = wl_iw_iscan_get(iscan);
4200 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4201 rtnl_unlock();
4202 #endif
4203
4204 switch (status) {
4205 case WL_SCAN_RESULTS_PARTIAL:
4206 WL_TRACE(("iscanresults incomplete\n"));
4207 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4208 rtnl_lock();
4209 #endif
4210 /* make sure our buffer size is enough before going next round */
4211 wl_iw_iscan(iscan, NULL, WL_SCAN_ACTION_CONTINUE);
4212 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
4213 rtnl_unlock();
4214 #endif
4215 /* Reschedule the timer */
4216 iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
4217 add_timer(&iscan->timer);
4218 iscan->timer_on = 1;
4219 break;
4220 case WL_SCAN_RESULTS_SUCCESS:
4221 WL_TRACE(("iscanresults complete\n"));
4222 iscan->iscan_state = ISCAN_STATE_IDLE;
4223 wl_iw_send_scan_complete(iscan);
4224 break;
4225 case WL_SCAN_RESULTS_PENDING:
4226 WL_TRACE(("iscanresults pending\n"));
4227 /* Reschedule the timer */
4228 iscan->timer.expires = jiffies + msecs_to_jiffies(iscan->timer_ms);
4229 add_timer(&iscan->timer);
4230 iscan->timer_on = 1;
4231 break;
4232 case WL_SCAN_RESULTS_ABORTED:
4233 WL_TRACE(("iscanresults aborted\n"));
4234 iscan->iscan_state = ISCAN_STATE_IDLE;
4235 wl_iw_send_scan_complete(iscan);
4236 break;
4237 default:
4238 WL_TRACE(("iscanresults returned unknown status %d\n", status));
4239 break;
4240 }
4241 }
4242 printf("%s: was terminated\n", __FUNCTION__);
4243 complete_and_exit(&iscan->sysioc_exited, 0);
4244 }
4245
wl_iw_down(dhd_pub_t * dhdp)4246 void wl_iw_down(dhd_pub_t *dhdp)
4247 {
4248 iscan_info_t *iscan = dhdp->iscan;
4249
4250 if (iscan)
4251 wl_ext_add_remove_pm_enable_work(&iscan->conn_info, FALSE);
4252 }
4253
4254 int
wl_iw_attach(struct net_device * dev,dhd_pub_t * dhdp)4255 wl_iw_attach(struct net_device *dev, dhd_pub_t *dhdp)
4256 {
4257 iscan_info_t *iscan = NULL;
4258
4259 printf("%s: Enter\n", __FUNCTION__);
4260
4261 if (!dev)
4262 return 0;
4263
4264 iscan = kmalloc(sizeof(iscan_info_t), GFP_KERNEL);
4265 if (!iscan)
4266 return -ENOMEM;
4267 dhdp->iscan = (void *)iscan;
4268 memset(iscan, 0, sizeof(iscan_info_t));
4269 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
4270 iscan->kthread = NULL;
4271 #endif
4272 iscan->sysioc_pid = -1;
4273 /* we only care about main interface so save a global here */
4274 iscan->dev = dev;
4275 iscan->conn_info.dev = dev;
4276 iscan->conn_info.dhd = dhdp;
4277 iscan->iscan_state = ISCAN_STATE_IDLE;
4278
4279 /* Set up the timer */
4280 iscan->timer_ms = 2000;
4281 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
4282 timer_setup(&iscan->timer, wl_iw_timerfunc, 0);
4283 #else
4284 init_timer(&iscan->timer);
4285 iscan->timer.data = (ulong)iscan;
4286 iscan->timer.function = wl_iw_timerfunc;
4287 #endif
4288
4289 sema_init(&iscan->sysioc_sem, 0);
4290 init_completion(&iscan->sysioc_exited);
4291 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
4292 iscan->kthread = kthread_run(_iscan_sysioc_thread, iscan, "iscan_sysioc");
4293 iscan->sysioc_pid = iscan->kthread->pid;
4294 #else
4295 iscan->sysioc_pid = kernel_thread(_iscan_sysioc_thread, iscan, 0);
4296 #endif
4297 if (iscan->sysioc_pid < 0)
4298 return -ENOMEM;
4299 mutex_init(&iscan->conn_info.pm_sync);
4300 INIT_DELAYED_WORK(&iscan->conn_info.pm_enable_work, wl_ext_pm_work_handler);
4301 return 0;
4302 }
4303
wl_iw_detach(dhd_pub_t * dhdp)4304 void wl_iw_detach(dhd_pub_t *dhdp)
4305 {
4306 iscan_buf_t *buf;
4307 iscan_info_t *iscan = dhdp->iscan;
4308 if (!iscan)
4309 return;
4310 if (iscan->sysioc_pid >= 0) {
4311 KILL_PROC(iscan->sysioc_pid, SIGTERM);
4312 wait_for_completion(&iscan->sysioc_exited);
4313 }
4314 wl_ext_add_remove_pm_enable_work(&iscan->conn_info, FALSE);
4315
4316 while (iscan->list_hdr) {
4317 buf = iscan->list_hdr->next;
4318 kfree(iscan->list_hdr);
4319 iscan->list_hdr = buf;
4320 }
4321 kfree(iscan);
4322 dhdp->iscan = NULL;
4323 }
4324 #endif /* !WL_ESCAN */
4325
4326 #endif /* USE_IW */
4327