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