summaryrefslogtreecommitdiffstats
path: root/sys/net80211/ieee80211_scan.c
diff options
context:
space:
mode:
authorthompsa <thompsa@FreeBSD.org>2009-05-02 15:14:18 +0000
committerthompsa <thompsa@FreeBSD.org>2009-05-02 15:14:18 +0000
commited7c3176b96444dc82c6a07114013192611024f3 (patch)
tree46c2c9933cb1753f24ffd7c0096b2ba9074180d2 /sys/net80211/ieee80211_scan.c
parentb704e6092a4076e74c276c0d531447a8cb3bf6a6 (diff)
downloadFreeBSD-src-ed7c3176b96444dc82c6a07114013192611024f3.zip
FreeBSD-src-ed7c3176b96444dc82c6a07114013192611024f3.tar.gz
Create a taskqueue for each wireless interface which provides a serialised
sleepable context for net80211 driver callbacks. This removes the need for USB and firmware based drivers to roll their own code to defer the chip programming for state changes, scan requests, channel changes and mcast/promisc updates. When a driver callback completes the hardware state is now guaranteed to have been updated and is in sync with net80211 layer. This nukes around 1300 lines of code from the wireless device drivers making them more readable and less race prone. The net80211 layer has been updated as follows - all state/channel changes are serialised on the taskqueue. - ieee80211_new_state() always queues and can now be called from any context - scanning runs from a single taskq function and executes to completion. driver callbacks are synchronous so the channel, phy mode and rx filters are guaranteed to be set in hardware before probe request frames are transmitted. Help and contributions from Sam Leffler. Reviewed by: sam
Diffstat (limited to 'sys/net80211/ieee80211_scan.c')
-rw-r--r--sys/net80211/ieee80211_scan.c407
1 files changed, 207 insertions, 200 deletions
diff --git a/sys/net80211/ieee80211_scan.c b/sys/net80211/ieee80211_scan.c
index 7ce2d31..c1f4a13 100644
--- a/sys/net80211/ieee80211_scan.c
+++ b/sys/net80211/ieee80211_scan.c
@@ -33,7 +33,9 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/proc.h>
#include <sys/kernel.h>
+#include <sys/condvar.h>
#include <sys/socket.h>
@@ -52,10 +54,12 @@ struct scan_state {
#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */
#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */
#define ISCAN_CANCEL 0x0004 /* cancel current scan */
-#define ISCAN_START 0x0008 /* 1st time through next_scan */
+#define ISCAN_ABORT 0x0008 /* end the scan immediately */
unsigned long ss_chanmindwell; /* min dwell on curchan */
unsigned long ss_scanend; /* time scan must stop */
u_int ss_duration; /* duration for next scan */
+ struct task ss_scan_task; /* scan execution */
+ struct cv ss_scan_cv; /* scan signal */
struct callout ss_scan_timer; /* scan timer */
};
#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)
@@ -88,10 +92,10 @@ struct scan_state {
#define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */
#define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */
-static void scan_restart_pwrsav(void *);
static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void scan_mindwell(struct ieee80211_scan_state *);
-static void scan_next(void *);
+static void scan_signal(void *);
+static void scan_task(void *, int);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@@ -107,7 +111,10 @@ ieee80211_scan_attach(struct ieee80211com *ic)
return;
}
callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0);
+ cv_init(&ss->ss_scan_cv, "scan");
+ TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss);
ic->ic_scan = &ss->base;
+ ss->base.ss_ic = ic;
ic->ic_scan_curchan = scan_curchan;
ic->ic_scan_mindwell = scan_mindwell;
@@ -119,12 +126,17 @@ ieee80211_scan_detach(struct ieee80211com *ic)
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss != NULL) {
- callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ IEEE80211_LOCK(ic);
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
+ scan_signal(ss);
+ IEEE80211_UNLOCK(ic);
+ ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
+ KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
+ ("scan still running"));
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
ss->ss_ops = NULL;
}
- ic->ic_flags &= ~IEEE80211_F_SCAN;
ic->ic_scan = NULL;
free(SCAN_PRIVATE(ss), M_80211_SCAN);
}
@@ -174,9 +186,8 @@ ieee80211_scan_vdetach(struct ieee80211vap *vap)
ss = ic->ic_scan;
if (ss != NULL && ss->ss_vap == vap) {
if (ic->ic_flags & IEEE80211_F_SCAN) {
- /* XXX callout_drain */
- callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer);
- ic->ic_flags &= ~IEEE80211_F_SCAN;
+ SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
+ scan_signal(ss);
}
if (ss->ss_ops != NULL) {
ss->ss_ops->scan_detach(ss);
@@ -293,15 +304,6 @@ scan_update_locked(struct ieee80211vap *vap,
}
}
-static void
-change_channel(struct ieee80211com *ic,
- struct ieee80211_channel *chan)
-{
- ic->ic_curchan = chan;
- ic->ic_rt = ieee80211_get_ratetable(chan);
- ic->ic_set_channel(ic);
-}
-
static char
channel_type(const struct ieee80211_channel *c)
{
@@ -325,7 +327,7 @@ channel_type(const struct ieee80211_channel *c)
void
ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_vap->iv_ic;
+ struct ieee80211com *ic = ss->ss_ic;
const char *sep;
int i;
@@ -352,76 +354,6 @@ scan_dump(struct ieee80211_scan_state *ss)
}
#endif /* IEEE80211_DEBUG */
-/*
- * Enable station power save mode and start/restart the scanning thread.
- */
-static void
-scan_restart_pwrsav(void *arg)
-{
- struct scan_state *ss = (struct scan_state *) arg;
- struct ieee80211vap *vap = ss->base.ss_vap;
- struct ieee80211com *ic = vap->iv_ic;
- int ticksdelay;
-
- ieee80211_sta_pwrsave(vap, 1);
- /*
- * Use an initial 1ms delay so the null
- * data frame has a chance to go out.
- * XXX 1ms is a lot, better to trigger scan
- * on tx complete.
- */
- ticksdelay = msecs_to_ticks(1);
- if (ticksdelay < 1)
- ticksdelay = 1;
- ic->ic_scan_start(ic); /* notify driver */
- ss->ss_scanend = ticks + ticksdelay + ss->ss_duration;
- ss->ss_iflags |= ISCAN_START;
- callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss);
-}
-
-/*
- * Start/restart scanning. If we're operating in station mode
- * and associated notify the ap we're going into power save mode
- * and schedule a callback to initiate the work (where there's a
- * better context for doing the work). Otherwise, start the scan
- * directly.
- */
-static int
-scan_restart(struct scan_state *ss, u_int duration)
-{
- struct ieee80211vap *vap = ss->base.ss_vap;
- struct ieee80211com *ic = vap->iv_ic;
- int defer = 0;
-
- if (ss->base.ss_next == ss->base.ss_last) {
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: no channels to scan\n", __func__);
- return 0;
- }
- if (vap->iv_opmode == IEEE80211_M_STA &&
- vap->iv_state == IEEE80211_S_RUN) {
- if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
- /*
- * Initiate power save before going off-channel.
- * Note that we cannot do this directly because
- * of locking issues; instead we defer it to a
- * tasklet.
- */
- ss->ss_duration = duration;
- defer = 1;
- }
- }
-
- if (!defer) {
- ic->ic_scan_start(ic); /* notify driver */
- ss->ss_scanend = ticks + duration;
- ss->ss_iflags |= ISCAN_START;
- callout_reset(&ss->ss_scan_timer, 0, scan_next, ss);
- } else
- scan_restart_pwrsav(ss);
- return 1;
-}
-
static void
copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss,
int nssid, const struct ieee80211_scan_ssid ssids[])
@@ -485,16 +417,18 @@ start_scan_locked(const struct ieee80211_scanner *scan,
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ SCAN_PRIVATE(ss)->ss_duration = duration;
ss->ss_next = 0;
ss->ss_mindwell = mindwell;
ss->ss_maxdwell = maxdwell;
+ /* NB: scan_start must be before the scan runtask */
ss->ss_ops->scan_start(ss, vap);
#ifdef IEEE80211_DEBUG
if (ieee80211_msg_scan(vap))
scan_dump(ss);
#endif /* IEEE80211_DEBUG */
- if (scan_restart(SCAN_PRIVATE(ss), duration))
- ic->ic_flags |= IEEE80211_F_SCAN;
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
}
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
@@ -716,11 +650,11 @@ ieee80211_bg_scan(struct ieee80211vap *vap, int flags)
}
/* NB: flush frames rx'd before 1st channel change */
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD;
+ SCAN_PRIVATE(ss)->ss_duration = duration;
ss->ss_maxdwell = duration;
- if (scan_restart(SCAN_PRIVATE(ss), duration)) {
- ic->ic_flags |= IEEE80211_F_SCAN;
- ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
- }
+ ic->ic_flags |= IEEE80211_F_SCAN;
+ ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
+ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
} else {
/* XXX msg+stat */
}
@@ -756,9 +690,8 @@ ieee80211_cancel_scan(struct ieee80211vap *vap)
/* clear bg scan NOPICK and mark cancel request */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
- /* force it to fire asap */
- callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- 0, scan_next, ss);
+ /* wake up the scan task */
+ scan_signal(ss);
}
IEEE80211_UNLOCK(ic);
}
@@ -783,9 +716,8 @@ ieee80211_cancel_anyscan(struct ieee80211vap *vap)
/* clear bg scan NOPICK and mark cancel request */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
- /* force it to fire asap */
- callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- 0, scan_next, ss);
+ /* wake up the scan task */
+ scan_signal(ss);
}
IEEE80211_UNLOCK(ic);
}
@@ -800,7 +732,10 @@ ieee80211_scan_next(struct ieee80211vap *vap)
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
- callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+ /* wake up the scan task */
+ IEEE80211_LOCK(ic);
+ scan_signal(ss);
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -816,7 +751,7 @@ ieee80211_scan_done(struct ieee80211vap *vap)
IEEE80211_LOCK(ic);
ss = ic->ic_scan;
ss->ss_next = ss->ss_last; /* all channels are complete */
- scan_next(ss);
+ scan_signal(ss);
IEEE80211_UNLOCK(ic);
}
@@ -866,12 +801,22 @@ scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
struct ieee80211vap *vap = ss->ss_vap;
- IEEE80211_LOCK_ASSERT(vap->iv_ic);
-
+ IEEE80211_LOCK(vap->iv_ic);
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
ieee80211_probe_curchan(vap, 0);
callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- maxdwell, scan_next, ss);
+ maxdwell, scan_signal, ss);
+ IEEE80211_UNLOCK(vap->iv_ic);
+}
+
+static void
+scan_signal(void *arg)
+{
+ struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+
+ IEEE80211_LOCK_ASSERT(ss->ss_ic);
+
+ cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv);
}
/*
@@ -881,35 +826,67 @@ scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
static void
scan_mindwell(struct ieee80211_scan_state *ss)
{
- callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss);
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_LOCK(ic);
+ scan_signal(ss);
+ IEEE80211_UNLOCK(ic);
}
-/*
- * Switch to the next channel marked for scanning.
- */
static void
-scan_next(void *arg)
+scan_task(void *arg, int pending)
{
-#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD)
+#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
struct ieee80211vap *vap = ss->ss_vap;
- struct ieee80211com *ic = vap->iv_ic;
+ struct ieee80211com *ic = ss->ss_ic;
struct ieee80211_channel *chan;
unsigned long maxdwell, scanend;
- int scandone;
+ int scandone = 0;
- IEEE80211_LOCK_ASSERT(ic);
+ IEEE80211_LOCK(ic);
+ if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) {
+ /* Cancelled before we started */
+ goto done;
+ }
+
+ if (ss->ss_next == ss->ss_last) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: no channels to scan\n", __func__);
+ goto done;
+ }
+
+ if (vap->iv_opmode == IEEE80211_M_STA &&
+ vap->iv_state == IEEE80211_S_RUN) {
+ if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
+ /* Enable station power save mode */
+ ieee80211_sta_pwrsave(vap, 1);
+ /*
+ * Use an 1ms delay so the null data frame has a chance
+ * to go out.
+ * XXX Should use M_TXCB mechanism to eliminate this.
+ */
+ cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv,
+ IEEE80211_LOCK_OBJ(ic), hz / 1000);
+ if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
+ goto done;
+ }
+ }
+
+ scanend = ticks + SCAN_PRIVATE(ss)->ss_duration;
+ IEEE80211_UNLOCK(ic);
+ ic->ic_scan_start(ic); /* notify driver */
+ IEEE80211_LOCK(ic);
+
+ for (;;) {
+ scandone = (ss->ss_next >= ss->ss_last) ||
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
+ if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
+ (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) ||
+ time_after(ticks + ss->ss_mindwell, scanend))
+ break;
- if ((ic->ic_flags & IEEE80211_F_SCAN) == 0)
- return;
-again:
- scandone = (ss->ss_next >= ss->ss_last) ||
- (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
- scanend = SCAN_PRIVATE(ss)->ss_scanend;
- if (!scandone &&
- (ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 &&
- ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) ||
- time_before(ticks + ss->ss_mindwell, scanend))) {
chan = ss->ss_chans[ss->ss_next++];
/*
@@ -934,7 +911,15 @@ again:
/*
* Potentially change channel and phy mode.
*/
- change_channel(ic, chan);
+ ic->ic_curchan = chan;
+ ic->ic_rt = ieee80211_get_ratetable(chan);
+ IEEE80211_UNLOCK(ic);
+ /*
+ * Perform the channel change and scan unlocked so the driver
+ * may sleep. Once set_channel returns the hardware has
+ * completed the channel change.
+ */
+ ic->ic_set_channel(ic);
/*
* Scan curchan. Drivers for "intelligent hardware"
@@ -942,93 +927,115 @@ again:
* the work. Otherwise we manage the work outselves;
* sending a probe request (as needed), and arming the
* timeout to switch channels after maxdwell ticks.
+ *
+ * scan_curchan should only pause for the time required to
+ * prepare/initiate the hardware for the scan (if at all), the
+ * below condvar is used to sleep for the channels dwell time
+ * and allows it to be signalled for abort.
*/
ic->ic_scan_curchan(ss, maxdwell);
+ IEEE80211_LOCK(ic);
SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
/* clear mindwell lock and initial channel change flush */
SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
- } else {
- ic->ic_scan_end(ic); /* notify driver */
- /*
- * Record scan complete time. Note that we also do
- * this when canceled so any background scan will
- * not be restarted for a while.
- */
- if (scandone)
- ic->ic_lastscan = ticks;
- /* return to the bss channel */
- if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
- ic->ic_curchan != ic->ic_bsschan)
- ieee80211_setcurchan(ic, ic->ic_bsschan);
- /* clear internal flags and any indication of a pick */
- SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
- ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
- /*
- * If not canceled and scan completed, do post-processing.
- * If the callback function returns 0, then it wants to
- * continue/restart scanning. Unfortunately we needed to
- * notify the driver to end the scan above to avoid having
- * rx frames alter the scan candidate list.
- */
- if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
- !ss->ss_ops->scan_end(ss, vap) &&
- (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
- time_before(ticks + ss->ss_mindwell, scanend)) {
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: done, restart "
- "[ticks %u, dwell min %lu scanend %lu]\n",
- __func__,
- ticks, ss->ss_mindwell, scanend);
- ss->ss_next = 0; /* reset to begining */
- if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
- vap->iv_stats.is_scan_active++;
- else
- vap->iv_stats.is_scan_passive++;
+ if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
+ continue;
- ss->ss_ops->scan_restart(ss, vap); /* XXX? */
- ic->ic_scan_start(ic); /* notify driver */
- goto again;
- } else {
- /* past here, scandone is ``true'' if not in bg mode */
- if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
- scandone = 1;
+ /* Wait to be signalled to scan the next channel */
+ cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic));
+ }
+ if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
+ goto done;
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: %s, "
- "[ticks %u, dwell min %lu scanend %lu]\n",
- __func__, scandone ? "done" : "stopped",
- ticks, ss->ss_mindwell, scanend);
+ IEEE80211_UNLOCK(ic);
+ ic->ic_scan_end(ic); /* notify driver */
+ IEEE80211_LOCK(ic);
- /*
- * Clear the SCAN bit first in case frames are
- * pending on the station power save queue. If
- * we defer this then the dispatch of the frames
- * may generate a request to cancel scanning.
- */
- ic->ic_flags &= ~IEEE80211_F_SCAN;
- /*
- * Drop out of power save mode when a scan has
- * completed. If this scan was prematurely terminated
- * because it is a background scan then don't notify
- * the ap; we'll either return to scanning after we
- * receive the beacon frame or we'll drop out of power
- * save mode because the beacon indicates we have frames
- * waiting for us.
- */
- if (scandone) {
- ieee80211_sta_pwrsave(vap, 0);
- if (ss->ss_next >= ss->ss_last) {
- ieee80211_notify_scan_done(vap);
- ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
- }
- }
- SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL;
- ss->ss_flags &=
- ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
+ /*
+ * Record scan complete time. Note that we also do
+ * this when canceled so any background scan will
+ * not be restarted for a while.
+ */
+ if (scandone)
+ ic->ic_lastscan = ticks;
+ /* return to the bss channel */
+ if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
+ ic->ic_curchan != ic->ic_bsschan) {
+ ieee80211_setupcurchan(ic, ic->ic_bsschan);
+ IEEE80211_UNLOCK(ic);
+ ic->ic_set_channel(ic);
+ IEEE80211_LOCK(ic);
+ }
+ /* clear internal flags and any indication of a pick */
+ SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+ ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
+
+ /*
+ * If not canceled and scan completed, do post-processing.
+ * If the callback function returns 0, then it wants to
+ * continue/restart scanning. Unfortunately we needed to
+ * notify the driver to end the scan above to avoid having
+ * rx frames alter the scan candidate list.
+ */
+ if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 &&
+ !ss->ss_ops->scan_end(ss, vap) &&
+ (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 &&
+ time_before(ticks + ss->ss_mindwell, scanend)) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: done, restart "
+ "[ticks %u, dwell min %lu scanend %lu]\n",
+ __func__,
+ ticks, ss->ss_mindwell, scanend);
+ ss->ss_next = 0; /* reset to begining */
+ if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
+ vap->iv_stats.is_scan_active++;
+ else
+ vap->iv_stats.is_scan_passive++;
+
+ ss->ss_ops->scan_restart(ss, vap); /* XXX? */
+ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
+ IEEE80211_UNLOCK(ic);
+ return;
+ }
+
+ /* past here, scandone is ``true'' if not in bg mode */
+ if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0)
+ scandone = 1;
+
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n",
+ __func__, scandone ? "done" : "stopped",
+ ticks, ss->ss_mindwell, scanend);
+
+ /*
+ * Clear the SCAN bit first in case frames are
+ * pending on the station power save queue. If
+ * we defer this then the dispatch of the frames
+ * may generate a request to cancel scanning.
+ */
+done:
+ ic->ic_flags &= ~IEEE80211_F_SCAN;
+ /*
+ * Drop out of power save mode when a scan has
+ * completed. If this scan was prematurely terminated
+ * because it is a background scan then don't notify
+ * the ap; we'll either return to scanning after we
+ * receive the beacon frame or we'll drop out of power
+ * save mode because the beacon indicates we have frames
+ * waiting for us.
+ */
+ if (scandone) {
+ ieee80211_sta_pwrsave(vap, 0);
+ if (ss->ss_next >= ss->ss_last) {
+ ieee80211_notify_scan_done(vap);
+ ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
}
}
+ SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
+ ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
+ IEEE80211_UNLOCK(ic);
#undef ISCAN_REP
}
OpenPOWER on IntegriCloud