14f898719SImre Vadász /*-
24f898719SImre Vadász  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
34f898719SImre Vadász  * All rights reserved.
44f898719SImre Vadász  *
54f898719SImre Vadász  * Redistribution and use in source and binary forms, with or without
64f898719SImre Vadász  * modification, are permitted provided that the following conditions
74f898719SImre Vadász  * are met:
84f898719SImre Vadász  * 1. Redistributions of source code must retain the above copyright
94f898719SImre Vadász  *    notice, this list of conditions and the following disclaimer.
104f898719SImre Vadász  * 2. Redistributions in binary form must reproduce the above copyright
114f898719SImre Vadász  *    notice, this list of conditions and the following disclaimer in the
124f898719SImre Vadász  *    documentation and/or other materials provided with the distribution.
134f898719SImre Vadász  *
144f898719SImre Vadász  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
154f898719SImre Vadász  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
164f898719SImre Vadász  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
174f898719SImre Vadász  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
184f898719SImre Vadász  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
194f898719SImre Vadász  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
204f898719SImre Vadász  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
214f898719SImre Vadász  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
224f898719SImre Vadász  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
234f898719SImre Vadász  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
244f898719SImre Vadász  */
254f898719SImre Vadász 
264f898719SImre Vadász #include <sys/cdefs.h>
274f898719SImre Vadász __FBSDID("$FreeBSD$");
284f898719SImre Vadász 
294f898719SImre Vadász /*
304f898719SImre Vadász  * IEEE 802.11 scanning support.
314f898719SImre Vadász  */
324f898719SImre Vadász #include "opt_wlan.h"
334f898719SImre Vadász 
344f898719SImre Vadász #include <sys/param.h>
354f898719SImre Vadász #include <sys/systm.h>
364f898719SImre Vadász #include <sys/proc.h>
374f898719SImre Vadász #include <sys/kernel.h>
384f655ef5SMatthew Dillon #include <sys/malloc.h>
394f898719SImre Vadász #include <sys/condvar.h>
404f898719SImre Vadász 
414f898719SImre Vadász #include <sys/socket.h>
424f898719SImre Vadász 
434f898719SImre Vadász #include <net/if.h>
444f898719SImre Vadász #include <net/if_var.h>
454f898719SImre Vadász #include <net/if_media.h>
464f898719SImre Vadász #include <net/ethernet.h>
474f898719SImre Vadász 
484f898719SImre Vadász #include <netproto/802_11/ieee80211_var.h>
494f898719SImre Vadász 
504f898719SImre Vadász #include <netproto/802_11/ieee80211_scan_sw.h>
514f898719SImre Vadász 
524f898719SImre Vadász #include <net/bpf.h>
534f898719SImre Vadász 
544f898719SImre Vadász struct scan_state {
554f898719SImre Vadász 	struct ieee80211_scan_state base;	/* public state */
564f898719SImre Vadász 
574f898719SImre Vadász 	u_int			ss_iflags;	/* flags used internally */
584f898719SImre Vadász #define	ISCAN_MINDWELL 		0x0001		/* min dwell time reached */
594f898719SImre Vadász #define	ISCAN_DISCARD		0x0002		/* discard rx'd frames */
604f898719SImre Vadász #define	ISCAN_CANCEL		0x0004		/* cancel current scan */
614f898719SImre Vadász #define	ISCAN_ABORT		0x0008		/* end the scan immediately */
624f655ef5SMatthew Dillon #define	ISCAN_RUNNING		0x0010		/* scan was started */
634f655ef5SMatthew Dillon 
644f898719SImre Vadász 	unsigned long		ss_chanmindwell;  /* min dwell on curchan */
654f898719SImre Vadász 	unsigned long		ss_scanend;	/* time scan must stop */
664f898719SImre Vadász 	u_int			ss_duration;	/* duration for next scan */
674f655ef5SMatthew Dillon 	struct task		ss_scan_start;	/* scan start */
684f655ef5SMatthew Dillon 	struct timeout_task	ss_scan_curchan;  /* scan execution */
694f898719SImre Vadász };
704f898719SImre Vadász #define	SCAN_PRIVATE(ss)	((struct scan_state *) ss)
714f898719SImre Vadász 
724f898719SImre Vadász /*
734f898719SImre Vadász  * Amount of time to go off-channel during a background
744f898719SImre Vadász  * scan.  This value should be large enough to catch most
754f898719SImre Vadász  * ap's but short enough that we can return on-channel
764f898719SImre Vadász  * before our listen interval expires.
774f898719SImre Vadász  *
784f898719SImre Vadász  * XXX tunable
794f898719SImre Vadász  * XXX check against configured listen interval
804f898719SImre Vadász  */
814f898719SImre Vadász #define	IEEE80211_SCAN_OFFCHANNEL	msecs_to_ticks(150)
824f898719SImre Vadász 
834f898719SImre Vadász /*
844f898719SImre Vadász  * Roaming-related defaults.  RSSI thresholds are as returned by the
854f898719SImre Vadász  * driver (.5dBm).  Transmit rate thresholds are IEEE rate codes (i.e
864f898719SImre Vadász  * .5M units) or MCS.
874f898719SImre Vadász  */
884f898719SImre Vadász /* rssi thresholds */
894f898719SImre Vadász #define	ROAM_RSSI_11A_DEFAULT		14	/* 11a bss */
904f898719SImre Vadász #define	ROAM_RSSI_11B_DEFAULT		14	/* 11b bss */
914f898719SImre Vadász #define	ROAM_RSSI_11BONLY_DEFAULT	14	/* 11b-only bss */
924f898719SImre Vadász /* transmit rate thresholds */
934f898719SImre Vadász #define	ROAM_RATE_11A_DEFAULT		2*12	/* 11a bss */
944f898719SImre Vadász #define	ROAM_RATE_11B_DEFAULT		2*5	/* 11b bss */
954f898719SImre Vadász #define	ROAM_RATE_11BONLY_DEFAULT	2*1	/* 11b-only bss */
964f898719SImre Vadász #define	ROAM_RATE_HALF_DEFAULT		2*6	/* half-width 11a/g bss */
974f898719SImre Vadász #define	ROAM_RATE_QUARTER_DEFAULT	2*3	/* quarter-width 11a/g bss */
984f898719SImre Vadász #define	ROAM_MCS_11N_DEFAULT		(1 | IEEE80211_RATE_MCS) /* 11n bss */
994f898719SImre Vadász 
1004f898719SImre Vadász static	void scan_curchan(struct ieee80211_scan_state *, unsigned long);
1014f898719SImre Vadász static	void scan_mindwell(struct ieee80211_scan_state *);
1024f655ef5SMatthew Dillon static	void scan_signal(struct ieee80211_scan_state *, int);
1034f655ef5SMatthew Dillon static	void scan_signal_locked(struct ieee80211_scan_state *, int);
1044f655ef5SMatthew Dillon static	void scan_start(void *, int);
1054f655ef5SMatthew Dillon static	void scan_curchan_task(void *, int);
1064f655ef5SMatthew Dillon static	void scan_end(struct ieee80211_scan_state *, int);
1074f655ef5SMatthew Dillon static	void scan_done(struct ieee80211_scan_state *, int);
1084f898719SImre Vadász 
1094f898719SImre Vadász MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
1104f898719SImre Vadász 
1114f898719SImre Vadász static void
ieee80211_swscan_detach(struct ieee80211com * ic)1124f898719SImre Vadász ieee80211_swscan_detach(struct ieee80211com *ic)
1134f898719SImre Vadász {
1144f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
1154f898719SImre Vadász 
1164f898719SImre Vadász 	if (ss != NULL) {
1174f655ef5SMatthew Dillon 		scan_signal(ss, ISCAN_ABORT);
1184f655ef5SMatthew Dillon 		ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
1194f655ef5SMatthew Dillon 		taskqueue_drain_timeout(ic->ic_tq,
1204f655ef5SMatthew Dillon 		    &SCAN_PRIVATE(ss)->ss_scan_curchan);
1214f898719SImre Vadász 		KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
1224f898719SImre Vadász 		    ("scan still running"));
1234f898719SImre Vadász 
1244f898719SImre Vadász 		/*
1254f898719SImre Vadász 		 * For now, do the ss_ops detach here rather
1264f898719SImre Vadász 		 * than ieee80211_scan_detach().
1274f898719SImre Vadász 		 *
1284f898719SImre Vadász 		 * I'll figure out how to cleanly split things up
1294f898719SImre Vadász 		 * at a later date.
1304f898719SImre Vadász 		 */
1314f898719SImre Vadász 		if (ss->ss_ops != NULL) {
1324f898719SImre Vadász 			ss->ss_ops->scan_detach(ss);
1334f898719SImre Vadász 			ss->ss_ops = NULL;
1344f898719SImre Vadász 		}
1354f898719SImre Vadász 		ic->ic_scan = NULL;
1364f655ef5SMatthew Dillon 		IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN);
1374f898719SImre Vadász 	}
1384f898719SImre Vadász }
1394f898719SImre Vadász 
1404f898719SImre Vadász static void
ieee80211_swscan_vattach(struct ieee80211vap * vap)1414f898719SImre Vadász ieee80211_swscan_vattach(struct ieee80211vap *vap)
1424f898719SImre Vadász {
1434f898719SImre Vadász 	/* nothing to do for now */
1444f898719SImre Vadász 	/*
1454f898719SImre Vadász 	 * TODO: all of the vap scan calls should be methods!
1464f898719SImre Vadász 	 */
1474f898719SImre Vadász 
1484f898719SImre Vadász }
1494f898719SImre Vadász 
1504f898719SImre Vadász static void
ieee80211_swscan_vdetach(struct ieee80211vap * vap)1514f898719SImre Vadász ieee80211_swscan_vdetach(struct ieee80211vap *vap)
1524f898719SImre Vadász {
1534f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
1544f655ef5SMatthew Dillon 	struct ieee80211_scan_state *ss = ic->ic_scan;
1554f898719SImre Vadász 
1564f898719SImre Vadász 	IEEE80211_LOCK_ASSERT(ic);
1574f655ef5SMatthew Dillon 
1584f655ef5SMatthew Dillon 	if (ss != NULL && ss->ss_vap == vap &&
1594f655ef5SMatthew Dillon 	    (ic->ic_flags & IEEE80211_F_SCAN))
1604f655ef5SMatthew Dillon 		scan_signal_locked(ss, ISCAN_ABORT);
1614f898719SImre Vadász }
1624f898719SImre Vadász 
1634f898719SImre Vadász static void
ieee80211_swscan_set_scan_duration(struct ieee80211vap * vap,u_int duration)1644f898719SImre Vadász ieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration)
1654f898719SImre Vadász {
1664f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
1674f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
1684f898719SImre Vadász 
1694f898719SImre Vadász 	IEEE80211_LOCK_ASSERT(ic);
1704f898719SImre Vadász 
1714f898719SImre Vadász 	/* NB: flush frames rx'd before 1st channel change */
1724f898719SImre Vadász 	SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
1734f898719SImre Vadász 	SCAN_PRIVATE(ss)->ss_duration = duration;
1744f898719SImre Vadász }
1754f898719SImre Vadász 
1764f898719SImre Vadász /*
1774f898719SImre Vadász  * Start a scan unless one is already going.
1784f898719SImre Vadász  */
1794f898719SImre Vadász 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[])1804f898719SImre Vadász ieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan,
1814f898719SImre Vadász     struct ieee80211vap *vap, int flags, u_int duration,
1824f898719SImre Vadász     u_int mindwell, u_int maxdwell,
1834f898719SImre Vadász     u_int nssid, const struct ieee80211_scan_ssid ssids[])
1844f898719SImre Vadász {
1854f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
1864f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
1874f898719SImre Vadász 
1884f898719SImre Vadász 	IEEE80211_LOCK_ASSERT(ic);
1894f898719SImre Vadász 
1904f898719SImre Vadász 	if (ic->ic_flags & IEEE80211_F_CSAPENDING) {
1914f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
1924f898719SImre Vadász 		    "%s: scan inhibited by pending channel change\n", __func__);
1934f898719SImre Vadász 	} else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
1944f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
1954f898719SImre Vadász 		    "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n"
1964f898719SImre Vadász 		    , __func__
1974f898719SImre Vadász 		    , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"
1984f898719SImre Vadász 		    , duration, mindwell, maxdwell
1994f898719SImre Vadász 		    , ieee80211_phymode_name[vap->iv_des_mode]
2004f898719SImre Vadász 		    , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append"
2014f898719SImre Vadász 		    , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : ""
2024f898719SImre Vadász 		    , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : ""
2034f898719SImre Vadász 		    , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : ""
2044f898719SImre Vadász 		    , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : ""
2054f898719SImre Vadász 		    , flags & IEEE80211_SCAN_ONCE ? ", once" : ""
2064f898719SImre Vadász 		);
2074f898719SImre Vadász 
2084f898719SImre Vadász 		ieee80211_scan_update_locked(vap, scan);
2094f898719SImre Vadász 		if (ss->ss_ops != NULL) {
2104f898719SImre Vadász 			if ((flags & IEEE80211_SCAN_NOSSID) == 0)
2114f898719SImre Vadász 				ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
2124f898719SImre Vadász 
2134f898719SImre Vadász 			/* NB: top 4 bits for internal use */
2144f898719SImre Vadász 			ss->ss_flags = flags & 0xfff;
2154f898719SImre Vadász 			if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
2164f898719SImre Vadász 				vap->iv_stats.is_scan_active++;
2174f898719SImre Vadász 			else
2184f898719SImre Vadász 				vap->iv_stats.is_scan_passive++;
2194f898719SImre Vadász 			if (flags & IEEE80211_SCAN_FLUSH)
2204f898719SImre Vadász 				ss->ss_ops->scan_flush(ss);
2214f898719SImre Vadász 			if (flags & IEEE80211_SCAN_BGSCAN)
2224f898719SImre Vadász 				ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
2234f898719SImre Vadász 
2244f898719SImre Vadász 			/* Set duration for this particular scan */
2254f898719SImre Vadász 			ieee80211_swscan_set_scan_duration(vap, duration);
2264f898719SImre Vadász 
2274f898719SImre Vadász 			ss->ss_next = 0;
2284f898719SImre Vadász 			ss->ss_mindwell = mindwell;
2294f898719SImre Vadász 			ss->ss_maxdwell = maxdwell;
2304f898719SImre Vadász 			/* NB: scan_start must be before the scan runtask */
2314f898719SImre Vadász 			ss->ss_ops->scan_start(ss, vap);
2324f898719SImre Vadász #ifdef IEEE80211_DEBUG
2334f898719SImre Vadász 			if (ieee80211_msg_scan(vap))
2344f898719SImre Vadász 				ieee80211_scan_dump(ss);
2354f898719SImre Vadász #endif /* IEEE80211_DEBUG */
2364f898719SImre Vadász 			ic->ic_flags |= IEEE80211_F_SCAN;
2374f898719SImre Vadász 
2384f898719SImre Vadász 			/* Start scan task */
2394f655ef5SMatthew Dillon 			ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
2404f898719SImre Vadász 		}
2414f898719SImre Vadász 		return 1;
2424f898719SImre Vadász 	} else {
2434f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
2444f898719SImre Vadász 		    "%s: %s scan already in progress\n", __func__,
2454f898719SImre Vadász 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
2464f898719SImre Vadász 	}
2474f898719SImre Vadász 	return 0;
2484f898719SImre Vadász }
2494f898719SImre Vadász 
2504f655ef5SMatthew Dillon 
2514f898719SImre Vadász /*
2524f898719SImre Vadász  * Start a scan unless one is already going.
2534f898719SImre Vadász  *
2544f898719SImre Vadász  * Called without the comlock held; grab the comlock as appropriate.
2554f898719SImre Vadász  */
2564f898719SImre Vadász 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[])2574f898719SImre Vadász ieee80211_swscan_start_scan(const struct ieee80211_scanner *scan,
2584f898719SImre Vadász     struct ieee80211vap *vap, int flags,
2594f898719SImre Vadász     u_int duration, u_int mindwell, u_int maxdwell,
2604f898719SImre Vadász     u_int nssid, const struct ieee80211_scan_ssid ssids[])
2614f898719SImre Vadász {
2624f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
2634f898719SImre Vadász 	int result;
2644f898719SImre Vadász 
2654f655ef5SMatthew Dillon 	IEEE80211_UNLOCK_ASSERT(ic);
2664f898719SImre Vadász 
2674f898719SImre Vadász 	IEEE80211_LOCK(ic);
2684f898719SImre Vadász 	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
2694f898719SImre Vadász 	    mindwell, maxdwell, nssid, ssids);
2704f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
2714f898719SImre Vadász 
2724f898719SImre Vadász 	return result;
2734f898719SImre Vadász }
2744f898719SImre Vadász 
2754f898719SImre Vadász /*
2764f898719SImre Vadász  * Check the scan cache for an ap/channel to use; if that
2774f898719SImre Vadász  * fails then kick off a new scan.
2784f898719SImre Vadász  *
2794f898719SImre Vadász  * Called with the comlock held.
2804f898719SImre Vadász  *
2814f898719SImre Vadász  * XXX TODO: split out!
2824f898719SImre Vadász  */
2834f898719SImre Vadász 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[])2844f898719SImre Vadász ieee80211_swscan_check_scan(const struct ieee80211_scanner *scan,
2854f898719SImre Vadász     struct ieee80211vap *vap, int flags,
2864f898719SImre Vadász     u_int duration, u_int mindwell, u_int maxdwell,
2874f898719SImre Vadász     u_int nssid, const struct ieee80211_scan_ssid ssids[])
2884f898719SImre Vadász {
2894f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
2904f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
2914f898719SImre Vadász 	int result;
2924f898719SImre Vadász 
2934f898719SImre Vadász 	IEEE80211_LOCK_ASSERT(ic);
2944f898719SImre Vadász 
2954f898719SImre Vadász 	if (ss->ss_ops != NULL) {
2964f898719SImre Vadász 		/* XXX verify ss_ops matches vap->iv_opmode */
2974f898719SImre Vadász 		if ((flags & IEEE80211_SCAN_NOSSID) == 0) {
2984f898719SImre Vadász 			/*
2994f898719SImre Vadász 			 * Update the ssid list and mark flags so if
3004f898719SImre Vadász 			 * we call start_scan it doesn't duplicate work.
3014f898719SImre Vadász 			 */
3024f898719SImre Vadász 			ieee80211_scan_copy_ssid(vap, ss, nssid, ssids);
3034f898719SImre Vadász 			flags |= IEEE80211_SCAN_NOSSID;
3044f898719SImre Vadász 		}
3054f898719SImre Vadász 		if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 &&
3064f898719SImre Vadász 		    (flags & IEEE80211_SCAN_FLUSH) == 0 &&
3074f655ef5SMatthew Dillon 		    ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) {
3084f898719SImre Vadász 			/*
3094f898719SImre Vadász 			 * We're not currently scanning and the cache is
3104f898719SImre Vadász 			 * deemed hot enough to consult.  Lock out others
3114f898719SImre Vadász 			 * by marking IEEE80211_F_SCAN while we decide if
3124f898719SImre Vadász 			 * something is already in the scan cache we can
3134f898719SImre Vadász 			 * use.  Also discard any frames that might come
3144f898719SImre Vadász 			 * in while temporarily marked as scanning.
3154f898719SImre Vadász 			 */
3164f898719SImre Vadász 			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
3174f898719SImre Vadász 			ic->ic_flags |= IEEE80211_F_SCAN;
3184f898719SImre Vadász 
3194f898719SImre Vadász 			/* NB: need to use supplied flags in check */
3204f898719SImre Vadász 			ss->ss_flags = flags & 0xff;
3214f898719SImre Vadász 			result = ss->ss_ops->scan_end(ss, vap);
3224f898719SImre Vadász 
3234f898719SImre Vadász 			ic->ic_flags &= ~IEEE80211_F_SCAN;
3244f898719SImre Vadász 			SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD;
3254f898719SImre Vadász 			if (result) {
3264f898719SImre Vadász 				ieee80211_notify_scan_done(vap);
3274f898719SImre Vadász 				return 1;
3284f898719SImre Vadász 			}
3294f898719SImre Vadász 		}
3304f898719SImre Vadász 	}
3314f898719SImre Vadász 	result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration,
3324f898719SImre Vadász 	    mindwell, maxdwell, nssid, ssids);
3334f898719SImre Vadász 
3344f898719SImre Vadász 	return result;
3354f898719SImre Vadász }
3364f898719SImre Vadász 
3374f898719SImre Vadász /*
3384f898719SImre Vadász  * Restart a previous scan.  If the previous scan completed
3394f898719SImre Vadász  * then we start again using the existing channel list.
3404f898719SImre Vadász  */
3414f898719SImre Vadász static int
ieee80211_swscan_bg_scan(const struct ieee80211_scanner * scan,struct ieee80211vap * vap,int flags)3424f898719SImre Vadász ieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan,
3434f898719SImre Vadász     struct ieee80211vap *vap, int flags)
3444f898719SImre Vadász {
3454f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
3464f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
3474f898719SImre Vadász 
3484f898719SImre Vadász 	/* XXX assert unlocked? */
3494f898719SImre Vadász 	// IEEE80211_UNLOCK_ASSERT(ic);
3504f898719SImre Vadász 
3514f898719SImre Vadász 	IEEE80211_LOCK(ic);
3524f898719SImre Vadász 	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
3534f898719SImre Vadász 		u_int duration;
3544f898719SImre Vadász 		/*
3554f898719SImre Vadász 		 * Go off-channel for a fixed interval that is large
3564f898719SImre Vadász 		 * enough to catch most ap's but short enough that
3574f898719SImre Vadász 		 * we can return on-channel before our listen interval
3584f898719SImre Vadász 		 * expires.
3594f898719SImre Vadász 		 */
3604f898719SImre Vadász 		duration = IEEE80211_SCAN_OFFCHANNEL;
3614f898719SImre Vadász 
3624f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
3634f898719SImre Vadász 		    "%s: %s scan, ticks %u duration %u\n", __func__,
3644f898719SImre Vadász 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive",
3654f898719SImre Vadász 		    ticks, duration);
3664f898719SImre Vadász 
3674f898719SImre Vadász 		ieee80211_scan_update_locked(vap, scan);
3684f898719SImre Vadász 		if (ss->ss_ops != NULL) {
3694f898719SImre Vadász 			ss->ss_vap = vap;
3704f898719SImre Vadász 			/*
3714f898719SImre Vadász 			 * A background scan does not select a new sta; it
3724f898719SImre Vadász 			 * just refreshes the scan cache.  Also, indicate
3734f898719SImre Vadász 			 * the scan logic should follow the beacon schedule:
3744f898719SImre Vadász 			 * we go off-channel and scan for a while, then
3754f898719SImre Vadász 			 * return to the bss channel to receive a beacon,
3764f898719SImre Vadász 			 * then go off-channel again.  All during this time
3774f898719SImre Vadász 			 * we notify the ap we're in power save mode.  When
3784f898719SImre Vadász 			 * the scan is complete we leave power save mode.
3794f898719SImre Vadász 			 * If any beacon indicates there are frames pending
3804f898719SImre Vadász 			 * for us then we drop out of power save mode
3814f898719SImre Vadász 			 * (and background scan) automatically by way of the
3824f898719SImre Vadász 			 * usual sta power save logic.
3834f898719SImre Vadász 			 */
3844f898719SImre Vadász 			ss->ss_flags |= IEEE80211_SCAN_NOPICK
3854f898719SImre Vadász 				     |  IEEE80211_SCAN_BGSCAN
3864f898719SImre Vadász 				     |  flags
3874f898719SImre Vadász 				     ;
3884f898719SImre Vadász 			/* if previous scan completed, restart */
3894f898719SImre Vadász 			if (ss->ss_next >= ss->ss_last) {
3904f898719SImre Vadász 				if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
3914f898719SImre Vadász 					vap->iv_stats.is_scan_active++;
3924f898719SImre Vadász 				else
3934f898719SImre Vadász 					vap->iv_stats.is_scan_passive++;
3944f898719SImre Vadász 				/*
3954f898719SImre Vadász 				 * NB: beware of the scan cache being flushed;
3964f898719SImre Vadász 				 *     if the channel list is empty use the
3974f898719SImre Vadász 				 *     scan_start method to populate it.
3984f898719SImre Vadász 				 */
3994f898719SImre Vadász 				ss->ss_next = 0;
4004f898719SImre Vadász 				if (ss->ss_last != 0)
4014f898719SImre Vadász 					ss->ss_ops->scan_restart(ss, vap);
4024f898719SImre Vadász 				else {
4034f898719SImre Vadász 					ss->ss_ops->scan_start(ss, vap);
4044f898719SImre Vadász #ifdef IEEE80211_DEBUG
4054f898719SImre Vadász 					if (ieee80211_msg_scan(vap))
4064f898719SImre Vadász 						ieee80211_scan_dump(ss);
4074f898719SImre Vadász #endif /* IEEE80211_DEBUG */
4084f898719SImre Vadász 				}
4094f898719SImre Vadász 			}
4104f898719SImre Vadász 			ieee80211_swscan_set_scan_duration(vap, duration);
4114f898719SImre Vadász 			ss->ss_maxdwell = duration;
4124f898719SImre Vadász 			ic->ic_flags |= IEEE80211_F_SCAN;
4134f898719SImre Vadász 			ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
4144f655ef5SMatthew Dillon 			ieee80211_runtask(ic,
4154f655ef5SMatthew Dillon 			    &SCAN_PRIVATE(ss)->ss_scan_start);
4164f898719SImre Vadász 		} else {
4174f898719SImre Vadász 			/* XXX msg+stat */
4184f898719SImre Vadász 		}
4194f898719SImre Vadász 	} else {
4204f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
4214f898719SImre Vadász 		    "%s: %s scan already in progress\n", __func__,
4224f898719SImre Vadász 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive");
4234f898719SImre Vadász 	}
4244f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
4254f898719SImre Vadász 
4264f898719SImre Vadász 	/* NB: racey, does it matter? */
4274f898719SImre Vadász 	return (ic->ic_flags & IEEE80211_F_SCAN);
4284f898719SImre Vadász }
4294f898719SImre Vadász 
430*84bcb7f7SImre Vadász /*
431*84bcb7f7SImre Vadász  * Taskqueue work to cancel a scan.
432*84bcb7f7SImre Vadász  *
433*84bcb7f7SImre Vadász  * Note: for offload scan devices, we may want to call into the
434*84bcb7f7SImre Vadász  * driver to try and cancel scanning, however it may not be cancelable.
435*84bcb7f7SImre Vadász  */
4364f898719SImre Vadász static void
cancel_scan(struct ieee80211vap * vap,int any,const char * func)4374f655ef5SMatthew Dillon cancel_scan(struct ieee80211vap *vap, int any, const char *func)
4384f898719SImre Vadász {
4394f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
4404f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
4414f898719SImre Vadász 
4424f898719SImre Vadász 	IEEE80211_LOCK(ic);
4434f898719SImre Vadász 	if ((ic->ic_flags & IEEE80211_F_SCAN) &&
4444f655ef5SMatthew Dillon 	    (any || ss->ss_vap == vap) &&
4454f898719SImre Vadász 	    (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
4464f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
4474f655ef5SMatthew Dillon 		    "%s: cancel %s scan\n", func,
4484f898719SImre Vadász 		    ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
4494f898719SImre Vadász 			"active" : "passive");
4504f898719SImre Vadász 
4514f655ef5SMatthew Dillon 		/* clear bg scan NOPICK */
4524f898719SImre Vadász 		ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
4534f655ef5SMatthew Dillon 		/* mark cancel request and wake up the scan task */
4544f655ef5SMatthew Dillon 		scan_signal_locked(ss, ISCAN_CANCEL);
4554f898719SImre Vadász 	} else {
4564f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
4574f898719SImre Vadász 		    "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n",
4584f655ef5SMatthew Dillon 			func,
4594f898719SImre Vadász 			!! (ic->ic_flags & IEEE80211_F_SCAN),
4604f898719SImre Vadász 			(ss->ss_vap == vap ? "match" : "nomatch"),
4614f898719SImre Vadász 			!! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL));
4624f898719SImre Vadász 	}
4634f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
4644f898719SImre Vadász }
4654f898719SImre Vadász 
4664f898719SImre Vadász /*
4674f655ef5SMatthew Dillon  * Cancel any scan currently going on for the specified vap.
4684f655ef5SMatthew Dillon  */
4694f655ef5SMatthew Dillon static void
ieee80211_swscan_cancel_scan(struct ieee80211vap * vap)4704f655ef5SMatthew Dillon ieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
4714f655ef5SMatthew Dillon {
4724f655ef5SMatthew Dillon 	cancel_scan(vap, 0, __func__);
4734f655ef5SMatthew Dillon }
4744f655ef5SMatthew Dillon 
4754f655ef5SMatthew Dillon /*
4764f898719SImre Vadász  * Cancel any scan currently going on.
4774f898719SImre Vadász  */
4784f898719SImre Vadász static void
ieee80211_swscan_cancel_anyscan(struct ieee80211vap * vap)4794f898719SImre Vadász ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap)
4804f898719SImre Vadász {
481*84bcb7f7SImre Vadász 	/* XXX for now - just don't do this per packet. */
482*84bcb7f7SImre Vadász 	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
483*84bcb7f7SImre Vadász 		return;
484*84bcb7f7SImre Vadász 
4854f655ef5SMatthew Dillon 	cancel_scan(vap, 1, __func__);
4864f898719SImre Vadász }
4874f898719SImre Vadász 
4884f898719SImre Vadász /*
4894f655ef5SMatthew Dillon  * Manually switch to the next channel in the channel list.
4904f655ef5SMatthew Dillon  * Provided for drivers that manage scanning themselves
4914f655ef5SMatthew Dillon  * (e.g. for firmware-based devices).
4924f898719SImre Vadász  */
4934f898719SImre Vadász static void
ieee80211_swscan_scan_next(struct ieee80211vap * vap)4944f898719SImre Vadász ieee80211_swscan_scan_next(struct ieee80211vap *vap)
4954f898719SImre Vadász {
4964f655ef5SMatthew Dillon 	struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
4974f898719SImre Vadász 
4984f898719SImre Vadász 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
4994f898719SImre Vadász 
5004f898719SImre Vadász 	/* wake up the scan task */
5014f655ef5SMatthew Dillon 	scan_signal(ss, 0);
5024f898719SImre Vadász }
5034f898719SImre Vadász 
5044f898719SImre Vadász /*
5054f655ef5SMatthew Dillon  * Manually stop a scan that is currently running.
5064f655ef5SMatthew Dillon  * Provided for drivers that are not able to scan single channels
5074f655ef5SMatthew Dillon  * (e.g. for firmware-based devices).
5084f898719SImre Vadász  */
5094f898719SImre Vadász static void
ieee80211_swscan_scan_done(struct ieee80211vap * vap)5104f898719SImre Vadász ieee80211_swscan_scan_done(struct ieee80211vap *vap)
5114f898719SImre Vadász {
5124f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
5134f655ef5SMatthew Dillon 	struct ieee80211_scan_state *ss = ic->ic_scan;
5144f898719SImre Vadász 
5154f898719SImre Vadász 	IEEE80211_LOCK_ASSERT(ic);
5164f898719SImre Vadász 
5174f655ef5SMatthew Dillon 	scan_signal_locked(ss, 0);
5184f898719SImre Vadász }
5194f898719SImre Vadász 
5204f898719SImre Vadász /*
5214f655ef5SMatthew Dillon  * Probe the current channel, if allowed, while scanning.
5224f898719SImre Vadász  * If the channel is not marked passive-only then send
5234f898719SImre Vadász  * a probe request immediately.  Otherwise mark state and
5244f898719SImre Vadász  * listen for beacons on the channel; if we receive something
5254f898719SImre Vadász  * then we'll transmit a probe request.
5264f898719SImre Vadász  */
5274f898719SImre Vadász static void
ieee80211_swscan_probe_curchan(struct ieee80211vap * vap,int force)5284f898719SImre Vadász ieee80211_swscan_probe_curchan(struct ieee80211vap *vap, int force)
5294f898719SImre Vadász {
5304f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
5314f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
5324f898719SImre Vadász 	struct ifnet *ifp = vap->iv_ifp;
5334f898719SImre Vadász 	int i;
5344f898719SImre Vadász 
5354f898719SImre Vadász 	/*
536*84bcb7f7SImre Vadász 	 * Full-offload scan devices don't require this.
537*84bcb7f7SImre Vadász 	 */
538*84bcb7f7SImre Vadász 	if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD)
539*84bcb7f7SImre Vadász 		return;
540*84bcb7f7SImre Vadász 
541*84bcb7f7SImre Vadász 	/*
5424f898719SImre Vadász 	 * Send directed probe requests followed by any
5434f898719SImre Vadász 	 * broadcast probe request.
5444f898719SImre Vadász 	 * XXX remove dependence on ic/vap->iv_bss
5454f898719SImre Vadász 	 */
5464f898719SImre Vadász 	for (i = 0; i < ss->ss_nssid; i++)
5474f898719SImre Vadász 		ieee80211_send_probereq(vap->iv_bss,
5484f898719SImre Vadász 			vap->iv_myaddr, ifp->if_broadcastaddr,
5494f898719SImre Vadász 			ifp->if_broadcastaddr,
5504f898719SImre Vadász 			ss->ss_ssid[i].ssid, ss->ss_ssid[i].len);
5514f898719SImre Vadász 	if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0)
5524f898719SImre Vadász 		ieee80211_send_probereq(vap->iv_bss,
5534f898719SImre Vadász 			vap->iv_myaddr, ifp->if_broadcastaddr,
5544f898719SImre Vadász 			ifp->if_broadcastaddr,
5554f898719SImre Vadász 			"", 0);
5564f898719SImre Vadász }
5574f898719SImre Vadász 
5584f898719SImre Vadász /*
5594f898719SImre Vadász  * Scan curchan.  If this is an active scan and the channel
5604f898719SImre Vadász  * is not marked passive then send probe request frame(s).
5614f898719SImre Vadász  * Arrange for the channel change after maxdwell ticks.
5624f898719SImre Vadász  */
5634f898719SImre Vadász static void
scan_curchan(struct ieee80211_scan_state * ss,unsigned long maxdwell)5644f898719SImre Vadász scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
5654f898719SImre Vadász {
5664f898719SImre Vadász 	struct ieee80211vap *vap  = ss->ss_vap;
5674f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
5684f898719SImre Vadász 
5694f898719SImre Vadász 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
5704f898719SImre Vadász 	    "%s: calling; maxdwell=%lu\n",
5714f898719SImre Vadász 	    __func__,
5724f898719SImre Vadász 	    maxdwell);
5734f655ef5SMatthew Dillon 	IEEE80211_LOCK(ic);
5744f898719SImre Vadász 	if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
5754f898719SImre Vadász 		ieee80211_probe_curchan(vap, 0);
5764f655ef5SMatthew Dillon 	taskqueue_enqueue_timeout(ic->ic_tq,
5774f655ef5SMatthew Dillon 	    &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell);
5784f655ef5SMatthew Dillon 	IEEE80211_UNLOCK(ic);
5794f898719SImre Vadász }
5804f898719SImre Vadász 
5814f898719SImre Vadász static void
scan_signal(struct ieee80211_scan_state * ss,int iflags)5824f655ef5SMatthew Dillon scan_signal(struct ieee80211_scan_state *ss, int iflags)
5834f898719SImre Vadász {
5844f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
5854f898719SImre Vadász 
5864f655ef5SMatthew Dillon 	IEEE80211_UNLOCK_ASSERT(ic);
5874f655ef5SMatthew Dillon 
5884f655ef5SMatthew Dillon 	IEEE80211_LOCK(ic);
5894f655ef5SMatthew Dillon 	scan_signal_locked(ss, iflags);
5904f655ef5SMatthew Dillon 	IEEE80211_UNLOCK(ic);
5914f655ef5SMatthew Dillon }
5924f655ef5SMatthew Dillon 
5934f655ef5SMatthew Dillon static void
scan_signal_locked(struct ieee80211_scan_state * ss,int iflags)5944f655ef5SMatthew Dillon scan_signal_locked(struct ieee80211_scan_state *ss, int iflags)
5954f655ef5SMatthew Dillon {
5964f655ef5SMatthew Dillon 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
5974f655ef5SMatthew Dillon 	struct timeout_task *scan_task = &ss_priv->ss_scan_curchan;
5984f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
5994f655ef5SMatthew Dillon 
6004f655ef5SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
6014f655ef5SMatthew Dillon 
6024f655ef5SMatthew Dillon 	ss_priv->ss_iflags |= iflags;
6034f655ef5SMatthew Dillon 	if (ss_priv->ss_iflags & ISCAN_RUNNING) {
6044f655ef5SMatthew Dillon 		if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0)
6054f655ef5SMatthew Dillon 			taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0);
6064f655ef5SMatthew Dillon 	}
6074f898719SImre Vadász }
6084f898719SImre Vadász 
6094f898719SImre Vadász /*
6104f898719SImre Vadász  * Handle mindwell requirements completed; initiate a channel
6114f898719SImre Vadász  * change to the next channel asap.
6124f898719SImre Vadász  */
6134f898719SImre Vadász static void
scan_mindwell(struct ieee80211_scan_state * ss)6144f898719SImre Vadász scan_mindwell(struct ieee80211_scan_state *ss)
6154f898719SImre Vadász {
6164f898719SImre Vadász 
6174f898719SImre Vadász 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n",
6184f898719SImre Vadász 	    __func__);
6194f898719SImre Vadász 
6204f655ef5SMatthew Dillon 	scan_signal(ss, 0);
6214f898719SImre Vadász }
6224f898719SImre Vadász 
6234f898719SImre Vadász static void
scan_start(void * arg,int pending)6244f655ef5SMatthew Dillon scan_start(void *arg, int pending)
6254f898719SImre Vadász {
6264f898719SImre Vadász #define	ISCAN_REP	(ISCAN_MINDWELL | ISCAN_DISCARD)
6274f898719SImre Vadász 	struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
6284f655ef5SMatthew Dillon 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
6294f898719SImre Vadász 	struct ieee80211vap *vap = ss->ss_vap;
6304f898719SImre Vadász 	struct ieee80211com *ic = ss->ss_ic;
6314f898719SImre Vadász 
6324f898719SImre Vadász 	IEEE80211_LOCK(ic);
6334f898719SImre Vadász 	if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
6344f655ef5SMatthew Dillon 	    (ss_priv->ss_iflags & ISCAN_ABORT)) {
6354f898719SImre Vadász 		/* Cancelled before we started */
6364f655ef5SMatthew Dillon 		scan_done(ss, 0);
6374f655ef5SMatthew Dillon 		return;
6384f898719SImre Vadász 	}
6394f898719SImre Vadász 
6404f898719SImre Vadász 	if (ss->ss_next == ss->ss_last) {
6414f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
6424f898719SImre Vadász 			"%s: no channels to scan\n", __func__);
6434f655ef5SMatthew Dillon 		scan_done(ss, 1);
6444f655ef5SMatthew Dillon 		return;
6454f898719SImre Vadász 	}
6464f898719SImre Vadász 
647*84bcb7f7SImre Vadász 	/*
648*84bcb7f7SImre Vadász 	 * Put the station into power save mode.
649*84bcb7f7SImre Vadász 	 *
650*84bcb7f7SImre Vadász 	 * This is only required if we're not a full-offload device;
651*84bcb7f7SImre Vadász 	 * those devices manage scan/traffic differently.
652*84bcb7f7SImre Vadász 	 */
653*84bcb7f7SImre Vadász 	if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) &&
654*84bcb7f7SImre Vadász 	    vap->iv_opmode == IEEE80211_M_STA &&
6554f898719SImre Vadász 	    vap->iv_state == IEEE80211_S_RUN) {
6564f898719SImre Vadász 		if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
6574f898719SImre Vadász 			/* Enable station power save mode */
6584f898719SImre Vadász 			vap->iv_sta_ps(vap, 1);
6594f655ef5SMatthew Dillon 			/* Wait until null data frame will be ACK'ed */
6604f655ef5SMatthew Dillon #if defined(__DragonFly__)
6614f655ef5SMatthew Dillon 			lksleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
6624f655ef5SMatthew Dillon 			    "sta_ps", msecs_to_ticks(10));
6634f655ef5SMatthew Dillon #else
6644f655ef5SMatthew Dillon 			mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
6654f655ef5SMatthew Dillon 			    "sta_ps", msecs_to_ticks(10));
6664f655ef5SMatthew Dillon #endif
6674f655ef5SMatthew Dillon 			if (ss_priv->ss_iflags & ISCAN_ABORT) {
6684f655ef5SMatthew Dillon 				scan_done(ss, 0);
6694f655ef5SMatthew Dillon 				return;
6704f655ef5SMatthew Dillon 			}
6714f898719SImre Vadász 		}
6724f898719SImre Vadász 	}
6734f898719SImre Vadász 
6744f655ef5SMatthew Dillon 	ss_priv->ss_scanend = ticks + ss_priv->ss_duration;
6754f898719SImre Vadász 
6764f898719SImre Vadász 	/* XXX scan state can change! Re-validate scan state! */
6774f898719SImre Vadász 
6784f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
6794f655ef5SMatthew Dillon 
6804f898719SImre Vadász 	ic->ic_scan_start(ic);		/* notify driver */
6814f655ef5SMatthew Dillon 
6824f655ef5SMatthew Dillon 	scan_curchan_task(ss, 0);
6834f655ef5SMatthew Dillon }
6844f655ef5SMatthew Dillon 
6854f655ef5SMatthew Dillon static void
scan_curchan_task(void * arg,int pending)6864f655ef5SMatthew Dillon scan_curchan_task(void *arg, int pending)
6874f655ef5SMatthew Dillon {
6884f655ef5SMatthew Dillon 	struct ieee80211_scan_state *ss = arg;
6894f655ef5SMatthew Dillon 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
6904f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
6914f655ef5SMatthew Dillon 	struct ieee80211_channel *chan;
6924f655ef5SMatthew Dillon 	unsigned long maxdwell;
6934f655ef5SMatthew Dillon 	int scandone;
6944f655ef5SMatthew Dillon 
6954f898719SImre Vadász 	IEEE80211_LOCK(ic);
6964f655ef5SMatthew Dillon end:
6974f898719SImre Vadász 	scandone = (ss->ss_next >= ss->ss_last) ||
6984f655ef5SMatthew Dillon 	    (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;
6994f898719SImre Vadász 
7004f655ef5SMatthew Dillon 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
7014f898719SImre Vadász 	    "%s: loop start; scandone=%d\n",
7024f898719SImre Vadász 	    __func__,
7034f898719SImre Vadász 	    scandone);
7044f898719SImre Vadász 
7054f898719SImre Vadász 	if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
7064f655ef5SMatthew Dillon 	    (ss_priv->ss_iflags & ISCAN_ABORT) ||
7074f655ef5SMatthew Dillon 	     ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
7084f655ef5SMatthew Dillon 		ss_priv->ss_iflags &= ~ISCAN_RUNNING;
7094f655ef5SMatthew Dillon 		scan_end(ss, scandone);
7104f655ef5SMatthew Dillon 		return;
7114f655ef5SMatthew Dillon 	} else
7124f655ef5SMatthew Dillon 		ss_priv->ss_iflags |= ISCAN_RUNNING;
7134f898719SImre Vadász 
7144f898719SImre Vadász 	chan = ss->ss_chans[ss->ss_next++];
7154f898719SImre Vadász 
7164f898719SImre Vadász 	/*
7174f898719SImre Vadász 	 * Watch for truncation due to the scan end time.
7184f898719SImre Vadász 	 */
7194f655ef5SMatthew Dillon 	if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
7204f655ef5SMatthew Dillon 		maxdwell = ss_priv->ss_scanend - ticks;
7214f898719SImre Vadász 	else
7224f898719SImre Vadász 		maxdwell = ss->ss_maxdwell;
7234f898719SImre Vadász 
7244f655ef5SMatthew Dillon 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN,
7254f898719SImre Vadász 	    "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
7264f898719SImre Vadász 	    __func__,
7274f898719SImre Vadász 	    ieee80211_chan2ieee(ic, ic->ic_curchan),
7284f898719SImre Vadász 	    ieee80211_channel_type_char(ic->ic_curchan),
7294f898719SImre Vadász 	    ieee80211_chan2ieee(ic, chan),
7304f898719SImre Vadász 	    ieee80211_channel_type_char(chan),
7314f898719SImre Vadász 	    (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
7324f898719SImre Vadász 		(chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
7334f898719SImre Vadász 		"active" : "passive",
7344f898719SImre Vadász 	    ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
7354f898719SImre Vadász 
7364f898719SImre Vadász 	/*
7374f898719SImre Vadász 	 * Potentially change channel and phy mode.
7384f898719SImre Vadász 	 */
7394f898719SImre Vadász 	ic->ic_curchan = chan;
7404f898719SImre Vadász 	ic->ic_rt = ieee80211_get_ratetable(chan);
7414f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
7424f898719SImre Vadász 	/*
7434f898719SImre Vadász 	 * Perform the channel change and scan unlocked so the driver
7444f898719SImre Vadász 	 * may sleep. Once set_channel returns the hardware has
7454f898719SImre Vadász 	 * completed the channel change.
7464f898719SImre Vadász 	 */
7474f898719SImre Vadász 	ic->ic_set_channel(ic);
7484f898719SImre Vadász 	ieee80211_radiotap_chan_change(ic);
7494f898719SImre Vadász 
7504f898719SImre Vadász 	/*
7514f898719SImre Vadász 	 * Scan curchan.  Drivers for "intelligent hardware"
7524f898719SImre Vadász 	 * override ic_scan_curchan to tell the device to do
7534f655ef5SMatthew Dillon 	 * the work.  Otherwise we manage the work ourselves;
7544f898719SImre Vadász 	 * sending a probe request (as needed), and arming the
7554f898719SImre Vadász 	 * timeout to switch channels after maxdwell ticks.
7564f898719SImre Vadász 	 *
7574f898719SImre Vadász 	 * scan_curchan should only pause for the time required to
7584f655ef5SMatthew Dillon 	 * prepare/initiate the hardware for the scan (if at all).
7594f898719SImre Vadász 	 */
7604f898719SImre Vadász 	ic->ic_scan_curchan(ss, maxdwell);
7614f898719SImre Vadász 	IEEE80211_LOCK(ic);
7624f898719SImre Vadász 
7634f898719SImre Vadász 	/* XXX scan state can change! Re-validate scan state! */
7644f898719SImre Vadász 
7654f655ef5SMatthew Dillon 	ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
7664f898719SImre Vadász 	/* clear mindwell lock and initial channel change flush */
7674f655ef5SMatthew Dillon 	ss_priv->ss_iflags &= ~ISCAN_REP;
7684f898719SImre Vadász 
7694f655ef5SMatthew Dillon 	if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) {
7704f655ef5SMatthew Dillon 		taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan,
7714f655ef5SMatthew Dillon 		    NULL);
7724f655ef5SMatthew Dillon 		goto end;
7734f898719SImre Vadász 	}
7744f898719SImre Vadász 
7754f655ef5SMatthew Dillon 	IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n",
7764f655ef5SMatthew Dillon 	    __func__);
7774f655ef5SMatthew Dillon 	IEEE80211_UNLOCK(ic);
7784f655ef5SMatthew Dillon }
7794f655ef5SMatthew Dillon 
7804f655ef5SMatthew Dillon static void
scan_end(struct ieee80211_scan_state * ss,int scandone)7814f655ef5SMatthew Dillon scan_end(struct ieee80211_scan_state *ss, int scandone)
7824f655ef5SMatthew Dillon {
7834f655ef5SMatthew Dillon 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
7844f655ef5SMatthew Dillon 	struct ieee80211vap *vap = ss->ss_vap;
7854f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
7864f655ef5SMatthew Dillon 
7874f655ef5SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
7884f655ef5SMatthew Dillon 
7894f898719SImre Vadász 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__);
7904f898719SImre Vadász 
7914f655ef5SMatthew Dillon 	if (ss_priv->ss_iflags & ISCAN_ABORT) {
7924f655ef5SMatthew Dillon 		scan_done(ss, scandone);
7934f655ef5SMatthew Dillon 		return;
7944f655ef5SMatthew Dillon 	}
7954f898719SImre Vadász 
7964f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
7974f898719SImre Vadász 	ic->ic_scan_end(ic);		/* notify driver */
7984f898719SImre Vadász 	IEEE80211_LOCK(ic);
7994f898719SImre Vadász 	/* XXX scan state can change! Re-validate scan state! */
8004f898719SImre Vadász 
8014f898719SImre Vadász 	/*
8024f655ef5SMatthew Dillon 	 * Since a cancellation may have occurred during one of the
8034f898719SImre Vadász 	 * driver calls (whilst unlocked), update scandone.
8044f898719SImre Vadász 	 */
8054f655ef5SMatthew Dillon 	if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
8064f898719SImre Vadász 		/* XXX printf? */
8074f898719SImre Vadász 		if_printf(vap->iv_ifp,
8084f898719SImre Vadász 		    "%s: OOPS! scan cancelled during driver call (1)!\n",
8094f898719SImre Vadász 		    __func__);
8104f898719SImre Vadász 		scandone = 1;
8114f898719SImre Vadász 	}
8124f898719SImre Vadász 
8134f898719SImre Vadász 	/*
8144f898719SImre Vadász 	 * Record scan complete time.  Note that we also do
8154f898719SImre Vadász 	 * this when canceled so any background scan will
8164f898719SImre Vadász 	 * not be restarted for a while.
8174f898719SImre Vadász 	 */
8184f898719SImre Vadász 	if (scandone)
8194f898719SImre Vadász 		ic->ic_lastscan = ticks;
8204f898719SImre Vadász 	/* return to the bss channel */
8214f898719SImre Vadász 	if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
8224f898719SImre Vadász 	    ic->ic_curchan != ic->ic_bsschan) {
8234f898719SImre Vadász 		ieee80211_setupcurchan(ic, ic->ic_bsschan);
8244f898719SImre Vadász 		IEEE80211_UNLOCK(ic);
8254f898719SImre Vadász 		ic->ic_set_channel(ic);
8264f898719SImre Vadász 		ieee80211_radiotap_chan_change(ic);
8274f898719SImre Vadász 		IEEE80211_LOCK(ic);
8284f898719SImre Vadász 	}
8294f898719SImre Vadász 	/* clear internal flags and any indication of a pick */
8304f655ef5SMatthew Dillon 	ss_priv->ss_iflags &= ~ISCAN_REP;
8314f898719SImre Vadász 	ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
8324f898719SImre Vadász 
8334f898719SImre Vadász 	/*
8344f898719SImre Vadász 	 * If not canceled and scan completed, do post-processing.
8354f898719SImre Vadász 	 * If the callback function returns 0, then it wants to
8364f898719SImre Vadász 	 * continue/restart scanning.  Unfortunately we needed to
8374f898719SImre Vadász 	 * notify the driver to end the scan above to avoid having
8384f898719SImre Vadász 	 * rx frames alter the scan candidate list.
8394f898719SImre Vadász 	 */
8404f655ef5SMatthew Dillon 	if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 &&
8414f898719SImre Vadász 	    !ss->ss_ops->scan_end(ss, vap) &&
8424f898719SImre Vadász 	    (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
8434f655ef5SMatthew Dillon 	    ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
8444f898719SImre Vadász 		IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
8454f898719SImre Vadász 		    "%s: done, restart "
8464f898719SImre Vadász 		    "[ticks %u, dwell min %lu scanend %lu]\n",
8474f898719SImre Vadász 		    __func__,
8484f655ef5SMatthew Dillon 		    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
8494f655ef5SMatthew Dillon 		ss->ss_next = 0;	/* reset to beginning */
8504f898719SImre Vadász 		if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
8514f898719SImre Vadász 			vap->iv_stats.is_scan_active++;
8524f898719SImre Vadász 		else
8534f898719SImre Vadász 			vap->iv_stats.is_scan_passive++;
8544f898719SImre Vadász 
8554f898719SImre Vadász 		ss->ss_ops->scan_restart(ss, vap);	/* XXX? */
8564f655ef5SMatthew Dillon 		ieee80211_runtask(ic, &ss_priv->ss_scan_start);
8574f898719SImre Vadász 		IEEE80211_UNLOCK(ic);
8584f898719SImre Vadász 		return;
8594f898719SImre Vadász 	}
8604f898719SImre Vadász 
8614f898719SImre Vadász 	/* past here, scandone is ``true'' if not in bg mode */
8624f898719SImre Vadász 	if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
8634f898719SImre Vadász 		scandone = 1;
8644f898719SImre Vadász 
8654f898719SImre Vadász 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
8664f898719SImre Vadász 	    "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
8674f898719SImre Vadász 	    __func__, scandone ? "done" : "stopped",
8684f655ef5SMatthew Dillon 	    ticks, ss->ss_mindwell, ss_priv->ss_scanend);
8694f898719SImre Vadász 
8704f898719SImre Vadász 	/*
8714f655ef5SMatthew Dillon 	 * Since a cancellation may have occurred during one of the
8724f898719SImre Vadász 	 * driver calls (whilst unlocked), update scandone.
8734f898719SImre Vadász 	 */
8744f655ef5SMatthew Dillon 	if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
8754f898719SImre Vadász 		/* XXX printf? */
8764f898719SImre Vadász 		if_printf(vap->iv_ifp,
8774f898719SImre Vadász 		    "%s: OOPS! scan cancelled during driver call (2)!\n",
8784f898719SImre Vadász 		    __func__);
8794f898719SImre Vadász 		scandone = 1;
8804f898719SImre Vadász 	}
8814f898719SImre Vadász 
8824f655ef5SMatthew Dillon 	scan_done(ss, scandone);
8834f655ef5SMatthew Dillon }
8844f655ef5SMatthew Dillon 
8854f655ef5SMatthew Dillon static void
scan_done(struct ieee80211_scan_state * ss,int scandone)8864f655ef5SMatthew Dillon scan_done(struct ieee80211_scan_state *ss, int scandone)
8874f655ef5SMatthew Dillon {
8884f655ef5SMatthew Dillon 	struct scan_state *ss_priv = SCAN_PRIVATE(ss);
8894f655ef5SMatthew Dillon 	struct ieee80211com *ic = ss->ss_ic;
8904f655ef5SMatthew Dillon 	struct ieee80211vap *vap = ss->ss_vap;
8914f655ef5SMatthew Dillon 
8924f655ef5SMatthew Dillon 	IEEE80211_LOCK_ASSERT(ic);
8934f655ef5SMatthew Dillon 
8944f898719SImre Vadász 	/*
8954f898719SImre Vadász 	 * Clear the SCAN bit first in case frames are
8964f898719SImre Vadász 	 * pending on the station power save queue.  If
8974f898719SImre Vadász 	 * we defer this then the dispatch of the frames
8984f898719SImre Vadász 	 * may generate a request to cancel scanning.
8994f898719SImre Vadász 	 */
9004f898719SImre Vadász 	ic->ic_flags &= ~IEEE80211_F_SCAN;
9014f655ef5SMatthew Dillon 
9024f898719SImre Vadász 	/*
9034f898719SImre Vadász 	 * Drop out of power save mode when a scan has
9044f898719SImre Vadász 	 * completed.  If this scan was prematurely terminated
9054f898719SImre Vadász 	 * because it is a background scan then don't notify
9064f898719SImre Vadász 	 * the ap; we'll either return to scanning after we
9074f898719SImre Vadász 	 * receive the beacon frame or we'll drop out of power
9084f898719SImre Vadász 	 * save mode because the beacon indicates we have frames
9094f898719SImre Vadász 	 * waiting for us.
9104f898719SImre Vadász 	 */
9114f898719SImre Vadász 	if (scandone) {
912*84bcb7f7SImre Vadász 		/*
913*84bcb7f7SImre Vadász 		 * If we're not a scan offload device, come back out of
914*84bcb7f7SImre Vadász 		 * station powersave.  Offload devices handle this themselves.
915*84bcb7f7SImre Vadász 		 */
916*84bcb7f7SImre Vadász 		if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0)
9174f898719SImre Vadász 			vap->iv_sta_ps(vap, 0);
91886682110SImre Vadász 		if (ss->ss_next >= ss->ss_last)
9194f898719SImre Vadász 			ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
92086682110SImre Vadász 		ieee80211_notify_scan_done(vap);
9214f898719SImre Vadász 	}
9224f655ef5SMatthew Dillon 	ss_priv->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
9234f655ef5SMatthew Dillon 	ss_priv->ss_scanend = 0;
9244f898719SImre Vadász 	ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
9254f898719SImre Vadász 	IEEE80211_UNLOCK(ic);
9264f898719SImre Vadász #undef ISCAN_REP
9274f898719SImre Vadász }
9284f898719SImre Vadász 
9294f898719SImre Vadász /*
9304f898719SImre Vadász  * Process a beacon or probe response frame.
9314f898719SImre Vadász  */
9324f898719SImre Vadász 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)9334f898719SImre Vadász ieee80211_swscan_add_scan(struct ieee80211vap *vap,
9344f898719SImre Vadász 	struct ieee80211_channel *curchan,
9354f898719SImre Vadász 	const struct ieee80211_scanparams *sp,
9364f898719SImre Vadász 	const struct ieee80211_frame *wh,
9374f898719SImre Vadász 	int subtype, int rssi, int noise)
9384f898719SImre Vadász {
9394f898719SImre Vadász 	struct ieee80211com *ic = vap->iv_ic;
9404f898719SImre Vadász 	struct ieee80211_scan_state *ss = ic->ic_scan;
9414f898719SImre Vadász 
9424f898719SImre Vadász 	/* XXX locking */
9434f898719SImre Vadász 	/*
9444f898719SImre Vadász 	 * Frames received during startup are discarded to avoid
9454f898719SImre Vadász 	 * using scan state setup on the initial entry to the timer
9464f898719SImre Vadász 	 * callback.  This can occur because the device may enable
9474f898719SImre Vadász 	 * rx prior to our doing the initial channel change in the
9484f898719SImre Vadász 	 * timer routine.
9494f898719SImre Vadász 	 */
9504f898719SImre Vadász 	if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD)
9514f898719SImre Vadász 		return;
9524f898719SImre Vadász #ifdef IEEE80211_DEBUG
9534f898719SImre Vadász 	if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN))
9544f898719SImre Vadász 		ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi);
9554f898719SImre Vadász #endif
9564f898719SImre Vadász 	if (ss->ss_ops != NULL &&
9574f898719SImre Vadász 	    ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) {
9584f898719SImre Vadász 		/*
9594f898719SImre Vadász 		 * If we've reached the min dwell time terminate
9604f898719SImre Vadász 		 * the timer so we'll switch to the next channel.
9614f898719SImre Vadász 		 */
9624f898719SImre Vadász 		if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 &&
9634f655ef5SMatthew Dillon 		    ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) {
9644f898719SImre Vadász 			IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
9654f898719SImre Vadász 			    "%s: chan %3d%c min dwell met (%u > %lu)\n",
9664f898719SImre Vadász 			    __func__,
9674f898719SImre Vadász 			    ieee80211_chan2ieee(ic, ic->ic_curchan),
9684f898719SImre Vadász 			    ieee80211_channel_type_char(ic->ic_curchan),
9694f898719SImre Vadász 			    ticks, SCAN_PRIVATE(ss)->ss_chanmindwell);
9704f898719SImre Vadász 			SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL;
9714f898719SImre Vadász 			/*
9724f898719SImre Vadász 			 * NB: trigger at next clock tick or wait for the
9734f898719SImre Vadász 			 * hardware.
9744f898719SImre Vadász 			 */
9754f898719SImre Vadász 			ic->ic_scan_mindwell(ss);
9764f898719SImre Vadász 		}
9774f898719SImre Vadász 	}
9784f898719SImre Vadász }
9794f898719SImre Vadász 
9804f898719SImre Vadász static struct ieee80211_scan_methods swscan_methods = {
9814f898719SImre Vadász 	.sc_attach = ieee80211_swscan_attach,
9824f898719SImre Vadász 	.sc_detach = ieee80211_swscan_detach,
9834f898719SImre Vadász 	.sc_vattach = ieee80211_swscan_vattach,
9844f898719SImre Vadász 	.sc_vdetach = ieee80211_swscan_vdetach,
9854f898719SImre Vadász 	.sc_set_scan_duration = ieee80211_swscan_set_scan_duration,
9864f898719SImre Vadász 	.sc_start_scan = ieee80211_swscan_start_scan,
9874f898719SImre Vadász 	.sc_check_scan = ieee80211_swscan_check_scan,
9884f898719SImre Vadász 	.sc_bg_scan = ieee80211_swscan_bg_scan,
9894f898719SImre Vadász 	.sc_cancel_scan = ieee80211_swscan_cancel_scan,
9904f898719SImre Vadász 	.sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan,
9914f898719SImre Vadász 	.sc_scan_next = ieee80211_swscan_scan_next,
9924f898719SImre Vadász 	.sc_scan_done = ieee80211_swscan_scan_done,
9934f898719SImre Vadász 	.sc_scan_probe_curchan = ieee80211_swscan_probe_curchan,
9944f898719SImre Vadász 	.sc_add_scan = ieee80211_swscan_add_scan
9954f898719SImre Vadász };
9964f898719SImre Vadász 
9974f898719SImre Vadász /*
9984f898719SImre Vadász  * Default scan attach method.
9994f898719SImre Vadász  */
10004f898719SImre Vadász void
ieee80211_swscan_attach(struct ieee80211com * ic)10014f898719SImre Vadász ieee80211_swscan_attach(struct ieee80211com *ic)
10024f898719SImre Vadász {
10034f898719SImre Vadász 	struct scan_state *ss;
10044f898719SImre Vadász 
10054f898719SImre Vadász 	/*
10064f898719SImre Vadász 	 * Setup the default methods
10074f898719SImre Vadász 	 */
10084f898719SImre Vadász 	ic->ic_scan_methods = &swscan_methods;
10094f898719SImre Vadász 
10104f898719SImre Vadász 	/* Allocate initial scan state */
10114f655ef5SMatthew Dillon #if defined(__DragonFly__)
10124f898719SImre Vadász 	ss = (struct scan_state *) kmalloc(sizeof(struct scan_state),
10134f898719SImre Vadász 		M_80211_SCAN, M_INTWAIT | M_ZERO);
10144f655ef5SMatthew Dillon #else
10154f655ef5SMatthew Dillon 	ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state),
10164f655ef5SMatthew Dillon 		M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);
10174f655ef5SMatthew Dillon #endif
10184f898719SImre Vadász 	if (ss == NULL) {
10194f898719SImre Vadász 		ic->ic_scan = NULL;
10204f898719SImre Vadász 		return;
10214f898719SImre Vadász 	}
10224f655ef5SMatthew Dillon 	TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss);
10234f655ef5SMatthew Dillon 	TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0,
10244f655ef5SMatthew Dillon 	    scan_curchan_task, ss);
10254f898719SImre Vadász 
10264f898719SImre Vadász 	ic->ic_scan = &ss->base;
10274f898719SImre Vadász 	ss->base.ss_ic = ic;
10284f898719SImre Vadász 
10294f898719SImre Vadász 	ic->ic_scan_curchan = scan_curchan;
10304f898719SImre Vadász 	ic->ic_scan_mindwell = scan_mindwell;
10314f898719SImre Vadász }
1032