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