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.
24 #include <linux/config.h>
25 #include <linux/module.h>
26 #include <linux/moduleparam.h>
27 #include <linux/init.h>
28 #include <linux/if_arp.h>
29 #include <linux/wireless.h>
30 #include <linux/timer.h>
31 #include <linux/delay.h>
32 #include <linux/random.h>
33 #include <net/iw_handler.h>
34 #include <asm/uaccess.h>
40 char buf
[WLC_IOCTL_MAXLEN
];
41 static struct net_device
*dev
;
43 static int random
= 1;
46 static struct iw_statistics wstats
;
47 static int last_mode
= -1;
48 static int scan_cur
= 0;
50 /* The frequency of each channel in MHz */
51 const long channel_frequency
[] = {
52 2412, 2417, 2422, 2427, 2432, 2437, 2442,
53 2447, 2452, 2457, 2462, 2467, 2472, 2484
55 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
58 #define SCAN_RETRY_MAX 5
59 #define RNG_POLL_FREQ 1
61 typedef struct internal_wsec_key
{
63 uint8 unknown_1
; // 0x01
65 uint8 unknown_2
[7]; // 0x03
68 char data
[32]; // 0x0e
73 void print_buffer(int len
, unsigned char *buf
);
76 static int wl_ioctl(struct net_device
*dev
, int cmd
, void *buf
, int len
)
78 mm_segment_t old_fs
= get_fs();
85 strncpy(ifr
.ifr_name
, dev
->name
, IFNAMSIZ
);
86 ifr
.ifr_data
= (caddr_t
) &ioc
;
88 ret
= dev
->do_ioctl(dev
,&ifr
,SIOCDEVPRIVATE
);
93 #if !defined(DEBUG) || !defined(WL_WEXT)
95 wl_iovar_getbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
101 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
102 iolen
= namelen
+ paramlen
;
104 /* check for overflow */
106 return (BCME_BUFTOOSHORT
);
108 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
109 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
111 err
= wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
117 wl_iovar_setbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
122 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
123 iolen
= namelen
+ paramlen
;
125 /* check for overflow */
127 return (BCME_BUFTOOSHORT
);
129 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
130 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
132 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
136 wl_iovar_set(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
)
138 char smbuf
[WLC_IOCTL_SMLEN
];
140 return wl_iovar_setbuf(dev
, iovar
, param
, paramlen
, smbuf
, sizeof(smbuf
));
144 wl_iovar_get(struct net_device
*dev
, char *iovar
, void *bufptr
, int buflen
)
146 char smbuf
[WLC_IOCTL_SMLEN
];
149 /* use the return buffer if it is bigger than what we have on the stack */
150 if (buflen
> sizeof(smbuf
)) {
151 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, bufptr
, buflen
);
153 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, smbuf
, sizeof(smbuf
));
155 memcpy(bufptr
, smbuf
, buflen
);
163 * format a bsscfg indexed iovar buffer
166 wl_bssiovar_mkbuf(char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
, int buflen
,
169 char *prefix
= "bsscfg:";
175 prefixlen
= strlen(prefix
); /* length of bsscfg prefix */
176 namelen
= strlen(iovar
) + 1; /* length of iovar name + null */
177 iolen
= prefixlen
+ namelen
+ sizeof(int) + paramlen
;
179 /* check for overflow */
180 if (buflen
< 0 || iolen
> (uint
)buflen
) {
182 return BCME_BUFTOOSHORT
;
187 /* copy prefix, no null */
188 memcpy(p
, prefix
, prefixlen
);
191 /* copy iovar name including null */
192 memcpy(p
, iovar
, namelen
);
195 /* bss config index as first param */
196 memcpy(p
, &bssidx
, sizeof(int32
));
199 /* parameter buffer follows */
201 memcpy(p
, param
, paramlen
);
208 * set named & bss indexed driver variable to buffer value
211 wl_bssiovar_setbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
217 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
221 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
225 * get named & bss indexed driver variable buffer value
228 wl_bssiovar_getbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
234 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
238 return wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
242 * set named & bss indexed driver variable to buffer value
245 wl_bssiovar_set(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
)
247 char smbuf
[WLC_IOCTL_SMLEN
];
249 return wl_bssiovar_setbuf(dev
, iovar
, bssidx
, param
, paramlen
, smbuf
, sizeof(smbuf
));
253 * get named & bss indexed driver variable buffer value
256 wl_bssiovar_get(struct net_device
*dev
, char *iovar
, int bssidx
, void *outbuf
, int len
)
258 char smbuf
[WLC_IOCTL_SMLEN
];
261 /* use the return buffer if it is bigger than what we have on the stack */
262 if (len
> (int)sizeof(smbuf
)) {
263 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, outbuf
, len
);
265 memset(smbuf
, 0, sizeof(smbuf
));
266 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, smbuf
, sizeof(smbuf
));
268 memcpy(outbuf
, smbuf
, len
);
278 int get_primary_key(struct net_device
*dev
)
282 for (key
= val
= 0; (key
< 4) && (val
== 0); key
++) {
284 if (wl_ioctl(dev
, WLC_GET_KEY_PRIMARY
, &val
, sizeof(val
)) < 0)
291 static int wlcompat_ioctl_getiwrange(struct net_device
*dev
,
295 struct iw_range
*range
;
297 range
= (struct iw_range
*) extra
;
298 memset(extra
, 0, sizeof(struct iw_range
));
300 range
->we_version_compiled
= WIRELESS_EXT
;
301 range
->we_version_source
= WIRELESS_EXT
;
303 range
->min_nwid
= range
->max_nwid
= 0;
305 range
->num_channels
= NUM_CHANNELS
;
307 for (i
= 0; i
< NUM_CHANNELS
; i
++) {
308 range
->freq
[k
].i
= i
+ 1;
309 range
->freq
[k
].m
= channel_frequency
[i
] * 100000;
310 range
->freq
[k
].e
= 1;
312 if (k
>= IW_MAX_FREQUENCIES
)
315 range
->num_frequency
= k
;
316 range
->sensitivity
= 3;
318 /* nbd: don't know what this means, but other drivers set it this way */
319 range
->pmp_flags
= IW_POWER_PERIOD
;
320 range
->pmt_flags
= IW_POWER_TIMEOUT
;
321 range
->pm_capa
= IW_POWER_PERIOD
| IW_POWER_TIMEOUT
| IW_POWER_UNICAST_R
;
324 range
->max_pmp
= 65535000;
326 range
->max_pmt
= 65535 * 1000;
328 range
->max_qual
.qual
= 0;
329 range
->max_qual
.level
= 0;
330 range
->max_qual
.noise
= 0;
333 if (wl_iovar_get(dev
, "rtsthresh", &range
->max_rts
, sizeof(int)) < 0)
334 range
->max_rts
= 2347;
336 range
->min_frag
= 256;
338 if (wl_iovar_get(dev
, "fragthresh", &range
->max_frag
, sizeof(int)) < 0)
339 range
->max_frag
= 2346;
341 range
->txpower_capa
= IW_TXPOW_DBM
;
347 static int wlcompat_set_scan(struct net_device
*dev
,
348 struct iw_request_info
*info
,
349 union iwreq_data
*wrqu
,
353 wl_scan_params_t params
;
355 memset(¶ms
, 0, sizeof(params
));
357 /* use defaults (same parameters as wl scan) */
358 memset(¶ms
.bssid
, 0xff, sizeof(params
.bssid
));
359 params
.bss_type
= DOT11_BSSTYPE_ANY
;
360 params
.scan_type
= -1;
362 params
.active_time
= -1;
363 params
.passive_time
= -1;
364 params
.home_time
= -1;
366 /* can only scan in STA mode */
367 wl_ioctl(dev
, WLC_GET_AP
, &last_mode
, sizeof(last_mode
));
369 /* switch to ap mode, scan result query will switch back */
370 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
372 /* wait 250 msec after mode change */
373 set_current_state(TASK_INTERRUPTIBLE
);
374 schedule_timeout(msecs_to_jiffies(250));
377 scan_cur
= SCAN_RETRY_MAX
;
378 while (scan_cur
-- && (wl_ioctl(dev
, WLC_SCAN
, ¶ms
, 64) < 0)) {
379 /* sometimes the driver takes a few tries... */
380 set_current_state(TASK_INTERRUPTIBLE
);
381 schedule_timeout(msecs_to_jiffies(250));
389 /* wait at least 2 seconds for results */
390 set_current_state(TASK_INTERRUPTIBLE
);
391 schedule_timeout(msecs_to_jiffies(2000));
397 struct iw_statistics
*wlcompat_get_wireless_stats(struct net_device
*dev
)
399 struct wl_bss_info
*bss_info
= (struct wl_bss_info
*) buf
;
401 unsigned int rssi
, noise
, ap
;
403 memset(&wstats
, 0, sizeof(wstats
));
404 memset(&pkt
, 0, sizeof(pkt
));
405 memset(buf
, 0, sizeof(buf
));
406 bss_info
->version
= 0x2000;
407 wl_ioctl(dev
, WLC_GET_BSS_INFO
, bss_info
, WLC_IOCTL_MAXLEN
);
408 wl_ioctl(dev
, WLC_GET_PKTCNTS
, &pkt
, sizeof(pkt
));
411 if ((wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) || ap
) {
412 if (wl_ioctl(dev
, WLC_GET_PHY_NOISE
, &noise
, sizeof(noise
)) < 0)
415 // somehow the structure doesn't fit here
419 rssi
= (rssi
== 0 ? 1 : rssi
);
420 wstats
.qual
.updated
= 0x10;
422 wstats
.qual
.updated
|= 0x20;
424 wstats
.qual
.updated
|= 0x40;
426 if ((wstats
.qual
.updated
& 0x60) == 0x60)
429 wstats
.qual
.level
= rssi
;
430 wstats
.qual
.noise
= noise
;
431 wstats
.discard
.misc
= pkt
.rx_bad_pkt
;
432 wstats
.discard
.retries
= pkt
.tx_bad_pkt
;
437 static int wlcompat_get_scan(struct net_device
*dev
,
438 struct iw_request_info
*info
,
439 union iwreq_data
*wrqu
,
442 wl_scan_results_t
*results
= (wl_scan_results_t
*) buf
;
443 wl_bss_info_t
*bss_info
;
445 char *current_ev
= extra
;
447 char *end_buf
= extra
+ IW_SCAN_MAX_DATA
;
452 memset(buf
, 0, WLC_IOCTL_MAXLEN
);
453 results
->buflen
= WLC_IOCTL_MAXLEN
- sizeof(wl_scan_results_t
);
455 if (wl_ioctl(dev
, WLC_SCAN_RESULTS
, buf
, WLC_IOCTL_MAXLEN
) < 0)
458 if ((results
->count
<= 0) && (scan_cur
++ < SCAN_RETRY_MAX
))
461 bss_info
= &(results
->bss_info
[0]);
462 info_ptr
= (char *) bss_info
;
463 for (i
= 0; i
< results
->count
; i
++) {
464 /* send the cell address (must be sent first) */
466 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
467 memcpy(&iwe
.u
.ap_addr
.sa_data
, &bss_info
->BSSID
, sizeof(bss_info
->BSSID
));
468 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_ADDR_LEN
);
471 iwe
.cmd
= SIOCGIWESSID
;
472 iwe
.u
.data
.length
= bss_info
->SSID_len
;
473 if (iwe
.u
.data
.length
> IW_ESSID_MAX_SIZE
)
474 iwe
.u
.data
.length
= IW_ESSID_MAX_SIZE
;
475 iwe
.u
.data
.flags
= 1;
476 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, bss_info
->SSID
);
479 if (bss_info
->capability
& (DOT11_CAP_ESS
| DOT11_CAP_IBSS
)) {
480 iwe
.cmd
= SIOCGIWMODE
;
481 if (bss_info
->capability
& DOT11_CAP_ESS
)
482 iwe
.u
.mode
= IW_MODE_MASTER
;
483 else if (bss_info
->capability
& DOT11_CAP_IBSS
)
484 iwe
.u
.mode
= IW_MODE_ADHOC
;
485 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_UINT_LEN
);
488 /* send frequency/channel info */
489 iwe
.cmd
= SIOCGIWFREQ
;
491 iwe
.u
.freq
.m
= bss_info
->chanspec
& WL_CHANSPEC_CHAN_MASK
;
492 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_FREQ_LEN
);
494 /* add quality statistics */
497 iwe
.u
.qual
.level
= bss_info
->RSSI
;
498 iwe
.u
.qual
.noise
= bss_info
->phy_noise
;
499 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_QUAL_LEN
);
501 /* send encryption capability */
502 iwe
.cmd
= SIOCGIWENCODE
;
503 iwe
.u
.data
.pointer
= NULL
;
504 iwe
.u
.data
.length
= 0;
505 if (bss_info
->capability
& DOT11_CAP_PRIVACY
)
506 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
508 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
509 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, NULL
);
511 /* send rate information */
512 iwe
.cmd
= SIOCGIWRATE
;
513 current_val
= current_ev
+ IW_EV_LCP_LEN
;
514 iwe
.u
.bitrate
.fixed
= iwe
.u
.bitrate
.disabled
= 0;
516 for(j
= 0 ; j
< bss_info
->rateset
.count
; j
++) {
517 iwe
.u
.bitrate
.value
= ((bss_info
->rateset
.rates
[j
] & 0x7f) * 500000);
518 current_val
= iwe_stream_add_value(current_ev
, current_val
, end_buf
, &iwe
, IW_EV_PARAM_LEN
);
520 if((current_val
- current_ev
) > IW_EV_LCP_LEN
)
521 current_ev
= current_val
;
523 info_ptr
+= sizeof(wl_bss_info_t
);
524 if (bss_info
->ie_length
% 4)
525 info_ptr
+= bss_info
->ie_length
+ 4 - (bss_info
->ie_length
% 4);
527 info_ptr
+= bss_info
->ie_length
;
528 bss_info
= (wl_bss_info_t
*) info_ptr
;
531 wrqu
->data
.length
= (current_ev
- extra
);
532 wrqu
->data
.flags
= 0;
535 /* switch back to ap mode */
536 wl_ioctl(dev
, WLC_SET_AP
, &last_mode
, sizeof(last_mode
));
541 static int wlcompat_ioctl(struct net_device
*dev
,
542 struct iw_request_info
*info
,
543 union iwreq_data
*wrqu
,
548 strcpy(wrqu
->name
, "IEEE 802.11-DS");
554 if (wl_ioctl(dev
,WLC_GET_CHANNEL
, &ci
, sizeof(ci
)) < 0)
557 wrqu
->freq
.m
= ci
.target_channel
;
563 if (wrqu
->freq
.m
== -1) {
565 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
568 if (wrqu
->freq
.e
== 1) {
570 int f
= wrqu
->freq
.m
/ 100000;
571 while ((channel
< NUM_CHANNELS
+ 1) && (f
!= channel_frequency
[channel
]))
574 if (channel
== NUM_CHANNELS
) // channel not found
578 wrqu
->freq
.m
= channel
+ 1;
580 if ((wrqu
->freq
.e
== 0) && (wrqu
->freq
.m
< 1000)) {
581 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
595 memset(®
, 0, sizeof(reg
));
597 if (wrqu
->ap_addr
.sa_family
!= ARPHRD_ETHER
)
600 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
603 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
607 wl_ioctl(dev
, WLC_SET_BSSID
, wrqu
->ap_addr
.sa_data
, 6);
609 if (wl_ioctl(dev
, ((ap
|| !infra
) ? WLC_SET_BSSID
: WLC_REASSOC
), wrqu
->ap_addr
.sa_data
, 6) < 0)
616 wrqu
->ap_addr
.sa_family
= ARPHRD_ETHER
;
617 if (wl_ioctl(dev
,WLC_GET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
625 if (wl_ioctl(dev
,WLC_GET_SSID
, &ssid
, sizeof(wlc_ssid_t
)) < 0)
628 wrqu
->essid
.flags
= wrqu
->data
.flags
= 1;
629 wrqu
->essid
.length
= wrqu
->data
.length
= ssid
.SSID_len
+ 1;
630 memcpy(extra
,ssid
.SSID
,ssid
.SSID_len
+ 1);
636 memset(&ssid
, 0, sizeof(ssid
));
637 ssid
.SSID_len
= strlen(extra
);
638 if (ssid
.SSID_len
> 32)
640 memcpy(ssid
.SSID
, extra
, ssid
.SSID_len
);
641 if (wl_ioctl(dev
, WLC_SET_SSID
, &ssid
, sizeof(ssid
)) < 0)
647 if (wl_iovar_get(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
653 if (wl_iovar_set(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
659 if (wl_iovar_get(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
665 if (wl_iovar_set(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
673 wl_ioctl(dev
, WLC_GET_RADIO
, &radio
, sizeof(int));
675 if (wl_iovar_get(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
678 override
= (wrqu
->txpower
.value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
;
679 wrqu
->txpower
.value
&= ~WL_TXPWR_OVERRIDE
;
680 if (!override
&& (wrqu
->txpower
.value
> 76))
681 wrqu
->txpower
.value
= 76;
682 wrqu
->txpower
.value
/= 4;
684 wrqu
->txpower
.fixed
= 0;
685 wrqu
->txpower
.disabled
= radio
;
686 wrqu
->txpower
.flags
= IW_TXPOW_DBM
;
691 /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
692 int radio
= wrqu
->txpower
.disabled
;
694 wl_ioctl(dev
, WLC_SET_RADIO
, &radio
, sizeof(int));
696 if (!wrqu
->txpower
.disabled
&& (wrqu
->txpower
.value
> 0)) {
699 if (wl_iovar_get(dev
, "qtxpower", &value
, sizeof(int)) < 0)
702 value
&= WL_TXPWR_OVERRIDE
;
703 wrqu
->txpower
.value
*= 4;
704 wrqu
->txpower
.value
|= value
;
706 if (wrqu
->txpower
.flags
!= IW_TXPOW_DBM
)
709 if (wrqu
->txpower
.value
> 0)
710 if (wl_iovar_set(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
717 int val
= 0, wep
= 1, wrestrict
= 1;
718 int index
= (wrqu
->data
.flags
& IW_ENCODE_INDEX
) - 1;
721 index
= get_primary_key(dev
);
723 if (wrqu
->data
.flags
& IW_ENCODE_DISABLED
) {
725 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
730 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
733 if (wrqu
->data
.flags
& IW_ENCODE_OPEN
)
736 if (wrqu
->data
.pointer
&& (wrqu
->data
.length
> 0) && (wrqu
->data
.length
<= 16)) {
738 memset(&key
, 0, sizeof(key
));
740 key
.flags
= WL_PRIMARY_KEY
;
741 key
.len
= wrqu
->data
.length
;
743 memcpy(key
.data
, wrqu
->data
.pointer
, wrqu
->data
.length
);
745 if (wl_ioctl(dev
, WLC_SET_KEY
, &key
, sizeof(key
)) < 0)
750 wl_ioctl(dev
, WLC_SET_KEY_PRIMARY
, &index
, sizeof(index
));
753 wl_ioctl(dev
, WLC_SET_WEP_RESTRICT
, &wrestrict
, sizeof(wrestrict
));
760 int key
= get_primary_key(dev
);
764 if (wl_ioctl(dev
, WLC_GET_WSEC
, &val
, sizeof(val
)) < 0)
766 if (!(val
& WEP_ENABLED
)) {
767 wrqu
->data
.flags
= IW_ENCODE_DISABLED
;
771 key
= get_primary_key(dev
);
772 wrqu
->data
.flags
= IW_ENCODE_ENABLED
;
774 /* the driver apparently doesn't allow us to read the wep key */
775 wrqu
->data
.flags
|= IW_ENCODE_NOKEY
;
781 return wlcompat_ioctl_getiwrange(dev
, extra
);
786 int ap
= -1, infra
= -1, passive
= 0, wet
= 0;
788 wl_ioctl(dev
, WLC_GET_WET
, &wet
, sizeof(wet
));
789 switch (wrqu
->mode
) {
790 case IW_MODE_MONITOR
:
815 wl_ioctl(dev
, WLC_SET_PASSIVE
, &passive
, sizeof(passive
));
816 wl_ioctl(dev
, WLC_SET_MONITOR
, &passive
, sizeof(passive
));
817 if ((ap
== 0) && (infra
== 1))
818 wl_ioctl(dev
, WLC_SET_WET
, &wet
, sizeof(wet
));
820 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
822 wl_ioctl(dev
, WLC_SET_INFRA
, &infra
, sizeof(infra
));
828 int ap
, infra
, wet
, passive
;
830 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
832 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
834 if (wl_ioctl(dev
, WLC_GET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
838 wrqu
->mode
= IW_MODE_MONITOR
;
840 wrqu
->mode
= IW_MODE_ADHOC
;
843 wrqu
->mode
= IW_MODE_MASTER
;
845 wrqu
->mode
= IW_MODE_INFRA
;
858 static const iw_handler wlcompat_handler
[] = {
859 NULL
, /* SIOCSIWCOMMIT */
860 wlcompat_ioctl
, /* SIOCGIWNAME */
861 NULL
, /* SIOCSIWNWID */
862 NULL
, /* SIOCGIWNWID */
863 wlcompat_ioctl
, /* SIOCSIWFREQ */
864 wlcompat_ioctl
, /* SIOCGIWFREQ */
865 wlcompat_ioctl
, /* SIOCSIWMODE */
866 wlcompat_ioctl
, /* SIOCGIWMODE */
867 NULL
, /* SIOCSIWSENS */
868 NULL
, /* SIOCGIWSENS */
869 NULL
, /* SIOCSIWRANGE, unused */
870 wlcompat_ioctl
, /* SIOCGIWRANGE */
871 NULL
, /* SIOCSIWPRIV */
872 NULL
, /* SIOCGIWPRIV */
873 NULL
, /* SIOCSIWSTATS */
874 NULL
, /* SIOCGIWSTATS */
875 iw_handler_set_spy
, /* SIOCSIWSPY */
876 iw_handler_get_spy
, /* SIOCGIWSPY */
877 iw_handler_set_thrspy
, /* SIOCSIWTHRSPY */
878 iw_handler_get_thrspy
, /* SIOCGIWTHRSPY */
879 wlcompat_ioctl
, /* SIOCSIWAP */
880 wlcompat_ioctl
, /* SIOCGIWAP */
881 NULL
, /* -- hole -- */
882 NULL
, /* SIOCGIWAPLIST */
883 wlcompat_set_scan
, /* SIOCSIWSCAN */
884 wlcompat_get_scan
, /* SIOCGIWSCAN */
885 wlcompat_ioctl
, /* SIOCSIWESSID */
886 wlcompat_ioctl
, /* SIOCGIWESSID */
887 NULL
, /* SIOCSIWNICKN */
888 NULL
, /* SIOCGIWNICKN */
889 NULL
, /* -- hole -- */
890 NULL
, /* -- hole -- */
891 NULL
, /* SIOCSIWRATE */
892 NULL
, /* SIOCGIWRATE */
893 wlcompat_ioctl
, /* SIOCSIWRTS */
894 wlcompat_ioctl
, /* SIOCGIWRTS */
895 wlcompat_ioctl
, /* SIOCSIWFRAG */
896 wlcompat_ioctl
, /* SIOCGIWFRAG */
897 wlcompat_ioctl
, /* SIOCSIWTXPOW */
898 wlcompat_ioctl
, /* SIOCGIWTXPOW */
899 NULL
, /* SIOCSIWRETRY */
900 NULL
, /* SIOCGIWRETRY */
901 wlcompat_ioctl
, /* SIOCSIWENCODE */
902 wlcompat_ioctl
, /* SIOCGIWENCODE */
906 static const struct iw_handler_def wlcompat_handler_def
=
908 .standard
= (iw_handler
*) wlcompat_handler
,
909 .num_standard
= sizeof(wlcompat_handler
)/sizeof(iw_handler
),
915 void print_buffer(int len
, unsigned char *buf
) {
918 for (x
=0;x
<len
&& x
<180 ;x
++) {
921 printk("%02X",buf
[x
]);
930 static int (*old_ioctl
)(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
);
931 static int new_ioctl(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
) {
935 printk("dev: %s ioctl: 0x%04x\n",dev
->name
,cmd
);
936 if (cmd
==SIOCDEVPRIVATE
) {
937 wl_ioctl_t
*ioc
= (wl_ioctl_t
*)ifr
->ifr_data
;
938 unsigned char *buf
= ioc
->buf
;
939 printk(" cmd: %d buf: 0x%08x len: %d\n",ioc
->cmd
,&(ioc
->buf
),ioc
->len
);
941 print_buffer(ioc
->len
, buf
);
942 ret
= old_ioctl(dev
,ifr
,cmd
);
944 print_buffer(ioc
->len
, buf
);
945 printk(" ret: %d\n", ret
);
949 ret
= old_ioctl(dev
,ifr
,cmd
);
955 static struct timer_list rng_timer
;
956 static spinlock_t rng_lock
= SPIN_LOCK_UNLOCKED
;
958 static void rng_timer_tick(unsigned long n
)
960 struct net_device
*dev
= (struct net_device
*) n
;
966 spin_lock_irqsave(&rng_lock
, flags
);
967 for (i
= 0; i
< 3; i
++) {
968 ret
|= wl_iovar_get(dev
, "rand", &data
[i
], sizeof(u16
));
970 spin_unlock_irqrestore(&rng_lock
, flags
);
973 batch_entropy_store(*((u32
*) &data
[0]), *((u32
*) &data
[2]), (jiffies
% 255));
975 mod_timer(&rng_timer
, jiffies
+ (HZ
/RNG_POLL_FREQ
));
979 static int __init
wlcompat_init()
982 char devname
[4] = "wl0";
984 while (!found
&& (dev
= dev_get_by_name(devname
))) {
985 if ((wl_ioctl(dev
, WLC_GET_MAGIC
, &i
, sizeof(i
)) == 0) && (i
== WLC_IOCTL_MAGIC
))
992 printk("No Broadcom devices found.\n");
996 old_ioctl
= dev
->do_ioctl
;
997 dev
->do_ioctl
= new_ioctl
;
999 dev
->wireless_handlers
= (struct iw_handler_def
*)&wlcompat_handler_def
;
1000 dev
->get_wireless_stats
= wlcompat_get_wireless_stats
;
1005 init_timer(&rng_timer
);
1006 rng_timer
.function
= rng_timer_tick
;
1007 rng_timer
.data
= (unsigned long) dev
;
1008 rng_timer_tick((unsigned long) dev
);
1013 printk("broadcom driver private data: 0x%08x\n", dev
->priv
);
1018 static void __exit
wlcompat_exit()
1022 del_timer(&rng_timer
);
1025 dev
->get_wireless_stats
= NULL
;
1026 dev
->wireless_handlers
= NULL
;
1028 dev
->do_ioctl
= old_ioctl
;
1033 MODULE_AUTHOR("openwrt.org");
1034 MODULE_LICENSE("GPL");
1037 module_param(random
, int, 0);
1039 module_init(wlcompat_init
);
1040 module_exit(wlcompat_exit
);