4 * Copyright (C) 2005 Mike Baker,
5 * Felix Fietkau <nbd@vd-s.ath.cx>
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/init.h>
28 #include <linux/if_arp.h>
29 #include <asm/uaccess.h>
30 #include <linux/wireless.h>
32 #include <net/iw_handler.h>
35 static struct net_device
*dev
;
36 char buf
[WLC_IOCTL_MAXLEN
];
38 /* The frequency of each channel in MHz */
39 const long channel_frequency
[] = {
40 2412, 2417, 2422, 2427, 2432, 2437, 2442,
41 2447, 2452, 2457, 2462, 2467, 2472, 2484
43 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
45 static int wlcompat_private_ioctl(struct net_device
*dev
,
46 struct iw_request_info
*info
,
47 union iwreq_data
*wrqu
,
50 static int wl_ioctl(struct net_device
*dev
, int cmd
, void *buf
, int len
)
52 mm_segment_t old_fs
= get_fs();
59 strncpy(ifr
.ifr_name
, dev
->name
, IFNAMSIZ
);
60 ifr
.ifr_data
= (caddr_t
) &ioc
;
62 ret
= dev
->do_ioctl(dev
,&ifr
,SIOCDEVPRIVATE
);
67 static int wl_set_val(struct net_device
*dev
, char *var
, void *val
, int len
)
72 /* check for overflow */
73 if ((buf_len
= strlen(var
)) + 1 + len
> sizeof(buf
))
79 /* append int value onto the end of the name string */
80 memcpy(&buf
[buf_len
], val
, len
);
83 return wl_ioctl(dev
, WLC_SET_VAR
, buf
, buf_len
);
86 static int wl_get_val(struct net_device
*dev
, char *var
, void *val
, int len
)
91 /* check for overflow */
92 if (strlen(var
) + 1 > sizeof(buf
) || len
> sizeof(buf
))
96 if ((ret
= wl_ioctl(dev
, WLC_GET_VAR
, buf
, sizeof(buf
))))
99 memcpy(val
, buf
, len
);
104 static int wlcompat_ioctl_getiwrange(struct net_device
*dev
,
108 struct iw_range
*range
;
110 range
= (struct iw_range
*) extra
;
112 range
->we_version_compiled
= WIRELESS_EXT
;
113 range
->we_version_source
= WIRELESS_EXT
;
115 range
->min_nwid
= range
->max_nwid
= 0;
117 range
->num_channels
= NUM_CHANNELS
;
119 for (i
= 0; i
< NUM_CHANNELS
; i
++) {
120 range
->freq
[k
].i
= i
+ 1;
121 range
->freq
[k
].m
= channel_frequency
[i
] * 100000;
122 range
->freq
[k
].e
= 1;
124 if (k
>= IW_MAX_FREQUENCIES
)
127 range
->num_frequency
= k
;
128 range
->sensitivity
= 3;
130 /* nbd: don't know what this means, but other drivers set it this way */
131 range
->pmp_flags
= IW_POWER_PERIOD
;
132 range
->pmt_flags
= IW_POWER_TIMEOUT
;
133 range
->pm_capa
= IW_POWER_PERIOD
| IW_POWER_TIMEOUT
| IW_POWER_UNICAST_R
;
136 range
->max_pmp
= 65535000;
138 range
->max_pmt
= 65535 * 1000;
141 if (wl_ioctl(dev
, WLC_GET_RTS
, &range
->max_rts
, sizeof(int)) < 0)
142 range
->max_rts
= 2347;
144 range
->min_frag
= 256;
146 if (wl_ioctl(dev
, WLC_GET_FRAG
, &range
->max_frag
, sizeof(int)) < 0)
147 range
->max_frag
= 2346;
149 range
->txpower_capa
= IW_TXPOW_MWATT
;
155 static int wlcompat_set_scan(struct net_device
*dev
,
156 struct iw_request_info
*info
,
157 union iwreq_data
*wrqu
,
160 int ap
= 0, oldap
= 0;
161 wl_scan_params_t params
;
163 memset(¶ms
, 0, sizeof(params
));
165 /* use defaults (same parameters as wl scan) */
166 memset(¶ms
.bssid
, 0xff, sizeof(params
.bssid
));
167 params
.bss_type
= DOT11_BSSTYPE_ANY
;
168 params
.scan_type
= -1;
170 params
.active_time
= -1;
171 params
.passive_time
= -1;
172 params
.home_time
= -1;
174 /* can only scan in STA mode */
175 wl_ioctl(dev
, WLC_GET_AP
, &oldap
, sizeof(oldap
));
177 wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
));
179 if (wl_ioctl(dev
, WLC_SCAN
, ¶ms
, 64) < 0)
183 wl_ioctl(dev
, WLC_SET_AP
, &oldap
, sizeof(oldap
));
189 static int wlcompat_get_scan(struct net_device
*dev
,
190 struct iw_request_info
*info
,
191 union iwreq_data
*wrqu
,
194 wl_scan_results_t
*results
= (wl_scan_results_t
*) buf
;
195 wl_bss_info_t
*bss_info
;
197 char *current_ev
= extra
;
199 char *end_buf
= extra
+ IW_SCAN_MAX_DATA
;
203 if (wl_ioctl(dev
, WLC_SCAN_RESULTS
, buf
, WLC_IOCTL_MAXLEN
) < 0)
206 bss_info
= &(results
->bss_info
[0]);
207 info_ptr
= (char *) bss_info
;
208 for (i
= 0; i
< results
->count
; i
++) {
210 /* send the cell address (must be sent first) */
212 iwe
.u
.ap_addr
.sa_family
= ARPHRD_ETHER
;
213 memcpy(&iwe
.u
.ap_addr
.sa_data
, &bss_info
->BSSID
, sizeof(bss_info
->BSSID
));
214 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_ADDR_LEN
);
217 iwe
.cmd
= SIOCGIWESSID
;
218 iwe
.u
.data
.length
= bss_info
->SSID_len
;
219 if (iwe
.u
.data
.length
> IW_ESSID_MAX_SIZE
)
220 iwe
.u
.data
.length
= IW_ESSID_MAX_SIZE
;
221 iwe
.u
.data
.flags
= 1;
222 current_ev
= iwe_stream_add_point(current_ev
, end_buf
, &iwe
, bss_info
->SSID
);
224 /* send frequency/channel info */
225 iwe
.cmd
= SIOCGIWFREQ
;
227 iwe
.u
.freq
.m
= bss_info
->channel
;
228 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_FREQ_LEN
);
230 /* add quality statistics */
232 iwe
.u
.qual
.level
= bss_info
->RSSI
;
233 iwe
.u
.qual
.noise
= bss_info
->phy_noise
;
235 current_ev
= iwe_stream_add_event(current_ev
, end_buf
, &iwe
, IW_EV_QUAL_LEN
);
237 /* send rate information */
238 iwe
.cmd
= SIOCGIWRATE
;
239 current_val
= current_ev
+ IW_EV_LCP_LEN
;
240 iwe
.u
.bitrate
.fixed
= iwe
.u
.bitrate
.disabled
= 0;
242 for(j
= 0 ; j
< bss_info
->rateset
.count
; j
++) {
243 iwe
.u
.bitrate
.value
= ((bss_info
->rateset
.rates
[j
] & 0x7f) * 500000);
244 current_val
= iwe_stream_add_value(current_ev
, current_val
, end_buf
, &iwe
, IW_EV_PARAM_LEN
);
246 if((current_val
- current_ev
) > IW_EV_LCP_LEN
)
247 current_ev
= current_val
;
249 info_ptr
+= sizeof(wl_bss_info_t
);
250 if (bss_info
->ie_length
% 4)
251 info_ptr
+= bss_info
->ie_length
+ 4 - (bss_info
->ie_length
% 4);
253 info_ptr
+= bss_info
->ie_length
;
254 bss_info
= (wl_bss_info_t
*) info_ptr
;
257 wrqu
->data
.length
= (current_ev
- extra
);
258 wrqu
->data
.flags
= 0;
263 static int wlcompat_ioctl(struct net_device
*dev
,
264 struct iw_request_info
*info
,
265 union iwreq_data
*wrqu
,
270 strcpy(wrqu
->name
, "IEEE 802.11-DS");
276 if (wl_ioctl(dev
,WLC_GET_CHANNEL
, &ci
, sizeof(ci
)) < 0)
279 wrqu
->freq
.m
= ci
.target_channel
;
285 if (wrqu
->freq
.e
== 1) {
287 int f
= wrqu
->freq
.m
/ 100000;
288 while ((channel
< NUM_CHANNELS
+ 1) && (f
!= channel_frequency
[channel
]))
291 if (channel
== NUM_CHANNELS
) // channel not found
295 wrqu
->freq
.m
= channel
+ 1;
297 if ((wrqu
->freq
.e
== 0) && (wrqu
->freq
.m
< 1000)) {
298 if (wl_ioctl(dev
, WLC_SET_CHANNEL
, &wrqu
->freq
.m
, sizeof(int)) < 0)
307 if (wrqu
->ap_addr
.sa_family
!= ARPHRD_ETHER
)
310 if (wl_ioctl(dev
,WLC_SET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
317 wrqu
->ap_addr
.sa_family
= ARPHRD_ETHER
;
318 if (wl_ioctl(dev
,WLC_GET_BSSID
,wrqu
->ap_addr
.sa_data
,6) < 0)
326 if (wl_ioctl(dev
,WLC_GET_SSID
, &ssid
, sizeof(wlc_ssid_t
)) < 0)
329 wrqu
->essid
.flags
= wrqu
->data
.flags
= 1;
330 wrqu
->essid
.length
= wrqu
->data
.length
= ssid
.SSID_len
+ 1;
331 memcpy(extra
,ssid
.SSID
,ssid
.SSID_len
+ 1);
337 memset(&ssid
, 0, sizeof(ssid
));
338 ssid
.SSID_len
= strlen(extra
);
339 if (ssid
.SSID_len
> WLC_ESSID_MAX_SIZE
)
340 ssid
.SSID_len
= WLC_ESSID_MAX_SIZE
;
341 memcpy(ssid
.SSID
, extra
, ssid
.SSID_len
);
342 if (wl_ioctl(dev
, WLC_SET_SSID
, &ssid
, sizeof(ssid
)) < 0)
348 if (wl_ioctl(dev
,WLC_GET_RTS
,&(wrqu
->rts
.value
),sizeof(int)) < 0)
354 if (wl_ioctl(dev
,WLC_SET_RTS
,&(wrqu
->rts
.value
),sizeof(int)) < 0)
360 if (wl_ioctl(dev
,WLC_GET_FRAG
,&(wrqu
->frag
.value
),sizeof(int)) < 0)
366 if (wl_ioctl(dev
,WLC_SET_FRAG
,&(wrqu
->frag
.value
),sizeof(int)) < 0)
372 if (wl_get_val(dev
, "qtxpower", &(wrqu
->txpower
.value
), sizeof(int)) < 0)
375 wrqu
->txpower
.value
&= ~WL_TXPWR_OVERRIDE
;
377 wrqu
->txpower
.fixed
= 0;
378 wrqu
->txpower
.disabled
= 0;
379 wrqu
->txpower
.flags
= IW_TXPOW_MWATT
;
386 if (wl_get_val(dev
, "qtxpower", &override
, sizeof(int)) < 0)
389 wrqu
->txpower
.value
|= override
& WL_TXPWR_OVERRIDE
;
391 if (wrqu
->txpower
.flags
!= IW_TXPOW_MWATT
)
394 if (wl_set_val(dev
, "qtxpower", &wrqu
->txpower
.value
, sizeof(int)) < 0)
400 if (wl_ioctl(dev
, WLC_GET_WEP
, &val
, sizeof(val
)) < 0)
404 wrqu
->data
.flags
= IW_ENCODE_ENABLED
| IW_ENCODE_NOKEY
;
406 wrqu
->data
.flags
= IW_ENCODE_DISABLED
;
415 return wlcompat_ioctl_getiwrange(dev
, extra
);
420 int ap
= -1, infra
= -1, passive
= 0, wet
= 0;
422 switch (wrqu
->mode
) {
423 case IW_MODE_MONITOR
:
447 if (wl_ioctl(dev
, WLC_SET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
449 if (wl_ioctl(dev
, WLC_SET_MONITOR
, &passive
, sizeof(passive
)) < 0)
451 if (wl_ioctl(dev
, WLC_SET_WET
, &wet
, sizeof(wet
)) < 0)
454 if (wl_ioctl(dev
, WLC_SET_AP
, &ap
, sizeof(ap
)) < 0)
457 if (wl_ioctl(dev
, WLC_SET_INFRA
, &infra
, sizeof(infra
)) < 0)
465 int ap
, infra
, wet
, passive
;
467 if (wl_ioctl(dev
, WLC_GET_AP
, &ap
, sizeof(ap
)) < 0)
469 if (wl_ioctl(dev
, WLC_GET_INFRA
, &infra
, sizeof(infra
)) < 0)
471 if (wl_ioctl(dev
, WLC_GET_PASSIVE
, &passive
, sizeof(passive
)) < 0)
473 if (wl_ioctl(dev
, WLC_GET_WET
, &wet
, sizeof(wet
)) < 0)
477 wrqu
->mode
= IW_MODE_MONITOR
;
479 wrqu
->mode
= IW_MODE_ADHOC
;
482 wrqu
->mode
= IW_MODE_MASTER
;
485 wrqu
->mode
= IW_MODE_REPEAT
;
487 wrqu
->mode
= IW_MODE_INFRA
;
495 if (info
->cmd
>= SIOCIWFIRSTPRIV
)
496 return wlcompat_private_ioctl(dev
, info
, wrqu
, extra
);
505 static const iw_handler wlcompat_handler
[] = {
506 NULL
, /* SIOCSIWCOMMIT */
507 wlcompat_ioctl
, /* SIOCGIWNAME */
508 NULL
, /* SIOCSIWNWID */
509 NULL
, /* SIOCGIWNWID */
510 wlcompat_ioctl
, /* SIOCSIWFREQ */
511 wlcompat_ioctl
, /* SIOCGIWFREQ */
512 wlcompat_ioctl
, /* SIOCSIWMODE */
513 wlcompat_ioctl
, /* SIOCGIWMODE */
514 NULL
, /* SIOCSIWSENS */
515 NULL
, /* SIOCGIWSENS */
516 NULL
, /* SIOCSIWRANGE, unused */
517 wlcompat_ioctl
, /* SIOCGIWRANGE */
518 NULL
, /* SIOCSIWPRIV */
519 NULL
, /* SIOCGIWPRIV */
520 NULL
, /* SIOCSIWSTATS */
521 NULL
, /* SIOCGIWSTATS */
522 iw_handler_set_spy
, /* SIOCSIWSPY */
523 iw_handler_get_spy
, /* SIOCGIWSPY */
524 iw_handler_set_thrspy
, /* SIOCSIWTHRSPY */
525 iw_handler_get_thrspy
, /* SIOCGIWTHRSPY */
526 wlcompat_ioctl
, /* SIOCSIWAP */
527 wlcompat_ioctl
, /* SIOCGIWAP */
528 NULL
, /* -- hole -- */
529 NULL
, /* SIOCGIWAPLIST */
530 wlcompat_set_scan
, /* SIOCSIWSCAN */
531 wlcompat_get_scan
, /* SIOCGIWSCAN */
532 wlcompat_ioctl
, /* SIOCSIWESSID */
533 wlcompat_ioctl
, /* SIOCGIWESSID */
534 NULL
, /* SIOCSIWNICKN */
535 NULL
, /* SIOCGIWNICKN */
536 NULL
, /* -- hole -- */
537 NULL
, /* -- hole -- */
538 NULL
, /* SIOCSIWRATE */
539 NULL
, /* SIOCGIWRATE */
540 wlcompat_ioctl
, /* SIOCSIWRTS */
541 wlcompat_ioctl
, /* SIOCGIWRTS */
542 wlcompat_ioctl
, /* SIOCSIWFRAG */
543 wlcompat_ioctl
, /* SIOCGIWFRAG */
544 wlcompat_ioctl
, /* SIOCSIWTXPOW */
545 wlcompat_ioctl
, /* SIOCGIWTXPOW */
546 NULL
, /* SIOCSIWRETRY */
547 NULL
, /* SIOCGIWRETRY */
548 NULL
, /* SIOCSIWENCODE */
549 wlcompat_ioctl
, /* SIOCGIWENCODE */
552 #define PRIV_SET_MONITOR SIOCIWFIRSTPRIV + 0
553 #define PRIV_GET_MONITOR SIOCIWFIRSTPRIV + 1
554 #define PRIV_SET_TXPWR_LIMIT SIOCIWFIRSTPRIV + 2
555 #define PRIV_GET_TXPWR_LIMIT SIOCIWFIRSTPRIV + 3
557 static const struct iw_priv_args wlcompat_private_args
[] =
560 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
566 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
569 { PRIV_SET_TXPWR_LIMIT
,
570 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
574 { PRIV_GET_TXPWR_LIMIT
,
576 IW_PRIV_TYPE_INT
| IW_PRIV_SIZE_FIXED
| 1,
581 static int wlcompat_private_ioctl(struct net_device
*dev
,
582 struct iw_request_info
*info
,
583 union iwreq_data
*wrqu
,
586 int *value
= (int *) wrqu
->name
;
589 case PRIV_SET_MONITOR
:
591 if (wl_ioctl(dev
, WLC_SET_MONITOR
, value
, sizeof(int)) < 0)
596 case PRIV_GET_MONITOR
:
598 if (wl_ioctl(dev
, WLC_GET_MONITOR
, extra
, sizeof(int)) < 0)
603 case PRIV_SET_TXPWR_LIMIT
:
608 if (wl_get_val(dev
, "qtxpower", &val
, sizeof(int)) < 0)
612 val
|= WL_TXPWR_OVERRIDE
;
614 val
&= ~WL_TXPWR_OVERRIDE
;
616 if (wl_set_val(dev
, "qtxpower", &val
, sizeof(int)) < 0)
621 case PRIV_GET_TXPWR_LIMIT
:
623 if (wl_get_val(dev
, "qtxpower", value
, sizeof(int)) < 0)
626 *value
= ((*value
& WL_TXPWR_OVERRIDE
) == WL_TXPWR_OVERRIDE
? 1 : 0);
640 static const iw_handler wlcompat_private
[] =
642 wlcompat_private_ioctl
,
643 wlcompat_private_ioctl
,
644 wlcompat_private_ioctl
,
645 wlcompat_private_ioctl
649 static const struct iw_handler_def wlcompat_handler_def
=
651 .standard
= (iw_handler
*) wlcompat_handler
,
652 .num_standard
= sizeof(wlcompat_handler
)/sizeof(iw_handler
),
653 .private = wlcompat_private
,
654 .num_private
= sizeof(wlcompat_private
)/sizeof(iw_handler
),
655 .private_args
= wlcompat_private_args
,
656 .num_private_args
= sizeof(wlcompat_private_args
)/sizeof(struct iw_priv_args
),
661 static int (*old_ioctl
)(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
);
662 void print_buffer(int len
, unsigned char *buf
) {
665 for (x
=0;x
<len
&& x
<180 ;x
++) {
668 printk("%02X",buf
[x
]);
676 static int new_ioctl(struct net_device
*dev
, struct ifreq
*ifr
, int cmd
) {
678 printk("dev: %s ioctl: 0x%04x\n",dev
->name
,cmd
);
679 if (cmd
==SIOCDEVPRIVATE
) {
680 wl_ioctl_t
*ioc
= (wl_ioctl_t
*)ifr
->ifr_data
;
681 unsigned char *buf
= ioc
->buf
;
682 printk(" cmd: %d buf: 0x%08x len: %d\n",ioc
->cmd
,&(ioc
->buf
),ioc
->len
);
684 print_buffer(ioc
->len
, buf
);
685 ret
= old_ioctl(dev
,ifr
,cmd
);
687 print_buffer(ioc
->len
, buf
);
688 printk(" ret: %d\n", ret
);
690 ret
= old_ioctl(dev
,ifr
,cmd
);
696 static int __init
wlcompat_init()
699 char *devname
= "eth0";
701 while (!found
&& devname
[3] < '3') {
704 dev
= dev_get_by_name(devname
);
705 if ((wl_ioctl(dev
, WLC_GET_MAGIC
, &i
, sizeof(i
)) == 0) && i
== WLC_IOCTL_MAGIC
)
710 printk("No Broadcom devices found.\n");
716 old_ioctl
= dev
->do_ioctl
;
717 dev
->do_ioctl
= new_ioctl
;
719 dev
->wireless_handlers
= (struct iw_handler_def
*)&wlcompat_handler_def
;
723 static void __exit
wlcompat_exit()
725 dev
->wireless_handlers
= NULL
;
727 dev
->do_ioctl
= old_ioctl
;
733 MODULE_AUTHOR("openwrt.org");
734 MODULE_LICENSE("GPL");
736 module_init(wlcompat_init
);
737 module_exit(wlcompat_exit
);