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