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