add d80211 from a recent wireless-dev checkout
[openwrt/svn-archive/archive.git] / package / d80211 / src / ieee80211_scan.c
1 /*
2 * Copyright 2002-2004, Instant802 Networks, Inc.
3 *
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.
7 */
8
9 #include <linux/module.h>
10 #include <linux/netdevice.h>
11 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <linux/skbuff.h>
14
15 #include <net/d80211.h>
16 #include "ieee80211_i.h"
17 #include "ieee80211_rate.h"
18
19
20 /* Maximum number of seconds to wait for the traffic load to get below
21 * threshold before forcing a passive scan. */
22 #define MAX_SCAN_WAIT 60
23 /* Threshold (pkts/sec TX or RX) for delaying passive scan */
24 #define SCAN_TXRX_THRESHOLD 75
25
26 static void get_channel_params(struct ieee80211_local *local, int channel,
27 struct ieee80211_hw_modes **mode,
28 struct ieee80211_channel **chan)
29 {
30 int m;
31
32 for (m = 0; m < local->hw.num_modes; m++) {
33 *mode = &local->hw.modes[m];
34 if ((*mode)->mode == local->hw.conf.phymode)
35 break;
36 }
37 local->scan.mode_idx = m;
38 local->scan.chan_idx = 0;
39 do {
40 *chan = &(*mode)->channels[local->scan.chan_idx];
41 if ((*chan)->chan == channel) {
42 return;
43 }
44 local->scan.chan_idx++;
45 } while (local->scan.chan_idx < (*mode)->num_channels);
46 *chan = NULL;
47 }
48
49
50 static void next_chan_same_mode(struct ieee80211_local *local,
51 struct ieee80211_hw_modes **mode,
52 struct ieee80211_channel **chan)
53 {
54 int m, prev;
55
56 for (m = 0; m < local->hw.num_modes; m++) {
57 *mode = &local->hw.modes[m];
58 if ((*mode)->mode == local->hw.conf.phymode)
59 break;
60 }
61 local->scan.mode_idx = m;
62
63 /* Select next channel - scan only channels marked with W_SCAN flag */
64 prev = local->scan.chan_idx;
65 do {
66 local->scan.chan_idx++;
67 if (local->scan.chan_idx >= (*mode)->num_channels)
68 local->scan.chan_idx = 0;
69 *chan = &(*mode)->channels[local->scan.chan_idx];
70 if ((*chan)->flag & IEEE80211_CHAN_W_SCAN)
71 break;
72 } while (local->scan.chan_idx != prev);
73 }
74
75
76 static void next_chan_all_modes(struct ieee80211_local *local,
77 struct ieee80211_hw_modes **mode,
78 struct ieee80211_channel **chan)
79 {
80 int prev, prev_m;
81
82 if (local->scan.mode_idx >= local->hw.num_modes) {
83 local->scan.mode_idx = 0;
84 local->scan.chan_idx = 0;
85 }
86
87 /* Select next channel - scan only channels marked with W_SCAN flag */
88 prev = local->scan.chan_idx;
89 prev_m = local->scan.mode_idx;
90 do {
91 *mode = &local->hw.modes[local->scan.mode_idx];
92 local->scan.chan_idx++;
93 if (local->scan.chan_idx >= (*mode)->num_channels) {
94 local->scan.chan_idx = 0;
95 local->scan.mode_idx++;
96 if (local->scan.mode_idx >= local->hw.num_modes)
97 local->scan.mode_idx = 0;
98 *mode = &local->hw.modes[local->scan.mode_idx];
99 }
100 *chan = &(*mode)->channels[local->scan.chan_idx];
101 if ((*chan)->flag & IEEE80211_CHAN_W_SCAN)
102 break;
103 } while (local->scan.chan_idx != prev ||
104 local->scan.mode_idx != prev_m);
105 }
106
107
108 static void ieee80211_scan_start(struct ieee80211_local *local,
109 struct ieee80211_scan_conf *conf)
110 {
111 int old_mode_idx = local->scan.mode_idx;
112 int old_chan_idx = local->scan.chan_idx;
113 struct ieee80211_hw_modes *mode = NULL;
114 struct ieee80211_channel *chan = NULL;
115 int ret;
116
117 if (!local->ops->passive_scan) {
118 printk(KERN_DEBUG "%s: Scan handler called, yet the hardware "
119 "does not support passive scanning. Disabled.\n",
120 local->mdev->name);
121 return;
122 }
123
124 if ((local->scan.tries < MAX_SCAN_WAIT &&
125 local->scan.txrx_count > SCAN_TXRX_THRESHOLD)) {
126 local->scan.tries++;
127 /* Count TX/RX packets during one second interval and allow
128 * scan to start only if the number of packets is below the
129 * threshold. */
130 local->scan.txrx_count = 0;
131 local->scan.timer.expires = jiffies + HZ;
132 add_timer(&local->scan.timer);
133 return;
134 }
135
136 if (!local->scan.skb) {
137 printk(KERN_DEBUG "%s: Scan start called even though scan.skb "
138 "is not set\n", local->mdev->name);
139 }
140
141 if (local->scan.our_mode_only) {
142 if (local->scan.channel > 0) {
143 get_channel_params(local, local->scan.channel, &mode,
144 &chan);
145 } else
146 next_chan_same_mode(local, &mode, &chan);
147 }
148 else
149 next_chan_all_modes(local, &mode, &chan);
150
151 conf->scan_channel = chan->chan;
152 conf->scan_freq = chan->freq;
153 conf->scan_channel_val = chan->val;
154 conf->scan_phymode = mode->mode;
155 conf->scan_power_level = chan->power_level;
156 conf->scan_antenna_max = chan->antenna_max;
157 conf->scan_time = 2 * local->hw.channel_change_time +
158 local->scan.time; /* 10ms scan time+hardware changes */
159 conf->skb = local->scan.skb ?
160 skb_clone(local->scan.skb, GFP_ATOMIC) : NULL;
161 conf->tx_control = &local->scan.tx_control;
162 #if 0
163 printk(KERN_DEBUG "%s: Doing scan on mode: %d freq: %d chan: %d "
164 "for %d ms\n",
165 local->mdev->name, conf->scan_phymode, conf->scan_freq,
166 conf->scan_channel, conf->scan_time);
167 #endif
168 local->scan.rx_packets = 0;
169 local->scan.rx_beacon = 0;
170 local->scan.freq = chan->freq;
171 local->scan.in_scan = 1;
172
173 ieee80211_netif_oper(local_to_hw(local), NETIF_STOP);
174
175 ret = local->ops->passive_scan(local_to_hw(local),
176 IEEE80211_SCAN_START, conf);
177
178 if (ret == 0) {
179 long usec = local->hw.channel_change_time +
180 local->scan.time;
181 usec += 1000000L / HZ - 1;
182 usec /= 1000000L / HZ;
183 local->scan.timer.expires = jiffies + usec;
184 } else {
185 local->scan.in_scan = 0;
186 if (conf->skb)
187 dev_kfree_skb(conf->skb);
188 ieee80211_netif_oper(local_to_hw(local), NETIF_WAKE);
189 if (ret == -EAGAIN) {
190 local->scan.timer.expires = jiffies +
191 (local->scan.interval * HZ / 100);
192 local->scan.mode_idx = old_mode_idx;
193 local->scan.chan_idx = old_chan_idx;
194 } else {
195 printk(KERN_DEBUG "%s: Got unknown error from "
196 "passive_scan %d\n", local->mdev->name, ret);
197 local->scan.timer.expires = jiffies +
198 (local->scan.interval * HZ);
199 }
200 local->scan.in_scan = 0;
201 }
202
203 add_timer(&local->scan.timer);
204 }
205
206
207 static void ieee80211_scan_stop(struct ieee80211_local *local,
208 struct ieee80211_scan_conf *conf)
209 {
210 struct ieee80211_hw_modes *mode;
211 struct ieee80211_channel *chan;
212 int wait;
213
214 if (!local->ops->passive_scan)
215 return;
216
217 if (local->scan.mode_idx >= local->hw.num_modes) {
218 local->scan.mode_idx = 0;
219 local->scan.chan_idx = 0;
220 }
221
222 mode = &local->hw.modes[local->scan.mode_idx];
223
224 if (local->scan.chan_idx >= mode->num_channels) {
225 local->scan.chan_idx = 0;
226 }
227
228 chan = &mode->channels[local->scan.chan_idx];
229
230 local->ops->passive_scan(local_to_hw(local), IEEE80211_SCAN_END,
231 conf);
232
233 #ifdef CONFIG_D80211_VERBOSE_DEBUG
234 printk(KERN_DEBUG "%s: Did scan on mode: %d freq: %d chan: %d "
235 "GOT: %d Beacon: %d (%d)\n",
236 local->mdev->name,
237 mode->mode, chan->freq, chan->chan,
238 local->scan.rx_packets, local->scan.rx_beacon,
239 local->scan.tries);
240 #endif /* CONFIG_D80211_VERBOSE_DEBUG */
241 local->scan.num_scans++;
242
243 local->scan.in_scan = 0;
244 ieee80211_netif_oper(local_to_hw(local), NETIF_WAKE);
245
246 local->scan.tries = 0;
247 /* Use random interval of scan.interval .. 2 * scan.interval */
248 wait = (local->scan.interval * HZ * ((net_random() & 127) + 128)) /
249 128;
250 local->scan.timer.expires = jiffies + wait;
251
252 add_timer(&local->scan.timer);
253 }
254
255
256 static void ieee80211_scan_handler(unsigned long ullocal)
257 {
258 struct ieee80211_local *local = (struct ieee80211_local *) ullocal;
259 struct ieee80211_scan_conf conf;
260
261 if (local->scan.interval == 0 && !local->scan.in_scan) {
262 /* Passive scanning is disabled - keep the timer always
263 * running to make code cleaner. */
264 local->scan.timer.expires = jiffies + 10 * HZ;
265 add_timer(&local->scan.timer);
266 return;
267 }
268
269 memset(&conf, 0, sizeof(struct ieee80211_scan_conf));
270 conf.running_freq = local->hw.conf.freq;
271 conf.running_channel = local->hw.conf.channel;
272 conf.running_phymode = local->hw.conf.phymode;
273 conf.running_channel_val = local->hw.conf.channel_val;
274 conf.running_power_level = local->hw.conf.power_level;
275 conf.running_antenna_max = local->hw.conf.antenna_max;
276
277 if (local->scan.in_scan == 0)
278 ieee80211_scan_start(local, &conf);
279 else
280 ieee80211_scan_stop(local, &conf);
281 }
282
283
284 void ieee80211_init_scan(struct ieee80211_local *local)
285 {
286 struct ieee80211_hdr hdr;
287 u16 fc;
288 int len = 10;
289 struct rate_control_extra extra;
290
291 /* Only initialize passive scanning if the hardware supports it */
292 if (!local->ops->passive_scan) {
293 local->scan.skb = NULL;
294 memset(&local->scan.tx_control, 0,
295 sizeof(local->scan.tx_control));
296 printk(KERN_DEBUG "%s: Does not support passive scan, "
297 "disabled\n", local->mdev->name);
298 return;
299 }
300
301 local->scan.interval = 0;
302 local->scan.our_mode_only = 1;
303 local->scan.time = 10000;
304 local->scan.timer.function = ieee80211_scan_handler;
305 local->scan.timer.data = (unsigned long) local;
306 local->scan.timer.expires = jiffies + local->scan.interval * HZ;
307 add_timer(&local->scan.timer);
308
309 /* Create a CTS from for broadcasting before
310 * the low level changes channels */
311 local->scan.skb = alloc_skb(len, GFP_KERNEL);
312 if (!local->scan.skb) {
313 printk(KERN_WARNING "%s: Failed to allocate CTS packet for "
314 "passive scan\n", local->mdev->name);
315 return;
316 }
317
318 fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_CTS;
319 hdr.frame_control = cpu_to_le16(fc);
320 hdr.duration_id =
321 cpu_to_le16(2 * local->hw.channel_change_time +
322 local->scan.time);
323 memcpy(hdr.addr1, local->mdev->dev_addr, ETH_ALEN); /* DA */
324 hdr.seq_ctrl = 0;
325
326 memcpy(skb_put(local->scan.skb, len), &hdr, len);
327
328 memset(&local->scan.tx_control, 0, sizeof(local->scan.tx_control));
329 local->scan.tx_control.key_idx = HW_KEY_IDX_INVALID;
330 local->scan.tx_control.flags |= IEEE80211_TXCTL_DO_NOT_ENCRYPT;
331 memset(&extra, 0, sizeof(extra));
332 extra.endidx = local->num_curr_rates;
333 local->scan.tx_control.tx_rate =
334 rate_control_get_rate(local, local->mdev,
335 local->scan.skb, &extra)->val;
336 local->scan.tx_control.flags |= IEEE80211_TXCTL_NO_ACK;
337 }
338
339
340 void ieee80211_stop_scan(struct ieee80211_local *local)
341 {
342 if (local->ops->passive_scan) {
343 del_timer_sync(&local->scan.timer);
344 dev_kfree_skb(local->scan.skb);
345 local->scan.skb = NULL;
346 }
347 }