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