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