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 <net/iw_handler.h>
34 #include <asm/uaccess.h>
40 static struct net_device
*dev
;
41 static struct iw_statistics wstats
;
43 static int random
= 1;
45 static int last_mode
= -1;
46 static int scan_cur
= 0;
47 char buf
[WLC_IOCTL_MAXLEN
];
49 /* The frequency of each channel in MHz */
50 const long channel_frequency
[] = {
51 2412, 2417, 2422, 2427, 2432, 2437, 2442,
52 2447, 2452, 2457, 2462, 2467, 2472, 2484
54 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
56 #define SCAN_RETRY_MAX 5
57 #define RNG_POLL_FREQ 1
59 typedef struct internal_wsec_key
{
61 uint8 unknown_1
; // 0x01
63 uint8 unknown_2
[7]; // 0x03
66 char data
[32]; // 0x0e
71 void print_buffer(int len
, unsigned char *buf
);
74 static int wl_ioctl(struct net_device
*dev
, int cmd
, void *buf
, int len
)
76 mm_segment_t old_fs
= get_fs();
83 strncpy(ifr
.ifr_name
, dev
->name
, IFNAMSIZ
);
84 ifr
.ifr_data
= (caddr_t
) &ioc
;
86 ret
= dev
->do_ioctl(dev
,&ifr
,SIOCDEVPRIVATE
);
92 wl_iovar_getbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
98 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
99 iolen
= namelen
+ paramlen
;
101 /* check for overflow */
103 return (BCME_BUFTOOSHORT
);
105 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
106 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
108 err
= wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
114 wl_iovar_setbuf(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
, void *bufptr
, int buflen
)
119 namelen
= strlen(iovar
) + 1; /* length of iovar name plus null */
120 iolen
= namelen
+ paramlen
;
122 /* check for overflow */
124 return (BCME_BUFTOOSHORT
);
126 memcpy(bufptr
, iovar
, namelen
); /* copy iovar name including null */
127 memcpy((int8
*)bufptr
+ namelen
, param
, paramlen
);
129 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
133 wl_iovar_set(struct net_device
*dev
, char *iovar
, void *param
, int paramlen
)
135 char smbuf
[WLC_IOCTL_SMLEN
];
137 return wl_iovar_setbuf(dev
, iovar
, param
, paramlen
, smbuf
, sizeof(smbuf
));
141 wl_iovar_get(struct net_device
*dev
, char *iovar
, void *bufptr
, int buflen
)
143 char smbuf
[WLC_IOCTL_SMLEN
];
146 /* use the return buffer if it is bigger than what we have on the stack */
147 if (buflen
> sizeof(smbuf
)) {
148 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, bufptr
, buflen
);
150 ret
= wl_iovar_getbuf(dev
, iovar
, NULL
, 0, smbuf
, sizeof(smbuf
));
152 memcpy(bufptr
, smbuf
, buflen
);
160 * format a bsscfg indexed iovar buffer
163 wl_bssiovar_mkbuf(char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
, int buflen
,
166 char *prefix
= "bsscfg:";
172 prefixlen
= strlen(prefix
); /* length of bsscfg prefix */
173 namelen
= strlen(iovar
) + 1; /* length of iovar name + null */
174 iolen
= prefixlen
+ namelen
+ sizeof(int) + paramlen
;
176 /* check for overflow */
177 if (buflen
< 0 || iolen
> (uint
)buflen
) {
179 return BCME_BUFTOOSHORT
;
184 /* copy prefix, no null */
185 memcpy(p
, prefix
, prefixlen
);
188 /* copy iovar name including null */
189 memcpy(p
, iovar
, namelen
);
192 /* bss config index as first param */
193 memcpy(p
, &bssidx
, sizeof(int32
));
196 /* parameter buffer follows */
198 memcpy(p
, param
, paramlen
);
205 * set named & bss indexed driver variable to buffer value
208 wl_bssiovar_setbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
214 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
218 return wl_ioctl(dev
, WLC_SET_VAR
, bufptr
, iolen
);
222 * get named & bss indexed driver variable buffer value
225 wl_bssiovar_getbuf(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
, void *bufptr
,
231 err
= wl_bssiovar_mkbuf(iovar
, bssidx
, param
, paramlen
, bufptr
, buflen
, &iolen
);
235 return wl_ioctl(dev
, WLC_GET_VAR
, bufptr
, buflen
);
239 * set named & bss indexed driver variable to buffer value
242 wl_bssiovar_set(struct net_device
*dev
, char *iovar
, int bssidx
, void *param
, int paramlen
)
244 char smbuf
[WLC_IOCTL_SMLEN
];
246 return wl_bssiovar_setbuf(dev
, iovar
, bssidx
, param
, paramlen
, smbuf
, sizeof(smbuf
));
250 * get named & bss indexed driver variable buffer value
253 wl_bssiovar_get(struct net_device
*dev
, char *iovar
, int bssidx
, void *outbuf
, int len
)
255 char smbuf
[WLC_IOCTL_SMLEN
];
258 /* use the return buffer if it is bigger than what we have on the stack */
259 if (len
> (int)sizeof(smbuf
)) {
260 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, outbuf
, len
);
262 memset(smbuf
, 0, sizeof(smbuf
));
263 err
= wl_bssiovar_getbuf(dev
, iovar
, bssidx
, NULL
, 0, smbuf
, sizeof(smbuf
));
265 memcpy(outbuf
, smbuf
, len
);
272 int get_primary_key(struct net_device
*dev
)
276 for (key
= val
= 0; (key
< 4) && (val
== 0); key
++) {
278 if (wl_ioctl(dev
, WLC_GET_KEY_PRIMARY
, &val
, sizeof(val
)) < 0)
285 static int wlcompat_ioctl_getiwrange(struct net_device
*dev
,
289 struct iw_range
*range
;
291 range
= (struct iw_range
*) extra
;
292 memset(extra
, 0, sizeof(struct iw_range
));
294 range
->we_version_compiled
= WIRELESS_EXT
;
295 range
->we_version_source
= WIRELESS_EXT
;
297 range
->min_nwid
= range
->max_nwid
= 0;
299 range
->num_channels
= NUM_CHANNELS
;
301 for (i
= 0; i
< NUM_CHANNELS
; i
++) {
302 range
->freq
[k
].i
= i
+ 1;
303 range
->freq
[k
].m
= channel_frequency
[i
] * 100000;
304 range
->freq
[k
].e
= 1;
306 if (k
>= IW_MAX_FREQUENCIES
)
309 range
->num_frequency
= k
;
310 range
->sensitivity
= 3;
312 /* nbd: don't know what this means, but other drivers set it this way */
313 range
->pmp_flags
= IW_POWER_PERIOD
;
314 range
->pmt_flags
= IW_POWER_TIMEOUT
;
315 range
->pm_capa
= IW_POWER_PERIOD
| IW_POWER_TIMEOUT
| IW_POWER_UNICAST_R
;
318 range
->max_pmp
= 65535000;
320 range
->max_pmt
= 65535 * 1000;
322 range
->max_qual
.qual
= 0;
323 range
->max_qual
.level
= 0;
324 range
->max_qual
.noise
= 0;
327 if (wl_iovar_get(dev
, "rtsthresh", &range
->max_rts
, sizeof(int)) < 0)
328 range
->max_rts
= 2347;
330 range
->min_frag
= 256;
332 if (wl_iovar_get(dev
, "fragthresh", &range
->max_frag
, sizeof(int)) < 0)
333 range
->max_frag
= 2346;
335 range
->txpower_capa
= IW_TXPOW_DBM
;
341 static int wlcompat_set_scan(struct net_device
*dev
,
342 struct iw_request_info
*info
,
343 union iwreq_data
*wrqu
,
347 wl_scan_params_t params
;
349 memset(¶ms
, 0, sizeof(params
));
351 /* use defaults (same parameters as wl scan) */
352 memset(¶ms
.bssid
, 0xff, sizeof(params
.bssid
));
353 params
.bss_type
= DOT11_BSSTYPE_ANY
;
354 params
.scan_type
= -1;
356 params
.active_time
= -1;
357 params
.passive_time
= -1;
358 params
.home_time
= -1;
360 /* can only scan in STA mode */
361 wl_ioctl(dev
, WLC_GET_AP
, &last_mode
, sizeof(last_mode
));
363 /* switch to ap mode, scan result query will switch back */
364 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
366 /* wait 250 msec after mode change */
367 set_current_state(TASK_INTERRUPTIBLE
);
368 schedule_timeout(msecs_to_jiffies(250));
371 scan_cur
= SCAN_RETRY_MAX
;
372 while (scan_cur
-- && (wl_ioctl(dev
, WLC_SCAN
, ¶ms
, 64) < 0)) {
373 /* sometimes the driver takes a few tries... */
374 set_current_state(TASK_INTERRUPTIBLE
);
375 schedule_timeout(msecs_to_jiffies(250));
383 /* wait at least 2 seconds for results */
384 set_current_state(TASK_INTERRUPTIBLE
);
385 schedule_timeout(msecs_to_jiffies(2000));
391 struct iw_statistics
*wlcompat_get_wireless_stats(struct net_device
*dev
)
393 struct wl_bss_info
*bss_info
= (struct wl_bss_info
*) buf
;
395 unsigned int rssi
, noise
, ap
;
397 memset(&wstats
, 0, sizeof(wstats
));
398 memset(&pkt
, 0, sizeof(pkt
));
399 memset(buf
, 0, sizeof(buf
));
400 bss_info
->version
= 0x2000;
401 wl_ioctl(dev
, WLC_GET_BSS_INFO
, bss_info
, WLC_IOCTL_MAXLEN
);
402 wl_ioctl(dev
, WLC_GET_PKTCNTS
, &pkt
, sizeof(pkt
));
405 if ((wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) || ap
) {
406 if (wl_ioctl(dev
, WLC_GET_PHY_NOISE
, &noise
, sizeof(noise
)) < 0)
409 // somehow the structure doesn't fit here
413 rssi
= (rssi
== 0 ? 1 : rssi
);
414 wstats
.qual
.updated
= 0x10;
416 wstats
.qual
.updated
|= 0x20;
418 wstats
.qual
.updated
|= 0x40;
420 if ((wstats
.qual
.updated
& 0x60) == 0x60)
423 wstats
.qual
.level
= rssi
;
424 wstats
.qual
.noise
= noise
;
425 wstats
.discard
.misc
= pkt
.rx_bad_pkt
;
426 wstats
.discard
.retries
= pkt
.tx_bad_pkt
;
431 static int wlcompat_get_scan(struct net_device
*dev
,
432 struct iw_request_info
*info
,
433 union iwreq_data
*wrqu
,
436 wl_scan_results_t
*results
= (wl_scan_results_t
*) buf
;
437 wl_bss_info_t
*bss_info
;
439 char *current_ev
= extra
;
441 char *end_buf
= extra
+ IW_SCAN_MAX_DATA
;
446 memset(buf
, 0, WLC_IOCTL_MAXLEN
);
447 results
->buflen
= WLC_IOCTL_MAXLEN
- sizeof(wl_scan_results_t
);
449 if (wl_ioctl(dev
, WLC_SCAN_RESULTS
, buf
, WLC_IOCTL_MAXLEN
) < 0)
452 if ((results
->count
<= 0) && (scan_cur
++ < SCAN_RETRY_MAX
))
455 bss_info
= &(results
->bss_info
[0]);
456 info_ptr
= (char *) bss_info
;
457 for (i
= 0; i
< results
->count
; i
++) {
458 /* send the cell address (must be sent first) */
460 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
461 memcpy(&iwe
.u
.ap_addr
.sa_data
, &bss_info
->BSSID
, sizeof(bss_info
->BSSID
));
462 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_ADDR_LEN
);
465 iwe
.cmd
= SIOCGIWESSID
;
466 iwe
.u
.data
.length
= bss_info
->SSID_len
;
467 if (iwe
.u
.data
.length
> IW_ESSID_MAX_SIZE
)
468 iwe
.u
.data
.length
= IW_ESSID_MAX_SIZE
;
469 iwe
.u
.data
.flags
= 1;
470 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, bss_info
->SSID
);
473 if (bss_info
->capability
& (DOT11_CAP_ESS
| DOT11_CAP_IBSS
)) {
474 iwe
.cmd
= SIOCGIWMODE
;
475 if (bss_info
->capability
& DOT11_CAP_ESS
)
476 iwe
.u
.mode
= IW_MODE_MASTER
;
477 else if (bss_info
->capability
& DOT11_CAP_IBSS
)
478 iwe
.u
.mode
= IW_MODE_ADHOC
;
479 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_UINT_LEN
);
482 /* send frequency/channel info */
483 iwe
.cmd
= SIOCGIWFREQ
;
485 iwe
.u
.freq
.m
= bss_info
->chanspec
& WL_CHANSPEC_CHAN_MASK
;
486 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_FREQ_LEN
);
488 /* add quality statistics */
491 iwe
.u
.qual
.level
= bss_info
->RSSI
;
492 iwe
.u
.qual
.noise
= bss_info
->phy_noise
;
493 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_QUAL_LEN
);
495 /* send encryption capability */
496 iwe
.cmd
= SIOCGIWENCODE
;
497 iwe
.u
.data
.pointer
= NULL
;
498 iwe
.u
.data
.length
= 0;
499 if (bss_info
->capability
& DOT11_CAP_PRIVACY
)
500 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
502 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
503 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, NULL
);
505 /* send rate information */
506 iwe
.cmd
= SIOCGIWRATE
;
507 current_val
= current_ev
+ IW_EV_LCP_LEN
;
508 iwe
.u
.bitrate
.fixed
= iwe
.u
.bitrate
.disabled
= 0;
510 for(j
= 0 ; j
< bss_info
->rateset
.count
; j
++) {
511 iwe
.u
.bitrate
.value
= ((bss_info
->rateset
.rates
[j
] & 0x7f) * 500000);
512 current_val
= iwe_stream_add_value(current_ev
, current_val
, end_buf
, &iwe
, IW_EV_PARAM_LEN
);
514 if((current_val
- current_ev
) > IW_EV_LCP_LEN
)
515 current_ev
= current_val
;
517 info_ptr
+= sizeof(wl_bss_info_t
);
518 if (bss_info
->ie_length
% 4)
519 info_ptr
+= bss_info
->ie_length
+ 4 - (bss_info
->ie_length
% 4);
521 info_ptr
+= bss_info
->ie_length
;
522 bss_info
= (wl_bss_info_t
*) info_ptr
;
525 wrqu
->data
.length
= (current_ev
- extra
);
526 wrqu
->data
.flags
= 0;
529 /* switch back to ap mode */
530 wl_ioctl(dev
, WLC_SET_AP
, &last_mode
, sizeof(last_mode
));
535 static int wlcompat_ioctl(struct net_device
*dev
,
536 struct iw_request_info
*info
,
537 union iwreq_data
*wrqu
,
542 strcpy(wrqu
->name
, "IEEE 802.11-DS");
548 if (wl_ioctl(dev
,WLC_GET_CHANNEL
, &ci
, sizeof(ci
)) < 0)
551 wrqu
->freq
.m
= ci
.target_channel
;
557 if (wrqu
->freq
.m
== -1) {
559 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
562 if (wrqu
->freq
.e
== 1) {
564 int f
= wrqu
->freq
.m
/ 100000;
565 while ((channel
< NUM_CHANNELS
+ 1) && (f
!= channel_frequency
[channel
]))
568 if (channel
== NUM_CHANNELS
) // channel not found
572 wrqu
->freq
.m
= channel
+ 1;
574 if ((wrqu
->freq
.e
== 0) && (wrqu
->freq
.m
< 1000)) {
575 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
589 memset(®
, 0, sizeof(reg
));
591 if (wrqu
->ap_addr
.sa_family
!= ARPHRD_ETHER
)
594 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
597 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
601 wl_ioctl(dev
, WLC_SET_BSSID
, wrqu
->ap_addr
.sa_data
, 6);
603 if (wl_ioctl(dev
, ((ap
|| !infra
) ? WLC_SET_BSSID
: WLC_REASSOC
), wrqu
->ap_addr
.sa_data
, 6) < 0)
610 wrqu
->ap_addr
.sa_family
= ARPHRD_ETHER
;
611 if (wl_ioctl(dev
,WLC_GET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
619 if (wl_ioctl(dev
,WLC_GET_SSID
, &ssid
, sizeof(wlc_ssid_t
)) < 0)
622 wrqu
->essid
.flags
= wrqu
->data
.flags
= 1;
623 wrqu
->essid
.length
= wrqu
->data
.length
= ssid
.SSID_len
+ 1;
624 memcpy(extra
,ssid
.SSID
,ssid
.SSID_len
+ 1);
630 memset(&ssid
, 0, sizeof(ssid
));
631 ssid
.SSID_len
= strlen(extra
);
632 if (ssid
.SSID_len
> 32)
634 memcpy(ssid
.SSID
, extra
, ssid
.SSID_len
);
635 if (wl_ioctl(dev
, WLC_SET_SSID
, &ssid
, sizeof(ssid
)) < 0)
641 if (wl_iovar_get(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
647 if (wl_iovar_set(dev
, "rtsthresh", &(wrqu
->rts
.value
), sizeof(int)) < 0)
653 if (wl_iovar_get(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
659 if (wl_iovar_set(dev
, "fragthresh", &(wrqu
->frag
.value
), sizeof(int)) < 0)
667 wl_ioctl(dev
, WLC_GET_RADIO
, &radio
, sizeof(int));
669 if (wl_iovar_get(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
672 override
= (wrqu
->txpower
.value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
;
673 wrqu
->txpower
.value
&= ~WL_TXPWR_OVERRIDE
;
674 if (!override
&& (wrqu
->txpower
.value
> 76))
675 wrqu
->txpower
.value
= 76;
676 wrqu
->txpower
.value
/= 4;
678 wrqu
->txpower
.fixed
= 0;
679 wrqu
->txpower
.disabled
= radio
;
680 wrqu
->txpower
.flags
= IW_TXPOW_DBM
;
685 /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
686 int radio
= wrqu
->txpower
.disabled
;
688 wl_ioctl(dev
, WLC_SET_RADIO
, &radio
, sizeof(int));
690 if (!wrqu
->txpower
.disabled
&& (wrqu
->txpower
.value
> 0)) {
693 if (wl_iovar_get(dev
, "qtxpower", &value
, sizeof(int)) < 0)
696 value
&= WL_TXPWR_OVERRIDE
;
697 wrqu
->txpower
.value
*= 4;
698 wrqu
->txpower
.value
|= value
;
700 if (wrqu
->txpower
.flags
!= IW_TXPOW_DBM
)
703 if (wrqu
->txpower
.value
> 0)
704 if (wl_iovar_set(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
711 int val
= 0, wep
= 1, wrestrict
= 1;
712 int index
= (wrqu
->data
.flags
& IW_ENCODE_INDEX
) - 1;
715 index
= get_primary_key(dev
);
717 if (wrqu
->data
.flags
& IW_ENCODE_DISABLED
) {
719 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
724 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
727 if (wrqu
->data
.flags
& IW_ENCODE_OPEN
)
730 if (wrqu
->data
.pointer
&& (wrqu
->data
.length
> 0) && (wrqu
->data
.length
<= 16)) {
732 memset(&key
, 0, sizeof(key
));
734 key
.flags
= WL_PRIMARY_KEY
;
735 key
.len
= wrqu
->data
.length
;
737 memcpy(key
.data
, wrqu
->data
.pointer
, wrqu
->data
.length
);
739 if (wl_ioctl(dev
, WLC_SET_KEY
, &key
, sizeof(key
)) < 0)
744 wl_ioctl(dev
, WLC_SET_KEY_PRIMARY
, &index
, sizeof(index
));
747 wl_ioctl(dev
, WLC_SET_WEP_RESTRICT
, &wrestrict
, sizeof(wrestrict
));
754 int key
= get_primary_key(dev
);
758 if (wl_ioctl(dev
, WLC_GET_WSEC
, &val
, sizeof(val
)) < 0)
760 if (!(val
& WEP_ENABLED
)) {
761 wrqu
->data
.flags
= IW_ENCODE_DISABLED
;
765 key
= get_primary_key(dev
);
766 wrqu
->data
.flags
= IW_ENCODE_ENABLED
;
768 /* the driver apparently doesn't allow us to read the wep key */
769 wrqu
->data
.flags
|= IW_ENCODE_NOKEY
;
775 return wlcompat_ioctl_getiwrange(dev
, extra
);
780 int ap
= -1, infra
= -1, passive
= 0, wet
= 0;
782 switch (wrqu
->mode
) {
783 case IW_MODE_MONITOR
:
808 wl_ioctl(dev
, WLC_SET_PASSIVE
, &passive
, sizeof(passive
));
809 wl_ioctl(dev
, WLC_SET_MONITOR
, &passive
, sizeof(passive
));
810 wl_ioctl(dev
, WLC_SET_WET
, &wet
, sizeof(wet
));
812 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
814 wl_ioctl(dev
, WLC_SET_INFRA
, &infra
, sizeof(infra
));
821 int ap
, infra
, wet
, passive
;
823 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
825 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
827 if (wl_ioctl(dev
, WLC_GET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
829 if (wl_ioctl(dev
, WLC_GET_WET
, &wet
, sizeof(wet
)) < 0)
833 wrqu
->mode
= IW_MODE_MONITOR
;
835 wrqu
->mode
= IW_MODE_ADHOC
;
838 wrqu
->mode
= IW_MODE_MASTER
;
841 wrqu
->mode
= IW_MODE_REPEAT
;
843 wrqu
->mode
= IW_MODE_INFRA
;
857 static const iw_handler wlcompat_handler
[] = {
858 NULL
, /* SIOCSIWCOMMIT */
859 wlcompat_ioctl
, /* SIOCGIWNAME */
860 NULL
, /* SIOCSIWNWID */
861 NULL
, /* SIOCGIWNWID */
862 wlcompat_ioctl
, /* SIOCSIWFREQ */
863 wlcompat_ioctl
, /* SIOCGIWFREQ */
864 wlcompat_ioctl
, /* SIOCSIWMODE */
865 wlcompat_ioctl
, /* SIOCGIWMODE */
866 NULL
, /* SIOCSIWSENS */
867 NULL
, /* SIOCGIWSENS */
868 NULL
, /* SIOCSIWRANGE, unused */
869 wlcompat_ioctl
, /* SIOCGIWRANGE */
870 NULL
, /* SIOCSIWPRIV */
871 NULL
, /* SIOCGIWPRIV */
872 NULL
, /* SIOCSIWSTATS */
873 NULL
, /* SIOCGIWSTATS */
874 iw_handler_set_spy
, /* SIOCSIWSPY */
875 iw_handler_get_spy
, /* SIOCGIWSPY */
876 iw_handler_set_thrspy
, /* SIOCSIWTHRSPY */
877 iw_handler_get_thrspy
, /* SIOCGIWTHRSPY */
878 wlcompat_ioctl
, /* SIOCSIWAP */
879 wlcompat_ioctl
, /* SIOCGIWAP */
880 NULL
, /* -- hole -- */
881 NULL
, /* SIOCGIWAPLIST */
882 wlcompat_set_scan
, /* SIOCSIWSCAN */
883 wlcompat_get_scan
, /* SIOCGIWSCAN */
884 wlcompat_ioctl
, /* SIOCSIWESSID */
885 wlcompat_ioctl
, /* SIOCGIWESSID */
886 NULL
, /* SIOCSIWNICKN */
887 NULL
, /* SIOCGIWNICKN */
888 NULL
, /* -- hole -- */
889 NULL
, /* -- hole -- */
890 NULL
, /* SIOCSIWRATE */
891 NULL
, /* SIOCGIWRATE */
892 wlcompat_ioctl
, /* SIOCSIWRTS */
893 wlcompat_ioctl
, /* SIOCGIWRTS */
894 wlcompat_ioctl
, /* SIOCSIWFRAG */
895 wlcompat_ioctl
, /* SIOCGIWFRAG */
896 wlcompat_ioctl
, /* SIOCSIWTXPOW */
897 wlcompat_ioctl
, /* SIOCGIWTXPOW */
898 NULL
, /* SIOCSIWRETRY */
899 NULL
, /* SIOCGIWRETRY */
900 wlcompat_ioctl
, /* SIOCSIWENCODE */
901 wlcompat_ioctl
, /* SIOCGIWENCODE */
905 static const struct iw_handler_def wlcompat_handler_def
=
907 .standard
= (iw_handler
*) wlcompat_handler
,
908 .num_standard
= sizeof(wlcompat_handler
)/sizeof(iw_handler
),
913 void print_buffer(int len
, unsigned char *buf
) {
916 for (x
=0;x
<len
&& x
<180 ;x
++) {
919 printk("%02X",buf
[x
]);
928 static int (*old_ioctl
)(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
);
929 static int new_ioctl(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
) {
931 struct iwreq
*iwr
= (struct iwreq
*) ifr
;
932 struct iw_request_info info
;
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 ((dev
->wireless_handlers
== NULL
) && ((wl_ioctl(dev
, WLC_GET_MAGIC
, &i
, sizeof(i
)) == 0) && i
== WLC_IOCTL_MAGIC
))
991 printk("No Broadcom devices found.\n");
996 old_ioctl
= dev
->do_ioctl
;
997 dev
->do_ioctl
= new_ioctl
;
998 dev
->wireless_handlers
= (struct iw_handler_def
*)&wlcompat_handler_def
;
999 dev
->get_wireless_stats
= wlcompat_get_wireless_stats
;
1003 init_timer(&rng_timer
);
1004 rng_timer
.function
= rng_timer_tick
;
1005 rng_timer
.data
= (unsigned long) dev
;
1006 rng_timer_tick((unsigned long) dev
);
1011 printk("broadcom driver private data: 0x%08x\n", dev
->priv
);
1016 static void __exit
wlcompat_exit()
1020 del_timer(&rng_timer
);
1022 dev
->get_wireless_stats
= NULL
;
1023 dev
->wireless_handlers
= NULL
;
1024 dev
->do_ioctl
= old_ioctl
;
1029 MODULE_AUTHOR("openwrt.org");
1030 MODULE_LICENSE("GPL");
1033 module_param(random
, int, 0);
1035 module_init(wlcompat_init
);
1036 module_exit(wlcompat_exit
);