1170530Ssam/*-
2178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3170530Ssam * All rights reserved.
4170530Ssam *
5170530Ssam * Redistribution and use in source and binary forms, with or without
6170530Ssam * modification, are permitted provided that the following conditions
7170530Ssam * are met:
8170530Ssam * 1. Redistributions of source code must retain the above copyright
9170530Ssam *    notice, this list of conditions and the following disclaimer.
10170530Ssam * 2. Redistributions in binary form must reproduce the above copyright
11170530Ssam *    notice, this list of conditions and the following disclaimer in the
12170530Ssam *    documentation and/or other materials provided with the distribution.
13170530Ssam *
14170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24170530Ssam */
25170530Ssam
26170530Ssam#include <sys/cdefs.h>
27170530Ssam__FBSDID("$FreeBSD: stable/11/sys/net80211/ieee80211_scan_sw.c 330460 2018-03-05 08:22:24Z eadler $");
28170530Ssam
29170530Ssam/*
30170530Ssam * IEEE 802.11 scanning support.
31170530Ssam */
32178354Ssam#include "opt_wlan.h"
33178354Ssam
34170530Ssam#include <sys/param.h>
35170530Ssam#include <sys/systm.h>
36191746Sthompsa#include <sys/proc.h>
37170530Ssam#include <sys/kernel.h>
38295126Sglebius#include <sys/malloc.h>
39191746Sthompsa#include <sys/condvar.h>
40170530Ssam
41170530Ssam#include <sys/socket.h>
42170530Ssam
43170530Ssam#include <net/if.h>
44257176Sglebius#include <net/if_var.h>
45170530Ssam#include <net/if_media.h>
46170530Ssam#include <net/ethernet.h>
47170530Ssam
48170530Ssam#include <net80211/ieee80211_var.h>
49170530Ssam
50276730Sadrian#include <net80211/ieee80211_scan_sw.h>
51276730Sadrian
52170530Ssam#include <net/bpf.h>
53170530Ssam
54170530Ssamstruct scan_state {
55170530Ssam	struct ieee80211_scan_state base;	/* public state */
56170530Ssam
57296235Savos	u_int			ss_iflags;	/* flags used internally */
58296235Savos#define	ISCAN_MINDWELL 		0x0001		/* min dwell time reached */
59296235Savos#define	ISCAN_DISCARD		0x0002		/* discard rx'd frames */
60296235Savos#define	ISCAN_CANCEL		0x0004		/* cancel current scan */
61296235Savos#define	ISCAN_ABORT		0x0008		/* end the scan immediately */
62296235Savos#define	ISCAN_RUNNING		0x0010		/* scan was started */
63296235Savos
64296235Savos	unsigned long		ss_chanmindwell;  /* min dwell on curchan */
65296235Savos	unsigned long		ss_scanend;	/* time scan must stop */
66296235Savos	u_int			ss_duration;	/* duration for next scan */
67296235Savos	struct task		ss_scan_start;	/* scan start */
68296235Savos	struct timeout_task	ss_scan_curchan;  /* scan execution */
69170530Ssam};
70170530Ssam#define	SCAN_PRIVATE(ss)	((struct scan_state *) ss)
71170530Ssam
72170530Ssam/*
73170530Ssam * Amount of time to go off-channel during a background
74170530Ssam * scan.  This value should be large enough to catch most
75170530Ssam * ap's but short enough that we can return on-channel
76170530Ssam * before our listen interval expires.
77170530Ssam *
78170530Ssam * XXX tunable
79170530Ssam * XXX check against configured listen interval
80170530Ssam */
81170530Ssam#define	IEEE80211_SCAN_OFFCHANNEL	msecs_to_ticks(150)
82170530Ssam
83178354Ssamstatic	void scan_curchan(struct ieee80211_scan_state *, unsigned long);
84178354Ssamstatic	void scan_mindwell(struct ieee80211_scan_state *);
85296234Savosstatic	void scan_signal(struct ieee80211_scan_state *, int);
86296234Savosstatic	void scan_signal_locked(struct ieee80211_scan_state *, int);
87296232Savosstatic	void scan_start(void *, int);
88296232Savosstatic	void scan_curchan_task(void *, int);
89296231Savosstatic	void scan_end(struct ieee80211_scan_state *, int);
90296230Savosstatic	void scan_done(struct ieee80211_scan_state *, int);
91170530Ssam
92170530SsamMALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
93170530Ssam
94284143Sadrianstatic void
95276730Sadrianieee80211_swscan_detach(struct ieee80211com *ic)
96170530Ssam{
97170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
98170530Ssam
99170530Ssam	if (ss != NULL) {
100296234Savos		scan_signal(ss, ISCAN_ABORT);
101296232Savos		ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
102296232Savos		taskqueue_drain_timeout(ic->ic_tq,
103296232Savos		    &SCAN_PRIVATE(ss)->ss_scan_curchan);
104191746Sthompsa		KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
105191746Sthompsa		    ("scan still running"));
106276730Sadrian
107276730Sadrian		/*
108276730Sadrian		 * For now, do the ss_ops detach here rather
109276730Sadrian		 * than ieee80211_scan_detach().
110276730Sadrian		 *
111276730Sadrian		 * I'll figure out how to cleanly split things up
112276730Sadrian		 * at a later date.
113276730Sadrian		 */
114170530Ssam		if (ss->ss_ops != NULL) {
115170530Ssam			ss->ss_ops->scan_detach(ss);
116170530Ssam			ss->ss_ops = NULL;
117170530Ssam		}
118170530Ssam		ic->ic_scan = NULL;
119283538Sadrian		IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
120170530Ssam	}
121170530Ssam}
122170530Ssam
123284143Sadrianstatic void
124276730Sadrianieee80211_swscan_vattach(struct ieee80211vap *vap)
125178354Ssam{
126276730Sadrian	/* nothing to do for now */
127276730Sadrian	/*
128276730Sadrian	 * TODO: all of the vap scan calls should be methods!
129276730Sadrian	 */
130178354Ssam
131178354Ssam}
132178354Ssam
133284143Sadrianstatic void
134276730Sadrianieee80211_swscan_vdetach(struct ieee80211vap *vap)
135178354Ssam{
136178354Ssam	struct ieee80211com *ic = vap->iv_ic;
137296235Savos	struct ieee80211_scan_state *ss = ic->ic_scan;
138178354Ssam
139276730Sadrian	IEEE80211_LOCK_ASSERT(ic);
140296235Savos
141296235Savos	if (ss != NULL && ss->ss_vap == vap &&
142296235Savos	    (ic->ic_flags & IEEE80211_F_SCAN))
143296235Savos		scan_signal_locked(ss, ISCAN_ABORT);
144178354Ssam}
145178354Ssam
146284143Sadrianstatic void
147276730Sadrianieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration)
148170530Ssam{
149276730Sadrian	struct ieee80211com *ic = vap->iv_ic;
150276730Sadrian	struct ieee80211_scan_state *ss = ic->ic_scan;
151170530Ssam
152276730Sadrian	IEEE80211_LOCK_ASSERT(ic);
153170530Ssam
154276730Sadrian	/* NB: flush frames rx'd before 1st channel change */
155276730Sadrian	SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
156276730Sadrian	SCAN_PRIVATE(ss)->ss_duration = duration;
157170530Ssam}
158170530Ssam
159178354Ssam/*
160178354Ssam * Start a scan unless one is already going.
161282705Sadrian */
162282705Sadrianstatic int
163282705Sadrianieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan,
164282705Sadrian	struct ieee80211vap *vap, int flags, u_int duration,
165282705Sadrian	u_int mindwell, u_int maxdwell,
166282705Sadrian	u_int nssid, const struct ieee80211_scan_ssid ssids[])
167282705Sadrian{
168282705Sadrian	struct ieee80211com *ic = vap->iv_ic;
169282705Sadrian	struct ieee80211_scan_state *ss = ic->ic_scan;
170282705Sadrian
171282705Sadrian	IEEE80211_LOCK_ASSERT(ic);
172282705Sadrian
173282705Sadrian	if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
174282705Sadrian		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
175282705Sadrian		    "%s: scan inhibited by pending channel change\n", __func__);
176282705Sadrian	} else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
177282705Sadrian		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
178282705Sadrian		    "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
179282705Sadrian		    , __func__
180282705Sadrian		    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
181282705Sadrian		    , duration, mindwell, maxdwell
182282705Sadrian		    , ieee80211_phymode_name[vap->iv_des_mode]
183282705Sadrian		    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
184282705Sadrian		    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
185282705Sadrian		    , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
186282705Sadrian		    , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
187282705Sadrian		    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
188282705Sadrian		    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
189282705Sadrian		);
190282705Sadrian
191282705Sadrian		ieee80211_scan_update_locked(vap, scan);
192282705Sadrian		if (ss->ss_ops != NULL) {
193282705Sadrian			if ((flags & IEEE80211_SCAN_NOSSID) == 0)
194282705Sadrian				ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
195282705Sadrian
196282705Sadrian			/* NB: top 4 bits for internal use */
197282705Sadrian			ss->ss_flags = flags & 0xfff;
198282705Sadrian			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
199282705Sadrian				vap->iv_stats.is_scan_active++;
200282705Sadrian			else
201282705Sadrian				vap->iv_stats.is_scan_passive++;
202282705Sadrian			if (flags & IEEE80211_SCAN_FLUSH)
203282705Sadrian				ss->ss_ops->scan_flush(ss);
204282705Sadrian			if (flags & IEEE80211_SCAN_BGSCAN)
205282705Sadrian				ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
206282705Sadrian
207282705Sadrian			/* Set duration for this particular scan */
208282705Sadrian			ieee80211_swscan_set_scan_duration(vap, duration);
209282705Sadrian
210282705Sadrian			ss->ss_next = 0;
211282705Sadrian			ss->ss_mindwell = mindwell;
212282705Sadrian			ss->ss_maxdwell = maxdwell;
213282705Sadrian			/* NB: scan_start must be before the scan runtask */
214282705Sadrian			ss->ss_ops->scan_start(ss, vap);
215282705Sadrian#ifdef IEEE80211_DEBUG
216282705Sadrian			if (ieee80211_msg_scan(vap))
217282705Sadrian				ieee80211_scan_dump(ss);
218282705Sadrian#endif /* IEEE80211_DEBUG */
219282705Sadrian			ic->ic_flags |= IEEE80211_F_SCAN;
220282705Sadrian
221282705Sadrian			/* Start scan task */
222296232Savos			ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
223282705Sadrian		}
224282705Sadrian		return 1;
225282705Sadrian	} else {
226282705Sadrian		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
227282705Sadrian		    "%s: %s scan already in progress\n", __func__,
228282705Sadrian		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
229282705Sadrian	}
230282705Sadrian	return 0;
231282705Sadrian}
232282705Sadrian
233282705Sadrian
234282705Sadrian/*
235282705Sadrian * Start a scan unless one is already going.
236276730Sadrian *
237276730Sadrian * Called without the comlock held; grab the comlock as appropriate.
238178354Ssam */
239284143Sadrianstatic int
240276730Sadrianieee80211_swscan_start_scan(const struct ieee80211_scanner *scan,
241276730Sadrian    struct ieee80211vap *vap, int flags,
242276730Sadrian    u_int duration, u_int mindwell, u_int maxdwell,
243276730Sadrian    u_int nssid, const struct ieee80211_scan_ssid ssids[])
244178354Ssam{
245178354Ssam	struct ieee80211com *ic = vap->iv_ic;
246178354Ssam	int result;
247178354Ssam
248276730Sadrian	IEEE80211_UNLOCK_ASSERT(ic);
249178354Ssam
250178354Ssam	IEEE80211_LOCK(ic);
251282705Sadrian	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
252178354Ssam	    mindwell, maxdwell, nssid, ssids);
253170530Ssam	IEEE80211_UNLOCK(ic);
254170530Ssam
255178354Ssam	return result;
256170530Ssam}
257170530Ssam
258170530Ssam/*
259170530Ssam * Check the scan cache for an ap/channel to use; if that
260170530Ssam * fails then kick off a new scan.
261276730Sadrian *
262276730Sadrian * Called with the comlock held.
263276730Sadrian *
264276730Sadrian * XXX TODO: split out!
265170530Ssam */
266284143Sadrianstatic int
267276730Sadrianieee80211_swscan_check_scan(const struct ieee80211_scanner *scan,
268276730Sadrian    struct ieee80211vap *vap, int flags,
269276730Sadrian    u_int duration, u_int mindwell, u_int maxdwell,
270276730Sadrian    u_int nssid, const struct ieee80211_scan_ssid ssids[])
271170530Ssam{
272178354Ssam	struct ieee80211com *ic = vap->iv_ic;
273170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
274179389Ssam	int result;
275170530Ssam
276276730Sadrian	IEEE80211_LOCK_ASSERT(ic);
277178354Ssam
278170530Ssam	if (ss->ss_ops != NULL) {
279178354Ssam		/* XXX verify ss_ops matches vap->iv_opmode */
280170530Ssam		if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
281170530Ssam			/*
282170530Ssam			 * Update the ssid list and mark flags so if
283170530Ssam			 * we call start_scan it doesn't duplicate work.
284170530Ssam			 */
285276730Sadrian			ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
286170530Ssam			flags |= IEEE80211_SCAN_NOSSID;
287170530Ssam		}
288170530Ssam		if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
289179389Ssam		    (flags & IEEE80211_SCAN_FLUSH) == 0 &&
290297405Sadrian		    ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
291170530Ssam			/*
292170530Ssam			 * We're not currently scanning and the cache is
293170530Ssam			 * deemed hot enough to consult.  Lock out others
294170530Ssam			 * by marking IEEE80211_F_SCAN while we decide if
295170530Ssam			 * something is already in the scan cache we can
296170530Ssam			 * use.  Also discard any frames that might come
297170530Ssam			 * in while temporarily marked as scanning.
298170530Ssam			 */
299170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
300170530Ssam			ic->ic_flags |= IEEE80211_F_SCAN;
301179389Ssam
302179389Ssam			/* NB: need to use supplied flags in check */
303178354Ssam			ss->ss_flags = flags & 0xff;
304179389Ssam			result = ss->ss_ops->scan_end(ss, vap);
305179389Ssam
306170530Ssam			ic->ic_flags &= ~IEEE80211_F_SCAN;
307179389Ssam			SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD;
308179389Ssam			if (result) {
309179389Ssam				ieee80211_notify_scan_done(vap);
310179389Ssam				return 1;
311179389Ssam			}
312170530Ssam		}
313170530Ssam	}
314282705Sadrian	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
315178354Ssam	    mindwell, maxdwell, nssid, ssids);
316178354Ssam
317178354Ssam	return result;
318170530Ssam}
319170530Ssam
320170530Ssam/*
321170530Ssam * Restart a previous scan.  If the previous scan completed
322170530Ssam * then we start again using the existing channel list.
323170530Ssam */
324284143Sadrianstatic int
325276730Sadrianieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan,
326276730Sadrian    struct ieee80211vap *vap, int flags)
327170530Ssam{
328178354Ssam	struct ieee80211com *ic = vap->iv_ic;
329170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
330170530Ssam
331276730Sadrian	/* XXX assert unlocked? */
332276730Sadrian	// IEEE80211_UNLOCK_ASSERT(ic);
333178354Ssam
334170530Ssam	IEEE80211_LOCK(ic);
335170530Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
336170530Ssam		u_int duration;
337170530Ssam		/*
338170530Ssam		 * Go off-channel for a fixed interval that is large
339170530Ssam		 * enough to catch most ap's but short enough that
340170530Ssam		 * we can return on-channel before our listen interval
341170530Ssam		 * expires.
342170530Ssam		 */
343170530Ssam		duration = IEEE80211_SCAN_OFFCHANNEL;
344170530Ssam
345178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
346293164Sadrian		    "%s: %s scan, ticks %u duration %u\n", __func__,
347170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
348170530Ssam		    ticks, duration);
349170530Ssam
350276730Sadrian		ieee80211_scan_update_locked(vap, scan);
351170530Ssam		if (ss->ss_ops != NULL) {
352178354Ssam			ss->ss_vap = vap;
353170530Ssam			/*
354170530Ssam			 * A background scan does not select a new sta; it
355170530Ssam			 * just refreshes the scan cache.  Also, indicate
356170530Ssam			 * the scan logic should follow the beacon schedule:
357170530Ssam			 * we go off-channel and scan for a while, then
358170530Ssam			 * return to the bss channel to receive a beacon,
359170530Ssam			 * then go off-channel again.  All during this time
360170530Ssam			 * we notify the ap we're in power save mode.  When
361170530Ssam			 * the scan is complete we leave power save mode.
362170530Ssam			 * If any beacon indicates there are frames pending
363170530Ssam			 * for us then we drop out of power save mode
364170530Ssam			 * (and background scan) automatically by way of the
365170530Ssam			 * usual sta power save logic.
366170530Ssam			 */
367170530Ssam			ss->ss_flags |= IEEE80211_SCAN_NOPICK
368178354Ssam				     |  IEEE80211_SCAN_BGSCAN
369178354Ssam				     |  flags
370178354Ssam				     ;
371170530Ssam			/* if previous scan completed, restart */
372170530Ssam			if (ss->ss_next >= ss->ss_last) {
373170530Ssam				if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
374178354Ssam					vap->iv_stats.is_scan_active++;
375170530Ssam				else
376178354Ssam					vap->iv_stats.is_scan_passive++;
377178354Ssam				/*
378178354Ssam				 * NB: beware of the scan cache being flushed;
379178354Ssam				 *     if the channel list is empty use the
380178354Ssam				 *     scan_start method to populate it.
381178354Ssam				 */
382178354Ssam				ss->ss_next = 0;
383178354Ssam				if (ss->ss_last != 0)
384178354Ssam					ss->ss_ops->scan_restart(ss, vap);
385178354Ssam				else {
386178354Ssam					ss->ss_ops->scan_start(ss, vap);
387178354Ssam#ifdef IEEE80211_DEBUG
388178354Ssam					if (ieee80211_msg_scan(vap))
389276730Sadrian						ieee80211_scan_dump(ss);
390178354Ssam#endif /* IEEE80211_DEBUG */
391178354Ssam				}
392170530Ssam			}
393276730Sadrian			ieee80211_swscan_set_scan_duration(vap, duration);
394170530Ssam			ss->ss_maxdwell = duration;
395191746Sthompsa			ic->ic_flags |= IEEE80211_F_SCAN;
396191746Sthompsa			ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
397296232Savos			ieee80211_runtask(ic,
398296232Savos			    &SCAN_PRIVATE(ss)->ss_scan_start);
399170530Ssam		} else {
400170530Ssam			/* XXX msg+stat */
401170530Ssam		}
402170530Ssam	} else {
403178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
404170530Ssam		    "%s: %s scan already in progress\n", __func__,
405170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
406170530Ssam	}
407170530Ssam	IEEE80211_UNLOCK(ic);
408170530Ssam
409170530Ssam	/* NB: racey, does it matter? */
410170530Ssam	return (ic->ic_flags & IEEE80211_F_SCAN);
411170530Ssam}
412170530Ssam
413330232Seadler/*
414330232Seadler * Taskqueue work to cancel a scan.
415330232Seadler *
416330232Seadler * Note: for offload scan devices, we may want to call into the
417330232Seadler * driver to try and cancel scanning, however it may not be cancelable.
418330232Seadler */
419284143Sadrianstatic void
420296227Savoscancel_scan(struct ieee80211vap *vap, int any, const char *func)
421178354Ssam{
422178354Ssam	struct ieee80211com *ic = vap->iv_ic;
423178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
424178354Ssam
425178354Ssam	IEEE80211_LOCK(ic);
426178354Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
427296227Savos	    (any || ss->ss_vap == vap) &&
428178354Ssam	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
429178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
430296227Savos		    "%s: cancel %s scan\n", func,
431178354Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
432178354Ssam			"active" : "passive");
433178354Ssam
434296234Savos		/* clear bg scan NOPICK */
435178354Ssam		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
436296234Savos		/* mark cancel request and wake up the scan task */
437296234Savos		scan_signal_locked(ss, ISCAN_CANCEL);
438275973Sadrian	} else {
439275973Sadrian		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
440275973Sadrian		    "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n",
441296227Savos			func,
442275973Sadrian			!! (ic->ic_flags & IEEE80211_F_SCAN),
443275973Sadrian			(ss->ss_vap == vap ? "match" : "nomatch"),
444275973Sadrian			!! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL));
445178354Ssam	}
446178354Ssam	IEEE80211_UNLOCK(ic);
447178354Ssam}
448178354Ssam
449178354Ssam/*
450296227Savos * Cancel any scan currently going on for the specified vap.
451296227Savos */
452296227Savosstatic void
453296227Savosieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
454296227Savos{
455296227Savos	cancel_scan(vap, 0, __func__);
456296227Savos}
457296227Savos
458296227Savos/*
459170530Ssam * Cancel any scan currently going on.
460170530Ssam */
461284143Sadrianstatic void
462276730Sadrianieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap)
463170530Ssam{
464330460Seadler
465330460Seadler	/* XXX for now - just don't do this per packet. */
466330460Seadler	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
467330460Seadler		return;
468330460Seadler
469296227Savos	cancel_scan(vap, 1, __func__);
470170530Ssam}
471170530Ssam
472170530Ssam/*
473298392Savos * Manually switch to the next channel in the channel list.
474298392Savos * Provided for drivers that manage scanning themselves
475298392Savos * (e.g. for firmware-based devices).
476170530Ssam */
477284143Sadrianstatic void
478276730Sadrianieee80211_swscan_scan_next(struct ieee80211vap *vap)
479170530Ssam{
480296234Savos	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
481178354Ssam
482275974Sadrian	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
483275974Sadrian
484191746Sthompsa	/* wake up the scan task */
485296234Savos	scan_signal(ss, 0);
486170530Ssam}
487170530Ssam
488170530Ssam/*
489298392Savos * Manually stop a scan that is currently running.
490298392Savos * Provided for drivers that are not able to scan single channels
491298392Savos * (e.g. for firmware-based devices).
492171125Sthompsa */
493284143Sadrianstatic void
494276730Sadrianieee80211_swscan_scan_done(struct ieee80211vap *vap)
495171125Sthompsa{
496178354Ssam	struct ieee80211com *ic = vap->iv_ic;
497296235Savos	struct ieee80211_scan_state *ss = ic->ic_scan;
498171125Sthompsa
499276730Sadrian	IEEE80211_LOCK_ASSERT(ic);
500275974Sadrian
501296234Savos	scan_signal_locked(ss, 0);
502171125Sthompsa}
503171125Sthompsa
504171125Sthompsa/*
505298392Savos * Probe the current channel, if allowed, while scanning.
506178354Ssam * If the channel is not marked passive-only then send
507178354Ssam * a probe request immediately.  Otherwise mark state and
508178354Ssam * listen for beacons on the channel; if we receive something
509178354Ssam * then we'll transmit a probe request.
510178354Ssam */
511284143Sadrianstatic void
512276730Sadrianieee80211_swscan_probe_curchan(struct ieee80211vap *vap, int force)
513178354Ssam{
514178354Ssam	struct ieee80211com *ic = vap->iv_ic;
515178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
516178354Ssam	struct ifnet *ifp = vap->iv_ifp;
517178354Ssam	int i;
518178354Ssam
519178354Ssam	/*
520330232Seadler	 * Full-offload scan devices don't require this.
521330232Seadler	 */
522330232Seadler	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
523330232Seadler		return;
524330232Seadler
525330232Seadler	/*
526178354Ssam	 * Send directed probe requests followed by any
527178354Ssam	 * broadcast probe request.
528178354Ssam	 * XXX remove dependence on ic/vap->iv_bss
529178354Ssam	 */
530178354Ssam	for (i = 0; i < ss->ss_nssid; i++)
531178354Ssam		ieee80211_send_probereq(vap->iv_bss,
532178354Ssam			vap->iv_myaddr, ifp->if_broadcastaddr,
533178354Ssam			ifp->if_broadcastaddr,
534178354Ssam			ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
535178354Ssam	if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
536178354Ssam		ieee80211_send_probereq(vap->iv_bss,
537178354Ssam			vap->iv_myaddr, ifp->if_broadcastaddr,
538178354Ssam			ifp->if_broadcastaddr,
539178354Ssam			"", 0);
540178354Ssam}
541178354Ssam
542178354Ssam/*
543170530Ssam * Scan curchan.  If this is an active scan and the channel
544170530Ssam * is not marked passive then send probe request frame(s).
545170530Ssam * Arrange for the channel change after maxdwell ticks.
546170530Ssam */
547170530Ssamstatic void
548178354Ssamscan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
549170530Ssam{
550178354Ssam	struct ieee80211vap *vap  = ss->ss_vap;
551296235Savos	struct ieee80211com *ic = ss->ss_ic;
552170530Ssam
553275974Sadrian	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
554275974Sadrian	    "%s: calling; maxdwell=%lu\n",
555275974Sadrian	    __func__,
556275974Sadrian	    maxdwell);
557296235Savos	IEEE80211_LOCK(ic);
558178354Ssam	if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
559178354Ssam		ieee80211_probe_curchan(vap, 0);
560296235Savos	taskqueue_enqueue_timeout(ic->ic_tq,
561296232Savos	    &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell);
562296235Savos	IEEE80211_UNLOCK(ic);
563170530Ssam}
564170530Ssam
565191746Sthompsastatic void
566296234Savosscan_signal(struct ieee80211_scan_state *ss, int iflags)
567191746Sthompsa{
568296234Savos	struct ieee80211com *ic = ss->ss_ic;
569296234Savos
570296234Savos	IEEE80211_UNLOCK_ASSERT(ic);
571296234Savos
572296234Savos	IEEE80211_LOCK(ic);
573296234Savos	scan_signal_locked(ss, iflags);
574296234Savos	IEEE80211_UNLOCK(ic);
575296234Savos}
576296234Savos
577296234Savosstatic void
578296234Savosscan_signal_locked(struct ieee80211_scan_state *ss, int iflags)
579296234Savos{
580296232Savos	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
581296232Savos	struct timeout_task *scan_task = &ss_priv->ss_scan_curchan;
582296232Savos	struct ieee80211com *ic = ss->ss_ic;
583191746Sthompsa
584296232Savos	IEEE80211_LOCK_ASSERT(ic);
585296232Savos
586296234Savos	ss_priv->ss_iflags |= iflags;
587296232Savos	if (ss_priv->ss_iflags & ISCAN_RUNNING) {
588296232Savos		if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0)
589296232Savos			taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0);
590296232Savos	}
591191746Sthompsa}
592191746Sthompsa
593170530Ssam/*
594170530Ssam * Handle mindwell requirements completed; initiate a channel
595170530Ssam * change to the next channel asap.
596170530Ssam */
597170530Ssamstatic void
598178354Ssamscan_mindwell(struct ieee80211_scan_state *ss)
599170530Ssam{
600191746Sthompsa
601296241Sglebius	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n",
602296241Sglebius	    __func__);
603275974Sadrian
604296234Savos	scan_signal(ss, 0);
605170530Ssam}
606170530Ssam
607170530Ssamstatic void
608296232Savosscan_start(void *arg, int pending)
609170530Ssam{
610191746Sthompsa#define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
611170530Ssam	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
612296228Savos	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
613178354Ssam	struct ieee80211vap *vap = ss->ss_vap;
614191746Sthompsa	struct ieee80211com *ic = ss->ss_ic;
615170530Ssam
616191746Sthompsa	IEEE80211_LOCK(ic);
617191746Sthompsa	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
618296228Savos	    (ss_priv->ss_iflags & ISCAN_ABORT)) {
619191746Sthompsa		/* Cancelled before we started */
620296230Savos		scan_done(ss, 0);
621296230Savos		return;
622191746Sthompsa	}
623178354Ssam
624191746Sthompsa	if (ss->ss_next == ss->ss_last) {
625191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
626191746Sthompsa			"%s: no channels to scan\n", __func__);
627296230Savos		scan_done(ss, 1);
628296230Savos		return;
629191746Sthompsa	}
630191746Sthompsa
631330232Seadler	/*
632330232Seadler	 * Put the station into power save mode.
633330232Seadler	 *
634330232Seadler	 * This is only required if we're not a full-offload devices;
635330232Seadler	 * those devices manage scan/traffic differently.
636330232Seadler	 */
637330232Seadler	if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) &&
638330232Seadler	    vap->iv_opmode == IEEE80211_M_STA &&
639191746Sthompsa	    vap->iv_state == IEEE80211_S_RUN) {
640191746Sthompsa		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
641191746Sthompsa			/* Enable station power save mode */
642241138Sadrian			vap->iv_sta_ps(vap, 1);
643296233Savos			/* Wait until null data frame will be ACK'ed */
644296232Savos			mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
645296233Savos			    "sta_ps", msecs_to_ticks(10));
646296230Savos			if (ss_priv->ss_iflags & ISCAN_ABORT) {
647296230Savos				scan_done(ss, 0);
648296230Savos				return;
649296230Savos			}
650191746Sthompsa		}
651191746Sthompsa	}
652191746Sthompsa
653296229Savos	ss_priv->ss_scanend = ticks + ss_priv->ss_duration;
654275964Sadrian
655275964Sadrian	/* XXX scan state can change! Re-validate scan state! */
656275964Sadrian
657191746Sthompsa	IEEE80211_UNLOCK(ic);
658296232Savos
659191746Sthompsa	ic->ic_scan_start(ic);		/* notify driver */
660191746Sthompsa
661296232Savos	scan_curchan_task(ss, 0);
662296232Savos}
663275974Sadrian
664296232Savosstatic void
665296232Savosscan_curchan_task(void *arg, int pending)
666296232Savos{
667296232Savos	struct ieee80211_scan_state *ss = arg;
668296232Savos	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
669296232Savos	struct ieee80211com *ic = ss->ss_ic;
670296232Savos	struct ieee80211_channel *chan;
671296232Savos	unsigned long maxdwell;
672296232Savos	int scandone;
673275974Sadrian
674296232Savos	IEEE80211_LOCK(ic);
675296232Savosend:
676296232Savos	scandone = (ss->ss_next >= ss->ss_last) ||
677296232Savos	    (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;
678275974Sadrian
679296241Sglebius	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
680296232Savos	    "%s: loop start; scandone=%d\n",
681296232Savos	    __func__,
682296232Savos	    scandone);
683191746Sthompsa
684296232Savos	if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
685296232Savos	    (ss_priv->ss_iflags & ISCAN_ABORT) ||
686297405Sadrian	     ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
687296232Savos		ss_priv->ss_iflags &= ~ISCAN_RUNNING;
688296232Savos		scan_end(ss, scandone);
689296232Savos		return;
690296232Savos	} else
691296232Savos		ss_priv->ss_iflags |= ISCAN_RUNNING;
692170530Ssam
693296232Savos	chan = ss->ss_chans[ss->ss_next++];
694170530Ssam
695296232Savos	/*
696296232Savos	 * Watch for truncation due to the scan end time.
697296232Savos	 */
698297405Sadrian	if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
699296232Savos		maxdwell = ss_priv->ss_scanend - ticks;
700296232Savos	else
701296232Savos		maxdwell = ss->ss_maxdwell;
702170530Ssam
703296241Sglebius	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
704296232Savos	    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
705296232Savos	    __func__,
706296232Savos	    ieee80211_chan2ieee(ic, ic->ic_curchan),
707296232Savos	    ieee80211_channel_type_char(ic->ic_curchan),
708296232Savos	    ieee80211_chan2ieee(ic, chan),
709296232Savos	    ieee80211_channel_type_char(chan),
710296232Savos	    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
711296232Savos		(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
712296232Savos		"active" : "passive",
713296232Savos	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
714170530Ssam
715296232Savos	/*
716296232Savos	 * Potentially change channel and phy mode.
717296232Savos	 */
718296232Savos	ic->ic_curchan = chan;
719296232Savos	ic->ic_rt = ieee80211_get_ratetable(chan);
720296232Savos	IEEE80211_UNLOCK(ic);
721296232Savos	/*
722296232Savos	 * Perform the channel change and scan unlocked so the driver
723296232Savos	 * may sleep. Once set_channel returns the hardware has
724296232Savos	 * completed the channel change.
725296232Savos	 */
726296232Savos	ic->ic_set_channel(ic);
727296232Savos	ieee80211_radiotap_chan_change(ic);
728170530Ssam
729296232Savos	/*
730296232Savos	 * Scan curchan.  Drivers for "intelligent hardware"
731296232Savos	 * override ic_scan_curchan to tell the device to do
732296232Savos	 * the work.  Otherwise we manage the work ourselves;
733296232Savos	 * sending a probe request (as needed), and arming the
734296232Savos	 * timeout to switch channels after maxdwell ticks.
735296232Savos	 *
736296232Savos	 * scan_curchan should only pause for the time required to
737296232Savos	 * prepare/initiate the hardware for the scan (if at all).
738296232Savos	 */
739296232Savos	ic->ic_scan_curchan(ss, maxdwell);
740296232Savos	IEEE80211_LOCK(ic);
741275964Sadrian
742296232Savos	/* XXX scan state can change! Re-validate scan state! */
743170530Ssam
744296232Savos	ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
745296232Savos	/* clear mindwell lock and initial channel change flush */
746296232Savos	ss_priv->ss_iflags &= ~ISCAN_REP;
747170530Ssam
748298293Savos	if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
749298293Savos		taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
750298293Savos		    NULL);
751296232Savos		goto end;
752298293Savos	}
753296232Savos
754296241Sglebius	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
755296241Sglebius	    __func__);
756296232Savos	IEEE80211_UNLOCK(ic);
757296231Savos}
758275974Sadrian
759296231Savosstatic void
760296231Savosscan_end(struct ieee80211_scan_state *ss, int scandone)
761296231Savos{
762296231Savos	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
763296231Savos	struct ieee80211vap *vap = ss->ss_vap;
764296231Savos	struct ieee80211com *ic = ss->ss_ic;
765296231Savos
766296231Savos	IEEE80211_LOCK_ASSERT(ic);
767296231Savos
768275974Sadrian	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__);
769275974Sadrian
770296230Savos	if (ss_priv->ss_iflags & ISCAN_ABORT) {
771296230Savos		scan_done(ss, scandone);
772296230Savos		return;
773296230Savos	}
774170530Ssam
775191746Sthompsa	IEEE80211_UNLOCK(ic);
776191746Sthompsa	ic->ic_scan_end(ic);		/* notify driver */
777191746Sthompsa	IEEE80211_LOCK(ic);
778275964Sadrian	/* XXX scan state can change! Re-validate scan state! */
779170530Ssam
780191746Sthompsa	/*
781298995Spfg	 * Since a cancellation may have occurred during one of the
782232373Sadrian	 * driver calls (whilst unlocked), update scandone.
783232373Sadrian	 */
784296228Savos	if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
785232373Sadrian		/* XXX printf? */
786232373Sadrian		if_printf(vap->iv_ifp,
787275964Sadrian		    "%s: OOPS! scan cancelled during driver call (1)!\n",
788232373Sadrian		    __func__);
789275964Sadrian		scandone = 1;
790232373Sadrian	}
791232373Sadrian
792232373Sadrian	/*
793191746Sthompsa	 * Record scan complete time.  Note that we also do
794191746Sthompsa	 * this when canceled so any background scan will
795191746Sthompsa	 * not be restarted for a while.
796191746Sthompsa	 */
797191746Sthompsa	if (scandone)
798191746Sthompsa		ic->ic_lastscan = ticks;
799191746Sthompsa	/* return to the bss channel */
800191746Sthompsa	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
801191746Sthompsa	    ic->ic_curchan != ic->ic_bsschan) {
802191746Sthompsa		ieee80211_setupcurchan(ic, ic->ic_bsschan);
803191746Sthompsa		IEEE80211_UNLOCK(ic);
804191746Sthompsa		ic->ic_set_channel(ic);
805192468Ssam		ieee80211_radiotap_chan_change(ic);
806191746Sthompsa		IEEE80211_LOCK(ic);
807191746Sthompsa	}
808191746Sthompsa	/* clear internal flags and any indication of a pick */
809296228Savos	ss_priv->ss_iflags &= ~ISCAN_REP;
810191746Sthompsa	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
811191746Sthompsa
812191746Sthompsa	/*
813191746Sthompsa	 * If not canceled and scan completed, do post-processing.
814191746Sthompsa	 * If the callback function returns 0, then it wants to
815191746Sthompsa	 * continue/restart scanning.  Unfortunately we needed to
816191746Sthompsa	 * notify the driver to end the scan above to avoid having
817191746Sthompsa	 * rx frames alter the scan candidate list.
818191746Sthompsa	 */
819296228Savos	if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 &&
820191746Sthompsa	    !ss->ss_ops->scan_end(ss, vap) &&
821191746Sthompsa	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
822297405Sadrian	    ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
823191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
824191746Sthompsa		    "%s: done, restart "
825191746Sthompsa		    "[ticks %u, dwell min %lu scanend %lu]\n",
826191746Sthompsa		    __func__,
827296229Savos		    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
828298995Spfg		ss->ss_next = 0;	/* reset to beginning */
829191746Sthompsa		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
830191746Sthompsa			vap->iv_stats.is_scan_active++;
831191746Sthompsa		else
832191746Sthompsa			vap->iv_stats.is_scan_passive++;
833191746Sthompsa
834191746Sthompsa		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
835296232Savos		ieee80211_runtask(ic, &ss_priv->ss_scan_start);
836191746Sthompsa		IEEE80211_UNLOCK(ic);
837191746Sthompsa		return;
838191746Sthompsa	}
839191746Sthompsa
840191746Sthompsa	/* past here, scandone is ``true'' if not in bg mode */
841191746Sthompsa	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
842191746Sthompsa		scandone = 1;
843191746Sthompsa
844191746Sthompsa	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
845191746Sthompsa	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
846191746Sthompsa	    __func__, scandone ? "done" : "stopped",
847296229Savos	    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
848191746Sthompsa
849191746Sthompsa	/*
850298995Spfg	 * Since a cancellation may have occurred during one of the
851275964Sadrian	 * driver calls (whilst unlocked), update scandone.
852275964Sadrian	 */
853296228Savos	if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
854275964Sadrian		/* XXX printf? */
855275964Sadrian		if_printf(vap->iv_ifp,
856275964Sadrian		    "%s: OOPS! scan cancelled during driver call (2)!\n",
857275964Sadrian		    __func__);
858275964Sadrian		scandone = 1;
859275964Sadrian	}
860275964Sadrian
861296230Savos	scan_done(ss, scandone);
862296230Savos}
863296230Savos
864296230Savosstatic void
865296230Savosscan_done(struct ieee80211_scan_state *ss, int scandone)
866296230Savos{
867296230Savos	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
868296230Savos	struct ieee80211com *ic = ss->ss_ic;
869296230Savos	struct ieee80211vap *vap = ss->ss_vap;
870296230Savos
871296230Savos	IEEE80211_LOCK_ASSERT(ic);
872296230Savos
873275964Sadrian	/*
874191746Sthompsa	 * Clear the SCAN bit first in case frames are
875191746Sthompsa	 * pending on the station power save queue.  If
876191746Sthompsa	 * we defer this then the dispatch of the frames
877191746Sthompsa	 * may generate a request to cancel scanning.
878191746Sthompsa	 */
879191746Sthompsa	ic->ic_flags &= ~IEEE80211_F_SCAN;
880296230Savos
881191746Sthompsa	/*
882191746Sthompsa	 * Drop out of power save mode when a scan has
883191746Sthompsa	 * completed.  If this scan was prematurely terminated
884191746Sthompsa	 * because it is a background scan then don't notify
885191746Sthompsa	 * the ap; we'll either return to scanning after we
886191746Sthompsa	 * receive the beacon frame or we'll drop out of power
887191746Sthompsa	 * save mode because the beacon indicates we have frames
888191746Sthompsa	 * waiting for us.
889191746Sthompsa	 */
890191746Sthompsa	if (scandone) {
891330232Seadler		/*
892330232Seadler		 * If we're not a scan offload device, come back out of
893330232Seadler		 * station powersave.  Offload devices handle this themselves.
894330232Seadler		 */
895330232Seadler		if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0)
896330232Seadler			vap->iv_sta_ps(vap, 0);
897300383Savos		if (ss->ss_next >= ss->ss_last)
898191746Sthompsa			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
899300383Savos
900300383Savos		ieee80211_notify_scan_done(vap);
901170530Ssam	}
902296228Savos	ss_priv->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
903296229Savos	ss_priv->ss_scanend = 0;
904191746Sthompsa	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
905191746Sthompsa	IEEE80211_UNLOCK(ic);
906170530Ssam#undef ISCAN_REP
907170530Ssam}
908170530Ssam
909170530Ssam/*
910170530Ssam * Process a beacon or probe response frame.
911170530Ssam */
912284143Sadrianstatic void
913276730Sadrianieee80211_swscan_add_scan(struct ieee80211vap *vap,
914282742Sadrian	struct ieee80211_channel *curchan,
915170530Ssam	const struct ieee80211_scanparams *sp,
916170530Ssam	const struct ieee80211_frame *wh,
917192468Ssam	int subtype, int rssi, int noise)
918170530Ssam{
919178354Ssam	struct ieee80211com *ic = vap->iv_ic;
920170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
921170530Ssam
922178354Ssam	/* XXX locking */
923170530Ssam	/*
924170530Ssam	 * Frames received during startup are discarded to avoid
925170530Ssam	 * using scan state setup on the initial entry to the timer
926170530Ssam	 * callback.  This can occur because the device may enable
927170530Ssam	 * rx prior to our doing the initial channel change in the
928178354Ssam	 * timer routine.
929170530Ssam	 */
930170530Ssam	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
931170530Ssam		return;
932170530Ssam#ifdef IEEE80211_DEBUG
933178354Ssam	if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
934276730Sadrian		ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
935170530Ssam#endif
936170530Ssam	if (ss->ss_ops != NULL &&
937282742Sadrian	    ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) {
938170530Ssam		/*
939170530Ssam		 * If we've reached the min dwell time terminate
940170530Ssam		 * the timer so we'll switch to the next channel.
941170530Ssam		 */
942170530Ssam		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
943297405Sadrian		    ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
944178354Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
945170530Ssam			    "%s: chan %3d%c min dwell met (%u > %lu)\n",
946170530Ssam			    __func__,
947170530Ssam			    ieee80211_chan2ieee(ic, ic->ic_curchan),
948276757Sadrian			    ieee80211_channel_type_char(ic->ic_curchan),
949170530Ssam			    ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
950170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
951170530Ssam			/*
952170530Ssam			 * NB: trigger at next clock tick or wait for the
953178354Ssam			 * hardware.
954170530Ssam			 */
955178354Ssam			ic->ic_scan_mindwell(ss);
956170530Ssam		}
957170530Ssam	}
958170530Ssam}
959170530Ssam
960284143Sadrianstatic struct ieee80211_scan_methods swscan_methods = {
961284143Sadrian	.sc_attach = ieee80211_swscan_attach,
962284143Sadrian	.sc_detach = ieee80211_swscan_detach,
963284143Sadrian	.sc_vattach = ieee80211_swscan_vattach,
964284143Sadrian	.sc_vdetach = ieee80211_swscan_vdetach,
965284143Sadrian	.sc_set_scan_duration = ieee80211_swscan_set_scan_duration,
966284143Sadrian	.sc_start_scan = ieee80211_swscan_start_scan,
967284143Sadrian	.sc_check_scan = ieee80211_swscan_check_scan,
968284143Sadrian	.sc_bg_scan = ieee80211_swscan_bg_scan,
969284143Sadrian	.sc_cancel_scan = ieee80211_swscan_cancel_scan,
970284143Sadrian	.sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan,
971284143Sadrian	.sc_scan_next = ieee80211_swscan_scan_next,
972284143Sadrian	.sc_scan_done = ieee80211_swscan_scan_done,
973284143Sadrian	.sc_scan_probe_curchan = ieee80211_swscan_probe_curchan,
974284143Sadrian	.sc_add_scan = ieee80211_swscan_add_scan
975284143Sadrian};
976284143Sadrian
977284143Sadrian/*
978284143Sadrian * Default scan attach method.
979284143Sadrian */
980284143Sadrianvoid
981284143Sadrianieee80211_swscan_attach(struct ieee80211com *ic)
982284143Sadrian{
983284143Sadrian	struct scan_state *ss;
984284143Sadrian
985284143Sadrian	/*
986284143Sadrian	 * Setup the default methods
987284143Sadrian	 */
988284143Sadrian	ic->ic_scan_methods = &swscan_methods;
989284143Sadrian
990284143Sadrian	/* Allocate initial scan state */
991284143Sadrian	ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state),
992284143Sadrian		M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
993284143Sadrian	if (ss == NULL) {
994284143Sadrian		ic->ic_scan = NULL;
995284143Sadrian		return;
996284143Sadrian	}
997296232Savos	TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss);
998296232Savos	TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0,
999296232Savos	    scan_curchan_task, ss);
1000284143Sadrian
1001284143Sadrian	ic->ic_scan = &ss->base;
1002284143Sadrian	ss->base.ss_ic = ic;
1003284143Sadrian
1004284143Sadrian	ic->ic_scan_curchan = scan_curchan;
1005284143Sadrian	ic->ic_scan_mindwell = scan_mindwell;
1006284143Sadrian}
1007