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$");
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>
38191746Sthompsa#include <sys/condvar.h>
39170530Ssam
40170530Ssam#include <sys/socket.h>
41170530Ssam
42170530Ssam#include <net/if.h>
43170530Ssam#include <net/if_media.h>
44170530Ssam#include <net/ethernet.h>
45170530Ssam
46170530Ssam#include <net80211/ieee80211_var.h>
47170530Ssam
48170530Ssam#include <net/bpf.h>
49170530Ssam
50170530Ssamstruct scan_state {
51170530Ssam	struct ieee80211_scan_state base;	/* public state */
52170530Ssam
53170530Ssam	u_int		ss_iflags;		/* flags used internally */
54170530Ssam#define	ISCAN_MINDWELL 	0x0001		/* min dwell time reached */
55170530Ssam#define	ISCAN_DISCARD	0x0002		/* discard rx'd frames */
56170530Ssam#define	ISCAN_CANCEL	0x0004		/* cancel current scan */
57191746Sthompsa#define	ISCAN_ABORT	0x0008		/* end the scan immediately */
58170530Ssam	unsigned long	ss_chanmindwell;	/* min dwell on curchan */
59170530Ssam	unsigned long	ss_scanend;		/* time scan must stop */
60170530Ssam	u_int		ss_duration;		/* duration for next scan */
61191746Sthompsa	struct task	ss_scan_task;		/* scan execution */
62191746Sthompsa	struct cv	ss_scan_cv;		/* scan signal */
63170530Ssam	struct callout	ss_scan_timer;		/* scan timer */
64170530Ssam};
65170530Ssam#define	SCAN_PRIVATE(ss)	((struct scan_state *) ss)
66170530Ssam
67170530Ssam/*
68170530Ssam * Amount of time to go off-channel during a background
69170530Ssam * scan.  This value should be large enough to catch most
70170530Ssam * ap's but short enough that we can return on-channel
71170530Ssam * before our listen interval expires.
72170530Ssam *
73170530Ssam * XXX tunable
74170530Ssam * XXX check against configured listen interval
75170530Ssam */
76170530Ssam#define	IEEE80211_SCAN_OFFCHANNEL	msecs_to_ticks(150)
77170530Ssam
78170530Ssam/*
79170530Ssam * Roaming-related defaults.  RSSI thresholds are as returned by the
80188776Ssam * driver (.5dBm).  Transmit rate thresholds are IEEE rate codes (i.e
81178354Ssam * .5M units) or MCS.
82170530Ssam */
83188776Ssam/* rssi thresholds */
84188776Ssam#define	ROAM_RSSI_11A_DEFAULT		14	/* 11a bss */
85188776Ssam#define	ROAM_RSSI_11B_DEFAULT		14	/* 11b bss */
86188776Ssam#define	ROAM_RSSI_11BONLY_DEFAULT	14	/* 11b-only bss */
87188776Ssam/* transmit rate thresholds */
88188776Ssam#define	ROAM_RATE_11A_DEFAULT		2*12	/* 11a bss */
89188776Ssam#define	ROAM_RATE_11B_DEFAULT		2*5	/* 11b bss */
90188776Ssam#define	ROAM_RATE_11BONLY_DEFAULT	2*1	/* 11b-only bss */
91188782Ssam#define	ROAM_RATE_HALF_DEFAULT		2*6	/* half-width 11a/g bss */
92188782Ssam#define	ROAM_RATE_QUARTER_DEFAULT	2*3	/* quarter-width 11a/g bss */
93188776Ssam#define	ROAM_MCS_11N_DEFAULT		(1 | IEEE80211_RATE_MCS) /* 11n bss */
94170530Ssam
95178354Ssamstatic	void scan_curchan(struct ieee80211_scan_state *, unsigned long);
96178354Ssamstatic	void scan_mindwell(struct ieee80211_scan_state *);
97191746Sthompsastatic	void scan_signal(void *);
98191746Sthompsastatic	void scan_task(void *, int);
99170530Ssam
100170530SsamMALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
101170530Ssam
102170530Ssamvoid
103170530Ssamieee80211_scan_attach(struct ieee80211com *ic)
104170530Ssam{
105170530Ssam	struct scan_state *ss;
106170530Ssam
107186302Ssam	ss = (struct scan_state *) malloc(sizeof(struct scan_state),
108170530Ssam		M_80211_SCAN, M_NOWAIT | M_ZERO);
109170530Ssam	if (ss == NULL) {
110170530Ssam		ic->ic_scan = NULL;
111170530Ssam		return;
112170530Ssam	}
113179388Ssam	callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0);
114191746Sthompsa	cv_init(&ss->ss_scan_cv, "scan");
115191746Sthompsa	TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss);
116170530Ssam	ic->ic_scan = &ss->base;
117191746Sthompsa	ss->base.ss_ic = ic;
118170530Ssam
119170530Ssam	ic->ic_scan_curchan = scan_curchan;
120170530Ssam	ic->ic_scan_mindwell = scan_mindwell;
121170530Ssam}
122170530Ssam
123170530Ssamvoid
124170530Ssamieee80211_scan_detach(struct ieee80211com *ic)
125170530Ssam{
126170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
127170530Ssam
128170530Ssam	if (ss != NULL) {
129191746Sthompsa		IEEE80211_LOCK(ic);
130191746Sthompsa		SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
131191746Sthompsa		scan_signal(ss);
132191746Sthompsa		IEEE80211_UNLOCK(ic);
133191746Sthompsa		ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
134193366Sweongyo		callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
135191746Sthompsa		KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
136191746Sthompsa		    ("scan still running"));
137170530Ssam		if (ss->ss_ops != NULL) {
138170530Ssam			ss->ss_ops->scan_detach(ss);
139170530Ssam			ss->ss_ops = NULL;
140170530Ssam		}
141170530Ssam		ic->ic_scan = NULL;
142186302Ssam		free(SCAN_PRIVATE(ss), M_80211_SCAN);
143170530Ssam	}
144170530Ssam}
145170530Ssam
146188776Ssamstatic const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = {
147188776Ssam	[IEEE80211_MODE_11A]	= { .rssi = ROAM_RSSI_11A_DEFAULT,
148188776Ssam				    .rate = ROAM_RATE_11A_DEFAULT },
149188776Ssam	[IEEE80211_MODE_11G]	= { .rssi = ROAM_RSSI_11B_DEFAULT,
150188776Ssam				    .rate = ROAM_RATE_11B_DEFAULT },
151188776Ssam	[IEEE80211_MODE_11B]	= { .rssi = ROAM_RSSI_11BONLY_DEFAULT,
152188776Ssam				    .rate = ROAM_RATE_11BONLY_DEFAULT },
153188776Ssam	[IEEE80211_MODE_TURBO_A]= { .rssi = ROAM_RSSI_11A_DEFAULT,
154188776Ssam				    .rate = ROAM_RATE_11A_DEFAULT },
155188776Ssam	[IEEE80211_MODE_TURBO_G]= { .rssi = ROAM_RSSI_11A_DEFAULT,
156188776Ssam				    .rate = ROAM_RATE_11A_DEFAULT },
157188776Ssam	[IEEE80211_MODE_STURBO_A]={ .rssi = ROAM_RSSI_11A_DEFAULT,
158188776Ssam				    .rate = ROAM_RATE_11A_DEFAULT },
159188782Ssam	[IEEE80211_MODE_HALF]	= { .rssi = ROAM_RSSI_11A_DEFAULT,
160188782Ssam				    .rate = ROAM_RATE_HALF_DEFAULT },
161188782Ssam	[IEEE80211_MODE_QUARTER]= { .rssi = ROAM_RSSI_11A_DEFAULT,
162188782Ssam				    .rate = ROAM_RATE_QUARTER_DEFAULT },
163188776Ssam	[IEEE80211_MODE_11NA]	= { .rssi = ROAM_RSSI_11A_DEFAULT,
164188776Ssam				    .rate = ROAM_MCS_11N_DEFAULT },
165188776Ssam	[IEEE80211_MODE_11NG]	= { .rssi = ROAM_RSSI_11B_DEFAULT,
166188776Ssam				    .rate = ROAM_MCS_11N_DEFAULT },
167188776Ssam};
168178354Ssam
169178354Ssamvoid
170178354Ssamieee80211_scan_vattach(struct ieee80211vap *vap)
171178354Ssam{
172178354Ssam	vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz;
173178354Ssam	vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz;
174178354Ssam	vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz;
175178354Ssam
176178354Ssam	vap->iv_roaming = IEEE80211_ROAMING_AUTO;
177188776Ssam	memcpy(vap->iv_roamparms, defroam, sizeof(defroam));
178178354Ssam}
179178354Ssam
180178354Ssamvoid
181178354Ssamieee80211_scan_vdetach(struct ieee80211vap *vap)
182178354Ssam{
183178354Ssam	struct ieee80211com *ic = vap->iv_ic;
184178354Ssam	struct ieee80211_scan_state *ss;
185178354Ssam
186178354Ssam	IEEE80211_LOCK(ic);
187178354Ssam	ss = ic->ic_scan;
188178354Ssam	if (ss != NULL && ss->ss_vap == vap) {
189178354Ssam		if (ic->ic_flags & IEEE80211_F_SCAN) {
190191746Sthompsa			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
191191746Sthompsa			scan_signal(ss);
192178354Ssam		}
193178354Ssam		if (ss->ss_ops != NULL) {
194178354Ssam			ss->ss_ops->scan_detach(ss);
195178354Ssam			ss->ss_ops = NULL;
196178354Ssam		}
197178354Ssam		ss->ss_vap = NULL;
198178354Ssam	}
199178354Ssam	IEEE80211_UNLOCK(ic);
200178354Ssam}
201178354Ssam
202170530Ssam/*
203170530Ssam * Simple-minded scanner module support.
204170530Ssam */
205178354Ssamstatic const char *scan_modnames[IEEE80211_OPMODE_MAX] = {
206170530Ssam	"wlan_scan_sta",	/* IEEE80211_M_IBSS */
207170530Ssam	"wlan_scan_sta",	/* IEEE80211_M_STA */
208170530Ssam	"wlan_scan_wds",	/* IEEE80211_M_WDS */
209170530Ssam	"wlan_scan_sta",	/* IEEE80211_M_AHDEMO */
210170530Ssam	"wlan_scan_ap",		/* IEEE80211_M_HOSTAP */
211170530Ssam	"wlan_scan_monitor",	/* IEEE80211_M_MONITOR */
212195618Srpaulo	"wlan_scan_sta",	/* IEEE80211_M_MBSS */
213170530Ssam};
214178354Ssamstatic const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX];
215170530Ssam
216170530Ssamconst struct ieee80211_scanner *
217170530Ssamieee80211_scanner_get(enum ieee80211_opmode mode)
218170530Ssam{
219178354Ssam	if (mode >= IEEE80211_OPMODE_MAX)
220170530Ssam		return NULL;
221178354Ssam	if (scanners[mode] == NULL)
222170530Ssam		ieee80211_load_module(scan_modnames[mode]);
223170530Ssam	return scanners[mode];
224170530Ssam}
225170530Ssam
226170530Ssamvoid
227170530Ssamieee80211_scanner_register(enum ieee80211_opmode mode,
228170530Ssam	const struct ieee80211_scanner *scan)
229170530Ssam{
230178354Ssam	if (mode >= IEEE80211_OPMODE_MAX)
231170530Ssam		return;
232170530Ssam	scanners[mode] = scan;
233170530Ssam}
234170530Ssam
235170530Ssamvoid
236170530Ssamieee80211_scanner_unregister(enum ieee80211_opmode mode,
237170530Ssam	const struct ieee80211_scanner *scan)
238170530Ssam{
239178354Ssam	if (mode >= IEEE80211_OPMODE_MAX)
240170530Ssam		return;
241170530Ssam	if (scanners[mode] == scan)
242170530Ssam		scanners[mode] = NULL;
243170530Ssam}
244170530Ssam
245170530Ssamvoid
246170530Ssamieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan)
247170530Ssam{
248170530Ssam	int m;
249170530Ssam
250178354Ssam	for (m = 0; m < IEEE80211_OPMODE_MAX; m++)
251170530Ssam		if (scanners[m] == scan)
252170530Ssam			scanners[m] = NULL;
253170530Ssam}
254170530Ssam
255170530Ssam/*
256170530Ssam * Update common scanner state to reflect the current
257170530Ssam * operating mode.  This is called when the state machine
258170530Ssam * is transitioned to RUN state w/o scanning--e.g. when
259170530Ssam * operating in monitor mode.  The purpose of this is to
260170530Ssam * ensure later callbacks find ss_ops set to properly
261170530Ssam * reflect current operating mode.
262170530Ssam */
263178354Ssamstatic void
264178354Ssamscan_update_locked(struct ieee80211vap *vap,
265178354Ssam	const struct ieee80211_scanner *scan)
266170530Ssam{
267178354Ssam	struct ieee80211com *ic = vap->iv_ic;
268170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
269170530Ssam
270178354Ssam	IEEE80211_LOCK_ASSERT(ic);
271178354Ssam
272178354Ssam#ifdef IEEE80211_DEBUG
273178354Ssam	if (ss->ss_vap != vap || ss->ss_ops != scan) {
274178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
275178354Ssam		    "%s: current scanner is <%s:%s>, switch to <%s:%s>\n",
276178354Ssam		    __func__,
277178354Ssam		    ss->ss_vap != NULL ?
278178354Ssam			ss->ss_vap->iv_ifp->if_xname : "none",
279178354Ssam		    ss->ss_vap != NULL ?
280178354Ssam			ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none",
281178354Ssam		    vap->iv_ifp->if_xname,
282178354Ssam		    ieee80211_opmode_name[vap->iv_opmode]);
283170530Ssam	}
284178354Ssam#endif
285178354Ssam	ss->ss_vap = vap;
286170530Ssam	if (ss->ss_ops != scan) {
287178354Ssam		/*
288178354Ssam		 * Switch scanners; detach old, attach new.  Special
289178354Ssam		 * case where a single scan module implements multiple
290178354Ssam		 * policies by using different scan ops but a common
291178354Ssam		 * core.  We assume if the old and new attach methods
292178354Ssam		 * are identical then it's ok to just change ss_ops
293178354Ssam		 * and not flush the internal state of the module.
294178354Ssam		 */
295178354Ssam		if (scan == NULL || ss->ss_ops == NULL ||
296178354Ssam		    ss->ss_ops->scan_attach != scan->scan_attach) {
297178354Ssam			if (ss->ss_ops != NULL)
298178354Ssam				ss->ss_ops->scan_detach(ss);
299178354Ssam			if (scan != NULL && !scan->scan_attach(ss)) {
300178354Ssam				/* XXX attach failure */
301178354Ssam				/* XXX stat+msg */
302178354Ssam				scan = NULL;
303178354Ssam			}
304178354Ssam		}
305178354Ssam		ss->ss_ops = scan;
306170530Ssam	}
307170530Ssam}
308170530Ssam
309170530Ssamstatic char
310170530Ssamchannel_type(const struct ieee80211_channel *c)
311170530Ssam{
312170530Ssam	if (IEEE80211_IS_CHAN_ST(c))
313170530Ssam		return 'S';
314170530Ssam	if (IEEE80211_IS_CHAN_108A(c))
315170530Ssam		return 'T';
316170530Ssam	if (IEEE80211_IS_CHAN_108G(c))
317170530Ssam		return 'G';
318170530Ssam	if (IEEE80211_IS_CHAN_HT(c))
319170530Ssam		return 'n';
320170530Ssam	if (IEEE80211_IS_CHAN_A(c))
321170530Ssam		return 'a';
322170530Ssam	if (IEEE80211_IS_CHAN_ANYG(c))
323170530Ssam		return 'g';
324170530Ssam	if (IEEE80211_IS_CHAN_B(c))
325170530Ssam		return 'b';
326170530Ssam	return 'f';
327170530Ssam}
328170530Ssam
329170530Ssamvoid
330170530Ssamieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
331170530Ssam{
332191746Sthompsa	struct ieee80211com *ic = ss->ss_ic;
333170530Ssam	const char *sep;
334170530Ssam	int i;
335170530Ssam
336170530Ssam	sep = "";
337170530Ssam	for (i = ss->ss_next; i < ss->ss_last; i++) {
338170530Ssam		const struct ieee80211_channel *c = ss->ss_chans[i];
339170530Ssam
340170530Ssam		printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c),
341170530Ssam			channel_type(c));
342170530Ssam		sep = ", ";
343170530Ssam	}
344170530Ssam}
345170530Ssam
346178354Ssam#ifdef IEEE80211_DEBUG
347178354Ssamstatic void
348178354Ssamscan_dump(struct ieee80211_scan_state *ss)
349178354Ssam{
350178354Ssam	struct ieee80211vap *vap = ss->ss_vap;
351178354Ssam
352178354Ssam	if_printf(vap->iv_ifp, "scan set ");
353178354Ssam	ieee80211_scan_dump_channels(ss);
354188924Ssam	printf(" dwell min %lums max %lums\n",
355188924Ssam	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(ss->ss_maxdwell));
356178354Ssam}
357178354Ssam#endif /* IEEE80211_DEBUG */
358178354Ssam
359170530Ssamstatic void
360178354Ssamcopy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
361170530Ssam	int nssid, const struct ieee80211_scan_ssid ssids[])
362170530Ssam{
363170530Ssam	if (nssid > IEEE80211_SCAN_MAX_SSID) {
364170530Ssam		/* XXX printf */
365178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
366170530Ssam		    "%s: too many ssid %d, ignoring all of them\n",
367170530Ssam		    __func__, nssid);
368170530Ssam		return;
369170530Ssam	}
370170530Ssam	memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0]));
371170530Ssam	ss->ss_nssid = nssid;
372170530Ssam}
373170530Ssam
374170530Ssam/*
375170530Ssam * Start a scan unless one is already going.
376170530Ssam */
377178354Ssamstatic int
378178354Ssamstart_scan_locked(const struct ieee80211_scanner *scan,
379178354Ssam	struct ieee80211vap *vap, int flags, u_int duration,
380178354Ssam	u_int mindwell, u_int maxdwell,
381170530Ssam	u_int nssid, const struct ieee80211_scan_ssid ssids[])
382170530Ssam{
383178354Ssam	struct ieee80211com *ic = vap->iv_ic;
384170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
385170530Ssam
386178354Ssam	IEEE80211_LOCK_ASSERT(ic);
387170530Ssam
388178354Ssam	if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
389178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
390178354Ssam		    "%s: scan inhibited by pending channel change\n", __func__);
391178354Ssam	} else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
392178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
393178354Ssam		    "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
394170530Ssam		    , __func__
395170530Ssam		    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
396178354Ssam		    , duration, mindwell, maxdwell
397178354Ssam		    , ieee80211_phymode_name[vap->iv_des_mode]
398170530Ssam		    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
399170530Ssam		    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
400178354Ssam		    , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
401178354Ssam		    , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
402170530Ssam		    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
403170530Ssam		    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
404170530Ssam		);
405170530Ssam
406178354Ssam		scan_update_locked(vap, scan);
407170530Ssam		if (ss->ss_ops != NULL) {
408170530Ssam			if ((flags & IEEE80211_SCAN_NOSSID) == 0)
409178354Ssam				copy_ssid(vap, ss, nssid, ssids);
410170530Ssam
411170530Ssam			/* NB: top 4 bits for internal use */
412170530Ssam			ss->ss_flags = flags & 0xfff;
413170530Ssam			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
414178354Ssam				vap->iv_stats.is_scan_active++;
415170530Ssam			else
416178354Ssam				vap->iv_stats.is_scan_passive++;
417170530Ssam			if (flags & IEEE80211_SCAN_FLUSH)
418170530Ssam				ss->ss_ops->scan_flush(ss);
419218085Sbschmidt			if (flags & IEEE80211_SCAN_BGSCAN)
420218085Sbschmidt				ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
421170530Ssam
422170530Ssam			/* NB: flush frames rx'd before 1st channel change */
423170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
424191746Sthompsa			SCAN_PRIVATE(ss)->ss_duration = duration;
425178354Ssam			ss->ss_next = 0;
426178354Ssam			ss->ss_mindwell = mindwell;
427178354Ssam			ss->ss_maxdwell = maxdwell;
428191746Sthompsa			/* NB: scan_start must be before the scan runtask */
429178354Ssam			ss->ss_ops->scan_start(ss, vap);
430178354Ssam#ifdef IEEE80211_DEBUG
431178354Ssam			if (ieee80211_msg_scan(vap))
432178354Ssam				scan_dump(ss);
433178354Ssam#endif /* IEEE80211_DEBUG */
434191746Sthompsa			ic->ic_flags |= IEEE80211_F_SCAN;
435191746Sthompsa			ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
436170530Ssam		}
437218091Sbschmidt		return 1;
438170530Ssam	} else {
439178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
440170530Ssam		    "%s: %s scan already in progress\n", __func__,
441170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
442170530Ssam	}
443218091Sbschmidt	return 0;
444178354Ssam}
445178354Ssam
446178354Ssam/*
447178354Ssam * Start a scan unless one is already going.
448178354Ssam */
449178354Ssamint
450178354Ssamieee80211_start_scan(struct ieee80211vap *vap, int flags,
451178354Ssam	u_int duration, u_int mindwell, u_int maxdwell,
452178354Ssam	u_int nssid, const struct ieee80211_scan_ssid ssids[])
453178354Ssam{
454178354Ssam	struct ieee80211com *ic = vap->iv_ic;
455178354Ssam	const struct ieee80211_scanner *scan;
456178354Ssam	int result;
457178354Ssam
458178354Ssam	scan = ieee80211_scanner_get(vap->iv_opmode);
459178354Ssam	if (scan == NULL) {
460178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
461178354Ssam		    "%s: no scanner support for %s mode\n",
462178354Ssam		    __func__, ieee80211_opmode_name[vap->iv_opmode]);
463178354Ssam		/* XXX stat */
464178354Ssam		return 0;
465178354Ssam	}
466178354Ssam
467178354Ssam	IEEE80211_LOCK(ic);
468178354Ssam	result = start_scan_locked(scan, vap, flags, duration,
469178354Ssam	    mindwell, maxdwell, nssid, ssids);
470170530Ssam	IEEE80211_UNLOCK(ic);
471170530Ssam
472178354Ssam	return result;
473170530Ssam}
474170530Ssam
475170530Ssam/*
476170530Ssam * Check the scan cache for an ap/channel to use; if that
477170530Ssam * fails then kick off a new scan.
478170530Ssam */
479170530Ssamint
480178354Ssamieee80211_check_scan(struct ieee80211vap *vap, int flags,
481178354Ssam	u_int duration, u_int mindwell, u_int maxdwell,
482170530Ssam	u_int nssid, const struct ieee80211_scan_ssid ssids[])
483170530Ssam{
484178354Ssam	struct ieee80211com *ic = vap->iv_ic;
485170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
486178354Ssam	const struct ieee80211_scanner *scan;
487179389Ssam	int result;
488170530Ssam
489178354Ssam	scan = ieee80211_scanner_get(vap->iv_opmode);
490178354Ssam	if (scan == NULL) {
491178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
492178354Ssam		    "%s: no scanner support for %s mode\n",
493178354Ssam		    __func__, vap->iv_opmode);
494178354Ssam		/* XXX stat */
495178354Ssam		return 0;
496178354Ssam	}
497178354Ssam
498170530Ssam	/*
499170530Ssam	 * Check if there's a list of scan candidates already.
500170530Ssam	 * XXX want more than the ap we're currently associated with
501170530Ssam	 */
502170530Ssam
503170530Ssam	IEEE80211_LOCK(ic);
504178354Ssam	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
505178354Ssam	    "%s: %s scan, %s%s%s%s%s\n"
506170530Ssam	    , __func__
507170530Ssam	    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
508170530Ssam	    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
509170530Ssam	    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
510178354Ssam	    , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
511170530Ssam	    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
512170530Ssam	    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
513170530Ssam	);
514170530Ssam
515178354Ssam	if (ss->ss_ops != scan) {
516178354Ssam		/* XXX re-use cache contents? e.g. adhoc<->sta */
517178354Ssam		flags |= IEEE80211_SCAN_FLUSH;
518178354Ssam	}
519178354Ssam	scan_update_locked(vap, scan);
520170530Ssam	if (ss->ss_ops != NULL) {
521178354Ssam		/* XXX verify ss_ops matches vap->iv_opmode */
522170530Ssam		if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
523170530Ssam			/*
524170530Ssam			 * Update the ssid list and mark flags so if
525170530Ssam			 * we call start_scan it doesn't duplicate work.
526170530Ssam			 */
527178354Ssam			copy_ssid(vap, ss, nssid, ssids);
528170530Ssam			flags |= IEEE80211_SCAN_NOSSID;
529170530Ssam		}
530170530Ssam		if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
531179389Ssam		    (flags & IEEE80211_SCAN_FLUSH) == 0 &&
532179389Ssam		    time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
533170530Ssam			/*
534170530Ssam			 * We're not currently scanning and the cache is
535170530Ssam			 * deemed hot enough to consult.  Lock out others
536170530Ssam			 * by marking IEEE80211_F_SCAN while we decide if
537170530Ssam			 * something is already in the scan cache we can
538170530Ssam			 * use.  Also discard any frames that might come
539170530Ssam			 * in while temporarily marked as scanning.
540170530Ssam			 */
541170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
542170530Ssam			ic->ic_flags |= IEEE80211_F_SCAN;
543179389Ssam
544179389Ssam			/* NB: need to use supplied flags in check */
545178354Ssam			ss->ss_flags = flags & 0xff;
546179389Ssam			result = ss->ss_ops->scan_end(ss, vap);
547179389Ssam
548170530Ssam			ic->ic_flags &= ~IEEE80211_F_SCAN;
549179389Ssam			SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD;
550179389Ssam			if (result) {
551179389Ssam				ieee80211_notify_scan_done(vap);
552179389Ssam				IEEE80211_UNLOCK(ic);
553179389Ssam				return 1;
554179389Ssam			}
555170530Ssam		}
556170530Ssam	}
557178354Ssam	result = start_scan_locked(scan, vap, flags, duration,
558178354Ssam	    mindwell, maxdwell, nssid, ssids);
559178354Ssam	IEEE80211_UNLOCK(ic);
560178354Ssam
561178354Ssam	return result;
562170530Ssam}
563170530Ssam
564170530Ssam/*
565178354Ssam * Check the scan cache for an ap/channel to use; if that fails
566178354Ssam * then kick off a scan using the current settings.
567178354Ssam */
568178354Ssamint
569178354Ssamieee80211_check_scan_current(struct ieee80211vap *vap)
570178354Ssam{
571178354Ssam	return ieee80211_check_scan(vap,
572178354Ssam	    IEEE80211_SCAN_ACTIVE,
573178354Ssam	    IEEE80211_SCAN_FOREVER, 0, 0,
574178354Ssam	    vap->iv_des_nssid, vap->iv_des_ssid);
575178354Ssam}
576178354Ssam
577178354Ssam/*
578170530Ssam * Restart a previous scan.  If the previous scan completed
579170530Ssam * then we start again using the existing channel list.
580170530Ssam */
581170530Ssamint
582178354Ssamieee80211_bg_scan(struct ieee80211vap *vap, int flags)
583170530Ssam{
584178354Ssam	struct ieee80211com *ic = vap->iv_ic;
585170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
586178354Ssam	const struct ieee80211_scanner *scan;
587170530Ssam
588178354Ssam	scan = ieee80211_scanner_get(vap->iv_opmode);
589178354Ssam	if (scan == NULL) {
590178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
591178354Ssam		    "%s: no scanner support for %s mode\n",
592178354Ssam		    __func__, vap->iv_opmode);
593178354Ssam		/* XXX stat */
594178354Ssam		return 0;
595178354Ssam	}
596178354Ssam
597170530Ssam	IEEE80211_LOCK(ic);
598170530Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
599170530Ssam		u_int duration;
600170530Ssam		/*
601170530Ssam		 * Go off-channel for a fixed interval that is large
602170530Ssam		 * enough to catch most ap's but short enough that
603170530Ssam		 * we can return on-channel before our listen interval
604170530Ssam		 * expires.
605170530Ssam		 */
606170530Ssam		duration = IEEE80211_SCAN_OFFCHANNEL;
607170530Ssam
608178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
609170530Ssam		    "%s: %s scan, ticks %u duration %lu\n", __func__,
610170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
611170530Ssam		    ticks, duration);
612170530Ssam
613178354Ssam		scan_update_locked(vap, scan);
614170530Ssam		if (ss->ss_ops != NULL) {
615178354Ssam			ss->ss_vap = vap;
616170530Ssam			/*
617170530Ssam			 * A background scan does not select a new sta; it
618170530Ssam			 * just refreshes the scan cache.  Also, indicate
619170530Ssam			 * the scan logic should follow the beacon schedule:
620170530Ssam			 * we go off-channel and scan for a while, then
621170530Ssam			 * return to the bss channel to receive a beacon,
622170530Ssam			 * then go off-channel again.  All during this time
623170530Ssam			 * we notify the ap we're in power save mode.  When
624170530Ssam			 * the scan is complete we leave power save mode.
625170530Ssam			 * If any beacon indicates there are frames pending
626170530Ssam			 * for us then we drop out of power save mode
627170530Ssam			 * (and background scan) automatically by way of the
628170530Ssam			 * usual sta power save logic.
629170530Ssam			 */
630170530Ssam			ss->ss_flags |= IEEE80211_SCAN_NOPICK
631178354Ssam				     |  IEEE80211_SCAN_BGSCAN
632178354Ssam				     |  flags
633178354Ssam				     ;
634170530Ssam			/* if previous scan completed, restart */
635170530Ssam			if (ss->ss_next >= ss->ss_last) {
636170530Ssam				if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
637178354Ssam					vap->iv_stats.is_scan_active++;
638170530Ssam				else
639178354Ssam					vap->iv_stats.is_scan_passive++;
640178354Ssam				/*
641178354Ssam				 * NB: beware of the scan cache being flushed;
642178354Ssam				 *     if the channel list is empty use the
643178354Ssam				 *     scan_start method to populate it.
644178354Ssam				 */
645178354Ssam				ss->ss_next = 0;
646178354Ssam				if (ss->ss_last != 0)
647178354Ssam					ss->ss_ops->scan_restart(ss, vap);
648178354Ssam				else {
649178354Ssam					ss->ss_ops->scan_start(ss, vap);
650178354Ssam#ifdef IEEE80211_DEBUG
651178354Ssam					if (ieee80211_msg_scan(vap))
652178354Ssam						scan_dump(ss);
653178354Ssam#endif /* IEEE80211_DEBUG */
654178354Ssam				}
655170530Ssam			}
656170530Ssam			/* NB: flush frames rx'd before 1st channel change */
657170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
658191746Sthompsa			SCAN_PRIVATE(ss)->ss_duration = duration;
659170530Ssam			ss->ss_maxdwell = duration;
660191746Sthompsa			ic->ic_flags |= IEEE80211_F_SCAN;
661191746Sthompsa			ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
662191746Sthompsa			ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
663170530Ssam		} else {
664170530Ssam			/* XXX msg+stat */
665170530Ssam		}
666170530Ssam	} else {
667178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
668170530Ssam		    "%s: %s scan already in progress\n", __func__,
669170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
670170530Ssam	}
671170530Ssam	IEEE80211_UNLOCK(ic);
672170530Ssam
673170530Ssam	/* NB: racey, does it matter? */
674170530Ssam	return (ic->ic_flags & IEEE80211_F_SCAN);
675170530Ssam}
676170530Ssam
677170530Ssam/*
678178354Ssam * Cancel any scan currently going on for the specified vap.
679178354Ssam */
680178354Ssamvoid
681178354Ssamieee80211_cancel_scan(struct ieee80211vap *vap)
682178354Ssam{
683178354Ssam	struct ieee80211com *ic = vap->iv_ic;
684178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
685178354Ssam
686178354Ssam	IEEE80211_LOCK(ic);
687178354Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
688178354Ssam	    ss->ss_vap == vap &&
689178354Ssam	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
690178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
691178354Ssam		    "%s: cancel %s scan\n", __func__,
692178354Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
693178354Ssam			"active" : "passive");
694178354Ssam
695178354Ssam		/* clear bg scan NOPICK and mark cancel request */
696178354Ssam		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
697178354Ssam		SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
698191746Sthompsa		/* wake up the scan task */
699191746Sthompsa		scan_signal(ss);
700178354Ssam	}
701178354Ssam	IEEE80211_UNLOCK(ic);
702178354Ssam}
703178354Ssam
704178354Ssam/*
705170530Ssam * Cancel any scan currently going on.
706170530Ssam */
707170530Ssamvoid
708178354Ssamieee80211_cancel_anyscan(struct ieee80211vap *vap)
709170530Ssam{
710178354Ssam	struct ieee80211com *ic = vap->iv_ic;
711170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
712170530Ssam
713170530Ssam	IEEE80211_LOCK(ic);
714170530Ssam	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
715170530Ssam	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
716178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
717170530Ssam		    "%s: cancel %s scan\n", __func__,
718170530Ssam		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
719170530Ssam			"active" : "passive");
720170530Ssam
721170530Ssam		/* clear bg scan NOPICK and mark cancel request */
722170530Ssam		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
723170530Ssam		SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
724191746Sthompsa		/* wake up the scan task */
725191746Sthompsa		scan_signal(ss);
726170530Ssam	}
727170530Ssam	IEEE80211_UNLOCK(ic);
728170530Ssam}
729170530Ssam
730170530Ssam/*
731170530Ssam * Public access to scan_next for drivers that manage
732170530Ssam * scanning themselves (e.g. for firmware-based devices).
733170530Ssam */
734170530Ssamvoid
735178354Ssamieee80211_scan_next(struct ieee80211vap *vap)
736170530Ssam{
737178354Ssam	struct ieee80211com *ic = vap->iv_ic;
738178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
739178354Ssam
740191746Sthompsa	/* wake up the scan task */
741191746Sthompsa	IEEE80211_LOCK(ic);
742191746Sthompsa	scan_signal(ss);
743191746Sthompsa	IEEE80211_UNLOCK(ic);
744170530Ssam}
745170530Ssam
746170530Ssam/*
747171125Sthompsa * Public access to scan_next for drivers that are not able to scan single
748171125Sthompsa * channels (e.g. for firmware-based devices).
749171125Sthompsa */
750171125Sthompsavoid
751178354Ssamieee80211_scan_done(struct ieee80211vap *vap)
752171125Sthompsa{
753178354Ssam	struct ieee80211com *ic = vap->iv_ic;
754178354Ssam	struct ieee80211_scan_state *ss;
755171125Sthompsa
756178354Ssam	IEEE80211_LOCK(ic);
757178354Ssam	ss = ic->ic_scan;
758171125Sthompsa	ss->ss_next = ss->ss_last; /* all channels are complete */
759191746Sthompsa	scan_signal(ss);
760178354Ssam	IEEE80211_UNLOCK(ic);
761171125Sthompsa}
762171125Sthompsa
763171125Sthompsa/*
764178354Ssam * Probe the curent channel, if allowed, while scanning.
765178354Ssam * If the channel is not marked passive-only then send
766178354Ssam * a probe request immediately.  Otherwise mark state and
767178354Ssam * listen for beacons on the channel; if we receive something
768178354Ssam * then we'll transmit a probe request.
769178354Ssam */
770178354Ssamvoid
771178354Ssamieee80211_probe_curchan(struct ieee80211vap *vap, int force)
772178354Ssam{
773178354Ssam	struct ieee80211com *ic = vap->iv_ic;
774178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
775178354Ssam	struct ifnet *ifp = vap->iv_ifp;
776178354Ssam	int i;
777178354Ssam
778178354Ssam	if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) {
779178354Ssam		ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN;
780178354Ssam		return;
781178354Ssam	}
782178354Ssam	/*
783178354Ssam	 * Send directed probe requests followed by any
784178354Ssam	 * broadcast probe request.
785178354Ssam	 * XXX remove dependence on ic/vap->iv_bss
786178354Ssam	 */
787178354Ssam	for (i = 0; i < ss->ss_nssid; i++)
788178354Ssam		ieee80211_send_probereq(vap->iv_bss,
789178354Ssam			vap->iv_myaddr, ifp->if_broadcastaddr,
790178354Ssam			ifp->if_broadcastaddr,
791178354Ssam			ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
792178354Ssam	if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
793178354Ssam		ieee80211_send_probereq(vap->iv_bss,
794178354Ssam			vap->iv_myaddr, ifp->if_broadcastaddr,
795178354Ssam			ifp->if_broadcastaddr,
796178354Ssam			"", 0);
797178354Ssam}
798178354Ssam
799178354Ssam/*
800170530Ssam * Scan curchan.  If this is an active scan and the channel
801170530Ssam * is not marked passive then send probe request frame(s).
802170530Ssam * Arrange for the channel change after maxdwell ticks.
803170530Ssam */
804170530Ssamstatic void
805178354Ssamscan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
806170530Ssam{
807178354Ssam	struct ieee80211vap *vap  = ss->ss_vap;
808170530Ssam
809191746Sthompsa	IEEE80211_LOCK(vap->iv_ic);
810178354Ssam	if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
811178354Ssam		ieee80211_probe_curchan(vap, 0);
812170530Ssam	callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
813191746Sthompsa	    maxdwell, scan_signal, ss);
814191746Sthompsa	IEEE80211_UNLOCK(vap->iv_ic);
815170530Ssam}
816170530Ssam
817191746Sthompsastatic void
818191746Sthompsascan_signal(void *arg)
819191746Sthompsa{
820191746Sthompsa	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
821191746Sthompsa
822191746Sthompsa	IEEE80211_LOCK_ASSERT(ss->ss_ic);
823191746Sthompsa
824191746Sthompsa	cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv);
825191746Sthompsa}
826191746Sthompsa
827170530Ssam/*
828170530Ssam * Handle mindwell requirements completed; initiate a channel
829170530Ssam * change to the next channel asap.
830170530Ssam */
831170530Ssamstatic void
832178354Ssamscan_mindwell(struct ieee80211_scan_state *ss)
833170530Ssam{
834191746Sthompsa	struct ieee80211com *ic = ss->ss_ic;
835191746Sthompsa
836191746Sthompsa	IEEE80211_LOCK(ic);
837191746Sthompsa	scan_signal(ss);
838191746Sthompsa	IEEE80211_UNLOCK(ic);
839170530Ssam}
840170530Ssam
841170530Ssamstatic void
842191746Sthompsascan_task(void *arg, int pending)
843170530Ssam{
844191746Sthompsa#define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
845170530Ssam	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
846178354Ssam	struct ieee80211vap *vap = ss->ss_vap;
847191746Sthompsa	struct ieee80211com *ic = ss->ss_ic;
848170530Ssam	struct ieee80211_channel *chan;
849170530Ssam	unsigned long maxdwell, scanend;
850191746Sthompsa	int scandone = 0;
851170530Ssam
852191746Sthompsa	IEEE80211_LOCK(ic);
853191746Sthompsa	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
854191746Sthompsa	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) {
855191746Sthompsa		/* Cancelled before we started */
856191746Sthompsa		goto done;
857191746Sthompsa	}
858178354Ssam
859191746Sthompsa	if (ss->ss_next == ss->ss_last) {
860191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
861191746Sthompsa			"%s: no channels to scan\n", __func__);
862259173Sgavin		scandone = 1;
863191746Sthompsa		goto done;
864191746Sthompsa	}
865191746Sthompsa
866191746Sthompsa	if (vap->iv_opmode == IEEE80211_M_STA &&
867191746Sthompsa	    vap->iv_state == IEEE80211_S_RUN) {
868191746Sthompsa		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
869191746Sthompsa			/* Enable station power save mode */
870241138Sadrian			vap->iv_sta_ps(vap, 1);
871191746Sthompsa			/*
872191746Sthompsa			 * Use an 1ms delay so the null data frame has a chance
873191746Sthompsa			 * to go out.
874191746Sthompsa			 * XXX Should use M_TXCB mechanism to eliminate this.
875191746Sthompsa			 */
876191746Sthompsa			cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv,
877191746Sthompsa			    IEEE80211_LOCK_OBJ(ic), hz / 1000);
878191746Sthompsa			if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
879191746Sthompsa				goto done;
880191746Sthompsa		}
881191746Sthompsa	}
882191746Sthompsa
883191746Sthompsa	scanend = ticks + SCAN_PRIVATE(ss)->ss_duration;
884191746Sthompsa	IEEE80211_UNLOCK(ic);
885191746Sthompsa	ic->ic_scan_start(ic);		/* notify driver */
886191746Sthompsa	IEEE80211_LOCK(ic);
887191746Sthompsa
888191746Sthompsa	for (;;) {
889191746Sthompsa		scandone = (ss->ss_next >= ss->ss_last) ||
890191746Sthompsa		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
891191746Sthompsa		if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
892191746Sthompsa		    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) ||
893191746Sthompsa		     time_after(ticks + ss->ss_mindwell, scanend))
894191746Sthompsa			break;
895191746Sthompsa
896170530Ssam		chan = ss->ss_chans[ss->ss_next++];
897170530Ssam
898170530Ssam		/*
899170530Ssam		 * Watch for truncation due to the scan end time.
900170530Ssam		 */
901170530Ssam		if (time_after(ticks + ss->ss_maxdwell, scanend))
902170530Ssam			maxdwell = scanend - ticks;
903170530Ssam		else
904170530Ssam			maxdwell = ss->ss_maxdwell;
905170530Ssam
906178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
907188924Ssam		    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
908170530Ssam		    __func__,
909170530Ssam		    ieee80211_chan2ieee(ic, ic->ic_curchan),
910170530Ssam		        channel_type(ic->ic_curchan),
911170530Ssam		    ieee80211_chan2ieee(ic, chan), channel_type(chan),
912170530Ssam		    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
913170530Ssam			(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
914170530Ssam			"active" : "passive",
915188924Ssam		    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
916170530Ssam
917170530Ssam		/*
918170530Ssam		 * Potentially change channel and phy mode.
919170530Ssam		 */
920191746Sthompsa		ic->ic_curchan = chan;
921191746Sthompsa		ic->ic_rt = ieee80211_get_ratetable(chan);
922191746Sthompsa		IEEE80211_UNLOCK(ic);
923191746Sthompsa		/*
924191746Sthompsa		 * Perform the channel change and scan unlocked so the driver
925191746Sthompsa		 * may sleep. Once set_channel returns the hardware has
926191746Sthompsa		 * completed the channel change.
927191746Sthompsa		 */
928191746Sthompsa		ic->ic_set_channel(ic);
929192468Ssam		ieee80211_radiotap_chan_change(ic);
930170530Ssam
931170530Ssam		/*
932170530Ssam		 * Scan curchan.  Drivers for "intelligent hardware"
933170530Ssam		 * override ic_scan_curchan to tell the device to do
934170530Ssam		 * the work.  Otherwise we manage the work outselves;
935170530Ssam		 * sending a probe request (as needed), and arming the
936170530Ssam		 * timeout to switch channels after maxdwell ticks.
937191746Sthompsa		 *
938191746Sthompsa		 * scan_curchan should only pause for the time required to
939191746Sthompsa		 * prepare/initiate the hardware for the scan (if at all), the
940191746Sthompsa		 * below condvar is used to sleep for the channels dwell time
941191746Sthompsa		 * and allows it to be signalled for abort.
942170530Ssam		 */
943178354Ssam		ic->ic_scan_curchan(ss, maxdwell);
944191746Sthompsa		IEEE80211_LOCK(ic);
945170530Ssam
946170530Ssam		SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
947170530Ssam		/* clear mindwell lock and initial channel change flush */
948170530Ssam		SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
949170530Ssam
950191746Sthompsa		if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
951191746Sthompsa			continue;
952170530Ssam
953191746Sthompsa		/* Wait to be signalled to scan the next channel */
954191746Sthompsa		cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic));
955191746Sthompsa	}
956191746Sthompsa	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
957191746Sthompsa		goto done;
958170530Ssam
959191746Sthompsa	IEEE80211_UNLOCK(ic);
960191746Sthompsa	ic->ic_scan_end(ic);		/* notify driver */
961191746Sthompsa	IEEE80211_LOCK(ic);
962170530Ssam
963191746Sthompsa	/*
964232373Sadrian	 * Since a cancellation may have occured during one of the
965232373Sadrian	 * driver calls (whilst unlocked), update scandone.
966232373Sadrian	 */
967232373Sadrian	if (scandone == 0 &&
968232373Sadrian	    ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) {
969232373Sadrian		/* XXX printf? */
970232373Sadrian		if_printf(vap->iv_ifp,
971232373Sadrian		    "%s: OOPS! scan cancelled during driver call!\n",
972232373Sadrian		    __func__);
973232373Sadrian	}
974232373Sadrian	scandone |= ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0);
975232373Sadrian
976232373Sadrian	/*
977191746Sthompsa	 * Record scan complete time.  Note that we also do
978191746Sthompsa	 * this when canceled so any background scan will
979191746Sthompsa	 * not be restarted for a while.
980191746Sthompsa	 */
981191746Sthompsa	if (scandone)
982191746Sthompsa		ic->ic_lastscan = ticks;
983191746Sthompsa	/* return to the bss channel */
984191746Sthompsa	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
985191746Sthompsa	    ic->ic_curchan != ic->ic_bsschan) {
986191746Sthompsa		ieee80211_setupcurchan(ic, ic->ic_bsschan);
987191746Sthompsa		IEEE80211_UNLOCK(ic);
988191746Sthompsa		ic->ic_set_channel(ic);
989192468Ssam		ieee80211_radiotap_chan_change(ic);
990191746Sthompsa		IEEE80211_LOCK(ic);
991191746Sthompsa	}
992191746Sthompsa	/* clear internal flags and any indication of a pick */
993191746Sthompsa	SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
994191746Sthompsa	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
995191746Sthompsa
996191746Sthompsa	/*
997191746Sthompsa	 * If not canceled and scan completed, do post-processing.
998191746Sthompsa	 * If the callback function returns 0, then it wants to
999191746Sthompsa	 * continue/restart scanning.  Unfortunately we needed to
1000191746Sthompsa	 * notify the driver to end the scan above to avoid having
1001191746Sthompsa	 * rx frames alter the scan candidate list.
1002191746Sthompsa	 */
1003191746Sthompsa	if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
1004191746Sthompsa	    !ss->ss_ops->scan_end(ss, vap) &&
1005191746Sthompsa	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
1006191746Sthompsa	    time_before(ticks + ss->ss_mindwell, scanend)) {
1007191746Sthompsa		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
1008191746Sthompsa		    "%s: done, restart "
1009191746Sthompsa		    "[ticks %u, dwell min %lu scanend %lu]\n",
1010191746Sthompsa		    __func__,
1011191746Sthompsa		    ticks, ss->ss_mindwell, scanend);
1012191746Sthompsa		ss->ss_next = 0;	/* reset to begining */
1013191746Sthompsa		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
1014191746Sthompsa			vap->iv_stats.is_scan_active++;
1015191746Sthompsa		else
1016191746Sthompsa			vap->iv_stats.is_scan_passive++;
1017191746Sthompsa
1018191746Sthompsa		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
1019191746Sthompsa		ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
1020191746Sthompsa		IEEE80211_UNLOCK(ic);
1021191746Sthompsa		return;
1022191746Sthompsa	}
1023191746Sthompsa
1024191746Sthompsa	/* past here, scandone is ``true'' if not in bg mode */
1025191746Sthompsa	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
1026191746Sthompsa		scandone = 1;
1027191746Sthompsa
1028191746Sthompsa	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
1029191746Sthompsa	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
1030191746Sthompsa	    __func__, scandone ? "done" : "stopped",
1031191746Sthompsa	    ticks, ss->ss_mindwell, scanend);
1032191746Sthompsa
1033191746Sthompsa	/*
1034191746Sthompsa	 * Clear the SCAN bit first in case frames are
1035191746Sthompsa	 * pending on the station power save queue.  If
1036191746Sthompsa	 * we defer this then the dispatch of the frames
1037191746Sthompsa	 * may generate a request to cancel scanning.
1038191746Sthompsa	 */
1039191746Sthompsadone:
1040191746Sthompsa	ic->ic_flags &= ~IEEE80211_F_SCAN;
1041191746Sthompsa	/*
1042191746Sthompsa	 * Drop out of power save mode when a scan has
1043191746Sthompsa	 * completed.  If this scan was prematurely terminated
1044191746Sthompsa	 * because it is a background scan then don't notify
1045191746Sthompsa	 * the ap; we'll either return to scanning after we
1046191746Sthompsa	 * receive the beacon frame or we'll drop out of power
1047191746Sthompsa	 * save mode because the beacon indicates we have frames
1048191746Sthompsa	 * waiting for us.
1049191746Sthompsa	 */
1050191746Sthompsa	if (scandone) {
1051241138Sadrian		vap->iv_sta_ps(vap, 0);
1052191746Sthompsa		if (ss->ss_next >= ss->ss_last) {
1053191746Sthompsa			ieee80211_notify_scan_done(vap);
1054191746Sthompsa			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
1055170530Ssam		}
1056170530Ssam	}
1057191746Sthompsa	SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
1058191746Sthompsa	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
1059191746Sthompsa	IEEE80211_UNLOCK(ic);
1060170530Ssam#undef ISCAN_REP
1061170530Ssam}
1062170530Ssam
1063170530Ssam#ifdef IEEE80211_DEBUG
1064170530Ssamstatic void
1065178354Ssamdump_country(const uint8_t *ie)
1066178354Ssam{
1067178354Ssam	const struct ieee80211_country_ie *cie =
1068178354Ssam	   (const struct ieee80211_country_ie *) ie;
1069178354Ssam	int i, nbands, schan, nchan;
1070178354Ssam
1071178354Ssam	if (cie->len < 3) {
1072178354Ssam		printf(" <bogus country ie, len %d>", cie->len);
1073178354Ssam		return;
1074178354Ssam	}
1075178354Ssam	printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]);
1076178354Ssam	nbands = (cie->len - 3) / sizeof(cie->band[0]);
1077178354Ssam	for (i = 0; i < nbands; i++) {
1078178354Ssam		schan = cie->band[i].schan;
1079178354Ssam		nchan = cie->band[i].nchan;
1080178354Ssam		if (nchan != 1)
1081178354Ssam			printf(" %u-%u,%u", schan, schan + nchan-1,
1082178354Ssam			    cie->band[i].maxtxpwr);
1083178354Ssam		else
1084178354Ssam			printf(" %u,%u", schan, cie->band[i].maxtxpwr);
1085178354Ssam	}
1086178354Ssam	printf("]");
1087178354Ssam}
1088178354Ssam
1089178354Ssamstatic void
1090170530Ssamdump_probe_beacon(uint8_t subtype, int isnew,
1091170530Ssam	const uint8_t mac[IEEE80211_ADDR_LEN],
1092178354Ssam	const struct ieee80211_scanparams *sp, int rssi)
1093170530Ssam{
1094170530Ssam
1095170530Ssam	printf("[%s] %s%s on chan %u (bss chan %u) ",
1096170530Ssam	    ether_sprintf(mac), isnew ? "new " : "",
1097170530Ssam	    ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
1098178354Ssam	    sp->chan, sp->bchan);
1099170530Ssam	ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
1100178354Ssam	printf(" rssi %d\n", rssi);
1101170530Ssam
1102170530Ssam	if (isnew) {
1103170530Ssam		printf("[%s] caps 0x%x bintval %u erp 0x%x",
1104170530Ssam			ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp);
1105178354Ssam		if (sp->country != NULL)
1106178354Ssam			dump_country(sp->country);
1107170530Ssam		printf("\n");
1108170530Ssam	}
1109170530Ssam}
1110170530Ssam#endif /* IEEE80211_DEBUG */
1111170530Ssam
1112170530Ssam/*
1113170530Ssam * Process a beacon or probe response frame.
1114170530Ssam */
1115170530Ssamvoid
1116178354Ssamieee80211_add_scan(struct ieee80211vap *vap,
1117170530Ssam	const struct ieee80211_scanparams *sp,
1118170530Ssam	const struct ieee80211_frame *wh,
1119192468Ssam	int subtype, int rssi, int noise)
1120170530Ssam{
1121178354Ssam	struct ieee80211com *ic = vap->iv_ic;
1122170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
1123170530Ssam
1124178354Ssam	/* XXX locking */
1125170530Ssam	/*
1126170530Ssam	 * Frames received during startup are discarded to avoid
1127170530Ssam	 * using scan state setup on the initial entry to the timer
1128170530Ssam	 * callback.  This can occur because the device may enable
1129170530Ssam	 * rx prior to our doing the initial channel change in the
1130178354Ssam	 * timer routine.
1131170530Ssam	 */
1132170530Ssam	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
1133170530Ssam		return;
1134170530Ssam#ifdef IEEE80211_DEBUG
1135178354Ssam	if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
1136178354Ssam		dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
1137170530Ssam#endif
1138170530Ssam	if (ss->ss_ops != NULL &&
1139192468Ssam	    ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise)) {
1140170530Ssam		/*
1141170530Ssam		 * If we've reached the min dwell time terminate
1142170530Ssam		 * the timer so we'll switch to the next channel.
1143170530Ssam		 */
1144170530Ssam		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
1145170530Ssam		    time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
1146178354Ssam			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
1147170530Ssam			    "%s: chan %3d%c min dwell met (%u > %lu)\n",
1148170530Ssam			    __func__,
1149170530Ssam			    ieee80211_chan2ieee(ic, ic->ic_curchan),
1150170530Ssam				channel_type(ic->ic_curchan),
1151170530Ssam			    ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
1152170530Ssam			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
1153170530Ssam			/*
1154170530Ssam			 * NB: trigger at next clock tick or wait for the
1155178354Ssam			 * hardware.
1156170530Ssam			 */
1157178354Ssam			ic->ic_scan_mindwell(ss);
1158170530Ssam		}
1159170530Ssam	}
1160170530Ssam}
1161170530Ssam
1162170530Ssam/*
1163170530Ssam * Timeout/age scan cache entries; called from sta timeout
1164170530Ssam * timer (XXX should be self-contained).
1165170530Ssam */
1166170530Ssamvoid
1167170530Ssamieee80211_scan_timeout(struct ieee80211com *ic)
1168170530Ssam{
1169170530Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
1170170530Ssam
1171170530Ssam	if (ss->ss_ops != NULL)
1172170530Ssam		ss->ss_ops->scan_age(ss);
1173170530Ssam}
1174170530Ssam
1175170530Ssam/*
1176170530Ssam * Mark a scan cache entry after a successful associate.
1177170530Ssam */
1178170530Ssamvoid
1179178354Ssamieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[])
1180170530Ssam{
1181178354Ssam	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
1182170530Ssam
1183170530Ssam	if (ss->ss_ops != NULL) {
1184178354Ssam		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN,
1185170530Ssam			mac, "%s",  __func__);
1186170530Ssam		ss->ss_ops->scan_assoc_success(ss, mac);
1187170530Ssam	}
1188170530Ssam}
1189170530Ssam
1190170530Ssam/*
1191170530Ssam * Demerit a scan cache entry after failing to associate.
1192170530Ssam */
1193170530Ssamvoid
1194178354Ssamieee80211_scan_assoc_fail(struct ieee80211vap *vap,
1195170530Ssam	const uint8_t mac[], int reason)
1196170530Ssam{
1197178354Ssam	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
1198170530Ssam
1199170530Ssam	if (ss->ss_ops != NULL) {
1200178354Ssam		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac,
1201170530Ssam			"%s: reason %u", __func__, reason);
1202170530Ssam		ss->ss_ops->scan_assoc_fail(ss, mac, reason);
1203170530Ssam	}
1204170530Ssam}
1205170530Ssam
1206170530Ssam/*
1207170530Ssam * Iterate over the contents of the scan cache.
1208170530Ssam */
1209170530Ssamvoid
1210178354Ssamieee80211_scan_iterate(struct ieee80211vap *vap,
1211170530Ssam	ieee80211_scan_iter_func *f, void *arg)
1212170530Ssam{
1213178354Ssam	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
1214170530Ssam
1215170530Ssam	if (ss->ss_ops != NULL)
1216170530Ssam		ss->ss_ops->scan_iterate(ss, f, arg);
1217170530Ssam}
1218170530Ssam
1219170530Ssam/*
1220170530Ssam * Flush the contents of the scan cache.
1221170530Ssam */
1222170530Ssamvoid
1223178354Ssamieee80211_scan_flush(struct ieee80211vap *vap)
1224170530Ssam{
1225178354Ssam	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
1226170530Ssam
1227178354Ssam	if (ss->ss_ops != NULL && ss->ss_vap == vap) {
1228178354Ssam		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n",  __func__);
1229170530Ssam		ss->ss_ops->scan_flush(ss);
1230170530Ssam	}
1231170530Ssam}
1232178354Ssam
1233178354Ssam/*
1234178354Ssam * Check the scan cache for an ap/channel to use; if that
1235178354Ssam * fails then kick off a new scan.
1236178354Ssam */
1237178354Ssamstruct ieee80211_channel *
1238178354Ssamieee80211_scan_pickchannel(struct ieee80211com *ic, int flags)
1239178354Ssam{
1240178354Ssam	struct ieee80211_scan_state *ss = ic->ic_scan;
1241178354Ssam
1242178354Ssam	IEEE80211_LOCK_ASSERT(ic);
1243178354Ssam
1244178354Ssam	if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) {
1245178354Ssam		/* XXX printf? */
1246178354Ssam		return NULL;
1247178354Ssam	}
1248178354Ssam	if (ss->ss_ops->scan_pickchan == NULL) {
1249178354Ssam		IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
1250178354Ssam		    "%s: scan module does not support picking a channel, "
1251178354Ssam		    "opmode %s\n", __func__, ss->ss_vap->iv_opmode);
1252178354Ssam		return NULL;
1253178354Ssam	}
1254178354Ssam	return ss->ss_ops->scan_pickchan(ss, flags);
1255178354Ssam}
1256