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