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