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