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