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