xref: /haiku/src/libs/compat/freebsd_wlan/net80211/ieee80211_scan_sw.c (revision da3ca31854ae1f34c46d4d29e44837da03d8cafa)
1753c7e08SAugustin Cavalier /*-
2753c7e08SAugustin Cavalier  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3753c7e08SAugustin Cavalier  * All rights reserved.
4753c7e08SAugustin Cavalier  *
5753c7e08SAugustin Cavalier  * Redistribution and use in source and binary forms, with or without
6753c7e08SAugustin Cavalier  * modification, are permitted provided that the following conditions
7753c7e08SAugustin Cavalier  * are met:
8753c7e08SAugustin Cavalier  * 1. Redistributions of source code must retain the above copyright
9753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer.
10753c7e08SAugustin Cavalier  * 2. Redistributions in binary form must reproduce the above copyright
11753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer in the
12753c7e08SAugustin Cavalier  *    documentation and/or other materials provided with the distribution.
13753c7e08SAugustin Cavalier  *
14753c7e08SAugustin Cavalier  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15753c7e08SAugustin Cavalier  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16753c7e08SAugustin Cavalier  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17753c7e08SAugustin Cavalier  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18753c7e08SAugustin Cavalier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19753c7e08SAugustin Cavalier  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20753c7e08SAugustin Cavalier  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21753c7e08SAugustin Cavalier  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22753c7e08SAugustin Cavalier  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23753c7e08SAugustin Cavalier  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24753c7e08SAugustin Cavalier  */
25753c7e08SAugustin Cavalier 
26753c7e08SAugustin Cavalier #include <sys/cdefs.h>
27753c7e08SAugustin Cavalier /*
28753c7e08SAugustin Cavalier  * IEEE 802.11 scanning support.
29753c7e08SAugustin Cavalier  */
30753c7e08SAugustin Cavalier #include "opt_wlan.h"
31753c7e08SAugustin Cavalier 
32753c7e08SAugustin Cavalier #include <sys/param.h>
33753c7e08SAugustin Cavalier #include <sys/systm.h>
34753c7e08SAugustin Cavalier #include <sys/proc.h>
35753c7e08SAugustin Cavalier #include <sys/kernel.h>
36753c7e08SAugustin Cavalier #include <sys/malloc.h>
37753c7e08SAugustin Cavalier #include <sys/condvar.h>
38753c7e08SAugustin Cavalier 
39753c7e08SAugustin Cavalier #include <sys/socket.h>
40753c7e08SAugustin Cavalier 
41753c7e08SAugustin Cavalier #include <net/if.h>
42753c7e08SAugustin Cavalier #include <net/if_var.h>
43753c7e08SAugustin Cavalier #include <net/if_media.h>
4486021fd4SAugustin Cavalier #include <net/if_private.h>
45753c7e08SAugustin Cavalier #include <net/ethernet.h>
46753c7e08SAugustin Cavalier 
47753c7e08SAugustin Cavalier #include <net80211/ieee80211_var.h>
48753c7e08SAugustin Cavalier 
49753c7e08SAugustin Cavalier #include <net80211/ieee80211_scan_sw.h>
50753c7e08SAugustin Cavalier 
51753c7e08SAugustin Cavalier #include <net/bpf.h>
52753c7e08SAugustin Cavalier 
53753c7e08SAugustin Cavalier struct scan_state {
54753c7e08SAugustin Cavalier 	struct ieee80211_scan_state base;	/* public state */
55753c7e08SAugustin Cavalier 
56753c7e08SAugustin Cavalier 	u_int			ss_iflags;	/* flags used internally */
57753c7e08SAugustin Cavalier #define	ISCAN_MINDWELL 		0x0001		/* min dwell time reached */
58753c7e08SAugustin Cavalier #define	ISCAN_DISCARD		0x0002		/* discard rx'd frames */
598244a9baSAugustin Cavalier #define ISCAN_INTERRUPT		0x0004		/* interrupt current scan */
608244a9baSAugustin Cavalier #define	ISCAN_CANCEL		0x0008		/* cancel current scan */
618244a9baSAugustin Cavalier #define ISCAN_PAUSE		(ISCAN_INTERRUPT | ISCAN_CANCEL)
628244a9baSAugustin Cavalier #define	ISCAN_ABORT		0x0010		/* end the scan immediately */
638244a9baSAugustin Cavalier #define	ISCAN_RUNNING		0x0020		/* scan was started */
64753c7e08SAugustin Cavalier 
65753c7e08SAugustin Cavalier 	unsigned long		ss_chanmindwell;  /* min dwell on curchan */
66753c7e08SAugustin Cavalier 	unsigned long		ss_scanend;	/* time scan must stop */
67753c7e08SAugustin Cavalier 	u_int			ss_duration;	/* duration for next scan */
68753c7e08SAugustin Cavalier 	struct task		ss_scan_start;	/* scan start */
69753c7e08SAugustin Cavalier 	struct timeout_task	ss_scan_curchan;  /* scan execution */
70753c7e08SAugustin Cavalier };
71753c7e08SAugustin Cavalier #define	SCAN_PRIVATE(ss)	((struct scan_state *) ss)
72753c7e08SAugustin Cavalier 
73753c7e08SAugustin Cavalier /*
74753c7e08SAugustin Cavalier  * Amount of time to go off-channel during a background
75753c7e08SAugustin Cavalier  * scan.  This value should be large enough to catch most
76753c7e08SAugustin Cavalier  * ap's but short enough that we can return on-channel
77753c7e08SAugustin Cavalier  * before our listen interval expires.
78753c7e08SAugustin Cavalier  *
79753c7e08SAugustin Cavalier  * XXX tunable
80753c7e08SAugustin Cavalier  * XXX check against configured listen interval
81753c7e08SAugustin Cavalier  */
82753c7e08SAugustin Cavalier #define	IEEE80211_SCAN_OFFCHANNEL	msecs_to_ticks(150)
83753c7e08SAugustin Cavalier 
84753c7e08SAugustin Cavalier static	void scan_curchan(struct ieee80211_scan_state *, unsigned long);
85753c7e08SAugustin Cavalier static	void scan_mindwell(struct ieee80211_scan_state *);
86753c7e08SAugustin Cavalier static	void scan_signal(struct ieee80211_scan_state *, int);
87753c7e08SAugustin Cavalier static	void scan_signal_locked(struct ieee80211_scan_state *, int);
88753c7e08SAugustin Cavalier static	void scan_start(void *, int);
89753c7e08SAugustin Cavalier static	void scan_curchan_task(void *, int);
90753c7e08SAugustin Cavalier static	void scan_end(struct ieee80211_scan_state *, int);
91753c7e08SAugustin Cavalier static	void scan_done(struct ieee80211_scan_state *, int);
92753c7e08SAugustin Cavalier 
93753c7e08SAugustin Cavalier MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
94753c7e08SAugustin Cavalier 
95753c7e08SAugustin Cavalier static void
ieee80211_swscan_detach(struct ieee80211com * ic)96753c7e08SAugustin Cavalier ieee80211_swscan_detach(struct ieee80211com *ic)
97753c7e08SAugustin Cavalier {
98753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
99753c7e08SAugustin Cavalier 
100753c7e08SAugustin Cavalier 	if (ss != NULL) {
101753c7e08SAugustin Cavalier 		scan_signal(ss, ISCAN_ABORT);
102753c7e08SAugustin Cavalier 		ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
103753c7e08SAugustin Cavalier 		taskqueue_drain_timeout(ic->ic_tq,
104753c7e08SAugustin Cavalier 		    &SCAN_PRIVATE(ss)->ss_scan_curchan);
105753c7e08SAugustin Cavalier 		KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
106753c7e08SAugustin Cavalier 		    ("scan still running"));
107753c7e08SAugustin Cavalier 
108753c7e08SAugustin Cavalier 		/*
109753c7e08SAugustin Cavalier 		 * For now, do the ss_ops detach here rather
110753c7e08SAugustin Cavalier 		 * than ieee80211_scan_detach().
111753c7e08SAugustin Cavalier 		 *
112753c7e08SAugustin Cavalier 		 * I'll figure out how to cleanly split things up
113753c7e08SAugustin Cavalier 		 * at a later date.
114753c7e08SAugustin Cavalier 		 */
115753c7e08SAugustin Cavalier 		if (ss->ss_ops != NULL) {
116753c7e08SAugustin Cavalier 			ss->ss_ops->scan_detach(ss);
117753c7e08SAugustin Cavalier 			ss->ss_ops = NULL;
118753c7e08SAugustin Cavalier 		}
119753c7e08SAugustin Cavalier 		ic->ic_scan = NULL;
120753c7e08SAugustin Cavalier 		IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
121753c7e08SAugustin Cavalier 	}
122753c7e08SAugustin Cavalier }
123753c7e08SAugustin Cavalier 
124753c7e08SAugustin Cavalier static void
ieee80211_swscan_vattach(struct ieee80211vap * vap)125753c7e08SAugustin Cavalier ieee80211_swscan_vattach(struct ieee80211vap *vap)
126753c7e08SAugustin Cavalier {
127753c7e08SAugustin Cavalier 	/* nothing to do for now */
128753c7e08SAugustin Cavalier 	/*
129753c7e08SAugustin Cavalier 	 * TODO: all of the vap scan calls should be methods!
130753c7e08SAugustin Cavalier 	 */
131753c7e08SAugustin Cavalier 
132753c7e08SAugustin Cavalier }
133753c7e08SAugustin Cavalier 
134753c7e08SAugustin Cavalier static void
ieee80211_swscan_vdetach(struct ieee80211vap * vap)135753c7e08SAugustin Cavalier ieee80211_swscan_vdetach(struct ieee80211vap *vap)
136753c7e08SAugustin Cavalier {
137753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
138753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
139753c7e08SAugustin Cavalier 
140753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
141753c7e08SAugustin Cavalier 
142753c7e08SAugustin Cavalier 	if (ss != NULL && ss->ss_vap == vap &&
143753c7e08SAugustin Cavalier 	    (ic->ic_flags & IEEE80211_F_SCAN))
144753c7e08SAugustin Cavalier 		scan_signal_locked(ss, ISCAN_ABORT);
145753c7e08SAugustin Cavalier }
146753c7e08SAugustin Cavalier 
147753c7e08SAugustin Cavalier static void
ieee80211_swscan_set_scan_duration(struct ieee80211vap * vap,u_int duration)148753c7e08SAugustin Cavalier ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration)
149753c7e08SAugustin Cavalier {
150753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
151753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
152753c7e08SAugustin Cavalier 
153753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
154753c7e08SAugustin Cavalier 
155753c7e08SAugustin Cavalier 	/* NB: flush frames rx'd before 1st channel change */
156753c7e08SAugustin Cavalier 	SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
157753c7e08SAugustin Cavalier 	SCAN_PRIVATE(ss)->ss_duration = duration;
158753c7e08SAugustin Cavalier }
159753c7e08SAugustin Cavalier 
160753c7e08SAugustin Cavalier /*
161753c7e08SAugustin Cavalier  * Start a scan unless one is already going.
162753c7e08SAugustin Cavalier  */
163753c7e08SAugustin Cavalier static int
ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner * scan,struct ieee80211vap * vap,int flags,u_int duration,u_int mindwell,u_int maxdwell,u_int nssid,const struct ieee80211_scan_ssid ssids[])164753c7e08SAugustin Cavalier ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan,
165753c7e08SAugustin Cavalier 	struct ieee80211vap *vap, int flags, u_int duration,
166753c7e08SAugustin Cavalier 	u_int mindwell, u_int maxdwell,
167753c7e08SAugustin Cavalier 	u_int nssid, const struct ieee80211_scan_ssid ssids[])
168753c7e08SAugustin Cavalier {
169753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
170753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
171753c7e08SAugustin Cavalier 
172753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
173753c7e08SAugustin Cavalier 
174753c7e08SAugustin Cavalier 	if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
175753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
176753c7e08SAugustin Cavalier 		    "%s: scan inhibited by pending channel change\n", __func__);
177753c7e08SAugustin Cavalier 	} else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
178753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
179753c7e08SAugustin Cavalier 		    "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
180753c7e08SAugustin Cavalier 		    , __func__
181753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
182753c7e08SAugustin Cavalier 		    , duration, mindwell, maxdwell
183753c7e08SAugustin Cavalier 		    , ieee80211_phymode_name[vap->iv_des_mode]
184753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
185753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
186753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
187753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
188753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
189753c7e08SAugustin Cavalier 		    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
190753c7e08SAugustin Cavalier 		);
191753c7e08SAugustin Cavalier 
192753c7e08SAugustin Cavalier 		ieee80211_scan_update_locked(vap, scan);
193753c7e08SAugustin Cavalier 		if (ss->ss_ops != NULL) {
194753c7e08SAugustin Cavalier 			if ((flags & IEEE80211_SCAN_NOSSID) == 0)
195753c7e08SAugustin Cavalier 				ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
196753c7e08SAugustin Cavalier 
19786021fd4SAugustin Cavalier 			ss->ss_flags = flags & IEEE80211_SCAN_PUBLIC_MASK;
198753c7e08SAugustin Cavalier 			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
199753c7e08SAugustin Cavalier 				vap->iv_stats.is_scan_active++;
200753c7e08SAugustin Cavalier 			else
201753c7e08SAugustin Cavalier 				vap->iv_stats.is_scan_passive++;
202753c7e08SAugustin Cavalier 			if (flags & IEEE80211_SCAN_FLUSH)
203753c7e08SAugustin Cavalier 				ss->ss_ops->scan_flush(ss);
204753c7e08SAugustin Cavalier 			if (flags & IEEE80211_SCAN_BGSCAN)
205753c7e08SAugustin Cavalier 				ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
206753c7e08SAugustin Cavalier 
207753c7e08SAugustin Cavalier 			/* Set duration for this particular scan */
208753c7e08SAugustin Cavalier 			ieee80211_swscan_set_scan_duration(vap, duration);
209753c7e08SAugustin Cavalier 
210753c7e08SAugustin Cavalier 			ss->ss_next = 0;
211753c7e08SAugustin Cavalier 			ss->ss_mindwell = mindwell;
212753c7e08SAugustin Cavalier 			ss->ss_maxdwell = maxdwell;
213753c7e08SAugustin Cavalier 			/* NB: scan_start must be before the scan runtask */
214753c7e08SAugustin Cavalier 			ss->ss_ops->scan_start(ss, vap);
215753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
216753c7e08SAugustin Cavalier 			if (ieee80211_msg_scan(vap))
217753c7e08SAugustin Cavalier 				ieee80211_scan_dump(ss);
218753c7e08SAugustin Cavalier #endif /* IEEE80211_DEBUG */
219753c7e08SAugustin Cavalier 			ic->ic_flags |= IEEE80211_F_SCAN;
220753c7e08SAugustin Cavalier 
221753c7e08SAugustin Cavalier 			/* Start scan task */
222753c7e08SAugustin Cavalier 			ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
223753c7e08SAugustin Cavalier 		}
224753c7e08SAugustin Cavalier 		return 1;
225753c7e08SAugustin Cavalier 	} else {
226753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
227753c7e08SAugustin Cavalier 		    "%s: %s scan already in progress\n", __func__,
228753c7e08SAugustin Cavalier 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
229753c7e08SAugustin Cavalier 	}
230753c7e08SAugustin Cavalier 	return 0;
231753c7e08SAugustin Cavalier }
232753c7e08SAugustin Cavalier 
233753c7e08SAugustin Cavalier /*
234753c7e08SAugustin Cavalier  * Start a scan unless one is already going.
235753c7e08SAugustin Cavalier  *
236753c7e08SAugustin Cavalier  * Called without the comlock held; grab the comlock as appropriate.
237753c7e08SAugustin Cavalier  */
238753c7e08SAugustin Cavalier static int
ieee80211_swscan_start_scan(const struct ieee80211_scanner * scan,struct ieee80211vap * vap,int flags,u_int duration,u_int mindwell,u_int maxdwell,u_int nssid,const struct ieee80211_scan_ssid ssids[])239753c7e08SAugustin Cavalier ieee80211_swscan_start_scan(const struct ieee80211_scanner *scan,
240753c7e08SAugustin Cavalier     struct ieee80211vap *vap, int flags,
241753c7e08SAugustin Cavalier     u_int duration, u_int mindwell, u_int maxdwell,
242753c7e08SAugustin Cavalier     u_int nssid, const struct ieee80211_scan_ssid ssids[])
243753c7e08SAugustin Cavalier {
244753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
245753c7e08SAugustin Cavalier 	int result;
246753c7e08SAugustin Cavalier 
247753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK_ASSERT(ic);
248753c7e08SAugustin Cavalier 
249753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
250753c7e08SAugustin Cavalier 	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
251753c7e08SAugustin Cavalier 	    mindwell, maxdwell, nssid, ssids);
252753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
253753c7e08SAugustin Cavalier 
254753c7e08SAugustin Cavalier 	return result;
255753c7e08SAugustin Cavalier }
256753c7e08SAugustin Cavalier 
257753c7e08SAugustin Cavalier /*
258753c7e08SAugustin Cavalier  * Check the scan cache for an ap/channel to use; if that
259753c7e08SAugustin Cavalier  * fails then kick off a new scan.
260753c7e08SAugustin Cavalier  *
261753c7e08SAugustin Cavalier  * Called with the comlock held.
262753c7e08SAugustin Cavalier  *
263753c7e08SAugustin Cavalier  * XXX TODO: split out!
264753c7e08SAugustin Cavalier  */
265753c7e08SAugustin Cavalier static int
ieee80211_swscan_check_scan(const struct ieee80211_scanner * scan,struct ieee80211vap * vap,int flags,u_int duration,u_int mindwell,u_int maxdwell,u_int nssid,const struct ieee80211_scan_ssid ssids[])266753c7e08SAugustin Cavalier ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan,
267753c7e08SAugustin Cavalier     struct ieee80211vap *vap, int flags,
268753c7e08SAugustin Cavalier     u_int duration, u_int mindwell, u_int maxdwell,
269753c7e08SAugustin Cavalier     u_int nssid, const struct ieee80211_scan_ssid ssids[])
270753c7e08SAugustin Cavalier {
271753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
272753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
273753c7e08SAugustin Cavalier 	int result;
274753c7e08SAugustin Cavalier 
275753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
276753c7e08SAugustin Cavalier 
277753c7e08SAugustin Cavalier 	if (ss->ss_ops != NULL) {
278753c7e08SAugustin Cavalier 		/* XXX verify ss_ops matches vap->iv_opmode */
279753c7e08SAugustin Cavalier 		if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
280753c7e08SAugustin Cavalier 			/*
281753c7e08SAugustin Cavalier 			 * Update the ssid list and mark flags so if
282753c7e08SAugustin Cavalier 			 * we call start_scan it doesn't duplicate work.
283753c7e08SAugustin Cavalier 			 */
284753c7e08SAugustin Cavalier 			ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
285753c7e08SAugustin Cavalier 			flags |= IEEE80211_SCAN_NOSSID;
286753c7e08SAugustin Cavalier 		}
287753c7e08SAugustin Cavalier 		if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
288753c7e08SAugustin Cavalier 		    (flags & IEEE80211_SCAN_FLUSH) == 0 &&
289753c7e08SAugustin Cavalier 		    ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
290753c7e08SAugustin Cavalier 			/*
291753c7e08SAugustin Cavalier 			 * We're not currently scanning and the cache is
292753c7e08SAugustin Cavalier 			 * deemed hot enough to consult.  Lock out others
293753c7e08SAugustin Cavalier 			 * by marking IEEE80211_F_SCAN while we decide if
294753c7e08SAugustin Cavalier 			 * something is already in the scan cache we can
295753c7e08SAugustin Cavalier 			 * use.  Also discard any frames that might come
296753c7e08SAugustin Cavalier 			 * in while temporarily marked as scanning.
297753c7e08SAugustin Cavalier 			 */
2985cad57c4SJérôme Duval 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
2995cad57c4SJérôme Duval 			    "cache hot; ic_lastscan=%d, scanvalid=%d, ticks=%d\n",
3005cad57c4SJérôme Duval 			    ic->ic_lastscan,
3015cad57c4SJérôme Duval 			    vap->iv_scanvalid,
3025cad57c4SJérôme Duval 			    ticks);
303753c7e08SAugustin Cavalier 			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
304753c7e08SAugustin Cavalier 			ic->ic_flags |= IEEE80211_F_SCAN;
305753c7e08SAugustin Cavalier 
306753c7e08SAugustin Cavalier 			/* NB: need to use supplied flags in check */
30786021fd4SAugustin Cavalier 			ss->ss_flags = flags & IEEE80211_SCAN_PUBLIC_MASK;
308753c7e08SAugustin Cavalier 			result = ss->ss_ops->scan_end(ss, vap);
309753c7e08SAugustin Cavalier 
310753c7e08SAugustin Cavalier 			ic->ic_flags &= ~IEEE80211_F_SCAN;
311753c7e08SAugustin Cavalier 			SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD;
3125cad57c4SJérôme Duval 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
3135cad57c4SJérôme Duval 			    "%s: scan_end returned %d\n", __func__, result);
314753c7e08SAugustin Cavalier 			if (result) {
315753c7e08SAugustin Cavalier 				ieee80211_notify_scan_done(vap);
316753c7e08SAugustin Cavalier 				return 1;
317753c7e08SAugustin Cavalier 			}
318753c7e08SAugustin Cavalier 		}
319753c7e08SAugustin Cavalier 	}
320753c7e08SAugustin Cavalier 	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
321753c7e08SAugustin Cavalier 	    mindwell, maxdwell, nssid, ssids);
322753c7e08SAugustin Cavalier 
323753c7e08SAugustin Cavalier 	return result;
324753c7e08SAugustin Cavalier }
325753c7e08SAugustin Cavalier 
326753c7e08SAugustin Cavalier /*
327753c7e08SAugustin Cavalier  * Restart a previous scan.  If the previous scan completed
328753c7e08SAugustin Cavalier  * then we start again using the existing channel list.
329753c7e08SAugustin Cavalier  */
330753c7e08SAugustin Cavalier static int
ieee80211_swscan_bg_scan(const struct ieee80211_scanner * scan,struct ieee80211vap * vap,int flags)331753c7e08SAugustin Cavalier ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan,
332753c7e08SAugustin Cavalier     struct ieee80211vap *vap, int flags)
333753c7e08SAugustin Cavalier {
334753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
335753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
33686021fd4SAugustin Cavalier 	bool scanning;
337753c7e08SAugustin Cavalier 
338753c7e08SAugustin Cavalier 	/* XXX assert unlocked? */
339753c7e08SAugustin Cavalier 	// IEEE80211_UNLOCK_ASSERT(ic);
340753c7e08SAugustin Cavalier 
341753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
34286021fd4SAugustin Cavalier 	scanning = ic->ic_flags & IEEE80211_F_SCAN;
34386021fd4SAugustin Cavalier 	if (!scanning) {
344753c7e08SAugustin Cavalier 		u_int duration;
345753c7e08SAugustin Cavalier 		/*
346753c7e08SAugustin Cavalier 		 * Go off-channel for a fixed interval that is large
347753c7e08SAugustin Cavalier 		 * enough to catch most ap's but short enough that
348753c7e08SAugustin Cavalier 		 * we can return on-channel before our listen interval
349753c7e08SAugustin Cavalier 		 * expires.
350753c7e08SAugustin Cavalier 		 */
351753c7e08SAugustin Cavalier 		duration = IEEE80211_SCAN_OFFCHANNEL;
352753c7e08SAugustin Cavalier 
353753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
354753c7e08SAugustin Cavalier 		    "%s: %s scan, ticks %u duration %u\n", __func__,
355753c7e08SAugustin Cavalier 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
356753c7e08SAugustin Cavalier 		    ticks, duration);
357753c7e08SAugustin Cavalier 
358753c7e08SAugustin Cavalier 		ieee80211_scan_update_locked(vap, scan);
359753c7e08SAugustin Cavalier 		if (ss->ss_ops != NULL) {
360753c7e08SAugustin Cavalier 			ss->ss_vap = vap;
361753c7e08SAugustin Cavalier 			/*
362753c7e08SAugustin Cavalier 			 * A background scan does not select a new sta; it
363753c7e08SAugustin Cavalier 			 * just refreshes the scan cache.  Also, indicate
364753c7e08SAugustin Cavalier 			 * the scan logic should follow the beacon schedule:
365753c7e08SAugustin Cavalier 			 * we go off-channel and scan for a while, then
366753c7e08SAugustin Cavalier 			 * return to the bss channel to receive a beacon,
367753c7e08SAugustin Cavalier 			 * then go off-channel again.  All during this time
368753c7e08SAugustin Cavalier 			 * we notify the ap we're in power save mode.  When
369753c7e08SAugustin Cavalier 			 * the scan is complete we leave power save mode.
370753c7e08SAugustin Cavalier 			 * If any beacon indicates there are frames pending
371753c7e08SAugustin Cavalier 			 * for us then we drop out of power save mode
372753c7e08SAugustin Cavalier 			 * (and background scan) automatically by way of the
373753c7e08SAugustin Cavalier 			 * usual sta power save logic.
374753c7e08SAugustin Cavalier 			 */
375753c7e08SAugustin Cavalier 			ss->ss_flags |= IEEE80211_SCAN_NOPICK
376753c7e08SAugustin Cavalier 				     |  IEEE80211_SCAN_BGSCAN
377753c7e08SAugustin Cavalier 				     |  flags
378753c7e08SAugustin Cavalier 				     ;
379753c7e08SAugustin Cavalier 			/* if previous scan completed, restart */
380753c7e08SAugustin Cavalier 			if (ss->ss_next >= ss->ss_last) {
381753c7e08SAugustin Cavalier 				if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
382753c7e08SAugustin Cavalier 					vap->iv_stats.is_scan_active++;
383753c7e08SAugustin Cavalier 				else
384753c7e08SAugustin Cavalier 					vap->iv_stats.is_scan_passive++;
385753c7e08SAugustin Cavalier 				/*
386753c7e08SAugustin Cavalier 				 * NB: beware of the scan cache being flushed;
387753c7e08SAugustin Cavalier 				 *     if the channel list is empty use the
388753c7e08SAugustin Cavalier 				 *     scan_start method to populate it.
389753c7e08SAugustin Cavalier 				 */
390753c7e08SAugustin Cavalier 				ss->ss_next = 0;
39186021fd4SAugustin Cavalier 				if (ss->ss_last != 0) {
39286021fd4SAugustin Cavalier 					ieee80211_notify_scan_done(vap);
393753c7e08SAugustin Cavalier 					ss->ss_ops->scan_restart(ss, vap);
39486021fd4SAugustin Cavalier 				} else {
395753c7e08SAugustin Cavalier 					ss->ss_ops->scan_start(ss, vap);
396753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
397753c7e08SAugustin Cavalier 					if (ieee80211_msg_scan(vap))
398753c7e08SAugustin Cavalier 						ieee80211_scan_dump(ss);
399753c7e08SAugustin Cavalier #endif /* IEEE80211_DEBUG */
400753c7e08SAugustin Cavalier 				}
401753c7e08SAugustin Cavalier 			}
402753c7e08SAugustin Cavalier 			ieee80211_swscan_set_scan_duration(vap, duration);
403753c7e08SAugustin Cavalier 			ss->ss_maxdwell = duration;
404753c7e08SAugustin Cavalier 			ic->ic_flags |= IEEE80211_F_SCAN;
405753c7e08SAugustin Cavalier 			ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
406753c7e08SAugustin Cavalier 			ieee80211_runtask(ic,
407753c7e08SAugustin Cavalier 			    &SCAN_PRIVATE(ss)->ss_scan_start);
40886021fd4SAugustin Cavalier 			scanning = true;
409753c7e08SAugustin Cavalier 		} else {
410753c7e08SAugustin Cavalier 			/* XXX msg+stat */
411753c7e08SAugustin Cavalier 		}
412753c7e08SAugustin Cavalier 	} else {
413753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
414753c7e08SAugustin Cavalier 		    "%s: %s scan already in progress\n", __func__,
415753c7e08SAugustin Cavalier 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
416753c7e08SAugustin Cavalier 	}
417753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
418753c7e08SAugustin Cavalier 
41986021fd4SAugustin Cavalier 	return (scanning);
420753c7e08SAugustin Cavalier }
421753c7e08SAugustin Cavalier 
4228244a9baSAugustin Cavalier /*
4238244a9baSAugustin Cavalier  * Taskqueue work to cancel a scan.
4248244a9baSAugustin Cavalier  *
4258244a9baSAugustin Cavalier  * Note: for offload scan devices, we may want to call into the
4268244a9baSAugustin Cavalier  * driver to try and cancel scanning, however it may not be cancelable.
4278244a9baSAugustin Cavalier  */
428753c7e08SAugustin Cavalier static void
cancel_scan(struct ieee80211vap * vap,int any,const char * func)429753c7e08SAugustin Cavalier cancel_scan(struct ieee80211vap *vap, int any, const char *func)
430753c7e08SAugustin Cavalier {
431753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
432753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
4338244a9baSAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
4348244a9baSAugustin Cavalier 	int signal;
435753c7e08SAugustin Cavalier 
436753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
4378244a9baSAugustin Cavalier 	signal = any ? ISCAN_PAUSE : ISCAN_CANCEL;
438753c7e08SAugustin Cavalier 	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
439753c7e08SAugustin Cavalier 	    (any || ss->ss_vap == vap) &&
4408244a9baSAugustin Cavalier 	    (ss_priv->ss_iflags & signal) == 0) {
441753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
4428244a9baSAugustin Cavalier 		    "%s: %s %s scan\n", func,
4438244a9baSAugustin Cavalier 		    any ? "pause" : "cancel",
444753c7e08SAugustin Cavalier 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
445753c7e08SAugustin Cavalier 			"active" : "passive");
446753c7e08SAugustin Cavalier 
447753c7e08SAugustin Cavalier 		/* clear bg scan NOPICK */
448753c7e08SAugustin Cavalier 		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
4498244a9baSAugustin Cavalier 		/* mark request and wake up the scan task */
4508244a9baSAugustin Cavalier 		scan_signal_locked(ss, signal);
451753c7e08SAugustin Cavalier 	} else {
452753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
4538244a9baSAugustin Cavalier 		    "%s: called; F_SCAN=%d, vap=%s, signal=%d\n",
454753c7e08SAugustin Cavalier 			func,
455753c7e08SAugustin Cavalier 			!! (ic->ic_flags & IEEE80211_F_SCAN),
456753c7e08SAugustin Cavalier 			(ss->ss_vap == vap ? "match" : "nomatch"),
4578244a9baSAugustin Cavalier 			!! (ss_priv->ss_iflags & signal));
458753c7e08SAugustin Cavalier 	}
459753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
460753c7e08SAugustin Cavalier }
461753c7e08SAugustin Cavalier 
462753c7e08SAugustin Cavalier /*
463753c7e08SAugustin Cavalier  * Cancel any scan currently going on for the specified vap.
464753c7e08SAugustin Cavalier  */
465753c7e08SAugustin Cavalier static void
ieee80211_swscan_cancel_scan(struct ieee80211vap * vap)466753c7e08SAugustin Cavalier ieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
467753c7e08SAugustin Cavalier {
468753c7e08SAugustin Cavalier 	cancel_scan(vap, 0, __func__);
469753c7e08SAugustin Cavalier }
470753c7e08SAugustin Cavalier 
471753c7e08SAugustin Cavalier /*
472753c7e08SAugustin Cavalier  * Cancel any scan currently going on.
473753c7e08SAugustin Cavalier  */
474753c7e08SAugustin Cavalier static void
ieee80211_swscan_cancel_anyscan(struct ieee80211vap * vap)475753c7e08SAugustin Cavalier ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap)
476753c7e08SAugustin Cavalier {
4778244a9baSAugustin Cavalier 
4788244a9baSAugustin Cavalier 	/* XXX for now - just don't do this per packet. */
4798244a9baSAugustin Cavalier 	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
4808244a9baSAugustin Cavalier 		return;
4818244a9baSAugustin Cavalier 
482753c7e08SAugustin Cavalier 	cancel_scan(vap, 1, __func__);
483753c7e08SAugustin Cavalier }
484753c7e08SAugustin Cavalier 
485753c7e08SAugustin Cavalier /*
486753c7e08SAugustin Cavalier  * Manually switch to the next channel in the channel list.
487753c7e08SAugustin Cavalier  * Provided for drivers that manage scanning themselves
488753c7e08SAugustin Cavalier  * (e.g. for firmware-based devices).
489753c7e08SAugustin Cavalier  */
490753c7e08SAugustin Cavalier static void
ieee80211_swscan_scan_next(struct ieee80211vap * vap)491753c7e08SAugustin Cavalier ieee80211_swscan_scan_next(struct ieee80211vap *vap)
492753c7e08SAugustin Cavalier {
493753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
494753c7e08SAugustin Cavalier 
495753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
496753c7e08SAugustin Cavalier 
497753c7e08SAugustin Cavalier 	/* wake up the scan task */
498753c7e08SAugustin Cavalier 	scan_signal(ss, 0);
499753c7e08SAugustin Cavalier }
500753c7e08SAugustin Cavalier 
501753c7e08SAugustin Cavalier /*
502753c7e08SAugustin Cavalier  * Manually stop a scan that is currently running.
503753c7e08SAugustin Cavalier  * Provided for drivers that are not able to scan single channels
504753c7e08SAugustin Cavalier  * (e.g. for firmware-based devices).
505753c7e08SAugustin Cavalier  */
506753c7e08SAugustin Cavalier static void
ieee80211_swscan_scan_done(struct ieee80211vap * vap)507753c7e08SAugustin Cavalier ieee80211_swscan_scan_done(struct ieee80211vap *vap)
508753c7e08SAugustin Cavalier {
509753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
510753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
511753c7e08SAugustin Cavalier 
512753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
513753c7e08SAugustin Cavalier 
514753c7e08SAugustin Cavalier 	scan_signal_locked(ss, 0);
515753c7e08SAugustin Cavalier }
516753c7e08SAugustin Cavalier 
517753c7e08SAugustin Cavalier /*
518753c7e08SAugustin Cavalier  * Probe the current channel, if allowed, while scanning.
519753c7e08SAugustin Cavalier  * If the channel is not marked passive-only then send
520753c7e08SAugustin Cavalier  * a probe request immediately.  Otherwise mark state and
521753c7e08SAugustin Cavalier  * listen for beacons on the channel; if we receive something
522753c7e08SAugustin Cavalier  * then we'll transmit a probe request.
523753c7e08SAugustin Cavalier  */
524753c7e08SAugustin Cavalier static void
ieee80211_swscan_probe_curchan(struct ieee80211vap * vap,bool force __unused)525*da3ca318SAugustin Cavalier ieee80211_swscan_probe_curchan(struct ieee80211vap *vap, bool force __unused)
526753c7e08SAugustin Cavalier {
527753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
528753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
529753c7e08SAugustin Cavalier 	struct ifnet *ifp = vap->iv_ifp;
530753c7e08SAugustin Cavalier 	int i;
531753c7e08SAugustin Cavalier 
532753c7e08SAugustin Cavalier 	/*
5338244a9baSAugustin Cavalier 	 * Full-offload scan devices don't require this.
5348244a9baSAugustin Cavalier 	 */
5358244a9baSAugustin Cavalier 	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
5368244a9baSAugustin Cavalier 		return;
5378244a9baSAugustin Cavalier 
5388244a9baSAugustin Cavalier 	/*
539753c7e08SAugustin Cavalier 	 * Send directed probe requests followed by any
540753c7e08SAugustin Cavalier 	 * broadcast probe request.
541753c7e08SAugustin Cavalier 	 * XXX remove dependence on ic/vap->iv_bss
542753c7e08SAugustin Cavalier 	 */
543753c7e08SAugustin Cavalier 	for (i = 0; i < ss->ss_nssid; i++)
544753c7e08SAugustin Cavalier 		ieee80211_send_probereq(vap->iv_bss,
545753c7e08SAugustin Cavalier 			vap->iv_myaddr, ifp->if_broadcastaddr,
546753c7e08SAugustin Cavalier 			ifp->if_broadcastaddr,
547753c7e08SAugustin Cavalier 			ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
548753c7e08SAugustin Cavalier 	if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
549753c7e08SAugustin Cavalier 		ieee80211_send_probereq(vap->iv_bss,
550753c7e08SAugustin Cavalier 			vap->iv_myaddr, ifp->if_broadcastaddr,
551753c7e08SAugustin Cavalier 			ifp->if_broadcastaddr,
552753c7e08SAugustin Cavalier 			"", 0);
553753c7e08SAugustin Cavalier }
554753c7e08SAugustin Cavalier 
555753c7e08SAugustin Cavalier /*
556753c7e08SAugustin Cavalier  * Scan curchan.  If this is an active scan and the channel
557753c7e08SAugustin Cavalier  * is not marked passive then send probe request frame(s).
558753c7e08SAugustin Cavalier  * Arrange for the channel change after maxdwell ticks.
559753c7e08SAugustin Cavalier  */
560753c7e08SAugustin Cavalier static void
scan_curchan(struct ieee80211_scan_state * ss,unsigned long maxdwell)561753c7e08SAugustin Cavalier scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
562753c7e08SAugustin Cavalier {
563753c7e08SAugustin Cavalier 	struct ieee80211vap *vap  = ss->ss_vap;
564753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
565753c7e08SAugustin Cavalier 
566753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
567753c7e08SAugustin Cavalier 	    "%s: calling; maxdwell=%lu\n",
568753c7e08SAugustin Cavalier 	    __func__,
569753c7e08SAugustin Cavalier 	    maxdwell);
570753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
571753c7e08SAugustin Cavalier 	if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
572*da3ca318SAugustin Cavalier 		ieee80211_probe_curchan(vap, false);
573753c7e08SAugustin Cavalier 	taskqueue_enqueue_timeout(ic->ic_tq,
574753c7e08SAugustin Cavalier 	    &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell);
575753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
576753c7e08SAugustin Cavalier }
577753c7e08SAugustin Cavalier 
578753c7e08SAugustin Cavalier static void
scan_signal(struct ieee80211_scan_state * ss,int iflags)579753c7e08SAugustin Cavalier scan_signal(struct ieee80211_scan_state *ss, int iflags)
580753c7e08SAugustin Cavalier {
581753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
582753c7e08SAugustin Cavalier 
583753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK_ASSERT(ic);
584753c7e08SAugustin Cavalier 
585753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
586753c7e08SAugustin Cavalier 	scan_signal_locked(ss, iflags);
587753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
588753c7e08SAugustin Cavalier }
589753c7e08SAugustin Cavalier 
590753c7e08SAugustin Cavalier static void
scan_signal_locked(struct ieee80211_scan_state * ss,int iflags)591753c7e08SAugustin Cavalier scan_signal_locked(struct ieee80211_scan_state *ss, int iflags)
592753c7e08SAugustin Cavalier {
593753c7e08SAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
594753c7e08SAugustin Cavalier 	struct timeout_task *scan_task = &ss_priv->ss_scan_curchan;
595753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
596753c7e08SAugustin Cavalier 
597753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
598753c7e08SAugustin Cavalier 
599753c7e08SAugustin Cavalier 	ss_priv->ss_iflags |= iflags;
600753c7e08SAugustin Cavalier 	if (ss_priv->ss_iflags & ISCAN_RUNNING) {
601753c7e08SAugustin Cavalier 		if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0)
602753c7e08SAugustin Cavalier 			taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0);
603753c7e08SAugustin Cavalier 	}
604753c7e08SAugustin Cavalier }
605753c7e08SAugustin Cavalier 
606753c7e08SAugustin Cavalier /*
607753c7e08SAugustin Cavalier  * Handle mindwell requirements completed; initiate a channel
608753c7e08SAugustin Cavalier  * change to the next channel asap.
609753c7e08SAugustin Cavalier  */
610753c7e08SAugustin Cavalier static void
scan_mindwell(struct ieee80211_scan_state * ss)611753c7e08SAugustin Cavalier scan_mindwell(struct ieee80211_scan_state *ss)
612753c7e08SAugustin Cavalier {
613753c7e08SAugustin Cavalier 
614753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n",
615753c7e08SAugustin Cavalier 	    __func__);
616753c7e08SAugustin Cavalier 
617753c7e08SAugustin Cavalier 	scan_signal(ss, 0);
618753c7e08SAugustin Cavalier }
619753c7e08SAugustin Cavalier 
620753c7e08SAugustin Cavalier static void
scan_start(void * arg,int pending)621753c7e08SAugustin Cavalier scan_start(void *arg, int pending)
622753c7e08SAugustin Cavalier {
623753c7e08SAugustin Cavalier #define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
624753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
625753c7e08SAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
626753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ss->ss_vap;
627753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
628753c7e08SAugustin Cavalier 
629753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
630753c7e08SAugustin Cavalier 	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
631753c7e08SAugustin Cavalier 	    (ss_priv->ss_iflags & ISCAN_ABORT)) {
632753c7e08SAugustin Cavalier 		/* Cancelled before we started */
633753c7e08SAugustin Cavalier 		scan_done(ss, 0);
634753c7e08SAugustin Cavalier 		return;
635753c7e08SAugustin Cavalier 	}
636753c7e08SAugustin Cavalier 
637753c7e08SAugustin Cavalier 	if (ss->ss_next == ss->ss_last) {
638753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
639753c7e08SAugustin Cavalier 			"%s: no channels to scan\n", __func__);
640753c7e08SAugustin Cavalier 		scan_done(ss, 1);
641753c7e08SAugustin Cavalier 		return;
642753c7e08SAugustin Cavalier 	}
643753c7e08SAugustin Cavalier 
6448244a9baSAugustin Cavalier 	/*
6458244a9baSAugustin Cavalier 	 * Put the station into power save mode.
6468244a9baSAugustin Cavalier 	 *
6478244a9baSAugustin Cavalier 	 * This is only required if we're not a full-offload devices;
6488244a9baSAugustin Cavalier 	 * those devices manage scan/traffic differently.
6498244a9baSAugustin Cavalier 	 */
6508244a9baSAugustin Cavalier 	if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) &&
6518244a9baSAugustin Cavalier 	    vap->iv_opmode == IEEE80211_M_STA &&
652753c7e08SAugustin Cavalier 	    vap->iv_state == IEEE80211_S_RUN) {
653753c7e08SAugustin Cavalier 		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
654753c7e08SAugustin Cavalier 			/* Enable station power save mode */
655753c7e08SAugustin Cavalier 			vap->iv_sta_ps(vap, 1);
656753c7e08SAugustin Cavalier 			/* Wait until null data frame will be ACK'ed */
6578244a9baSAugustin Cavalier 			mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
658753c7e08SAugustin Cavalier 			    "sta_ps", msecs_to_ticks(10));
659753c7e08SAugustin Cavalier 			if (ss_priv->ss_iflags & ISCAN_ABORT) {
660753c7e08SAugustin Cavalier 				scan_done(ss, 0);
661753c7e08SAugustin Cavalier 				return;
662753c7e08SAugustin Cavalier 			}
663753c7e08SAugustin Cavalier 		}
664753c7e08SAugustin Cavalier 	}
665753c7e08SAugustin Cavalier 
666753c7e08SAugustin Cavalier 	ss_priv->ss_scanend = ticks + ss_priv->ss_duration;
667753c7e08SAugustin Cavalier 
668753c7e08SAugustin Cavalier 	/* XXX scan state can change! Re-validate scan state! */
669753c7e08SAugustin Cavalier 
670753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
671753c7e08SAugustin Cavalier 
672753c7e08SAugustin Cavalier 	ic->ic_scan_start(ic);		/* notify driver */
673753c7e08SAugustin Cavalier 
674753c7e08SAugustin Cavalier 	scan_curchan_task(ss, 0);
675753c7e08SAugustin Cavalier }
676753c7e08SAugustin Cavalier 
677753c7e08SAugustin Cavalier static void
scan_curchan_task(void * arg,int pending __unused)67886021fd4SAugustin Cavalier scan_curchan_task(void *arg, int pending __unused)
679753c7e08SAugustin Cavalier {
680753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = arg;
681753c7e08SAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
682753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
683753c7e08SAugustin Cavalier 	struct ieee80211_channel *chan;
684753c7e08SAugustin Cavalier 	unsigned long maxdwell;
6855cad57c4SJérôme Duval 	int scandone, scanstop;
686753c7e08SAugustin Cavalier 
687753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
688753c7e08SAugustin Cavalier end:
6895cad57c4SJérôme Duval 	/*
6905cad57c4SJérôme Duval 	 * Note: only /end/ the scan if we're CANCEL rather than
6915cad57c4SJérôme Duval 	 * CANCEL+INTERRUPT (ie, 'PAUSE').
6925cad57c4SJérôme Duval 	 *
6935cad57c4SJérôme Duval 	 * We can stop the scan if we hit cancel, but we shouldn't
6945cad57c4SJérôme Duval 	 * call scan_end(ss, 1) if we're just PAUSEing the scan.
6955cad57c4SJérôme Duval 	 */
696753c7e08SAugustin Cavalier 	scandone = (ss->ss_next >= ss->ss_last) ||
6975cad57c4SJérôme Duval 	    ((ss_priv->ss_iflags & ISCAN_PAUSE) == ISCAN_CANCEL);
6985cad57c4SJérôme Duval 	scanstop = (ss->ss_next >= ss->ss_last) ||
6995cad57c4SJérôme Duval 	    ((ss_priv->ss_iflags & ISCAN_CANCEL) != 0);
700753c7e08SAugustin Cavalier 
701753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
7025cad57c4SJérôme Duval 	    "%s: loop start; scandone=%d, scanstop=%d, ss_iflags=0x%x, ss_next=%u, ss_last=%u\n",
703753c7e08SAugustin Cavalier 	    __func__,
7045cad57c4SJérôme Duval 	    scandone,
7055cad57c4SJérôme Duval 	    scanstop,
7065cad57c4SJérôme Duval 	    (uint32_t) ss_priv->ss_iflags,
7075cad57c4SJérôme Duval 	    (uint32_t) ss->ss_next,
7085cad57c4SJérôme Duval 	    (uint32_t) ss->ss_last);
709753c7e08SAugustin Cavalier 
7105cad57c4SJérôme Duval 	if (scanstop || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
711753c7e08SAugustin Cavalier 	    (ss_priv->ss_iflags & ISCAN_ABORT) ||
712753c7e08SAugustin Cavalier 	     ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
713753c7e08SAugustin Cavalier 		ss_priv->ss_iflags &= ~ISCAN_RUNNING;
714753c7e08SAugustin Cavalier 		scan_end(ss, scandone);
715753c7e08SAugustin Cavalier 		return;
716753c7e08SAugustin Cavalier 	} else
717753c7e08SAugustin Cavalier 		ss_priv->ss_iflags |= ISCAN_RUNNING;
718753c7e08SAugustin Cavalier 
719753c7e08SAugustin Cavalier 	chan = ss->ss_chans[ss->ss_next++];
720753c7e08SAugustin Cavalier 
721753c7e08SAugustin Cavalier 	/*
722753c7e08SAugustin Cavalier 	 * Watch for truncation due to the scan end time.
723753c7e08SAugustin Cavalier 	 */
724753c7e08SAugustin Cavalier 	if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
725753c7e08SAugustin Cavalier 		maxdwell = ss_priv->ss_scanend - ticks;
726753c7e08SAugustin Cavalier 	else
727753c7e08SAugustin Cavalier 		maxdwell = ss->ss_maxdwell;
728753c7e08SAugustin Cavalier 
729753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
730753c7e08SAugustin Cavalier 	    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
731753c7e08SAugustin Cavalier 	    __func__,
732753c7e08SAugustin Cavalier 	    ieee80211_chan2ieee(ic, ic->ic_curchan),
733753c7e08SAugustin Cavalier 	    ieee80211_channel_type_char(ic->ic_curchan),
734753c7e08SAugustin Cavalier 	    ieee80211_chan2ieee(ic, chan),
735753c7e08SAugustin Cavalier 	    ieee80211_channel_type_char(chan),
736753c7e08SAugustin Cavalier 	    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
737753c7e08SAugustin Cavalier 		(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
738753c7e08SAugustin Cavalier 		"active" : "passive",
739753c7e08SAugustin Cavalier 	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
740753c7e08SAugustin Cavalier 
741753c7e08SAugustin Cavalier 	/*
742753c7e08SAugustin Cavalier 	 * Potentially change channel and phy mode.
743753c7e08SAugustin Cavalier 	 */
744753c7e08SAugustin Cavalier 	ic->ic_curchan = chan;
745753c7e08SAugustin Cavalier 	ic->ic_rt = ieee80211_get_ratetable(chan);
746753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
747753c7e08SAugustin Cavalier 	/*
748753c7e08SAugustin Cavalier 	 * Perform the channel change and scan unlocked so the driver
749753c7e08SAugustin Cavalier 	 * may sleep. Once set_channel returns the hardware has
750753c7e08SAugustin Cavalier 	 * completed the channel change.
751753c7e08SAugustin Cavalier 	 */
752753c7e08SAugustin Cavalier 	ic->ic_set_channel(ic);
753753c7e08SAugustin Cavalier 	ieee80211_radiotap_chan_change(ic);
754753c7e08SAugustin Cavalier 
755753c7e08SAugustin Cavalier 	/*
756753c7e08SAugustin Cavalier 	 * Scan curchan.  Drivers for "intelligent hardware"
757753c7e08SAugustin Cavalier 	 * override ic_scan_curchan to tell the device to do
758753c7e08SAugustin Cavalier 	 * the work.  Otherwise we manage the work ourselves;
759753c7e08SAugustin Cavalier 	 * sending a probe request (as needed), and arming the
760753c7e08SAugustin Cavalier 	 * timeout to switch channels after maxdwell ticks.
761753c7e08SAugustin Cavalier 	 *
762753c7e08SAugustin Cavalier 	 * scan_curchan should only pause for the time required to
763753c7e08SAugustin Cavalier 	 * prepare/initiate the hardware for the scan (if at all).
764753c7e08SAugustin Cavalier 	 */
765753c7e08SAugustin Cavalier 	ic->ic_scan_curchan(ss, maxdwell);
766753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
767753c7e08SAugustin Cavalier 
768753c7e08SAugustin Cavalier 	/* XXX scan state can change! Re-validate scan state! */
769753c7e08SAugustin Cavalier 
770753c7e08SAugustin Cavalier 	ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
771753c7e08SAugustin Cavalier 	/* clear mindwell lock and initial channel change flush */
772753c7e08SAugustin Cavalier 	ss_priv->ss_iflags &= ~ISCAN_REP;
773753c7e08SAugustin Cavalier 
774753c7e08SAugustin Cavalier 	if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
775753c7e08SAugustin Cavalier 		taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
776753c7e08SAugustin Cavalier 		    NULL);
777753c7e08SAugustin Cavalier 		goto end;
778753c7e08SAugustin Cavalier 	}
779753c7e08SAugustin Cavalier 
780753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
781753c7e08SAugustin Cavalier 	    __func__);
782753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
783753c7e08SAugustin Cavalier }
784753c7e08SAugustin Cavalier 
785753c7e08SAugustin Cavalier static void
scan_end(struct ieee80211_scan_state * ss,int scandone)786753c7e08SAugustin Cavalier scan_end(struct ieee80211_scan_state *ss, int scandone)
787753c7e08SAugustin Cavalier {
788753c7e08SAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
789753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ss->ss_vap;
790753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
791753c7e08SAugustin Cavalier 
792753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
793753c7e08SAugustin Cavalier 
794753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__);
795753c7e08SAugustin Cavalier 
796753c7e08SAugustin Cavalier 	if (ss_priv->ss_iflags & ISCAN_ABORT) {
797753c7e08SAugustin Cavalier 		scan_done(ss, scandone);
798753c7e08SAugustin Cavalier 		return;
799753c7e08SAugustin Cavalier 	}
800753c7e08SAugustin Cavalier 
801753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
802753c7e08SAugustin Cavalier 	ic->ic_scan_end(ic);		/* notify driver */
803753c7e08SAugustin Cavalier 	IEEE80211_LOCK(ic);
804753c7e08SAugustin Cavalier 	/* XXX scan state can change! Re-validate scan state! */
805753c7e08SAugustin Cavalier 
806753c7e08SAugustin Cavalier 	/*
807753c7e08SAugustin Cavalier 	 * Since a cancellation may have occurred during one of the
808753c7e08SAugustin Cavalier 	 * driver calls (whilst unlocked), update scandone.
809753c7e08SAugustin Cavalier 	 */
8105cad57c4SJérôme Duval 	if ((scandone == 0) && ((ss_priv->ss_iflags & ISCAN_PAUSE) == ISCAN_CANCEL)) {
811753c7e08SAugustin Cavalier 		/* XXX printf? */
812753c7e08SAugustin Cavalier 		if_printf(vap->iv_ifp,
8135cad57c4SJérôme Duval 		    "%s: OOPS! scan cancelled during driver call (1) (ss_iflags=0x%x)!\n",
8145cad57c4SJérôme Duval 		    __func__,
8155cad57c4SJérôme Duval 		    ss_priv->ss_iflags);
816753c7e08SAugustin Cavalier 		scandone = 1;
817753c7e08SAugustin Cavalier 	}
818753c7e08SAugustin Cavalier 
819753c7e08SAugustin Cavalier 	/*
820753c7e08SAugustin Cavalier 	 * Record scan complete time.  Note that we also do
821753c7e08SAugustin Cavalier 	 * this when canceled so any background scan will
822753c7e08SAugustin Cavalier 	 * not be restarted for a while.
823753c7e08SAugustin Cavalier 	 */
824753c7e08SAugustin Cavalier 	if (scandone)
825753c7e08SAugustin Cavalier 		ic->ic_lastscan = ticks;
826753c7e08SAugustin Cavalier 	/* return to the bss channel */
827753c7e08SAugustin Cavalier 	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
828753c7e08SAugustin Cavalier 	    ic->ic_curchan != ic->ic_bsschan) {
829753c7e08SAugustin Cavalier 		ieee80211_setupcurchan(ic, ic->ic_bsschan);
830753c7e08SAugustin Cavalier 		IEEE80211_UNLOCK(ic);
831753c7e08SAugustin Cavalier 		ic->ic_set_channel(ic);
832753c7e08SAugustin Cavalier 		ieee80211_radiotap_chan_change(ic);
833753c7e08SAugustin Cavalier 		IEEE80211_LOCK(ic);
834753c7e08SAugustin Cavalier 	}
835753c7e08SAugustin Cavalier 	/* clear internal flags and any indication of a pick */
836753c7e08SAugustin Cavalier 	ss_priv->ss_iflags &= ~ISCAN_REP;
837753c7e08SAugustin Cavalier 	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
838753c7e08SAugustin Cavalier 
839753c7e08SAugustin Cavalier 	/*
840753c7e08SAugustin Cavalier 	 * If not canceled and scan completed, do post-processing.
841753c7e08SAugustin Cavalier 	 * If the callback function returns 0, then it wants to
842753c7e08SAugustin Cavalier 	 * continue/restart scanning.  Unfortunately we needed to
843753c7e08SAugustin Cavalier 	 * notify the driver to end the scan above to avoid having
844753c7e08SAugustin Cavalier 	 * rx frames alter the scan candidate list.
845753c7e08SAugustin Cavalier 	 */
846753c7e08SAugustin Cavalier 	if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 &&
847753c7e08SAugustin Cavalier 	    !ss->ss_ops->scan_end(ss, vap) &&
848753c7e08SAugustin Cavalier 	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
849753c7e08SAugustin Cavalier 	    ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
850753c7e08SAugustin Cavalier 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
851753c7e08SAugustin Cavalier 		    "%s: done, restart "
852753c7e08SAugustin Cavalier 		    "[ticks %u, dwell min %lu scanend %lu]\n",
853753c7e08SAugustin Cavalier 		    __func__,
854753c7e08SAugustin Cavalier 		    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
855753c7e08SAugustin Cavalier 		ss->ss_next = 0;	/* reset to beginning */
856753c7e08SAugustin Cavalier 		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
857753c7e08SAugustin Cavalier 			vap->iv_stats.is_scan_active++;
858753c7e08SAugustin Cavalier 		else
859753c7e08SAugustin Cavalier 			vap->iv_stats.is_scan_passive++;
860753c7e08SAugustin Cavalier 
86186021fd4SAugustin Cavalier 		ieee80211_notify_scan_done(vap);
862753c7e08SAugustin Cavalier 		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
863753c7e08SAugustin Cavalier 		ieee80211_runtask(ic, &ss_priv->ss_scan_start);
864753c7e08SAugustin Cavalier 		IEEE80211_UNLOCK(ic);
865753c7e08SAugustin Cavalier 		return;
866753c7e08SAugustin Cavalier 	}
867753c7e08SAugustin Cavalier 
868753c7e08SAugustin Cavalier 	/* past here, scandone is ``true'' if not in bg mode */
869753c7e08SAugustin Cavalier 	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
870753c7e08SAugustin Cavalier 		scandone = 1;
871753c7e08SAugustin Cavalier 
872753c7e08SAugustin Cavalier 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
873753c7e08SAugustin Cavalier 	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
874753c7e08SAugustin Cavalier 	    __func__, scandone ? "done" : "stopped",
875753c7e08SAugustin Cavalier 	    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
876753c7e08SAugustin Cavalier 
877753c7e08SAugustin Cavalier 	/*
878753c7e08SAugustin Cavalier 	 * Since a cancellation may have occurred during one of the
879753c7e08SAugustin Cavalier 	 * driver calls (whilst unlocked), update scandone.
880753c7e08SAugustin Cavalier 	 */
8815cad57c4SJérôme Duval 	if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_PAUSE) == ISCAN_CANCEL) {
882753c7e08SAugustin Cavalier 		/* XXX printf? */
883753c7e08SAugustin Cavalier 		if_printf(vap->iv_ifp,
8845cad57c4SJérôme Duval 		    "%s: OOPS! scan cancelled during driver call (2) (ss_iflags=0x%x)!\n",
8855cad57c4SJérôme Duval 		    __func__,
8865cad57c4SJérôme Duval 		    ss_priv->ss_iflags);
887753c7e08SAugustin Cavalier 		scandone = 1;
888753c7e08SAugustin Cavalier 	}
889753c7e08SAugustin Cavalier 
890753c7e08SAugustin Cavalier 	scan_done(ss, scandone);
891753c7e08SAugustin Cavalier }
892753c7e08SAugustin Cavalier 
893753c7e08SAugustin Cavalier static void
scan_done(struct ieee80211_scan_state * ss,int scandone)894753c7e08SAugustin Cavalier scan_done(struct ieee80211_scan_state *ss, int scandone)
895753c7e08SAugustin Cavalier {
896753c7e08SAugustin Cavalier 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
897753c7e08SAugustin Cavalier 	struct ieee80211com *ic = ss->ss_ic;
898753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = ss->ss_vap;
899753c7e08SAugustin Cavalier 
900753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
901753c7e08SAugustin Cavalier 
902753c7e08SAugustin Cavalier 	/*
903753c7e08SAugustin Cavalier 	 * Clear the SCAN bit first in case frames are
904753c7e08SAugustin Cavalier 	 * pending on the station power save queue.  If
905753c7e08SAugustin Cavalier 	 * we defer this then the dispatch of the frames
906753c7e08SAugustin Cavalier 	 * may generate a request to cancel scanning.
907753c7e08SAugustin Cavalier 	 */
908753c7e08SAugustin Cavalier 	ic->ic_flags &= ~IEEE80211_F_SCAN;
909753c7e08SAugustin Cavalier 
910753c7e08SAugustin Cavalier 	/*
911753c7e08SAugustin Cavalier 	 * Drop out of power save mode when a scan has
912753c7e08SAugustin Cavalier 	 * completed.  If this scan was prematurely terminated
913753c7e08SAugustin Cavalier 	 * because it is a background scan then don't notify
914753c7e08SAugustin Cavalier 	 * the ap; we'll either return to scanning after we
915753c7e08SAugustin Cavalier 	 * receive the beacon frame or we'll drop out of power
916753c7e08SAugustin Cavalier 	 * save mode because the beacon indicates we have frames
917753c7e08SAugustin Cavalier 	 * waiting for us.
918753c7e08SAugustin Cavalier 	 */
919753c7e08SAugustin Cavalier 	if (scandone) {
9208244a9baSAugustin Cavalier 		/*
9218244a9baSAugustin Cavalier 		 * If we're not a scan offload device, come back out of
9228244a9baSAugustin Cavalier 		 * station powersave.  Offload devices handle this themselves.
9238244a9baSAugustin Cavalier 		 */
9248244a9baSAugustin Cavalier 		if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0)
925753c7e08SAugustin Cavalier 			vap->iv_sta_ps(vap, 0);
9265cad57c4SJérôme Duval 		if (ss->ss_next >= ss->ss_last) {
9275cad57c4SJérôme Duval 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
9285cad57c4SJérôme Duval 			    "%s: Dropping out of scan; ss_next=%u, ss_last=%u\n",
9295cad57c4SJérôme Duval 			    __func__,
9305cad57c4SJérôme Duval 			    (uint32_t) ss->ss_next,
9315cad57c4SJérôme Duval 			    (uint32_t) ss->ss_last);
932753c7e08SAugustin Cavalier 			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
9335cad57c4SJérôme Duval 		}
934753c7e08SAugustin Cavalier 
9358244a9baSAugustin Cavalier 		/* send 'scan done' event if not interrupted due to traffic. */
9365cad57c4SJérôme Duval 		if (!(ss_priv->ss_iflags & ISCAN_INTERRUPT) ||
9375cad57c4SJérôme Duval 		    (ss->ss_next >= ss->ss_last))
938753c7e08SAugustin Cavalier 			ieee80211_notify_scan_done(vap);
939753c7e08SAugustin Cavalier 	}
9408244a9baSAugustin Cavalier 	ss_priv->ss_iflags &= ~(ISCAN_PAUSE | ISCAN_ABORT);
941753c7e08SAugustin Cavalier 	ss_priv->ss_scanend = 0;
942753c7e08SAugustin Cavalier 	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
943753c7e08SAugustin Cavalier 	IEEE80211_UNLOCK(ic);
944753c7e08SAugustin Cavalier #undef ISCAN_REP
945753c7e08SAugustin Cavalier }
946753c7e08SAugustin Cavalier 
947753c7e08SAugustin Cavalier /*
948753c7e08SAugustin Cavalier  * Process a beacon or probe response frame.
949753c7e08SAugustin Cavalier  */
950753c7e08SAugustin Cavalier static void
ieee80211_swscan_add_scan(struct ieee80211vap * vap,struct ieee80211_channel * curchan,const struct ieee80211_scanparams * sp,const struct ieee80211_frame * wh,int subtype,int rssi,int noise)951753c7e08SAugustin Cavalier ieee80211_swscan_add_scan(struct ieee80211vap *vap,
952753c7e08SAugustin Cavalier 	struct ieee80211_channel *curchan,
953753c7e08SAugustin Cavalier 	const struct ieee80211_scanparams *sp,
954753c7e08SAugustin Cavalier 	const struct ieee80211_frame *wh,
955753c7e08SAugustin Cavalier 	int subtype, int rssi, int noise)
956753c7e08SAugustin Cavalier {
957753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
958753c7e08SAugustin Cavalier 	struct ieee80211_scan_state *ss = ic->ic_scan;
959753c7e08SAugustin Cavalier 
960753c7e08SAugustin Cavalier 	/* XXX locking */
961753c7e08SAugustin Cavalier 	/*
962753c7e08SAugustin Cavalier 	 * Frames received during startup are discarded to avoid
963753c7e08SAugustin Cavalier 	 * using scan state setup on the initial entry to the timer
964753c7e08SAugustin Cavalier 	 * callback.  This can occur because the device may enable
965753c7e08SAugustin Cavalier 	 * rx prior to our doing the initial channel change in the
966753c7e08SAugustin Cavalier 	 * timer routine.
967753c7e08SAugustin Cavalier 	 */
968753c7e08SAugustin Cavalier 	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
969753c7e08SAugustin Cavalier 		return;
970753c7e08SAugustin Cavalier #ifdef IEEE80211_DEBUG
971753c7e08SAugustin Cavalier 	if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
972753c7e08SAugustin Cavalier 		ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
973753c7e08SAugustin Cavalier #endif
974753c7e08SAugustin Cavalier 	if (ss->ss_ops != NULL &&
975753c7e08SAugustin Cavalier 	    ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) {
976753c7e08SAugustin Cavalier 		/*
977753c7e08SAugustin Cavalier 		 * If we've reached the min dwell time terminate
978753c7e08SAugustin Cavalier 		 * the timer so we'll switch to the next channel.
979753c7e08SAugustin Cavalier 		 */
980753c7e08SAugustin Cavalier 		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
981753c7e08SAugustin Cavalier 		    ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
982753c7e08SAugustin Cavalier 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
983753c7e08SAugustin Cavalier 			    "%s: chan %3d%c min dwell met (%u > %lu)\n",
984753c7e08SAugustin Cavalier 			    __func__,
985753c7e08SAugustin Cavalier 			    ieee80211_chan2ieee(ic, ic->ic_curchan),
986753c7e08SAugustin Cavalier 			    ieee80211_channel_type_char(ic->ic_curchan),
987753c7e08SAugustin Cavalier 			    ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
988753c7e08SAugustin Cavalier 			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
989753c7e08SAugustin Cavalier 			/*
990753c7e08SAugustin Cavalier 			 * NB: trigger at next clock tick or wait for the
991753c7e08SAugustin Cavalier 			 * hardware.
992753c7e08SAugustin Cavalier 			 */
993753c7e08SAugustin Cavalier 			ic->ic_scan_mindwell(ss);
994753c7e08SAugustin Cavalier 		}
995753c7e08SAugustin Cavalier 	}
996753c7e08SAugustin Cavalier }
997753c7e08SAugustin Cavalier 
998753c7e08SAugustin Cavalier static struct ieee80211_scan_methods swscan_methods = {
999753c7e08SAugustin Cavalier 	.sc_attach = ieee80211_swscan_attach,
1000753c7e08SAugustin Cavalier 	.sc_detach = ieee80211_swscan_detach,
1001753c7e08SAugustin Cavalier 	.sc_vattach = ieee80211_swscan_vattach,
1002753c7e08SAugustin Cavalier 	.sc_vdetach = ieee80211_swscan_vdetach,
1003753c7e08SAugustin Cavalier 	.sc_set_scan_duration = ieee80211_swscan_set_scan_duration,
1004753c7e08SAugustin Cavalier 	.sc_start_scan = ieee80211_swscan_start_scan,
1005753c7e08SAugustin Cavalier 	.sc_check_scan = ieee80211_swscan_check_scan,
1006753c7e08SAugustin Cavalier 	.sc_bg_scan = ieee80211_swscan_bg_scan,
1007753c7e08SAugustin Cavalier 	.sc_cancel_scan = ieee80211_swscan_cancel_scan,
1008753c7e08SAugustin Cavalier 	.sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan,
1009753c7e08SAugustin Cavalier 	.sc_scan_next = ieee80211_swscan_scan_next,
1010753c7e08SAugustin Cavalier 	.sc_scan_done = ieee80211_swscan_scan_done,
1011753c7e08SAugustin Cavalier 	.sc_scan_probe_curchan = ieee80211_swscan_probe_curchan,
1012753c7e08SAugustin Cavalier 	.sc_add_scan = ieee80211_swscan_add_scan
1013753c7e08SAugustin Cavalier };
1014753c7e08SAugustin Cavalier 
1015753c7e08SAugustin Cavalier /*
1016753c7e08SAugustin Cavalier  * Default scan attach method.
1017753c7e08SAugustin Cavalier  */
1018753c7e08SAugustin Cavalier void
ieee80211_swscan_attach(struct ieee80211com * ic)1019753c7e08SAugustin Cavalier ieee80211_swscan_attach(struct ieee80211com *ic)
1020753c7e08SAugustin Cavalier {
1021753c7e08SAugustin Cavalier 	struct scan_state *ss;
1022753c7e08SAugustin Cavalier 
1023753c7e08SAugustin Cavalier 	/*
1024753c7e08SAugustin Cavalier 	 * Setup the default methods
1025753c7e08SAugustin Cavalier 	 */
1026753c7e08SAugustin Cavalier 	ic->ic_scan_methods = &swscan_methods;
1027753c7e08SAugustin Cavalier 
1028753c7e08SAugustin Cavalier 	/* Allocate initial scan state */
1029753c7e08SAugustin Cavalier 	ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state),
1030753c7e08SAugustin Cavalier 		M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
1031753c7e08SAugustin Cavalier 	if (ss == NULL) {
1032753c7e08SAugustin Cavalier 		ic->ic_scan = NULL;
1033753c7e08SAugustin Cavalier 		return;
1034753c7e08SAugustin Cavalier 	}
1035753c7e08SAugustin Cavalier 	TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss);
1036753c7e08SAugustin Cavalier 	TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0,
1037753c7e08SAugustin Cavalier 	    scan_curchan_task, ss);
1038753c7e08SAugustin Cavalier 
1039753c7e08SAugustin Cavalier 	ic->ic_scan = &ss->base;
1040753c7e08SAugustin Cavalier 	ss->base.ss_ic = ic;
1041753c7e08SAugustin Cavalier 
1042753c7e08SAugustin Cavalier 	ic->ic_scan_curchan = scan_curchan;
1043753c7e08SAugustin Cavalier 	ic->ic_scan_mindwell = scan_mindwell;
1044753c7e08SAugustin Cavalier }
1045