2 * Copyright 2002-2004, Instant802 Networks, Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
9 #include <linux/config.h>
10 #include <linux/version.h>
11 #include <linux/module.h>
12 #include <linux/netdevice.h>
13 #include <linux/types.h>
14 #include <linux/slab.h>
15 #include <linux/skbuff.h>
17 #include <net/ieee80211.h>
18 #include "ieee80211_i.h"
19 #include "rate_control.h"
22 /* Maximum number of seconds to wait for the traffic load to get below
23 * threshold before forcing a passive scan. */
24 #define MAX_SCAN_WAIT 60
25 /* Threshold (pkts/sec TX or RX) for delaying passive scan */
26 #define SCAN_TXRX_THRESHOLD 75
28 static void get_channel_params(struct ieee80211_local
*local
, int channel
,
29 struct ieee80211_hw_modes
**mode
,
30 struct ieee80211_channel
**chan
)
34 for (m
= 0; m
< local
->hw
->num_modes
; m
++) {
35 *mode
= &local
->hw
->modes
[m
];
36 if ((*mode
)->mode
== local
->conf
.phymode
)
39 local
->scan
.mode_idx
= m
;
40 local
->scan
.chan_idx
= 0;
42 *chan
= &(*mode
)->channels
[local
->scan
.chan_idx
];
43 if ((*chan
)->chan
== channel
) {
46 local
->scan
.chan_idx
++;
47 } while (local
->scan
.chan_idx
< (*mode
)->num_channels
);
52 static void next_chan_same_mode(struct ieee80211_local
*local
,
53 struct ieee80211_hw_modes
**mode
,
54 struct ieee80211_channel
**chan
)
58 for (m
= 0; m
< local
->hw
->num_modes
; m
++) {
59 *mode
= &local
->hw
->modes
[m
];
60 if ((*mode
)->mode
== local
->conf
.phymode
)
63 local
->scan
.mode_idx
= m
;
65 /* Select next channel - scan only channels marked with W_SCAN flag */
66 prev
= local
->scan
.chan_idx
;
68 local
->scan
.chan_idx
++;
69 if (local
->scan
.chan_idx
>= (*mode
)->num_channels
)
70 local
->scan
.chan_idx
= 0;
71 *chan
= &(*mode
)->channels
[local
->scan
.chan_idx
];
72 if ((*chan
)->flag
& IEEE80211_CHAN_W_SCAN
)
74 } while (local
->scan
.chan_idx
!= prev
);
78 static void next_chan_all_modes(struct ieee80211_local
*local
,
79 struct ieee80211_hw_modes
**mode
,
80 struct ieee80211_channel
**chan
)
84 if (local
->scan
.mode_idx
>= local
->hw
->num_modes
) {
85 local
->scan
.mode_idx
= 0;
86 local
->scan
.chan_idx
= 0;
89 /* Select next channel - scan only channels marked with W_SCAN flag */
90 prev
= local
->scan
.chan_idx
;
91 prev_m
= local
->scan
.mode_idx
;
93 *mode
= &local
->hw
->modes
[local
->scan
.mode_idx
];
94 local
->scan
.chan_idx
++;
95 if (local
->scan
.chan_idx
>= (*mode
)->num_channels
) {
96 local
->scan
.chan_idx
= 0;
97 local
->scan
.mode_idx
++;
98 if (local
->scan
.mode_idx
>= local
->hw
->num_modes
)
99 local
->scan
.mode_idx
= 0;
100 *mode
= &local
->hw
->modes
[local
->scan
.mode_idx
];
102 *chan
= &(*mode
)->channels
[local
->scan
.chan_idx
];
103 if ((*chan
)->flag
& IEEE80211_CHAN_W_SCAN
)
105 } while (local
->scan
.chan_idx
!= prev
||
106 local
->scan
.mode_idx
!= prev_m
);
110 static void ieee80211_scan_start(struct net_device
*dev
,
111 struct ieee80211_scan_conf
*conf
)
113 struct ieee80211_local
*local
= dev
->priv
;
114 int old_mode_idx
= local
->scan
.mode_idx
;
115 int old_chan_idx
= local
->scan
.chan_idx
;
116 struct ieee80211_hw_modes
*mode
= NULL
;
117 struct ieee80211_channel
*chan
= NULL
;
120 if (local
->hw
->passive_scan
== 0) {
121 printk(KERN_DEBUG
"%s: Scan handler called, yet the hardware "
122 "does not support passive scanning. Disabled.\n",
127 if ((local
->scan
.tries
< MAX_SCAN_WAIT
&&
128 local
->scan
.txrx_count
> SCAN_TXRX_THRESHOLD
)) {
130 /* Count TX/RX packets during one second interval and allow
131 * scan to start only if the number of packets is below the
133 local
->scan
.txrx_count
= 0;
134 local
->scan
.timer
.expires
= jiffies
+ HZ
;
135 add_timer(&local
->scan
.timer
);
139 if (local
->scan
.skb
== NULL
) {
140 printk(KERN_DEBUG
"%s: Scan start called even though scan.skb "
141 "is not set\n", dev
->name
);
144 if (local
->scan
.our_mode_only
) {
145 if (local
->scan
.channel
> 0) {
146 get_channel_params(local
, local
->scan
.channel
, &mode
,
149 next_chan_same_mode(local
, &mode
, &chan
);
152 next_chan_all_modes(local
, &mode
, &chan
);
154 conf
->scan_channel
= chan
->chan
;
155 conf
->scan_freq
= chan
->freq
;
156 conf
->scan_channel_val
= chan
->val
;
157 conf
->scan_phymode
= mode
->mode
;
158 conf
->scan_power_level
= chan
->power_level
;
159 conf
->scan_antenna_max
= chan
->antenna_max
;
160 conf
->scan_time
= 2 * local
->hw
->channel_change_time
+
161 local
->scan
.time
; /* 10ms scan time+hardware changes */
162 conf
->skb
= local
->scan
.skb
?
163 skb_clone(local
->scan
.skb
, GFP_ATOMIC
) : NULL
;
164 conf
->tx_control
= &local
->scan
.tx_control
;
166 printk(KERN_DEBUG
"%s: Doing scan on mode: %d freq: %d chan: %d "
168 dev
->name
, conf
->scan_phymode
, conf
->scan_freq
,
169 conf
->scan_channel
, conf
->scan_time
);
171 local
->scan
.rx_packets
= 0;
172 local
->scan
.rx_beacon
= 0;
173 local
->scan
.freq
= chan
->freq
;
174 local
->scan
.in_scan
= 1;
176 ieee80211_netif_oper(dev
, NETIF_STOP
);
178 ret
= local
->hw
->passive_scan(dev
, IEEE80211_SCAN_START
, conf
);
181 long usec
= local
->hw
->channel_change_time
+
183 usec
+= 1000000L / HZ
- 1;
184 usec
/= 1000000L / HZ
;
185 local
->scan
.timer
.expires
= jiffies
+ usec
;
187 local
->scan
.in_scan
= 0;
189 dev_kfree_skb(conf
->skb
);
190 ieee80211_netif_oper(dev
, NETIF_WAKE
);
191 if (ret
== -EAGAIN
) {
192 local
->scan
.timer
.expires
= jiffies
+
193 (local
->scan
.interval
* HZ
/ 100);
194 local
->scan
.mode_idx
= old_mode_idx
;
195 local
->scan
.chan_idx
= old_chan_idx
;
197 printk(KERN_DEBUG
"%s: Got unknown error from "
198 "passive_scan %d\n", dev
->name
, ret
);
199 local
->scan
.timer
.expires
= jiffies
+
200 (local
->scan
.interval
* HZ
);
202 local
->scan
.in_scan
= 0;
205 add_timer(&local
->scan
.timer
);
209 static void ieee80211_scan_stop(struct net_device
*dev
,
210 struct ieee80211_scan_conf
*conf
)
212 struct ieee80211_local
*local
= dev
->priv
;
213 struct ieee80211_hw_modes
*mode
;
214 struct ieee80211_channel
*chan
;
217 if (local
->hw
->passive_scan
== NULL
)
220 if (local
->scan
.mode_idx
>= local
->hw
->num_modes
) {
221 local
->scan
.mode_idx
= 0;
222 local
->scan
.chan_idx
= 0;
225 mode
= &local
->hw
->modes
[local
->scan
.mode_idx
];
227 if (local
->scan
.chan_idx
>= mode
->num_channels
) {
228 local
->scan
.chan_idx
= 0;
231 chan
= &mode
->channels
[local
->scan
.chan_idx
];
233 local
->hw
->passive_scan(dev
, IEEE80211_SCAN_END
, conf
);
235 #ifdef CONFIG_IEEE80211_VERBOSE_DEBUG
236 printk(KERN_DEBUG
"%s: Did scan on mode: %d freq: %d chan: %d "
237 "GOT: %d Beacon: %d (%d)\n",
239 mode
->mode
, chan
->freq
, chan
->chan
,
240 local
->scan
.rx_packets
, local
->scan
.rx_beacon
,
242 #endif /* CONFIG_IEEE80211_VERBOSE_DEBUG */
243 local
->scan
.num_scans
++;
245 local
->scan
.in_scan
= 0;
246 ieee80211_netif_oper(dev
, NETIF_WAKE
);
248 local
->scan
.tries
= 0;
249 /* Use random interval of scan.interval .. 2 * scan.interval */
250 wait
= (local
->scan
.interval
* HZ
* ((net_random() & 127) + 128)) /
252 local
->scan
.timer
.expires
= jiffies
+ wait
;
254 add_timer(&local
->scan
.timer
);
258 static void ieee80211_scan_handler(unsigned long uldev
)
260 struct net_device
*dev
= (struct net_device
*) uldev
;
261 struct ieee80211_local
*local
= dev
->priv
;
262 struct ieee80211_scan_conf conf
;
264 if (local
->scan
.interval
== 0 && !local
->scan
.in_scan
) {
265 /* Passive scanning is disabled - keep the timer always
266 * running to make code cleaner. */
267 local
->scan
.timer
.expires
= jiffies
+ 10 * HZ
;
268 add_timer(&local
->scan
.timer
);
272 memset(&conf
, 0, sizeof(struct ieee80211_scan_conf
));
273 conf
.running_freq
= local
->conf
.freq
;
274 conf
.running_channel
= local
->conf
.channel
;
275 conf
.running_phymode
= local
->conf
.phymode
;
276 conf
.running_channel_val
= local
->conf
.channel_val
;
277 conf
.running_power_level
= local
->conf
.power_level
;
278 conf
.running_antenna_max
= local
->conf
.antenna_max
;
280 if (local
->scan
.in_scan
== 0)
281 ieee80211_scan_start(dev
, &conf
);
283 ieee80211_scan_stop(dev
, &conf
);
287 void ieee80211_init_scan(struct net_device
*dev
)
289 struct ieee80211_local
*local
= dev
->priv
;
290 struct ieee80211_hdr hdr
;
293 struct rate_control_extra extra
;
295 /* Only initialize passive scanning if the hardware supports it */
296 if (!local
->hw
->passive_scan
) {
297 local
->scan
.skb
= NULL
;
298 memset(&local
->scan
.tx_control
, 0,
299 sizeof(local
->scan
.tx_control
));
300 printk(KERN_DEBUG
"%s: Does not support passive scan, "
301 "disabled\n", dev
->name
);
305 local
->scan
.interval
= 0;
306 local
->scan
.our_mode_only
= 1;
307 local
->scan
.time
= 10000;
308 local
->scan
.timer
.function
= ieee80211_scan_handler
;
309 local
->scan
.timer
.data
= (unsigned long) dev
;
310 local
->scan
.timer
.expires
= jiffies
+ local
->scan
.interval
* HZ
;
311 add_timer(&local
->scan
.timer
);
313 /* Create a CTS from for broadcasting before
314 * the low level changes channels */
315 local
->scan
.skb
= alloc_skb(len
, GFP_KERNEL
);
316 if (local
->scan
.skb
== NULL
) {
317 printk(KERN_WARNING
"%s: Failed to allocate CTS packet for "
318 "passive scan\n", dev
->name
);
322 fc
= (WLAN_FC_TYPE_CTRL
<< 2) | (WLAN_FC_STYPE_CTS
<< 4);
323 hdr
.frame_control
= cpu_to_le16(fc
);
325 cpu_to_le16(2 * local
->hw
->channel_change_time
+
327 memcpy(hdr
.addr1
, dev
->dev_addr
, ETH_ALEN
); /* DA */
330 memcpy(skb_put(local
->scan
.skb
, len
), &hdr
, len
);
332 memset(&local
->scan
.tx_control
, 0, sizeof(local
->scan
.tx_control
));
333 local
->scan
.tx_control
.key_idx
= HW_KEY_IDX_INVALID
;
334 local
->scan
.tx_control
.do_not_encrypt
= 1;
335 memset(&extra
, 0, sizeof(extra
));
336 extra
.endidx
= local
->num_curr_rates
;
337 local
->scan
.tx_control
.tx_rate
=
338 rate_control_get_rate(dev
, local
->scan
.skb
, &extra
)->val
;
339 local
->scan
.tx_control
.no_ack
= 1;
343 void ieee80211_stop_scan(struct net_device
*dev
)
345 struct ieee80211_local
*local
= dev
->priv
;
347 if (local
->hw
->passive_scan
!= 0) {
348 del_timer_sync(&local
->scan
.timer
);
349 dev_kfree_skb(local
->scan
.skb
);
350 local
->scan
.skb
= NULL
;
This page took 0.061018 seconds and 5 git commands to generate.