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