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