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