4 * Copyright (C) 2005 Mike Baker,
5 * Felix Fietkau <openwrt@nbd.name>
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 <asm/uaccess.h>
31 #include <linux/wireless.h>
32 #include <linux/timer.h>
34 #include <net/iw_handler.h>
36 #include <proto/802.11.h>
38 static struct net_device
*dev
;
39 static unsigned short bss_force
;
40 static struct iw_statistics wstats
;
41 static int random
= 1;
42 char buf
[WLC_IOCTL_MAXLEN
];
44 /* The frequency of each channel in MHz */
45 const long channel_frequency
[] = {
46 2412, 2417, 2422, 2427, 2432, 2437, 2442,
47 2447, 2452, 2457, 2462, 2467, 2472, 2484
49 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
51 #define RNG_POLL_FREQ 20
53 typedef struct internal_wsec_key
{
55 uint8 unknown_1
; // 0x01
57 uint8 unknown_2
[7]; // 0x03
60 char data
[32]; // 0x0e
64 static int wlcompat_private_ioctl(struct net_device
*dev
,
65 struct iw_request_info
*info
,
66 union iwreq_data
*wrqu
,
69 void print_buffer(int len
, unsigned char *buf
);
72 static int wl_ioctl(struct net_device
*dev
, int cmd
, void *buf
, int len
)
74 mm_segment_t old_fs
= get_fs();
81 strncpy(ifr
.ifr_name
, dev
->name
, IFNAMSIZ
);
82 ifr
.ifr_data
= (caddr_t
) &ioc
;
84 ret
= dev
->do_ioctl(dev
,&ifr
,SIOCDEVPRIVATE
);
89 static int wl_set_val(struct net_device
*dev
, char *var
, void *val
, int len
)
95 /* check for overflow */
96 if ((buf_len
= strlen(var
)) + 1 + len
> sizeof(buf
))
102 /* append int value onto the end of the name string */
103 memcpy(&(buf
[buf_len
]), val
, len
);
106 ret
= wl_ioctl(dev
, WLC_SET_VAR
, buf
, buf_len
);
110 static int wl_get_val(struct net_device
*dev
, char *var
, void *val
, int len
)
116 /* check for overflow */
117 if ((buf_len
= strlen(var
)) + 1 > sizeof(buf
) || len
> sizeof(buf
))
121 if (ret
= wl_ioctl(dev
, WLC_GET_VAR
, buf
, buf_len
+ len
))
124 memcpy(val
, buf
, len
);
128 int get_primary_key(struct net_device
*dev
)
132 for (key
= val
= 0; (key
< 4) && (val
== 0); key
++) {
134 if (wl_ioctl(dev
, WLC_GET_KEY_PRIMARY
, &val
, sizeof(val
)) < 0)
141 static int wlcompat_ioctl_getiwrange(struct net_device
*dev
,
145 struct iw_range
*range
;
147 range
= (struct iw_range
*) extra
;
148 bzero(extra
, sizeof(struct iw_range
));
150 range
->we_version_compiled
= WIRELESS_EXT
;
151 range
->we_version_source
= WIRELESS_EXT
;
153 range
->min_nwid
= range
->max_nwid
= 0;
155 range
->num_channels
= NUM_CHANNELS
;
157 for (i
= 0; i
< NUM_CHANNELS
; i
++) {
158 range
->freq
[k
].i
= i
+ 1;
159 range
->freq
[k
].m
= channel_frequency
[i
] * 100000;
160 range
->freq
[k
].e
= 1;
162 if (k
>= IW_MAX_FREQUENCIES
)
165 range
->num_frequency
= k
;
166 range
->sensitivity
= 3;
168 /* nbd: don't know what this means, but other drivers set it this way */
169 range
->pmp_flags
= IW_POWER_PERIOD
;
170 range
->pmt_flags
= IW_POWER_TIMEOUT
;
171 range
->pm_capa
= IW_POWER_PERIOD
| IW_POWER_TIMEOUT
| IW_POWER_UNICAST_R
;
174 range
->max_pmp
= 65535000;
176 range
->max_pmt
= 65535 * 1000;
178 range
->max_qual
.qual
= 0;
179 range
->max_qual
.level
= 0;
180 range
->max_qual
.noise
= 0;
183 if (wl_ioctl(dev
, WLC_GET_RTS
, &range
->max_rts
, sizeof(int)) < 0)
184 range
->max_rts
= 2347;
186 range
->min_frag
= 256;
188 if (wl_ioctl(dev
, WLC_GET_FRAG
, &range
->max_frag
, sizeof(int)) < 0)
189 range
->max_frag
= 2346;
191 range
->txpower_capa
= IW_TXPOW_DBM
;
197 static int wlcompat_set_scan(struct net_device
*dev
,
198 struct iw_request_info
*info
,
199 union iwreq_data
*wrqu
,
202 int ap
= 0, oldap
= 0;
203 wl_scan_params_t params
;
205 memset(¶ms
, 0, sizeof(params
));
207 /* use defaults (same parameters as wl scan) */
208 memset(¶ms
.bssid
, 0xff, sizeof(params
.bssid
));
209 params
.bss_type
= DOT11_BSSTYPE_ANY
;
210 params
.scan_type
= -1;
212 params
.active_time
= -1;
213 params
.passive_time
= -1;
214 params
.home_time
= -1;
216 /* can only scan in STA mode */
217 wl_ioctl(dev
, WLC_GET_AP
, &oldap
, sizeof(oldap
));
219 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
221 if (wl_ioctl(dev
, WLC_SCAN
, ¶ms
, 64) < 0)
225 wl_ioctl(dev
, WLC_SET_AP
, &oldap
, sizeof(oldap
));
231 struct iw_statistics
*wlcompat_get_wireless_stats(struct net_device
*dev
)
233 wl_bss_info_t
*bss_info
= (wl_bss_info_t
*) buf
;
235 unsigned int rssi
, noise
, ap
;
237 memset(&wstats
, 0, sizeof(wstats
));
238 memset(&pkt
, 0, sizeof(pkt
));
239 memset(buf
, 0, sizeof(buf
));
240 bss_info
->version
= 0x2000;
241 wl_ioctl(dev
, WLC_GET_BSS_INFO
, bss_info
, WLC_IOCTL_MAXLEN
);
242 wl_ioctl(dev
, WLC_GET_PKTCNTS
, &pkt
, sizeof(pkt
));
245 if ((wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0) || ap
) {
246 if (wl_ioctl(dev
, WLC_GET_PHY_NOISE
, &noise
, sizeof(noise
)) < 0)
249 // somehow the structure doesn't fit here
253 rssi
= (rssi
== 0 ? 1 : rssi
);
254 wstats
.qual
.updated
= 0x10;
256 wstats
.qual
.updated
|= 0x20;
258 wstats
.qual
.updated
|= 0x40;
260 if ((wstats
.qual
.updated
& 0x60) == 0x60)
263 wstats
.qual
.level
= rssi
;
264 wstats
.qual
.noise
= noise
;
265 wstats
.discard
.misc
= pkt
.rx_bad_pkt
;
266 wstats
.discard
.retries
= pkt
.tx_bad_pkt
;
271 static int wlcompat_get_scan(struct net_device
*dev
,
272 struct iw_request_info
*info
,
273 union iwreq_data
*wrqu
,
276 wl_scan_results_t
*results
= (wl_scan_results_t
*) buf
;
277 wl_bss_info_t
*bss_info
;
279 char *current_ev
= extra
;
281 char *end_buf
= extra
+ IW_SCAN_MAX_DATA
;
286 results
->buflen
= WLC_IOCTL_MAXLEN
- sizeof(wl_scan_results_t
);
288 if (wl_ioctl(dev
, WLC_SCAN_RESULTS
, buf
, WLC_IOCTL_MAXLEN
) < 0)
291 bss_info
= &(results
->bss_info
[0]);
292 info_ptr
= (char *) bss_info
;
293 for (i
= 0; i
< results
->count
; i
++) {
295 /* send the cell address (must be sent first) */
297 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
298 memcpy(&iwe
.u
.ap_addr
.sa_data
, &bss_info
->BSSID
, sizeof(bss_info
->BSSID
));
299 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_ADDR_LEN
);
302 iwe
.cmd
= SIOCGIWESSID
;
303 iwe
.u
.data
.length
= bss_info
->SSID_len
;
304 if (iwe
.u
.data
.length
> IW_ESSID_MAX_SIZE
)
305 iwe
.u
.data
.length
= IW_ESSID_MAX_SIZE
;
306 iwe
.u
.data
.flags
= 1;
307 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, bss_info
->SSID
);
310 if (bss_info
->capability
& (DOT11_CAP_ESS
| DOT11_CAP_IBSS
)) {
311 iwe
.cmd
= SIOCGIWMODE
;
312 if (bss_info
->capability
& DOT11_CAP_ESS
)
313 iwe
.u
.mode
= IW_MODE_MASTER
;
314 else if (bss_info
->capability
& DOT11_CAP_IBSS
)
315 iwe
.u
.mode
= IW_MODE_ADHOC
;
316 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_UINT_LEN
);
319 /* send frequency/channel info */
320 iwe
.cmd
= SIOCGIWFREQ
;
322 iwe
.u
.freq
.m
= bss_info
->channel
;
323 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_FREQ_LEN
);
325 /* add quality statistics */
328 iwe
.u
.qual
.level
= bss_info
->RSSI
;
329 iwe
.u
.qual
.noise
= bss_info
->phy_noise
;
330 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_QUAL_LEN
);
332 /* send encryption capability */
333 iwe
.cmd
= SIOCGIWENCODE
;
334 iwe
.u
.data
.pointer
= NULL
;
335 iwe
.u
.data
.length
= 0;
336 if (bss_info
->capability
& DOT11_CAP_PRIVACY
)
337 iwe
.u
.data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
339 iwe
.u
.data
.flags
= IW_ENCODE_DISABLED
;
340 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, NULL
);
342 /* send rate information */
343 iwe
.cmd
= SIOCGIWRATE
;
344 current_val
= current_ev
+ IW_EV_LCP_LEN
;
345 iwe
.u
.bitrate
.fixed
= iwe
.u
.bitrate
.disabled
= 0;
347 for(j
= 0 ; j
< bss_info
->rateset
.count
; j
++) {
348 iwe
.u
.bitrate
.value
= ((bss_info
->rateset
.rates
[j
] & 0x7f) * 500000);
349 current_val
= iwe_stream_add_value(current_ev
, current_val
, end_buf
, &iwe
, IW_EV_PARAM_LEN
);
351 if((current_val
- current_ev
) > IW_EV_LCP_LEN
)
352 current_ev
= current_val
;
354 info_ptr
+= sizeof(wl_bss_info_t
);
355 if (bss_info
->ie_length
% 4)
356 info_ptr
+= bss_info
->ie_length
+ 4 - (bss_info
->ie_length
% 4);
358 info_ptr
+= bss_info
->ie_length
;
359 bss_info
= (wl_bss_info_t
*) info_ptr
;
362 wrqu
->data
.length
= (current_ev
- extra
);
363 wrqu
->data
.flags
= 0;
368 static int wlcompat_ioctl(struct net_device
*dev
,
369 struct iw_request_info
*info
,
370 union iwreq_data
*wrqu
,
375 strcpy(wrqu
->name
, "IEEE 802.11-DS");
381 if (wl_ioctl(dev
,WLC_GET_CHANNEL
, &ci
, sizeof(ci
)) < 0)
384 wrqu
->freq
.m
= ci
.target_channel
;
390 if (wrqu
->freq
.m
== -1) {
392 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
395 if (wrqu
->freq
.e
== 1) {
397 int f
= wrqu
->freq
.m
/ 100000;
398 while ((channel
< NUM_CHANNELS
+ 1) && (f
!= channel_frequency
[channel
]))
401 if (channel
== NUM_CHANNELS
) // channel not found
405 wrqu
->freq
.m
= channel
+ 1;
407 if ((wrqu
->freq
.e
== 0) && (wrqu
->freq
.m
< 1000)) {
408 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
422 memset(®
, 0, sizeof(reg
));
424 if (wrqu
->ap_addr
.sa_family
!= ARPHRD_ETHER
)
427 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
430 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
434 wl_ioctl(dev
, WLC_SET_BSSID
, wrqu
->ap_addr
.sa_data
, 6);
438 reg
.val
= bss_force
<< 16 | bss_force
;
439 wl_ioctl(dev
, WLC_W_REG
, ®
, sizeof(reg
));
442 wl_ioctl(dev
, WLC_R_REG
, ®
, sizeof(reg
));
443 reg
.val
= bss_force
<< 16;
444 wl_ioctl(dev
, WLC_W_REG
, ®
, sizeof(reg
));
447 if (wl_ioctl(dev
, ((ap
|| !infra
) ? WLC_SET_BSSID
: WLC_REASSOC
), wrqu
->ap_addr
.sa_data
, 6) < 0)
456 memset(®
, 0, sizeof(reg
));
460 wl_ioctl(dev
, WLC_R_REG
, ®
, sizeof(reg
));
461 printk("bss time = 0x%08x", reg
.val
);
464 wl_ioctl(dev
, WLC_R_REG
, ®
, sizeof(reg
));
465 printk("%08x\n", reg
.val
);
468 wrqu
->ap_addr
.sa_family
= ARPHRD_ETHER
;
469 if (wl_ioctl(dev
,WLC_GET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
477 if (wl_ioctl(dev
,WLC_GET_SSID
, &ssid
, sizeof(wlc_ssid_t
)) < 0)
480 wrqu
->essid
.flags
= wrqu
->data
.flags
= 1;
481 wrqu
->essid
.length
= wrqu
->data
.length
= ssid
.SSID_len
+ 1;
482 memcpy(extra
,ssid
.SSID
,ssid
.SSID_len
+ 1);
488 memset(&ssid
, 0, sizeof(ssid
));
489 ssid
.SSID_len
= strlen(extra
);
490 if (ssid
.SSID_len
> WLC_ESSID_MAX_SIZE
)
491 ssid
.SSID_len
= WLC_ESSID_MAX_SIZE
;
492 memcpy(ssid
.SSID
, extra
, ssid
.SSID_len
);
493 if (wl_ioctl(dev
, WLC_SET_SSID
, &ssid
, sizeof(ssid
)) < 0)
499 if (wl_ioctl(dev
,WLC_GET_RTS
,&(wrqu
->rts
.value
),sizeof(int)) < 0)
505 if (wl_ioctl(dev
,WLC_SET_RTS
,&(wrqu
->rts
.value
),sizeof(int)) < 0)
511 if (wl_ioctl(dev
,WLC_GET_FRAG
,&(wrqu
->frag
.value
),sizeof(int)) < 0)
517 if (wl_ioctl(dev
,WLC_SET_FRAG
,&(wrqu
->frag
.value
),sizeof(int)) < 0)
525 wl_ioctl(dev
, WLC_GET_RADIO
, &radio
, sizeof(int));
527 if (wl_get_val(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
530 override
= (wrqu
->txpower
.value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
;
531 wrqu
->txpower
.value
&= ~WL_TXPWR_OVERRIDE
;
532 if (!override
&& (wrqu
->txpower
.value
> 76))
533 wrqu
->txpower
.value
= 76;
534 wrqu
->txpower
.value
/= 4;
536 wrqu
->txpower
.fixed
= 0;
537 wrqu
->txpower
.disabled
= radio
;
538 wrqu
->txpower
.flags
= IW_TXPOW_DBM
;
543 /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
544 int radio
= wrqu
->txpower
.disabled
;
546 wl_ioctl(dev
, WLC_SET_RADIO
, &radio
, sizeof(int));
548 if (!wrqu
->txpower
.disabled
&& (wrqu
->txpower
.value
> 0)) {
551 if (wl_get_val(dev
, "qtxpower", &value
, sizeof(int)) < 0)
554 value
&= WL_TXPWR_OVERRIDE
;
555 wrqu
->txpower
.value
*= 4;
556 wrqu
->txpower
.value
|= value
;
558 if (wrqu
->txpower
.flags
!= IW_TXPOW_DBM
)
561 if (wrqu
->txpower
.value
> 0)
562 if (wl_set_val(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
569 int val
= 0, wep
= 1, wrestrict
= 1;
570 int index
= (wrqu
->data
.flags
& IW_ENCODE_INDEX
) - 1;
573 index
= get_primary_key(dev
);
575 if (wrqu
->data
.flags
& IW_ENCODE_DISABLED
) {
577 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
582 if (wl_ioctl(dev
, WLC_SET_WSEC
, &wep
, sizeof(val
)) < 0)
585 if (wrqu
->data
.flags
& IW_ENCODE_OPEN
)
588 if (wrqu
->data
.pointer
&& (wrqu
->data
.length
> 0) && (wrqu
->data
.length
<= 16)) {
590 memset(&key
, 0, sizeof(key
));
592 key
.flags
= WL_PRIMARY_KEY
;
593 key
.len
= wrqu
->data
.length
;
595 memcpy(key
.data
, wrqu
->data
.pointer
, wrqu
->data
.length
);
597 if (wl_ioctl(dev
, WLC_SET_KEY
, &key
, sizeof(key
)) < 0)
602 wl_ioctl(dev
, WLC_SET_KEY_PRIMARY
, &index
, sizeof(index
));
605 wl_ioctl(dev
, WLC_SET_WEP_RESTRICT
, &wrestrict
, sizeof(wrestrict
));
613 if (wl_ioctl(dev
, WLC_GET_WEP
, &val
, sizeof(val
)) < 0)
618 int key
= get_primary_key(dev
);
620 wrqu
->data
.flags
= IW_ENCODE_ENABLED
;
625 info_addr
= (int *) dev
->priv
;
626 wep_key
= (wkey
*) ((*info_addr
) + 0x2752 + (key
* 0x110));
628 wrqu
->data
.flags
|= key
+ 1;
629 wrqu
->data
.length
= wep_key
->len
;
631 memset(extra
, 0, 16);
632 memcpy(extra
, wep_key
->data
, 16);
634 wrqu
->data
.flags
|= IW_ENCODE_NOKEY
;
637 wrqu
->data
.flags
= IW_ENCODE_DISABLED
;
644 return wlcompat_ioctl_getiwrange(dev
, extra
);
649 int ap
= -1, infra
= -1, passive
= 0, wet
= 0;
651 switch (wrqu
->mode
) {
652 case IW_MODE_MONITOR
:
677 wl_ioctl(dev
, WLC_SET_PASSIVE
, &passive
, sizeof(passive
));
678 wl_ioctl(dev
, WLC_SET_MONITOR
, &passive
, sizeof(passive
));
679 wl_ioctl(dev
, WLC_SET_WET
, &wet
, sizeof(wet
));
681 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
683 wl_ioctl(dev
, WLC_SET_INFRA
, &infra
, sizeof(infra
));
690 int ap
, infra
, wet
, passive
;
692 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
694 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
696 if (wl_ioctl(dev
, WLC_GET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
698 if (wl_ioctl(dev
, WLC_GET_WET
, &wet
, sizeof(wet
)) < 0)
702 wrqu
->mode
= IW_MODE_MONITOR
;
704 wrqu
->mode
= IW_MODE_ADHOC
;
707 wrqu
->mode
= IW_MODE_MASTER
;
710 wrqu
->mode
= IW_MODE_REPEAT
;
712 wrqu
->mode
= IW_MODE_INFRA
;
720 if (info
->cmd
>= SIOCIWFIRSTPRIV
)
721 return wlcompat_private_ioctl(dev
, info
, wrqu
, extra
);
730 static const iw_handler wlcompat_handler
[] = {
731 NULL
, /* SIOCSIWCOMMIT */
732 wlcompat_ioctl
, /* SIOCGIWNAME */
733 NULL
, /* SIOCSIWNWID */
734 NULL
, /* SIOCGIWNWID */
735 wlcompat_ioctl
, /* SIOCSIWFREQ */
736 wlcompat_ioctl
, /* SIOCGIWFREQ */
737 wlcompat_ioctl
, /* SIOCSIWMODE */
738 wlcompat_ioctl
, /* SIOCGIWMODE */
739 NULL
, /* SIOCSIWSENS */
740 NULL
, /* SIOCGIWSENS */
741 NULL
, /* SIOCSIWRANGE, unused */
742 wlcompat_ioctl
, /* SIOCGIWRANGE */
743 NULL
, /* SIOCSIWPRIV */
744 NULL
, /* SIOCGIWPRIV */
745 NULL
, /* SIOCSIWSTATS */
746 NULL
, /* SIOCGIWSTATS */
747 iw_handler_set_spy
, /* SIOCSIWSPY */
748 iw_handler_get_spy
, /* SIOCGIWSPY */
749 iw_handler_set_thrspy
, /* SIOCSIWTHRSPY */
750 iw_handler_get_thrspy
, /* SIOCGIWTHRSPY */
751 wlcompat_ioctl
, /* SIOCSIWAP */
752 wlcompat_ioctl
, /* SIOCGIWAP */
753 NULL
, /* -- hole -- */
754 NULL
, /* SIOCGIWAPLIST */
755 wlcompat_set_scan
, /* SIOCSIWSCAN */
756 wlcompat_get_scan
, /* SIOCGIWSCAN */
757 wlcompat_ioctl
, /* SIOCSIWESSID */
758 wlcompat_ioctl
, /* SIOCGIWESSID */
759 NULL
, /* SIOCSIWNICKN */
760 NULL
, /* SIOCGIWNICKN */
761 NULL
, /* -- hole -- */
762 NULL
, /* -- hole -- */
763 NULL
, /* SIOCSIWRATE */
764 NULL
, /* SIOCGIWRATE */
765 wlcompat_ioctl
, /* SIOCSIWRTS */
766 wlcompat_ioctl
, /* SIOCGIWRTS */
767 wlcompat_ioctl
, /* SIOCSIWFRAG */
768 wlcompat_ioctl
, /* SIOCGIWFRAG */
769 wlcompat_ioctl
, /* SIOCSIWTXPOW */
770 wlcompat_ioctl
, /* SIOCGIWTXPOW */
771 NULL
, /* SIOCSIWRETRY */
772 NULL
, /* SIOCGIWRETRY */
773 wlcompat_ioctl
, /* SIOCSIWENCODE */
774 wlcompat_ioctl
, /* SIOCGIWENCODE */
778 #define WLCOMPAT_SET_MONITOR SIOCIWFIRSTPRIV + 0
779 #define WLCOMPAT_GET_MONITOR SIOCIWFIRSTPRIV + 1
780 #define WLCOMPAT_SET_TXPWR_LIMIT SIOCIWFIRSTPRIV + 2
781 #define WLCOMPAT_GET_TXPWR_LIMIT SIOCIWFIRSTPRIV + 3
782 #define WLCOMPAT_SET_ANTDIV SIOCIWFIRSTPRIV + 4
783 #define WLCOMPAT_GET_ANTDIV SIOCIWFIRSTPRIV + 5
784 #define WLCOMPAT_SET_TXANT SIOCIWFIRSTPRIV + 6
785 #define WLCOMPAT_GET_TXANT SIOCIWFIRSTPRIV + 7
786 #define WLCOMPAT_SET_BSS_FORCE SIOCIWFIRSTPRIV + 8
787 #define WLCOMPAT_GET_BSS_FORCE SIOCIWFIRSTPRIV + 9
790 static int wlcompat_private_ioctl(struct net_device
*dev
,
791 struct iw_request_info
*info
,
792 union iwreq_data
*wrqu
,
795 int *value
= (int *) wrqu
->name
;
798 case WLCOMPAT_SET_MONITOR
:
800 if (wl_ioctl(dev
, WLC_SET_MONITOR
, value
, sizeof(int)) < 0)
805 case WLCOMPAT_GET_MONITOR
:
807 if (wl_ioctl(dev
, WLC_GET_MONITOR
, extra
, sizeof(int)) < 0)
812 case WLCOMPAT_SET_TXPWR_LIMIT
:
817 if (wl_get_val(dev
, "qtxpower", &val
, sizeof(int)) < 0)
821 val
|= WL_TXPWR_OVERRIDE
;
823 val
&= ~WL_TXPWR_OVERRIDE
;
825 if (wl_set_val(dev
, "qtxpower", &val
, sizeof(int)) < 0)
830 case WLCOMPAT_GET_TXPWR_LIMIT
:
832 if (wl_get_val(dev
, "qtxpower", value
, sizeof(int)) < 0)
835 *value
= ((*value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
? 1 : 0);
839 case WLCOMPAT_SET_ANTDIV
:
841 if (wl_ioctl(dev
, WLC_SET_ANTDIV
, value
, sizeof(int)) < 0)
846 case WLCOMPAT_GET_ANTDIV
:
848 if (wl_ioctl(dev
, WLC_GET_ANTDIV
, extra
, sizeof(int)) < 0)
853 case WLCOMPAT_SET_TXANT
:
855 if (wl_ioctl(dev
, WLC_SET_TXANT
, value
, sizeof(int)) < 0)
860 case WLCOMPAT_GET_TXANT
:
862 if (wl_ioctl(dev
, WLC_GET_TXANT
, extra
, sizeof(int)) < 0)
867 case WLCOMPAT_SET_BSS_FORCE
:
869 bss_force
= (unsigned short) *value
;
872 case WLCOMPAT_GET_BSS_FORCE
:
874 *extra
= (int) bss_force
;
886 static const struct iw_priv_args wlcompat_private_args
[] =
888 { WLCOMPAT_SET_MONITOR
,
889 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
893 { WLCOMPAT_GET_MONITOR
,
895 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
898 { WLCOMPAT_SET_TXPWR_LIMIT
,
899 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
903 { WLCOMPAT_GET_TXPWR_LIMIT
,
905 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
908 { WLCOMPAT_SET_ANTDIV
,
909 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
913 { WLCOMPAT_GET_ANTDIV
,
915 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
918 { WLCOMPAT_SET_TXANT
,
919 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
923 { WLCOMPAT_GET_TXANT
,
925 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
928 { WLCOMPAT_SET_BSS_FORCE
,
929 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
933 { WLCOMPAT_GET_BSS_FORCE
,
935 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
940 static const iw_handler wlcompat_private
[] =
942 wlcompat_private_ioctl
,
947 static const struct iw_handler_def wlcompat_handler_def
=
949 .standard
= (iw_handler
*) wlcompat_handler
,
950 .num_standard
= sizeof(wlcompat_handler
)/sizeof(iw_handler
),
951 .private = wlcompat_private
,
953 .private_args
= wlcompat_private_args
,
954 .num_private_args
= sizeof(wlcompat_private_args
) / sizeof(wlcompat_private_args
[0])
959 void print_buffer(int len
, unsigned char *buf
) {
962 for (x
=0;x
<len
&& x
<180 ;x
++) {
965 printk("%02X",buf
[x
]);
974 static int (*old_ioctl
)(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
);
975 static int new_ioctl(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
) {
977 struct iwreq
*iwr
= (struct iwreq
*) ifr
;
978 struct iw_request_info info
;
981 printk("dev: %s ioctl: 0x%04x\n",dev
->name
,cmd
);
984 if (cmd
>= SIOCIWFIRSTPRIV
) {
987 ret
= wlcompat_private_ioctl(dev
, &info
, &(iwr
->u
), (char *) &(iwr
->u
));
989 } else if (cmd
==SIOCDEVPRIVATE
) {
990 wl_ioctl_t
*ioc
= (wl_ioctl_t
*)ifr
->ifr_data
;
991 unsigned char *buf
= ioc
->buf
;
992 printk(" cmd: %d buf: 0x%08x len: %d\n",ioc
->cmd
,&(ioc
->buf
),ioc
->len
);
994 print_buffer(ioc
->len
, buf
);
995 ret
= old_ioctl(dev
,ifr
,cmd
);
997 print_buffer(ioc
->len
, buf
);
998 printk(" ret: %d\n", ret
);
1001 ret
= old_ioctl(dev
,ifr
,cmd
);
1007 static struct timer_list rng_timer
;
1009 static void rng_timer_tick(unsigned long n
)
1011 struct net_device
*dev
= (struct net_device
*) n
;
1016 for (i
= 0; i
< 3; i
++) {
1017 ret
|= wl_get_val(dev
, "rand", &data
[i
], sizeof(u16
));
1020 batch_entropy_store(*((u32
*) &data
[0]), *((u32
*) &data
[2]), (jiffies
% 255));
1022 mod_timer(&rng_timer
, jiffies
+ (HZ
/RNG_POLL_FREQ
));
1026 static int __init
wlcompat_init()
1029 char devname
[4] = "wl0";
1032 while (!found
&& (dev
= dev_get_by_name(devname
))) {
1033 if ((dev
->wireless_handlers
== NULL
) && ((wl_ioctl(dev
, WLC_GET_MAGIC
, &i
, sizeof(i
)) == 0) && i
== WLC_IOCTL_MAGIC
))
1039 printk("No Broadcom devices found.\n");
1044 old_ioctl
= dev
->do_ioctl
;
1045 dev
->do_ioctl
= new_ioctl
;
1046 dev
->wireless_handlers
= (struct iw_handler_def
*)&wlcompat_handler_def
;
1047 dev
->get_wireless_stats
= wlcompat_get_wireless_stats
;
1051 init_timer(&rng_timer
);
1052 rng_timer
.function
= rng_timer_tick
;
1053 rng_timer
.data
= (unsigned long) dev
;
1054 rng_timer_tick((unsigned long) dev
);
1059 printk("broadcom driver private data: 0x%08x\n", dev
->priv
);
1064 static void __exit
wlcompat_exit()
1068 del_timer(&rng_timer
);
1070 dev
->get_wireless_stats
= NULL
;
1071 dev
->wireless_handlers
= NULL
;
1072 dev
->do_ioctl
= old_ioctl
;
1077 MODULE_AUTHOR("openwrt.org");
1078 MODULE_LICENSE("GPL");
1081 module_param(random
, int, 0);
1083 module_init(wlcompat_init
);
1084 module_exit(wlcompat_exit
);