4 * Copyright (C) 2005 Mike Baker
5 * Copyright (C) 2005-2007 Felix Fietkau <nbd@openwrt.org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 #include <linux/config.h>
26 #include <linux/module.h>
27 #include <linux/moduleparam.h>
28 #include <linux/init.h>
29 #include <linux/if_arp.h>
30 #include <linux/wireless.h>
31 #include <linux/timer.h>
32 #include <linux/delay.h>
33 #include <linux/random.h>
34 #include <net/iw_handler.h>
35 #include <asm/uaccess.h>
41 char buf
[WLC_IOCTL_MAXLEN
];
42 static struct net_device
*dev
;
44 static int random
= 1;
47 static struct iw_statistics wstats
;
48 static int last_mode
= -1;
49 static int scan_cur
= 0;
51 /* The frequency of each channel in MHz */
52 const long channel_frequency
[] = {
53 2412, 2417, 2422, 2427, 2432, 2437, 2442,
54 2447, 2452, 2457, 2462, 2467, 2472, 2484
56 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
59 #define SCAN_RETRY_MAX 5
60 #define RNG_POLL_FREQ 1
62 typedef struct internal_wsec_key
{
64 uint8 unknown_1
; // 0x01
66 uint8 unknown_2
[7]; // 0x03
69 char data
[32]; // 0x0e
74 void print_buffer(int len
, unsigned char *buf
);
77 static int wl_ioctl(struct net_device
*dev
, int cmd
, void *buf
, int len
)
79 mm_segment_t old_fs
= get_fs();
86 strncpy(ifr
.ifr_name
, dev
->name
, IFNAMSIZ
);
87 ifr
.ifr_data
= (caddr_t
) &ioc
;
89 ret
= dev
->do_ioctl(dev
,&ifr
,SIOCDEVPRIVATE
);
94 #if !defined(DEBUG) || !defined(WL_WEXT)
96 wl_iovar_getbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
102 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
103 iolen
= namelen
+ paramlen
;
105 /* check for overflow */
107 return (BCME_BUFTOOSHORT
);
109 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
110 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
112 err
= wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
118 wl_iovar_setbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
123 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
124 iolen
= namelen
+ paramlen
;
126 /* check for overflow */
128 return (BCME_BUFTOOSHORT
);
130 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
131 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
133 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
137 wl_iovar_set(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
)
139 char smbuf
[WLC_IOCTL_SMLEN
];
141 return wl_iovar_setbuf(dev
, iovar
, param
, paramlen
, smbuf
, sizeof(smbuf
));
145 wl_iovar_get(struct net_device
*dev
, char *iovar
, void *bufptr
, int buflen
)
147 char smbuf
[WLC_IOCTL_SMLEN
];
150 /* use the return buffer if it is bigger than what we have on the stack */
151 if (buflen
> sizeof(smbuf
)) {
152 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, bufptr
, buflen
);
154 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, smbuf
, sizeof(smbuf
));
156 memcpy(bufptr
, smbuf
, buflen
);
164 * format a bsscfg indexed iovar buffer
167 wl_bssiovar_mkbuf(char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
, int buflen
,
170 char *prefix
= "bsscfg:";
176 prefixlen
= strlen(prefix
); /* length of bsscfg prefix */
177 namelen
= strlen(iovar
) + 1; /* length of iovar name + null */
178 iolen
= prefixlen
+ namelen
+ sizeof(int) + paramlen
;
180 /* check for overflow */
181 if (buflen
< 0 || iolen
> (uint
)buflen
) {
183 return BCME_BUFTOOSHORT
;
188 /* copy prefix, no null */
189 memcpy(p
, prefix
, prefixlen
);
192 /* copy iovar name including null */
193 memcpy(p
, iovar
, namelen
);
196 /* bss config index as first param */
197 memcpy(p
, &bssidx
, sizeof(int32
));
200 /* parameter buffer follows */
202 memcpy(p
, param
, paramlen
);
209 * set named & bss indexed driver variable to buffer value
212 wl_bssiovar_setbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
218 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
222 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
226 * get named & bss indexed driver variable buffer value
229 wl_bssiovar_getbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
235 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
239 return wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
243 * set named & bss indexed driver variable to buffer value
246 wl_bssiovar_set(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
)
248 char smbuf
[WLC_IOCTL_SMLEN
];
250 return wl_bssiovar_setbuf(dev
, iovar
, bssidx
, param
, paramlen
, smbuf
, sizeof(smbuf
));
254 * get named & bss indexed driver variable buffer value
257 wl_bssiovar_get(struct net_device
*dev
, char *iovar
, int bssidx
, void *outbuf
, int len
)
259 char smbuf
[WLC_IOCTL_SMLEN
];
262 /* use the return buffer if it is bigger than what we have on the stack */
263 if (len
> (int)sizeof(smbuf
)) {
264 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, outbuf
, len
);
266 memset(smbuf
, 0, sizeof(smbuf
));
267 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, smbuf
, sizeof(smbuf
));
269 memcpy(outbuf
, smbuf
, len
);
279 int get_primary_key(struct net_device
*dev
)
283 for (key
= val
= 0; (key
< 4) && (val
== 0); key
++) {
285 if (wl_ioctl(dev
, WLC_GET_KEY_PRIMARY
, &val
, sizeof(val
)) < 0)
292 static int wlcompat_ioctl_getiwrange(struct net_device
*dev
,
296 struct iw_range
*range
;
298 range
= (struct iw_range
*) extra
;
299 memset(extra
, 0, sizeof(struct iw_range
));
301 range
->we_version_compiled
= WIRELESS_EXT
;
302 range
->we_version_source
= WIRELESS_EXT
;
304 range
->min_nwid
= range
->max_nwid
= 0;
306 range
->num_channels
= NUM_CHANNELS
;
308 for (i
= 0; i
< NUM_CHANNELS
; i
++) {
309 range
->freq
[k
].i
= i
+ 1;
310 range
->freq
[k
].m
= channel_frequency
[i
] * 100000;
311 range
->freq
[k
].e
= 1;
313 if (k
>= IW_MAX_FREQUENCIES
)
316 range
->num_frequency
= k
;
317 range
->sensitivity
= 3;
319 /* nbd: don't know what this means, but other drivers set it this way */
320 range
->pmp_flags
= IW_POWER_PERIOD
;
321 range
->pmt_flags
= IW_POWER_TIMEOUT
;
322 range
->pm_capa
= IW_POWER_PERIOD
| IW_POWER_TIMEOUT
| IW_POWER_UNICAST_R
;
325 range
->max_pmp
= 65535000;
327 range
->max_pmt
= 65535 * 1000;
329 range
->max_qual
.qual
= 0;
330 range
->max_qual
.level
= 0;
331 range
->max_qual
.noise
= 0;
334 if (wl_iovar_get(dev
, "rtsthresh", &range
->max_rts
, sizeof(int)) < 0)
335 range
->max_rts
= 2347;
337 range
->min_frag
= 256;
339 if (wl_iovar_get(dev
, "fragthresh", &range
->max_frag
, sizeof(int)) < 0)
340 range
->max_frag
= 2346;
342 range
->txpower_capa
= IW_TXPOW_DBM
;
348 static int wlcompat_set_scan(struct net_device
*dev
,
349 struct iw_request_info
*info
,
350 union iwreq_data
*wrqu
,
354 wl_scan_params_t params
;
356 memset(¶ms
, 0, sizeof(params
));
358 /* use defaults (same parameters as wl scan) */
359 memset(¶ms
.bssid
, 0xff, sizeof(params
.bssid
));
360 params
.bss_type
= DOT11_BSSTYPE_ANY
;
361 params
.scan_type
= -1;
363 params
.active_time
= -1;
364 params
.passive_time
= -1;
365 params
.home_time
= -1;
367 /* can only scan in STA mode */
368 wl_ioctl(dev
, WLC_GET_AP
, &last_mode
, sizeof(last_mode
));
370 /* switch to ap mode, scan result query will switch back */
371 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
373 /* wait 250 msec after mode change */
374 set_current_state(TASK_INTERRUPTIBLE
);
375 schedule_timeout(msecs_to_jiffies(250));
378 scan_cur
= SCAN_RETRY_MAX
;
379 while (scan_cur
-- && (wl_ioctl(dev
, WLC_SCAN
, ¶ms
, 64) < 0)) {
380 /* sometimes the driver takes a few tries... */
381 set_current_state(TASK_INTERRUPTIBLE
);
382 schedule_timeout(msecs_to_jiffies(250));
390 /* wait at least 2 seconds for results */
391 set_current_state(TASK_INTERRUPTIBLE
);
392 schedule_timeout(msecs_to_jiffies(2000));
398 struct iw_statistics
*wlcompat_get_wireless_stats(struct net_device
*dev
)
400 struct wl_bss_info
*bss_info
= (struct wl_bss_info
*) buf
;
402 unsigned int rssi
, noise
, ap
;
404 memset(&wstats
, 0, sizeof(wstats
));
405 memset(&pkt
, 0, sizeof(pkt
));
406 memset(buf
, 0, sizeof(buf
));
407 bss_info
->version
= 0x2000;
408 wl_ioctl(dev
, WLC_GET_BSS_INFO
, bss_info
, WLC_IOCTL_MAXLEN
);
409 wl_ioctl(dev
, WLC_GET_PKTCNTS
, &pkt
, sizeof(pkt
));
412 if ((wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) || ap
) {
413 if (wl_ioctl(dev
, WLC_GET_PHY_NOISE
, &noise
, sizeof(noise
)) < 0)
416 // somehow the structure doesn't fit here
420 rssi
= (rssi
== 0 ? 1 : rssi
);
421 wstats
.qual
.updated
= 0x10;
423 wstats
.qual
.updated
|= 0x20;
425 wstats
.qual
.updated
|= 0x40;
427 if ((wstats
.qual
.updated
& 0x60) == 0x60)
430 wstats
.qual
.level
= rssi
;
431 wstats
.qual
.noise
= noise
;
432 wstats
.discard
.misc
= pkt
.rx_bad_pkt
;
433 wstats
.discard
.retries
= pkt
.tx_bad_pkt
;
438 static int wlcompat_get_scan(struct net_device
*dev
,
439 struct iw_request_info
*info
,
440 union iwreq_data
*wrqu
,
443 wl_scan_results_t
*results
= (wl_scan_results_t
*) buf
;
444 wl_bss_info_t
*bss_info
;
446 char *current_ev
= extra
;
448 char *end_buf
= extra
+ IW_SCAN_MAX_DATA
;
453 memset(buf
, 0, WLC_IOCTL_MAXLEN
);
454 results
->buflen
= WLC_IOCTL_MAXLEN
- sizeof(wl_scan_results_t
);
456 if (wl_ioctl(dev
, WLC_SCAN_RESULTS
, buf
, WLC_IOCTL_MAXLEN
) < 0)
459 if ((results
->count
<= 0) && (scan_cur
++ < SCAN_RETRY_MAX
))
462 bss_info
= &(results
->bss_info
[0]);
463 info_ptr
= (char *) bss_info
;
464 for (i
= 0; i
< results
->count
; i
++) {
465 /* send the cell address (must be sent first) */
467 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
468 memcpy(&iwe
.u
.ap_addr
.sa_data
, &bss_info
->BSSID
, sizeof(bss_info
->BSSID
));
469 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_ADDR_LEN
);
472 iwe
.cmd
= SIOCGIWESSID
;
473 iwe
.u
.data
.length
= bss_info
->SSID_len
;
474 if (iwe
.u
.data
.length
> IW_ESSID_MAX_SIZE
)
475 iwe
.u
.data
.length
= IW_ESSID_MAX_SIZE
;
476 iwe
.u
.data
.flags
= 1;
477 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, bss_info
->SSID
);
480 if (bss_info
->capability
& (DOT11_CAP_ESS
| DOT11_CAP_IBSS
)) {
481 iwe
.cmd
= SIOCGIWMODE
;
482 if (bss_info
->capability
& DOT11_CAP_ESS
)
483 iwe
.u
.mode
= IW_MODE_MASTER
;
484 else if (bss_info
->capability
& DOT11_CAP_IBSS
)
485 iwe
.u
.mode
= IW_MODE_ADHOC
;
486 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_UINT_LEN
);
489 /* send frequency/channel info */
490 iwe
.cmd
= SIOCGIWFREQ
;
492 iwe
.u
.freq
.m
= bss_info
->chanspec
& WL_CHANSPEC_CHAN_MASK
;
493 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_FREQ_LEN
);
495 /* add quality statistics */
498 iwe
.u
.qual
.level
= bss_info
->RSSI
;
499 iwe
.u
.qual
.noise
= bss_info
->phy_noise
;
500 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_QUAL_LEN
);
502 /* send encryption capability */
503 iwe
.cmd
= SIOCGIWENCODE
;
504 iwe
.u
.data
.pointer
= NULL
;
505 iwe
.u
.data
.length
= 0;
506 if (bss_info
->capability
& DOT11_CAP_PRIVACY
)
507 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
509 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
510 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, NULL
);
512 /* send rate information */
513 iwe
.cmd
= SIOCGIWRATE
;
514 current_val
= current_ev
+ IW_EV_LCP_LEN
;
515 iwe
.u
.bitrate
.fixed
= iwe
.u
.bitrate
.disabled
= 0;
517 for(j
= 0 ; j
< bss_info
->rateset
.count
; j
++) {
518 iwe
.u
.bitrate
.value
= ((bss_info
->rateset
.rates
[j
] & 0x7f) * 500000);
519 current_val
= iwe_stream_add_value(current_ev
, current_val
, end_buf
, &iwe
, IW_EV_PARAM_LEN
);
521 if((current_val
- current_ev
) > IW_EV_LCP_LEN
)
522 current_ev
= current_val
;
524 info_ptr
+= sizeof(wl_bss_info_t
);
525 if (bss_info
->ie_length
% 4)
526 info_ptr
+= bss_info
->ie_length
+ 4 - (bss_info
->ie_length
% 4);
528 info_ptr
+= bss_info
->ie_length
;
529 bss_info
= (wl_bss_info_t
*) info_ptr
;
532 wrqu
->data
.length
= (current_ev
- extra
);
533 wrqu
->data
.flags
= 0;
536 /* switch back to ap mode */
537 wl_ioctl(dev
, WLC_SET_AP
, &last_mode
, sizeof(last_mode
));
542 static int wlcompat_ioctl(struct net_device
*dev
,
543 struct iw_request_info
*info
,
544 union iwreq_data
*wrqu
,
549 strcpy(wrqu
->name
, "IEEE 802.11-DS");
555 if (wl_ioctl(dev
,WLC_GET_CHANNEL
, &ci
, sizeof(ci
)) < 0)
558 wrqu
->freq
.m
= ci
.target_channel
;
564 if (wrqu
->freq
.m
== -1) {
566 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
569 if (wrqu
->freq
.e
== 1) {
571 int f
= wrqu
->freq
.m
/ 100000;
572 while ((channel
< NUM_CHANNELS
+ 1) && (f
!= channel_frequency
[channel
]))
575 if (channel
== NUM_CHANNELS
) // channel not found
579 wrqu
->freq
.m
= channel
+ 1;
581 if ((wrqu
->freq
.e
== 0) && (wrqu
->freq
.m
< 1000)) {
582 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
596 memset(®
, 0, sizeof(reg
));
598 if (wrqu
->ap_addr
.sa_family
!= ARPHRD_ETHER
)
601 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
604 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
608 wl_ioctl(dev
, WLC_SET_BSSID
, wrqu
->ap_addr
.sa_data
, 6);
610 if (wl_ioctl(dev
, ((ap
|| !infra
) ? WLC_SET_BSSID
: WLC_REASSOC
), wrqu
->ap_addr
.sa_data
, 6) < 0)
617 wrqu
->ap_addr
.sa_family
= ARPHRD_ETHER
;
618 if (wl_ioctl(dev
,WLC_GET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
626 if (wl_ioctl(dev
,WLC_GET_SSID
, &ssid
, sizeof(wlc_ssid_t
)) < 0)
629 wrqu
->essid
.flags
= wrqu
->data
.flags
= 1;
630 wrqu
->essid
.length
= wrqu
->data
.length
= ssid
.SSID_len
+ 1;
631 memcpy(extra
,ssid
.SSID
,ssid
.SSID_len
+ 1);
637 memset(&ssid
, 0, sizeof(ssid
));
638 ssid
.SSID_len
= strlen(extra
);
639 if (ssid
.SSID_len
> 32)
641 memcpy(ssid
.SSID
, extra
, ssid
.SSID_len
);
642 if (wl_ioctl(dev
, WLC_SET_SSID
, &ssid
, sizeof(ssid
)) < 0)
648 if (wl_iovar_get(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
654 if (wl_iovar_set(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
660 if (wl_iovar_get(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
666 if (wl_iovar_set(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
674 wl_ioctl(dev
, WLC_GET_RADIO
, &radio
, sizeof(int));
676 if (wl_iovar_get(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
679 override
= (wrqu
->txpower
.value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
;
680 wrqu
->txpower
.value
&= ~WL_TXPWR_OVERRIDE
;
681 if (!override
&& (wrqu
->txpower
.value
> 76))
682 wrqu
->txpower
.value
= 76;
683 wrqu
->txpower
.value
/= 4;
685 wrqu
->txpower
.fixed
= 0;
686 wrqu
->txpower
.disabled
= radio
;
687 wrqu
->txpower
.flags
= IW_TXPOW_DBM
;
692 /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
693 int radio
= wrqu
->txpower
.disabled
;
695 wl_ioctl(dev
, WLC_SET_RADIO
, &radio
, sizeof(int));
697 if (!wrqu
->txpower
.disabled
&& (wrqu
->txpower
.value
> 0)) {
700 if (wl_iovar_get(dev
, "qtxpower", &value
, sizeof(int)) < 0)
703 value
&= WL_TXPWR_OVERRIDE
;
704 wrqu
->txpower
.value
*= 4;
705 wrqu
->txpower
.value
|= value
;
707 if (wrqu
->txpower
.flags
!= IW_TXPOW_DBM
)
710 if (wrqu
->txpower
.value
> 0)
711 if (wl_iovar_set(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
718 int val
= 0, wep
= 1, wrestrict
= 1;
719 int index
= (wrqu
->data
.flags
& IW_ENCODE_INDEX
) - 1;
722 index
= get_primary_key(dev
);
724 if (wrqu
->data
.flags
& IW_ENCODE_DISABLED
) {
726 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
731 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
734 if (wrqu
->data
.flags
& IW_ENCODE_OPEN
)
737 if (wrqu
->data
.pointer
&& (wrqu
->data
.length
> 0) && (wrqu
->data
.length
<= 16)) {
739 memset(&key
, 0, sizeof(key
));
741 key
.flags
= WL_PRIMARY_KEY
;
742 key
.len
= wrqu
->data
.length
;
744 memcpy(key
.data
, wrqu
->data
.pointer
, wrqu
->data
.length
);
746 if (wl_ioctl(dev
, WLC_SET_KEY
, &key
, sizeof(key
)) < 0)
751 wl_ioctl(dev
, WLC_SET_KEY_PRIMARY
, &index
, sizeof(index
));
754 wl_ioctl(dev
, WLC_SET_WEP_RESTRICT
, &wrestrict
, sizeof(wrestrict
));
761 int key
= get_primary_key(dev
);
765 if (wl_ioctl(dev
, WLC_GET_WSEC
, &val
, sizeof(val
)) < 0)
767 if (!(val
& WEP_ENABLED
)) {
768 wrqu
->data
.flags
= IW_ENCODE_DISABLED
;
772 key
= get_primary_key(dev
);
773 wrqu
->data
.flags
= IW_ENCODE_ENABLED
;
775 /* the driver apparently doesn't allow us to read the wep key */
776 wrqu
->data
.flags
|= IW_ENCODE_NOKEY
;
782 return wlcompat_ioctl_getiwrange(dev
, extra
);
787 int ap
= -1, infra
= -1, passive
= 0, wet
= 0;
789 wl_ioctl(dev
, WLC_GET_WET
, &wet
, sizeof(wet
));
790 switch (wrqu
->mode
) {
791 case IW_MODE_MONITOR
:
816 wl_ioctl(dev
, WLC_SET_PASSIVE
, &passive
, sizeof(passive
));
817 wl_ioctl(dev
, WLC_SET_MONITOR
, &passive
, sizeof(passive
));
818 if ((ap
== 0) && (infra
== 1))
819 wl_ioctl(dev
, WLC_SET_WET
, &wet
, sizeof(wet
));
821 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
823 wl_ioctl(dev
, WLC_SET_INFRA
, &infra
, sizeof(infra
));
829 int ap
, infra
, wet
, passive
;
831 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
833 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
835 if (wl_ioctl(dev
, WLC_GET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
839 wrqu
->mode
= IW_MODE_MONITOR
;
841 wrqu
->mode
= IW_MODE_ADHOC
;
844 wrqu
->mode
= IW_MODE_MASTER
;
846 wrqu
->mode
= IW_MODE_INFRA
;
859 static const iw_handler wlcompat_handler
[] = {
860 NULL
, /* SIOCSIWCOMMIT */
861 wlcompat_ioctl
, /* SIOCGIWNAME */
862 NULL
, /* SIOCSIWNWID */
863 NULL
, /* SIOCGIWNWID */
864 wlcompat_ioctl
, /* SIOCSIWFREQ */
865 wlcompat_ioctl
, /* SIOCGIWFREQ */
866 wlcompat_ioctl
, /* SIOCSIWMODE */
867 wlcompat_ioctl
, /* SIOCGIWMODE */
868 NULL
, /* SIOCSIWSENS */
869 NULL
, /* SIOCGIWSENS */
870 NULL
, /* SIOCSIWRANGE, unused */
871 wlcompat_ioctl
, /* SIOCGIWRANGE */
872 NULL
, /* SIOCSIWPRIV */
873 NULL
, /* SIOCGIWPRIV */
874 NULL
, /* SIOCSIWSTATS */
875 NULL
, /* SIOCGIWSTATS */
876 iw_handler_set_spy
, /* SIOCSIWSPY */
877 iw_handler_get_spy
, /* SIOCGIWSPY */
878 iw_handler_set_thrspy
, /* SIOCSIWTHRSPY */
879 iw_handler_get_thrspy
, /* SIOCGIWTHRSPY */
880 wlcompat_ioctl
, /* SIOCSIWAP */
881 wlcompat_ioctl
, /* SIOCGIWAP */
882 NULL
, /* -- hole -- */
883 NULL
, /* SIOCGIWAPLIST */
884 wlcompat_set_scan
, /* SIOCSIWSCAN */
885 wlcompat_get_scan
, /* SIOCGIWSCAN */
886 wlcompat_ioctl
, /* SIOCSIWESSID */
887 wlcompat_ioctl
, /* SIOCGIWESSID */
888 NULL
, /* SIOCSIWNICKN */
889 NULL
, /* SIOCGIWNICKN */
890 NULL
, /* -- hole -- */
891 NULL
, /* -- hole -- */
892 NULL
, /* SIOCSIWRATE */
893 NULL
, /* SIOCGIWRATE */
894 wlcompat_ioctl
, /* SIOCSIWRTS */
895 wlcompat_ioctl
, /* SIOCGIWRTS */
896 wlcompat_ioctl
, /* SIOCSIWFRAG */
897 wlcompat_ioctl
, /* SIOCGIWFRAG */
898 wlcompat_ioctl
, /* SIOCSIWTXPOW */
899 wlcompat_ioctl
, /* SIOCGIWTXPOW */
900 NULL
, /* SIOCSIWRETRY */
901 NULL
, /* SIOCGIWRETRY */
902 wlcompat_ioctl
, /* SIOCSIWENCODE */
903 wlcompat_ioctl
, /* SIOCGIWENCODE */
907 static const struct iw_handler_def wlcompat_handler_def
=
909 .standard
= (iw_handler
*) wlcompat_handler
,
910 .num_standard
= sizeof(wlcompat_handler
)/sizeof(iw_handler
),
916 void print_buffer(int len
, unsigned char *buf
) {
919 for (x
=0;x
<len
&& x
<180 ;x
++) {
922 printk("%02X",buf
[x
]);
931 static int (*old_ioctl
)(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
);
932 static int new_ioctl(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
) {
936 printk("dev: %s ioctl: 0x%04x\n",dev
->name
,cmd
);
937 if (cmd
==SIOCDEVPRIVATE
) {
938 wl_ioctl_t
*ioc
= (wl_ioctl_t
*)ifr
->ifr_data
;
939 unsigned char *buf
= ioc
->buf
;
940 printk(" cmd: %d buf: 0x%08x len: %d\n",ioc
->cmd
,&(ioc
->buf
),ioc
->len
);
942 print_buffer(ioc
->len
, buf
);
943 ret
= old_ioctl(dev
,ifr
,cmd
);
945 print_buffer(ioc
->len
, buf
);
946 printk(" ret: %d\n", ret
);
950 ret
= old_ioctl(dev
,ifr
,cmd
);
956 static struct timer_list rng_timer
;
957 static spinlock_t rng_lock
= SPIN_LOCK_UNLOCKED
;
959 static void rng_timer_tick(unsigned long n
)
961 struct net_device
*dev
= (struct net_device
*) n
;
967 spin_lock_irqsave(&rng_lock
, flags
);
968 for (i
= 0; i
< 3; i
++) {
969 ret
|= wl_iovar_get(dev
, "rand", &data
[i
], sizeof(u16
));
971 spin_unlock_irqrestore(&rng_lock
, flags
);
974 batch_entropy_store(*((u32
*) &data
[0]), *((u32
*) &data
[2]), (jiffies
% 255));
976 mod_timer(&rng_timer
, jiffies
+ (HZ
/RNG_POLL_FREQ
));
980 static int __init
wlcompat_init()
983 char devname
[4] = "wl0";
985 while (!found
&& (dev
= dev_get_by_name(devname
))) {
986 if ((wl_ioctl(dev
, WLC_GET_MAGIC
, &i
, sizeof(i
)) == 0) && (i
== WLC_IOCTL_MAGIC
))
993 printk("No Broadcom devices found.\n");
997 old_ioctl
= dev
->do_ioctl
;
998 dev
->do_ioctl
= new_ioctl
;
1000 dev
->wireless_handlers
= (struct iw_handler_def
*)&wlcompat_handler_def
;
1001 dev
->get_wireless_stats
= wlcompat_get_wireless_stats
;
1006 init_timer(&rng_timer
);
1007 rng_timer
.function
= rng_timer_tick
;
1008 rng_timer
.data
= (unsigned long) dev
;
1009 rng_timer_tick((unsigned long) dev
);
1014 printk("broadcom driver private data: 0x%08x\n", dev
->priv
);
1019 static void __exit
wlcompat_exit()
1023 del_timer(&rng_timer
);
1026 dev
->get_wireless_stats
= NULL
;
1027 dev
->wireless_handlers
= NULL
;
1029 dev
->do_ioctl
= old_ioctl
;
1034 MODULE_AUTHOR("openwrt.org");
1035 MODULE_LICENSE("GPL");
1038 module_param(random
, int, 0);
1040 module_init(wlcompat_init
);
1041 module_exit(wlcompat_exit
);