1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
---|
5 | * All rights reserved. |
---|
6 | * |
---|
7 | * Redistribution and use in source and binary forms, with or without |
---|
8 | * modification, are permitted provided that the following conditions |
---|
9 | * are met: |
---|
10 | * 1. Redistributions of source code must retain the above copyright |
---|
11 | * notice, this list of conditions and the following disclaimer. |
---|
12 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
13 | * notice, this list of conditions and the following disclaimer in the |
---|
14 | * documentation and/or other materials provided with the distribution. |
---|
15 | * |
---|
16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
---|
17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
---|
18 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
---|
19 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
---|
20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
---|
21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
---|
22 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
---|
23 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
---|
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
---|
25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
26 | */ |
---|
27 | |
---|
28 | #include <sys/cdefs.h> |
---|
29 | __FBSDID("$FreeBSD$"); |
---|
30 | |
---|
31 | /* |
---|
32 | * IEEE 802.11 scanning support. |
---|
33 | */ |
---|
34 | #include <rtems/bsd/local/opt_wlan.h> |
---|
35 | |
---|
36 | #include <rtems/bsd/sys/param.h> |
---|
37 | #include <sys/systm.h> |
---|
38 | #include <sys/proc.h> |
---|
39 | #include <sys/kernel.h> |
---|
40 | #include <sys/malloc.h> |
---|
41 | #include <sys/condvar.h> |
---|
42 | |
---|
43 | #include <sys/socket.h> |
---|
44 | |
---|
45 | #include <net/if.h> |
---|
46 | #include <net/if_var.h> |
---|
47 | #include <net/if_media.h> |
---|
48 | #include <net/ethernet.h> |
---|
49 | |
---|
50 | #include <net80211/ieee80211_var.h> |
---|
51 | |
---|
52 | #include <net80211/ieee80211_scan_sw.h> |
---|
53 | |
---|
54 | #include <net/bpf.h> |
---|
55 | |
---|
56 | struct scan_state { |
---|
57 | struct ieee80211_scan_state base; /* public state */ |
---|
58 | |
---|
59 | u_int ss_iflags; /* flags used internally */ |
---|
60 | #define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ |
---|
61 | #define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ |
---|
62 | #define ISCAN_INTERRUPT 0x0004 /* interrupt current scan */ |
---|
63 | #define ISCAN_CANCEL 0x0008 /* cancel current scan */ |
---|
64 | #define ISCAN_PAUSE (ISCAN_INTERRUPT | ISCAN_CANCEL) |
---|
65 | #define ISCAN_ABORT 0x0010 /* end the scan immediately */ |
---|
66 | #define ISCAN_RUNNING 0x0020 /* scan was started */ |
---|
67 | |
---|
68 | unsigned long ss_chanmindwell; /* min dwell on curchan */ |
---|
69 | unsigned long ss_scanend; /* time scan must stop */ |
---|
70 | u_int ss_duration; /* duration for next scan */ |
---|
71 | struct task ss_scan_start; /* scan start */ |
---|
72 | struct timeout_task ss_scan_curchan; /* scan execution */ |
---|
73 | }; |
---|
74 | #define SCAN_PRIVATE(ss) ((struct scan_state *) ss) |
---|
75 | |
---|
76 | /* |
---|
77 | * Amount of time to go off-channel during a background |
---|
78 | * scan. This value should be large enough to catch most |
---|
79 | * ap's but short enough that we can return on-channel |
---|
80 | * before our listen interval expires. |
---|
81 | * |
---|
82 | * XXX tunable |
---|
83 | * XXX check against configured listen interval |
---|
84 | */ |
---|
85 | #define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) |
---|
86 | |
---|
87 | static void scan_curchan(struct ieee80211_scan_state *, unsigned long); |
---|
88 | static void scan_mindwell(struct ieee80211_scan_state *); |
---|
89 | static void scan_signal(struct ieee80211_scan_state *, int); |
---|
90 | static void scan_signal_locked(struct ieee80211_scan_state *, int); |
---|
91 | static void scan_start(void *, int); |
---|
92 | static void scan_curchan_task(void *, int); |
---|
93 | static void scan_end(struct ieee80211_scan_state *, int); |
---|
94 | static void scan_done(struct ieee80211_scan_state *, int); |
---|
95 | |
---|
96 | MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); |
---|
97 | |
---|
98 | static void |
---|
99 | ieee80211_swscan_detach(struct ieee80211com *ic) |
---|
100 | { |
---|
101 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
102 | |
---|
103 | if (ss != NULL) { |
---|
104 | scan_signal(ss, ISCAN_ABORT); |
---|
105 | ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); |
---|
106 | taskqueue_drain_timeout(ic->ic_tq, |
---|
107 | &SCAN_PRIVATE(ss)->ss_scan_curchan); |
---|
108 | KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, |
---|
109 | ("scan still running")); |
---|
110 | |
---|
111 | /* |
---|
112 | * For now, do the ss_ops detach here rather |
---|
113 | * than ieee80211_scan_detach(). |
---|
114 | * |
---|
115 | * I'll figure out how to cleanly split things up |
---|
116 | * at a later date. |
---|
117 | */ |
---|
118 | if (ss->ss_ops != NULL) { |
---|
119 | ss->ss_ops->scan_detach(ss); |
---|
120 | ss->ss_ops = NULL; |
---|
121 | } |
---|
122 | ic->ic_scan = NULL; |
---|
123 | IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN); |
---|
124 | } |
---|
125 | } |
---|
126 | |
---|
127 | static void |
---|
128 | ieee80211_swscan_vattach(struct ieee80211vap *vap) |
---|
129 | { |
---|
130 | /* nothing to do for now */ |
---|
131 | /* |
---|
132 | * TODO: all of the vap scan calls should be methods! |
---|
133 | */ |
---|
134 | |
---|
135 | } |
---|
136 | |
---|
137 | static void |
---|
138 | ieee80211_swscan_vdetach(struct ieee80211vap *vap) |
---|
139 | { |
---|
140 | struct ieee80211com *ic = vap->iv_ic; |
---|
141 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
142 | |
---|
143 | IEEE80211_LOCK_ASSERT(ic); |
---|
144 | |
---|
145 | if (ss != NULL && ss->ss_vap == vap && |
---|
146 | (ic->ic_flags & IEEE80211_F_SCAN)) |
---|
147 | scan_signal_locked(ss, ISCAN_ABORT); |
---|
148 | } |
---|
149 | |
---|
150 | static void |
---|
151 | ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration) |
---|
152 | { |
---|
153 | struct ieee80211com *ic = vap->iv_ic; |
---|
154 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
155 | |
---|
156 | IEEE80211_LOCK_ASSERT(ic); |
---|
157 | |
---|
158 | /* NB: flush frames rx'd before 1st channel change */ |
---|
159 | SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; |
---|
160 | SCAN_PRIVATE(ss)->ss_duration = duration; |
---|
161 | } |
---|
162 | |
---|
163 | /* |
---|
164 | * Start a scan unless one is already going. |
---|
165 | */ |
---|
166 | static int |
---|
167 | ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan, |
---|
168 | struct ieee80211vap *vap, int flags, u_int duration, |
---|
169 | u_int mindwell, u_int maxdwell, |
---|
170 | u_int nssid, const struct ieee80211_scan_ssid ssids[]) |
---|
171 | { |
---|
172 | struct ieee80211com *ic = vap->iv_ic; |
---|
173 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
174 | |
---|
175 | IEEE80211_LOCK_ASSERT(ic); |
---|
176 | |
---|
177 | if (ic->ic_flags & IEEE80211_F_CSAPENDING) { |
---|
178 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
179 | "%s: scan inhibited by pending channel change\n", __func__); |
---|
180 | } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { |
---|
181 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
182 | "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" |
---|
183 | , __func__ |
---|
184 | , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" |
---|
185 | , duration, mindwell, maxdwell |
---|
186 | , ieee80211_phymode_name[vap->iv_des_mode] |
---|
187 | , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" |
---|
188 | , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" |
---|
189 | , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" |
---|
190 | , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" |
---|
191 | , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" |
---|
192 | , flags & IEEE80211_SCAN_ONCE ? ", once" : "" |
---|
193 | ); |
---|
194 | |
---|
195 | ieee80211_scan_update_locked(vap, scan); |
---|
196 | if (ss->ss_ops != NULL) { |
---|
197 | if ((flags & IEEE80211_SCAN_NOSSID) == 0) |
---|
198 | ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); |
---|
199 | |
---|
200 | /* NB: top 4 bits for internal use */ |
---|
201 | ss->ss_flags = flags & 0xfff; |
---|
202 | if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) |
---|
203 | vap->iv_stats.is_scan_active++; |
---|
204 | else |
---|
205 | vap->iv_stats.is_scan_passive++; |
---|
206 | if (flags & IEEE80211_SCAN_FLUSH) |
---|
207 | ss->ss_ops->scan_flush(ss); |
---|
208 | if (flags & IEEE80211_SCAN_BGSCAN) |
---|
209 | ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; |
---|
210 | |
---|
211 | /* Set duration for this particular scan */ |
---|
212 | ieee80211_swscan_set_scan_duration(vap, duration); |
---|
213 | |
---|
214 | ss->ss_next = 0; |
---|
215 | ss->ss_mindwell = mindwell; |
---|
216 | ss->ss_maxdwell = maxdwell; |
---|
217 | /* NB: scan_start must be before the scan runtask */ |
---|
218 | ss->ss_ops->scan_start(ss, vap); |
---|
219 | #ifdef IEEE80211_DEBUG |
---|
220 | if (ieee80211_msg_scan(vap)) |
---|
221 | ieee80211_scan_dump(ss); |
---|
222 | #endif /* IEEE80211_DEBUG */ |
---|
223 | ic->ic_flags |= IEEE80211_F_SCAN; |
---|
224 | |
---|
225 | /* Start scan task */ |
---|
226 | ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); |
---|
227 | } |
---|
228 | return 1; |
---|
229 | } else { |
---|
230 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
231 | "%s: %s scan already in progress\n", __func__, |
---|
232 | ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); |
---|
233 | } |
---|
234 | return 0; |
---|
235 | } |
---|
236 | |
---|
237 | |
---|
238 | /* |
---|
239 | * Start a scan unless one is already going. |
---|
240 | * |
---|
241 | * Called without the comlock held; grab the comlock as appropriate. |
---|
242 | */ |
---|
243 | static int |
---|
244 | ieee80211_swscan_start_scan(const struct ieee80211_scanner *scan, |
---|
245 | struct ieee80211vap *vap, int flags, |
---|
246 | u_int duration, u_int mindwell, u_int maxdwell, |
---|
247 | u_int nssid, const struct ieee80211_scan_ssid ssids[]) |
---|
248 | { |
---|
249 | struct ieee80211com *ic = vap->iv_ic; |
---|
250 | int result; |
---|
251 | |
---|
252 | IEEE80211_UNLOCK_ASSERT(ic); |
---|
253 | |
---|
254 | IEEE80211_LOCK(ic); |
---|
255 | result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, |
---|
256 | mindwell, maxdwell, nssid, ssids); |
---|
257 | IEEE80211_UNLOCK(ic); |
---|
258 | |
---|
259 | return result; |
---|
260 | } |
---|
261 | |
---|
262 | /* |
---|
263 | * Check the scan cache for an ap/channel to use; if that |
---|
264 | * fails then kick off a new scan. |
---|
265 | * |
---|
266 | * Called with the comlock held. |
---|
267 | * |
---|
268 | * XXX TODO: split out! |
---|
269 | */ |
---|
270 | static int |
---|
271 | ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan, |
---|
272 | struct ieee80211vap *vap, int flags, |
---|
273 | u_int duration, u_int mindwell, u_int maxdwell, |
---|
274 | u_int nssid, const struct ieee80211_scan_ssid ssids[]) |
---|
275 | { |
---|
276 | struct ieee80211com *ic = vap->iv_ic; |
---|
277 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
278 | int result; |
---|
279 | |
---|
280 | IEEE80211_LOCK_ASSERT(ic); |
---|
281 | |
---|
282 | if (ss->ss_ops != NULL) { |
---|
283 | /* XXX verify ss_ops matches vap->iv_opmode */ |
---|
284 | if ((flags & IEEE80211_SCAN_NOSSID) == 0) { |
---|
285 | /* |
---|
286 | * Update the ssid list and mark flags so if |
---|
287 | * we call start_scan it doesn't duplicate work. |
---|
288 | */ |
---|
289 | ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); |
---|
290 | flags |= IEEE80211_SCAN_NOSSID; |
---|
291 | } |
---|
292 | if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && |
---|
293 | (flags & IEEE80211_SCAN_FLUSH) == 0 && |
---|
294 | ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { |
---|
295 | /* |
---|
296 | * We're not currently scanning and the cache is |
---|
297 | * deemed hot enough to consult. Lock out others |
---|
298 | * by marking IEEE80211_F_SCAN while we decide if |
---|
299 | * something is already in the scan cache we can |
---|
300 | * use. Also discard any frames that might come |
---|
301 | * in while temporarily marked as scanning. |
---|
302 | */ |
---|
303 | SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; |
---|
304 | ic->ic_flags |= IEEE80211_F_SCAN; |
---|
305 | |
---|
306 | /* NB: need to use supplied flags in check */ |
---|
307 | ss->ss_flags = flags & 0xff; |
---|
308 | result = ss->ss_ops->scan_end(ss, vap); |
---|
309 | |
---|
310 | ic->ic_flags &= ~IEEE80211_F_SCAN; |
---|
311 | SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; |
---|
312 | if (result) { |
---|
313 | ieee80211_notify_scan_done(vap); |
---|
314 | return 1; |
---|
315 | } |
---|
316 | } |
---|
317 | } |
---|
318 | result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, |
---|
319 | mindwell, maxdwell, nssid, ssids); |
---|
320 | |
---|
321 | return result; |
---|
322 | } |
---|
323 | |
---|
324 | /* |
---|
325 | * Restart a previous scan. If the previous scan completed |
---|
326 | * then we start again using the existing channel list. |
---|
327 | */ |
---|
328 | static int |
---|
329 | ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan, |
---|
330 | struct ieee80211vap *vap, int flags) |
---|
331 | { |
---|
332 | struct ieee80211com *ic = vap->iv_ic; |
---|
333 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
334 | |
---|
335 | /* XXX assert unlocked? */ |
---|
336 | // IEEE80211_UNLOCK_ASSERT(ic); |
---|
337 | |
---|
338 | IEEE80211_LOCK(ic); |
---|
339 | if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { |
---|
340 | u_int duration; |
---|
341 | /* |
---|
342 | * Go off-channel for a fixed interval that is large |
---|
343 | * enough to catch most ap's but short enough that |
---|
344 | * we can return on-channel before our listen interval |
---|
345 | * expires. |
---|
346 | */ |
---|
347 | duration = IEEE80211_SCAN_OFFCHANNEL; |
---|
348 | |
---|
349 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
350 | "%s: %s scan, ticks %u duration %u\n", __func__, |
---|
351 | ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", |
---|
352 | ticks, duration); |
---|
353 | |
---|
354 | ieee80211_scan_update_locked(vap, scan); |
---|
355 | if (ss->ss_ops != NULL) { |
---|
356 | ss->ss_vap = vap; |
---|
357 | /* |
---|
358 | * A background scan does not select a new sta; it |
---|
359 | * just refreshes the scan cache. Also, indicate |
---|
360 | * the scan logic should follow the beacon schedule: |
---|
361 | * we go off-channel and scan for a while, then |
---|
362 | * return to the bss channel to receive a beacon, |
---|
363 | * then go off-channel again. All during this time |
---|
364 | * we notify the ap we're in power save mode. When |
---|
365 | * the scan is complete we leave power save mode. |
---|
366 | * If any beacon indicates there are frames pending |
---|
367 | * for us then we drop out of power save mode |
---|
368 | * (and background scan) automatically by way of the |
---|
369 | * usual sta power save logic. |
---|
370 | */ |
---|
371 | ss->ss_flags |= IEEE80211_SCAN_NOPICK |
---|
372 | | IEEE80211_SCAN_BGSCAN |
---|
373 | | flags |
---|
374 | ; |
---|
375 | /* if previous scan completed, restart */ |
---|
376 | if (ss->ss_next >= ss->ss_last) { |
---|
377 | if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) |
---|
378 | vap->iv_stats.is_scan_active++; |
---|
379 | else |
---|
380 | vap->iv_stats.is_scan_passive++; |
---|
381 | /* |
---|
382 | * NB: beware of the scan cache being flushed; |
---|
383 | * if the channel list is empty use the |
---|
384 | * scan_start method to populate it. |
---|
385 | */ |
---|
386 | ss->ss_next = 0; |
---|
387 | if (ss->ss_last != 0) |
---|
388 | ss->ss_ops->scan_restart(ss, vap); |
---|
389 | else { |
---|
390 | ss->ss_ops->scan_start(ss, vap); |
---|
391 | #ifdef IEEE80211_DEBUG |
---|
392 | if (ieee80211_msg_scan(vap)) |
---|
393 | ieee80211_scan_dump(ss); |
---|
394 | #endif /* IEEE80211_DEBUG */ |
---|
395 | } |
---|
396 | } |
---|
397 | ieee80211_swscan_set_scan_duration(vap, duration); |
---|
398 | ss->ss_maxdwell = duration; |
---|
399 | ic->ic_flags |= IEEE80211_F_SCAN; |
---|
400 | ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; |
---|
401 | ieee80211_runtask(ic, |
---|
402 | &SCAN_PRIVATE(ss)->ss_scan_start); |
---|
403 | } else { |
---|
404 | /* XXX msg+stat */ |
---|
405 | } |
---|
406 | } else { |
---|
407 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
408 | "%s: %s scan already in progress\n", __func__, |
---|
409 | ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); |
---|
410 | } |
---|
411 | IEEE80211_UNLOCK(ic); |
---|
412 | |
---|
413 | /* NB: racey, does it matter? */ |
---|
414 | return (ic->ic_flags & IEEE80211_F_SCAN); |
---|
415 | } |
---|
416 | |
---|
417 | /* |
---|
418 | * Taskqueue work to cancel a scan. |
---|
419 | * |
---|
420 | * Note: for offload scan devices, we may want to call into the |
---|
421 | * driver to try and cancel scanning, however it may not be cancelable. |
---|
422 | */ |
---|
423 | static void |
---|
424 | cancel_scan(struct ieee80211vap *vap, int any, const char *func) |
---|
425 | { |
---|
426 | struct ieee80211com *ic = vap->iv_ic; |
---|
427 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
428 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
429 | int signal; |
---|
430 | |
---|
431 | IEEE80211_LOCK(ic); |
---|
432 | signal = any ? ISCAN_PAUSE : ISCAN_CANCEL; |
---|
433 | if ((ic->ic_flags & IEEE80211_F_SCAN) && |
---|
434 | (any || ss->ss_vap == vap) && |
---|
435 | (ss_priv->ss_iflags & signal) == 0) { |
---|
436 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
437 | "%s: %s %s scan\n", func, |
---|
438 | any ? "pause" : "cancel", |
---|
439 | ss->ss_flags & IEEE80211_SCAN_ACTIVE ? |
---|
440 | "active" : "passive"); |
---|
441 | |
---|
442 | /* clear bg scan NOPICK */ |
---|
443 | ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; |
---|
444 | /* mark request and wake up the scan task */ |
---|
445 | scan_signal_locked(ss, signal); |
---|
446 | } else { |
---|
447 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
448 | "%s: called; F_SCAN=%d, vap=%s, signal=%d\n", |
---|
449 | func, |
---|
450 | !! (ic->ic_flags & IEEE80211_F_SCAN), |
---|
451 | (ss->ss_vap == vap ? "match" : "nomatch"), |
---|
452 | !! (ss_priv->ss_iflags & signal)); |
---|
453 | } |
---|
454 | IEEE80211_UNLOCK(ic); |
---|
455 | } |
---|
456 | |
---|
457 | /* |
---|
458 | * Cancel any scan currently going on for the specified vap. |
---|
459 | */ |
---|
460 | static void |
---|
461 | ieee80211_swscan_cancel_scan(struct ieee80211vap *vap) |
---|
462 | { |
---|
463 | cancel_scan(vap, 0, __func__); |
---|
464 | } |
---|
465 | |
---|
466 | /* |
---|
467 | * Cancel any scan currently going on. |
---|
468 | */ |
---|
469 | static void |
---|
470 | ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap) |
---|
471 | { |
---|
472 | |
---|
473 | /* XXX for now - just don't do this per packet. */ |
---|
474 | if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) |
---|
475 | return; |
---|
476 | |
---|
477 | cancel_scan(vap, 1, __func__); |
---|
478 | } |
---|
479 | |
---|
480 | /* |
---|
481 | * Manually switch to the next channel in the channel list. |
---|
482 | * Provided for drivers that manage scanning themselves |
---|
483 | * (e.g. for firmware-based devices). |
---|
484 | */ |
---|
485 | static void |
---|
486 | ieee80211_swscan_scan_next(struct ieee80211vap *vap) |
---|
487 | { |
---|
488 | struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; |
---|
489 | |
---|
490 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); |
---|
491 | |
---|
492 | /* wake up the scan task */ |
---|
493 | scan_signal(ss, 0); |
---|
494 | } |
---|
495 | |
---|
496 | /* |
---|
497 | * Manually stop a scan that is currently running. |
---|
498 | * Provided for drivers that are not able to scan single channels |
---|
499 | * (e.g. for firmware-based devices). |
---|
500 | */ |
---|
501 | static void |
---|
502 | ieee80211_swscan_scan_done(struct ieee80211vap *vap) |
---|
503 | { |
---|
504 | struct ieee80211com *ic = vap->iv_ic; |
---|
505 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
506 | |
---|
507 | IEEE80211_LOCK_ASSERT(ic); |
---|
508 | |
---|
509 | scan_signal_locked(ss, 0); |
---|
510 | } |
---|
511 | |
---|
512 | /* |
---|
513 | * Probe the current channel, if allowed, while scanning. |
---|
514 | * If the channel is not marked passive-only then send |
---|
515 | * a probe request immediately. Otherwise mark state and |
---|
516 | * listen for beacons on the channel; if we receive something |
---|
517 | * then we'll transmit a probe request. |
---|
518 | */ |
---|
519 | static void |
---|
520 | ieee80211_swscan_probe_curchan(struct ieee80211vap *vap, int force) |
---|
521 | { |
---|
522 | struct ieee80211com *ic = vap->iv_ic; |
---|
523 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
524 | struct ifnet *ifp = vap->iv_ifp; |
---|
525 | int i; |
---|
526 | |
---|
527 | /* |
---|
528 | * Full-offload scan devices don't require this. |
---|
529 | */ |
---|
530 | if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) |
---|
531 | return; |
---|
532 | |
---|
533 | /* |
---|
534 | * Send directed probe requests followed by any |
---|
535 | * broadcast probe request. |
---|
536 | * XXX remove dependence on ic/vap->iv_bss |
---|
537 | */ |
---|
538 | for (i = 0; i < ss->ss_nssid; i++) |
---|
539 | ieee80211_send_probereq(vap->iv_bss, |
---|
540 | vap->iv_myaddr, ifp->if_broadcastaddr, |
---|
541 | ifp->if_broadcastaddr, |
---|
542 | ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); |
---|
543 | if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) |
---|
544 | ieee80211_send_probereq(vap->iv_bss, |
---|
545 | vap->iv_myaddr, ifp->if_broadcastaddr, |
---|
546 | ifp->if_broadcastaddr, |
---|
547 | "", 0); |
---|
548 | } |
---|
549 | |
---|
550 | /* |
---|
551 | * Scan curchan. If this is an active scan and the channel |
---|
552 | * is not marked passive then send probe request frame(s). |
---|
553 | * Arrange for the channel change after maxdwell ticks. |
---|
554 | */ |
---|
555 | static void |
---|
556 | scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) |
---|
557 | { |
---|
558 | struct ieee80211vap *vap = ss->ss_vap; |
---|
559 | struct ieee80211com *ic = ss->ss_ic; |
---|
560 | |
---|
561 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
562 | "%s: calling; maxdwell=%lu\n", |
---|
563 | __func__, |
---|
564 | maxdwell); |
---|
565 | IEEE80211_LOCK(ic); |
---|
566 | if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) |
---|
567 | ieee80211_probe_curchan(vap, 0); |
---|
568 | taskqueue_enqueue_timeout(ic->ic_tq, |
---|
569 | &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell); |
---|
570 | IEEE80211_UNLOCK(ic); |
---|
571 | } |
---|
572 | |
---|
573 | static void |
---|
574 | scan_signal(struct ieee80211_scan_state *ss, int iflags) |
---|
575 | { |
---|
576 | struct ieee80211com *ic = ss->ss_ic; |
---|
577 | |
---|
578 | IEEE80211_UNLOCK_ASSERT(ic); |
---|
579 | |
---|
580 | IEEE80211_LOCK(ic); |
---|
581 | scan_signal_locked(ss, iflags); |
---|
582 | IEEE80211_UNLOCK(ic); |
---|
583 | } |
---|
584 | |
---|
585 | static void |
---|
586 | scan_signal_locked(struct ieee80211_scan_state *ss, int iflags) |
---|
587 | { |
---|
588 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
589 | struct timeout_task *scan_task = &ss_priv->ss_scan_curchan; |
---|
590 | struct ieee80211com *ic = ss->ss_ic; |
---|
591 | |
---|
592 | IEEE80211_LOCK_ASSERT(ic); |
---|
593 | |
---|
594 | ss_priv->ss_iflags |= iflags; |
---|
595 | if (ss_priv->ss_iflags & ISCAN_RUNNING) { |
---|
596 | if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0) |
---|
597 | taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0); |
---|
598 | } |
---|
599 | } |
---|
600 | |
---|
601 | /* |
---|
602 | * Handle mindwell requirements completed; initiate a channel |
---|
603 | * change to the next channel asap. |
---|
604 | */ |
---|
605 | static void |
---|
606 | scan_mindwell(struct ieee80211_scan_state *ss) |
---|
607 | { |
---|
608 | |
---|
609 | IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n", |
---|
610 | __func__); |
---|
611 | |
---|
612 | scan_signal(ss, 0); |
---|
613 | } |
---|
614 | |
---|
615 | static void |
---|
616 | scan_start(void *arg, int pending) |
---|
617 | { |
---|
618 | #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) |
---|
619 | struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; |
---|
620 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
621 | struct ieee80211vap *vap = ss->ss_vap; |
---|
622 | struct ieee80211com *ic = ss->ss_ic; |
---|
623 | |
---|
624 | IEEE80211_LOCK(ic); |
---|
625 | if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || |
---|
626 | (ss_priv->ss_iflags & ISCAN_ABORT)) { |
---|
627 | /* Cancelled before we started */ |
---|
628 | scan_done(ss, 0); |
---|
629 | return; |
---|
630 | } |
---|
631 | |
---|
632 | if (ss->ss_next == ss->ss_last) { |
---|
633 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
634 | "%s: no channels to scan\n", __func__); |
---|
635 | scan_done(ss, 1); |
---|
636 | return; |
---|
637 | } |
---|
638 | |
---|
639 | /* |
---|
640 | * Put the station into power save mode. |
---|
641 | * |
---|
642 | * This is only required if we're not a full-offload devices; |
---|
643 | * those devices manage scan/traffic differently. |
---|
644 | */ |
---|
645 | if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) && |
---|
646 | vap->iv_opmode == IEEE80211_M_STA && |
---|
647 | vap->iv_state == IEEE80211_S_RUN) { |
---|
648 | if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { |
---|
649 | /* Enable station power save mode */ |
---|
650 | vap->iv_sta_ps(vap, 1); |
---|
651 | /* Wait until null data frame will be ACK'ed */ |
---|
652 | mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH, |
---|
653 | "sta_ps", msecs_to_ticks(10)); |
---|
654 | if (ss_priv->ss_iflags & ISCAN_ABORT) { |
---|
655 | scan_done(ss, 0); |
---|
656 | return; |
---|
657 | } |
---|
658 | } |
---|
659 | } |
---|
660 | |
---|
661 | ss_priv->ss_scanend = ticks + ss_priv->ss_duration; |
---|
662 | |
---|
663 | /* XXX scan state can change! Re-validate scan state! */ |
---|
664 | |
---|
665 | IEEE80211_UNLOCK(ic); |
---|
666 | |
---|
667 | ic->ic_scan_start(ic); /* notify driver */ |
---|
668 | |
---|
669 | scan_curchan_task(ss, 0); |
---|
670 | } |
---|
671 | |
---|
672 | static void |
---|
673 | scan_curchan_task(void *arg, int pending) |
---|
674 | { |
---|
675 | struct ieee80211_scan_state *ss = arg; |
---|
676 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
677 | struct ieee80211com *ic = ss->ss_ic; |
---|
678 | struct ieee80211_channel *chan; |
---|
679 | unsigned long maxdwell; |
---|
680 | int scandone; |
---|
681 | |
---|
682 | IEEE80211_LOCK(ic); |
---|
683 | end: |
---|
684 | scandone = (ss->ss_next >= ss->ss_last) || |
---|
685 | (ss_priv->ss_iflags & ISCAN_CANCEL) != 0; |
---|
686 | |
---|
687 | IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, |
---|
688 | "%s: loop start; scandone=%d\n", |
---|
689 | __func__, |
---|
690 | scandone); |
---|
691 | |
---|
692 | if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || |
---|
693 | (ss_priv->ss_iflags & ISCAN_ABORT) || |
---|
694 | ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { |
---|
695 | ss_priv->ss_iflags &= ~ISCAN_RUNNING; |
---|
696 | scan_end(ss, scandone); |
---|
697 | return; |
---|
698 | } else |
---|
699 | ss_priv->ss_iflags |= ISCAN_RUNNING; |
---|
700 | |
---|
701 | chan = ss->ss_chans[ss->ss_next++]; |
---|
702 | |
---|
703 | /* |
---|
704 | * Watch for truncation due to the scan end time. |
---|
705 | */ |
---|
706 | if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend)) |
---|
707 | maxdwell = ss_priv->ss_scanend - ticks; |
---|
708 | else |
---|
709 | maxdwell = ss->ss_maxdwell; |
---|
710 | |
---|
711 | IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, |
---|
712 | "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", |
---|
713 | __func__, |
---|
714 | ieee80211_chan2ieee(ic, ic->ic_curchan), |
---|
715 | ieee80211_channel_type_char(ic->ic_curchan), |
---|
716 | ieee80211_chan2ieee(ic, chan), |
---|
717 | ieee80211_channel_type_char(chan), |
---|
718 | (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && |
---|
719 | (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? |
---|
720 | "active" : "passive", |
---|
721 | ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); |
---|
722 | |
---|
723 | /* |
---|
724 | * Potentially change channel and phy mode. |
---|
725 | */ |
---|
726 | ic->ic_curchan = chan; |
---|
727 | ic->ic_rt = ieee80211_get_ratetable(chan); |
---|
728 | IEEE80211_UNLOCK(ic); |
---|
729 | /* |
---|
730 | * Perform the channel change and scan unlocked so the driver |
---|
731 | * may sleep. Once set_channel returns the hardware has |
---|
732 | * completed the channel change. |
---|
733 | */ |
---|
734 | ic->ic_set_channel(ic); |
---|
735 | ieee80211_radiotap_chan_change(ic); |
---|
736 | |
---|
737 | /* |
---|
738 | * Scan curchan. Drivers for "intelligent hardware" |
---|
739 | * override ic_scan_curchan to tell the device to do |
---|
740 | * the work. Otherwise we manage the work ourselves; |
---|
741 | * sending a probe request (as needed), and arming the |
---|
742 | * timeout to switch channels after maxdwell ticks. |
---|
743 | * |
---|
744 | * scan_curchan should only pause for the time required to |
---|
745 | * prepare/initiate the hardware for the scan (if at all). |
---|
746 | */ |
---|
747 | ic->ic_scan_curchan(ss, maxdwell); |
---|
748 | IEEE80211_LOCK(ic); |
---|
749 | |
---|
750 | /* XXX scan state can change! Re-validate scan state! */ |
---|
751 | |
---|
752 | ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell; |
---|
753 | /* clear mindwell lock and initial channel change flush */ |
---|
754 | ss_priv->ss_iflags &= ~ISCAN_REP; |
---|
755 | |
---|
756 | if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) { |
---|
757 | taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan, |
---|
758 | NULL); |
---|
759 | goto end; |
---|
760 | } |
---|
761 | |
---|
762 | IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n", |
---|
763 | __func__); |
---|
764 | IEEE80211_UNLOCK(ic); |
---|
765 | } |
---|
766 | |
---|
767 | static void |
---|
768 | scan_end(struct ieee80211_scan_state *ss, int scandone) |
---|
769 | { |
---|
770 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
771 | struct ieee80211vap *vap = ss->ss_vap; |
---|
772 | struct ieee80211com *ic = ss->ss_ic; |
---|
773 | |
---|
774 | IEEE80211_LOCK_ASSERT(ic); |
---|
775 | |
---|
776 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__); |
---|
777 | |
---|
778 | if (ss_priv->ss_iflags & ISCAN_ABORT) { |
---|
779 | scan_done(ss, scandone); |
---|
780 | return; |
---|
781 | } |
---|
782 | |
---|
783 | IEEE80211_UNLOCK(ic); |
---|
784 | ic->ic_scan_end(ic); /* notify driver */ |
---|
785 | IEEE80211_LOCK(ic); |
---|
786 | /* XXX scan state can change! Re-validate scan state! */ |
---|
787 | |
---|
788 | /* |
---|
789 | * Since a cancellation may have occurred during one of the |
---|
790 | * driver calls (whilst unlocked), update scandone. |
---|
791 | */ |
---|
792 | if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { |
---|
793 | /* XXX printf? */ |
---|
794 | if_printf(vap->iv_ifp, |
---|
795 | "%s: OOPS! scan cancelled during driver call (1)!\n", |
---|
796 | __func__); |
---|
797 | scandone = 1; |
---|
798 | } |
---|
799 | |
---|
800 | /* |
---|
801 | * Record scan complete time. Note that we also do |
---|
802 | * this when canceled so any background scan will |
---|
803 | * not be restarted for a while. |
---|
804 | */ |
---|
805 | if (scandone) |
---|
806 | ic->ic_lastscan = ticks; |
---|
807 | /* return to the bss channel */ |
---|
808 | if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && |
---|
809 | ic->ic_curchan != ic->ic_bsschan) { |
---|
810 | ieee80211_setupcurchan(ic, ic->ic_bsschan); |
---|
811 | IEEE80211_UNLOCK(ic); |
---|
812 | ic->ic_set_channel(ic); |
---|
813 | ieee80211_radiotap_chan_change(ic); |
---|
814 | IEEE80211_LOCK(ic); |
---|
815 | } |
---|
816 | /* clear internal flags and any indication of a pick */ |
---|
817 | ss_priv->ss_iflags &= ~ISCAN_REP; |
---|
818 | ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; |
---|
819 | |
---|
820 | /* |
---|
821 | * If not canceled and scan completed, do post-processing. |
---|
822 | * If the callback function returns 0, then it wants to |
---|
823 | * continue/restart scanning. Unfortunately we needed to |
---|
824 | * notify the driver to end the scan above to avoid having |
---|
825 | * rx frames alter the scan candidate list. |
---|
826 | */ |
---|
827 | if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 && |
---|
828 | !ss->ss_ops->scan_end(ss, vap) && |
---|
829 | (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && |
---|
830 | ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { |
---|
831 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
832 | "%s: done, restart " |
---|
833 | "[ticks %u, dwell min %lu scanend %lu]\n", |
---|
834 | __func__, |
---|
835 | ticks, ss->ss_mindwell, ss_priv->ss_scanend); |
---|
836 | ss->ss_next = 0; /* reset to beginning */ |
---|
837 | if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) |
---|
838 | vap->iv_stats.is_scan_active++; |
---|
839 | else |
---|
840 | vap->iv_stats.is_scan_passive++; |
---|
841 | |
---|
842 | ss->ss_ops->scan_restart(ss, vap); /* XXX? */ |
---|
843 | ieee80211_runtask(ic, &ss_priv->ss_scan_start); |
---|
844 | IEEE80211_UNLOCK(ic); |
---|
845 | return; |
---|
846 | } |
---|
847 | |
---|
848 | /* past here, scandone is ``true'' if not in bg mode */ |
---|
849 | if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) |
---|
850 | scandone = 1; |
---|
851 | |
---|
852 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
853 | "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", |
---|
854 | __func__, scandone ? "done" : "stopped", |
---|
855 | ticks, ss->ss_mindwell, ss_priv->ss_scanend); |
---|
856 | |
---|
857 | /* |
---|
858 | * Since a cancellation may have occurred during one of the |
---|
859 | * driver calls (whilst unlocked), update scandone. |
---|
860 | */ |
---|
861 | if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { |
---|
862 | /* XXX printf? */ |
---|
863 | if_printf(vap->iv_ifp, |
---|
864 | "%s: OOPS! scan cancelled during driver call (2)!\n", |
---|
865 | __func__); |
---|
866 | scandone = 1; |
---|
867 | } |
---|
868 | |
---|
869 | scan_done(ss, scandone); |
---|
870 | } |
---|
871 | |
---|
872 | static void |
---|
873 | scan_done(struct ieee80211_scan_state *ss, int scandone) |
---|
874 | { |
---|
875 | struct scan_state *ss_priv = SCAN_PRIVATE(ss); |
---|
876 | struct ieee80211com *ic = ss->ss_ic; |
---|
877 | struct ieee80211vap *vap = ss->ss_vap; |
---|
878 | |
---|
879 | IEEE80211_LOCK_ASSERT(ic); |
---|
880 | |
---|
881 | /* |
---|
882 | * Clear the SCAN bit first in case frames are |
---|
883 | * pending on the station power save queue. If |
---|
884 | * we defer this then the dispatch of the frames |
---|
885 | * may generate a request to cancel scanning. |
---|
886 | */ |
---|
887 | ic->ic_flags &= ~IEEE80211_F_SCAN; |
---|
888 | |
---|
889 | /* |
---|
890 | * Drop out of power save mode when a scan has |
---|
891 | * completed. If this scan was prematurely terminated |
---|
892 | * because it is a background scan then don't notify |
---|
893 | * the ap; we'll either return to scanning after we |
---|
894 | * receive the beacon frame or we'll drop out of power |
---|
895 | * save mode because the beacon indicates we have frames |
---|
896 | * waiting for us. |
---|
897 | */ |
---|
898 | if (scandone) { |
---|
899 | /* |
---|
900 | * If we're not a scan offload device, come back out of |
---|
901 | * station powersave. Offload devices handle this themselves. |
---|
902 | */ |
---|
903 | if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) |
---|
904 | vap->iv_sta_ps(vap, 0); |
---|
905 | if (ss->ss_next >= ss->ss_last) |
---|
906 | ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; |
---|
907 | |
---|
908 | /* send 'scan done' event if not interrupted due to traffic. */ |
---|
909 | if (!(ss_priv->ss_iflags & ISCAN_INTERRUPT)) |
---|
910 | ieee80211_notify_scan_done(vap); |
---|
911 | } |
---|
912 | ss_priv->ss_iflags &= ~(ISCAN_PAUSE | ISCAN_ABORT); |
---|
913 | ss_priv->ss_scanend = 0; |
---|
914 | ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); |
---|
915 | IEEE80211_UNLOCK(ic); |
---|
916 | #undef ISCAN_REP |
---|
917 | } |
---|
918 | |
---|
919 | /* |
---|
920 | * Process a beacon or probe response frame. |
---|
921 | */ |
---|
922 | static void |
---|
923 | ieee80211_swscan_add_scan(struct ieee80211vap *vap, |
---|
924 | struct ieee80211_channel *curchan, |
---|
925 | const struct ieee80211_scanparams *sp, |
---|
926 | const struct ieee80211_frame *wh, |
---|
927 | int subtype, int rssi, int noise) |
---|
928 | { |
---|
929 | struct ieee80211com *ic = vap->iv_ic; |
---|
930 | struct ieee80211_scan_state *ss = ic->ic_scan; |
---|
931 | |
---|
932 | /* XXX locking */ |
---|
933 | /* |
---|
934 | * Frames received during startup are discarded to avoid |
---|
935 | * using scan state setup on the initial entry to the timer |
---|
936 | * callback. This can occur because the device may enable |
---|
937 | * rx prior to our doing the initial channel change in the |
---|
938 | * timer routine. |
---|
939 | */ |
---|
940 | if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) |
---|
941 | return; |
---|
942 | #ifdef IEEE80211_DEBUG |
---|
943 | if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) |
---|
944 | ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); |
---|
945 | #endif |
---|
946 | if (ss->ss_ops != NULL && |
---|
947 | ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) { |
---|
948 | /* |
---|
949 | * If we've reached the min dwell time terminate |
---|
950 | * the timer so we'll switch to the next channel. |
---|
951 | */ |
---|
952 | if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && |
---|
953 | ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { |
---|
954 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, |
---|
955 | "%s: chan %3d%c min dwell met (%u > %lu)\n", |
---|
956 | __func__, |
---|
957 | ieee80211_chan2ieee(ic, ic->ic_curchan), |
---|
958 | ieee80211_channel_type_char(ic->ic_curchan), |
---|
959 | ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); |
---|
960 | SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; |
---|
961 | /* |
---|
962 | * NB: trigger at next clock tick or wait for the |
---|
963 | * hardware. |
---|
964 | */ |
---|
965 | ic->ic_scan_mindwell(ss); |
---|
966 | } |
---|
967 | } |
---|
968 | } |
---|
969 | |
---|
970 | static struct ieee80211_scan_methods swscan_methods = { |
---|
971 | .sc_attach = ieee80211_swscan_attach, |
---|
972 | .sc_detach = ieee80211_swscan_detach, |
---|
973 | .sc_vattach = ieee80211_swscan_vattach, |
---|
974 | .sc_vdetach = ieee80211_swscan_vdetach, |
---|
975 | .sc_set_scan_duration = ieee80211_swscan_set_scan_duration, |
---|
976 | .sc_start_scan = ieee80211_swscan_start_scan, |
---|
977 | .sc_check_scan = ieee80211_swscan_check_scan, |
---|
978 | .sc_bg_scan = ieee80211_swscan_bg_scan, |
---|
979 | .sc_cancel_scan = ieee80211_swscan_cancel_scan, |
---|
980 | .sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan, |
---|
981 | .sc_scan_next = ieee80211_swscan_scan_next, |
---|
982 | .sc_scan_done = ieee80211_swscan_scan_done, |
---|
983 | .sc_scan_probe_curchan = ieee80211_swscan_probe_curchan, |
---|
984 | .sc_add_scan = ieee80211_swscan_add_scan |
---|
985 | }; |
---|
986 | |
---|
987 | /* |
---|
988 | * Default scan attach method. |
---|
989 | */ |
---|
990 | void |
---|
991 | ieee80211_swscan_attach(struct ieee80211com *ic) |
---|
992 | { |
---|
993 | struct scan_state *ss; |
---|
994 | |
---|
995 | /* |
---|
996 | * Setup the default methods |
---|
997 | */ |
---|
998 | ic->ic_scan_methods = &swscan_methods; |
---|
999 | |
---|
1000 | /* Allocate initial scan state */ |
---|
1001 | ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state), |
---|
1002 | M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); |
---|
1003 | if (ss == NULL) { |
---|
1004 | ic->ic_scan = NULL; |
---|
1005 | return; |
---|
1006 | } |
---|
1007 | TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss); |
---|
1008 | TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0, |
---|
1009 | scan_curchan_task, ss); |
---|
1010 | |
---|
1011 | ic->ic_scan = &ss->base; |
---|
1012 | ss->base.ss_ic = ic; |
---|
1013 | |
---|
1014 | ic->ic_scan_curchan = scan_curchan; |
---|
1015 | ic->ic_scan_mindwell = scan_mindwell; |
---|
1016 | } |
---|