Date: Sat, 20 Feb 2016 12:06:17 +0200 From: "Andriy Voskoboinyk" <s3erios@gmail.com> To: "freebsd-wireless@freebsd.org" <freebsd-wireless@freebsd.org>, "Adrian Chadd" <adrian@freebsd.org> Subject: software scan fix - please test (Was: why we can't use the net80211 taskqueue for everything) Message-ID: <op.yc4mcrg5iew4ia@localhost> In-Reply-To: <CAJ-VmoneUBz4Vt3hFj8S4G_o8ptd3Z-NL5%2B6HuG33C3C_x-2jQ@mail.gmail.com> References: <CAJ-VmoneUBz4Vt3hFj8S4G_o8ptd3Z-NL5%2B6HuG33C3C_x-2jQ@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
> hi,
>
> andriy has a few reviews out that tidy up some things, which I'd reply
> to, but .. reviews is offline. So, here's the 30 second version:
>
> * the net80211 taskqueue runs the software scan engine, and the
> software scan engine currently sleeps whilst it's running.
>
> This means that if you put newstate, deferred transmit, etc into the
> net80211 taskqueue, then it just won't run during scan.
>
> The net80211 software scan thing should be modified to not sleep
> whilst it's waiting for scan results and instead just kick off another
> timer event to finish that part of the loop. Then yes, we can just
> migrate * to the net80211 task queue and use it for all serialisation
> of a wifi driver.
>
> (And yes, I'd like to see that done ASAP..)
>
> Thanks,
>
>
> -adrian
Hi,
I have replaced sleeping on conditional variable inside scan task
with scan_curchan task rescheduling (so this problem should be fixed now).
For everyone, who wishes to test: apply the attached patch
(merged from D5133, D5137, D5139, D5140, D5142, D5143, D5145, D5147, D5148
and D5152)
and rebuild + install the kernel. Scan should work as before.
Thanks!
[-- Attachment #2 --]
Index: sys/net80211/ieee80211_output.c
===================================================================
--- sys/net80211/ieee80211_output.c (revision 295834)
+++ sys/net80211/ieee80211_output.c (working copy)
@@ -849,6 +849,15 @@
return (ret);
}
+static void
+ieee80211_nulldata_transmitted(struct ieee80211_node *ni, void *arg,
+ int status)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ wakeup(vap);
+}
+
/*
* Send a null data frame to the specified node. If the station
* is setup for QoS then a QoS Null Data frame is constructed.
@@ -937,6 +946,11 @@
vap->iv_opmode != IEEE80211_M_HOSTAP)
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
}
+ if ((ic->ic_flags & IEEE80211_F_SCAN) &&
+ (ni->ni_flags & IEEE80211_NODE_PWR_MGT)) {
+ ieee80211_add_callback(m, ieee80211_nulldata_transmitted,
+ NULL);
+ }
m->m_len = m->m_pkthdr.len = hdrlen;
m->m_flags |= M_ENCAP; /* mark encapsulated */
Index: sys/net80211/ieee80211_scan.c
===================================================================
--- sys/net80211/ieee80211_scan.c (revision 295834)
+++ sys/net80211/ieee80211_scan.c (working copy)
@@ -453,8 +453,9 @@
}
/*
- * Public access to scan_next for drivers that manage
- * scanning themselves (e.g. for firmware-based devices).
+ * Manually switch to the next channel in the channel list.
+ * Provided for drivers that manage scanning themselves
+ * (e.g. for firmware-based devices).
*/
void
ieee80211_scan_next(struct ieee80211vap *vap)
@@ -465,8 +466,9 @@
}
/*
- * Public access to scan_next for drivers that are not able to scan single
- * channels (e.g. for firmware-based devices).
+ * Manually stop a scan that is currently running.
+ * Provided for drivers that are not able to scan single channels
+ * (e.g. for firmware-based devices).
*/
void
ieee80211_scan_done(struct ieee80211vap *vap)
@@ -486,7 +488,7 @@
}
/*
- * Probe the curent channel, if allowed, while scanning.
+ * Probe the current channel, if allowed, while scanning.
* If the channel is not marked passive-only then send
* a probe request immediately. Otherwise mark state and
* listen for beacons on the channel; if we receive something
Index: sys/net80211/ieee80211_scan_sw.c
===================================================================
--- sys/net80211/ieee80211_scan_sw.c (revision 295834)
+++ sys/net80211/ieee80211_scan_sw.c (working copy)
@@ -54,17 +54,18 @@
struct scan_state {
struct ieee80211_scan_state base; /* public state */
- u_int ss_iflags; /* flags used internally */
-#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_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 */
+ u_int ss_iflags; /* flags used internally */
+#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_ABORT 0x0008 /* end the scan immediately */
+#define ISCAN_RUNNING 0x0010 /* scan was started */
+
+ 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_start; /* scan start */
+ struct timeout_task ss_scan_curchan; /* scan execution */
};
#define SCAN_PRIVATE(ss) ((struct scan_state *) ss)
@@ -98,8 +99,12 @@
static void scan_curchan(struct ieee80211_scan_state *, unsigned long);
static void scan_mindwell(struct ieee80211_scan_state *);
-static void scan_signal(void *);
-static void scan_task(void *, int);
+static void scan_signal(struct ieee80211_scan_state *, int);
+static void scan_signal_locked(struct ieee80211_scan_state *, int);
+static void scan_start(void *, int);
+static void scan_curchan_task(void *, int);
+static void scan_end(struct ieee80211_scan_state *, int);
+static void scan_done(struct ieee80211_scan_state *, int);
MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state");
@@ -109,12 +114,10 @@
struct ieee80211_scan_state *ss = ic->ic_scan;
if (ss != NULL) {
- 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);
- callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer);
+ scan_signal(ss, ISCAN_ABORT);
+ ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
+ taskqueue_drain_timeout(ic->ic_tq,
+ &SCAN_PRIVATE(ss)->ss_scan_curchan);
KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0,
("scan still running"));
@@ -148,16 +151,13 @@
ieee80211_swscan_vdetach(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_scan_state *ss;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_ASSERT(ic);
- ss = ic->ic_scan;
- if (ss != NULL && ss->ss_vap == vap) {
- if (ic->ic_flags & IEEE80211_F_SCAN) {
- SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT;
- scan_signal(ss);
- }
- }
+
+ if (ss != NULL && ss->ss_vap == vap &&
+ (ic->ic_flags & IEEE80211_F_SCAN))
+ scan_signal_locked(ss, ISCAN_ABORT);
}
static void
@@ -236,7 +236,7 @@
ic->ic_flags |= IEEE80211_F_SCAN;
/* Start scan task */
- ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
+ ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start);
}
return 1;
} else {
@@ -411,7 +411,8 @@
ss->ss_maxdwell = duration;
ic->ic_flags |= IEEE80211_F_SCAN;
ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN;
- ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
+ ieee80211_runtask(ic,
+ &SCAN_PRIVATE(ss)->ss_scan_start);
} else {
/* XXX msg+stat */
}
@@ -426,11 +427,8 @@
return (ic->ic_flags & IEEE80211_F_SCAN);
}
-/*
- * Cancel any scan currently going on for the specified vap.
- */
static void
-ieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
+cancel_scan(struct ieee80211vap *vap, int any, const char *func)
{
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_scan_state *ss = ic->ic_scan;
@@ -437,22 +435,21 @@
IEEE80211_LOCK(ic);
if ((ic->ic_flags & IEEE80211_F_SCAN) &&
- ss->ss_vap == vap &&
+ (any || ss->ss_vap == vap) &&
(SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: cancel %s scan\n", __func__,
+ "%s: cancel %s scan\n", func,
ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
"active" : "passive");
- /* clear bg scan NOPICK and mark cancel request */
+ /* clear bg scan NOPICK */
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
- SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
- /* wake up the scan task */
- scan_signal(ss);
+ /* mark cancel request and wake up the scan task */
+ scan_signal_locked(ss, ISCAN_CANCEL);
} else {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n",
- __func__,
+ func,
!! (ic->ic_flags & IEEE80211_F_SCAN),
(ss->ss_vap == vap ? "match" : "nomatch"),
!! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL));
@@ -461,74 +458,57 @@
}
/*
+ * Cancel any scan currently going on for the specified vap.
+ */
+static void
+ieee80211_swscan_cancel_scan(struct ieee80211vap *vap)
+{
+ cancel_scan(vap, 0, __func__);
+}
+
+/*
* Cancel any scan currently going on.
*/
static void
ieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap)
{
- struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_scan_state *ss = ic->ic_scan;
-
- IEEE80211_LOCK(ic);
- if ((ic->ic_flags & IEEE80211_F_SCAN) &&
- (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) {
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: cancel %s scan\n", __func__,
- ss->ss_flags & IEEE80211_SCAN_ACTIVE ?
- "active" : "passive");
-
- /* clear bg scan NOPICK and mark cancel request */
- ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
- SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL;
- /* wake up the scan task */
- scan_signal(ss);
- } else {
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n",
- __func__,
- !! (ic->ic_flags & IEEE80211_F_SCAN),
- (ss->ss_vap == vap ? "match" : "nomatch"),
- !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL));
- }
- IEEE80211_UNLOCK(ic);
+ cancel_scan(vap, 1, __func__);
}
/*
- * Public access to scan_next for drivers that manage
- * scanning themselves (e.g. for firmware-based devices).
+ * Manually switch to the next channel in the channel list.
+ * Provided for drivers that manage scanning themselves
+ * (e.g. for firmware-based devices).
*/
static void
ieee80211_swscan_scan_next(struct ieee80211vap *vap)
{
- struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_scan_state *ss = ic->ic_scan;
+ struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
/* wake up the scan task */
- IEEE80211_LOCK(ic);
- scan_signal(ss);
- IEEE80211_UNLOCK(ic);
+ scan_signal(ss, 0);
}
/*
- * Public access to scan_next for drivers that are not able to scan single
- * channels (e.g. for firmware-based devices).
+ * Manually stop a scan that is currently running.
+ * Provided for drivers that are not able to scan single channels
+ * (e.g. for firmware-based devices).
*/
static void
ieee80211_swscan_scan_done(struct ieee80211vap *vap)
{
struct ieee80211com *ic = vap->iv_ic;
- struct ieee80211_scan_state *ss;
+ struct ieee80211_scan_state *ss = ic->ic_scan;
IEEE80211_LOCK_ASSERT(ic);
- ss = ic->ic_scan;
- scan_signal(ss);
+ scan_signal_locked(ss, 0);
}
/*
- * Probe the curent channel, if allowed, while scanning.
+ * Probe the current channel, if allowed, while scanning.
* If the channel is not marked passive-only then send
* a probe request immediately. Otherwise mark state and
* listen for beacons on the channel; if we receive something
@@ -568,28 +548,48 @@
scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell)
{
struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = ss->ss_ic;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: calling; maxdwell=%lu\n",
__func__,
maxdwell);
- IEEE80211_LOCK(vap->iv_ic);
+ IEEE80211_LOCK(ic);
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
ieee80211_probe_curchan(vap, 0);
- callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer,
- maxdwell, scan_signal, ss);
- IEEE80211_UNLOCK(vap->iv_ic);
+ taskqueue_enqueue_timeout(ic->ic_tq,
+ &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell);
+ IEEE80211_UNLOCK(ic);
}
static void
-scan_signal(void *arg)
+scan_signal(struct ieee80211_scan_state *ss, int iflags)
{
- struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+ struct ieee80211com *ic = ss->ss_ic;
- IEEE80211_LOCK_ASSERT(ss->ss_ic);
- cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv);
+ IEEE80211_UNLOCK_ASSERT(ic);
+
+ IEEE80211_LOCK(ic);
+ scan_signal_locked(ss, iflags);
+ IEEE80211_UNLOCK(ic);
}
+static void
+scan_signal_locked(struct ieee80211_scan_state *ss, int iflags)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct timeout_task *scan_task = &ss_priv->ss_scan_curchan;
+ struct ieee80211com *ic = ss->ss_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ ss_priv->ss_iflags |= iflags;
+ if (ss_priv->ss_iflags & ISCAN_RUNNING) {
+ if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0)
+ taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0);
+ }
+}
+
/*
* Handle mindwell requirements completed; initiate a channel
* change to the next channel asap.
@@ -597,38 +597,35 @@
static void
scan_mindwell(struct ieee80211_scan_state *ss)
{
- struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
- IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__);
- IEEE80211_LOCK(ic);
- scan_signal(ss);
- IEEE80211_UNLOCK(ic);
+ scan_signal(ss, 0);
}
static void
-scan_task(void *arg, int pending)
+scan_start(void *arg, int pending)
{
#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD)
struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg;
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
struct ieee80211vap *vap = ss->ss_vap;
struct ieee80211com *ic = ss->ss_ic;
- struct ieee80211_channel *chan;
- unsigned long maxdwell, scanend;
- int scandone = 0;
IEEE80211_LOCK(ic);
if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
- (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) {
+ (ss_priv->ss_iflags & ISCAN_ABORT)) {
/* Cancelled before we started */
- goto done;
+ scan_done(ss, 0);
+ return;
}
if (ss->ss_next == ss->ss_last) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: no channels to scan\n", __func__);
- scandone = 1;
- goto done;
+ scan_done(ss, 1);
+ return;
}
if (vap->iv_opmode == IEEE80211_M_STA &&
@@ -636,110 +633,134 @@
if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) {
/* Enable station power save mode */
vap->iv_sta_ps(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), msecs_to_ticks(1));
- if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
- goto done;
+ /* Wait until null data frame will be ACK'ed */
+ mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH,
+ "sta_ps", msecs_to_ticks(10));
+ if (ss_priv->ss_iflags & ISCAN_ABORT) {
+ scan_done(ss, 0);
+ return;
+ }
}
}
- scanend = ticks + SCAN_PRIVATE(ss)->ss_duration;
+ ss_priv->ss_scanend = ticks + ss_priv->ss_duration;
/* XXX scan state can change! Re-validate scan state! */
IEEE80211_UNLOCK(ic);
+
ic->ic_scan_start(ic); /* notify driver */
+
+ scan_curchan_task(ss, 0);
+}
+
+static void
+scan_curchan_task(void *arg, int pending)
+{
+ struct ieee80211_scan_state *ss = arg;
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211_channel *chan;
+ unsigned long maxdwell;
+ int scandone;
+
IEEE80211_LOCK(ic);
+end:
+ scandone = (ss->ss_next >= ss->ss_last) ||
+ (ss_priv->ss_iflags & ISCAN_CANCEL) != 0;
- for (;;) {
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: loop start; scandone=%d\n",
+ __func__,
+ scandone);
- scandone = (ss->ss_next >= ss->ss_last) ||
- (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0;
+ if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
+ (ss_priv->ss_iflags & ISCAN_ABORT) ||
+ time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
+ ss_priv->ss_iflags &= ~ISCAN_RUNNING;
+ scan_end(ss, scandone);
+ return;
+ } else
+ ss_priv->ss_iflags |= ISCAN_RUNNING;
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: loop start; scandone=%d\n",
- __func__,
- scandone);
+ chan = ss->ss_chans[ss->ss_next++];
- if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) ||
- (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) ||
- time_after(ticks + ss->ss_mindwell, scanend))
- break;
+ /*
+ * Watch for truncation due to the scan end time.
+ */
+ if (time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend))
+ maxdwell = ss_priv->ss_scanend - ticks;
+ else
+ maxdwell = ss->ss_maxdwell;
- chan = ss->ss_chans[ss->ss_next++];
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
+ "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
+ __func__,
+ ieee80211_chan2ieee(ic, ic->ic_curchan),
+ ieee80211_channel_type_char(ic->ic_curchan),
+ ieee80211_chan2ieee(ic, chan),
+ ieee80211_channel_type_char(chan),
+ (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
+ (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
+ "active" : "passive",
+ ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
- /*
- * Watch for truncation due to the scan end time.
- */
- if (time_after(ticks + ss->ss_maxdwell, scanend))
- maxdwell = scanend - ticks;
- else
- maxdwell = ss->ss_maxdwell;
+ /*
+ * Potentially change channel and phy mode.
+ */
+ 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);
+ ieee80211_radiotap_chan_change(ic);
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
- "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n",
- __func__,
- ieee80211_chan2ieee(ic, ic->ic_curchan),
- ieee80211_channel_type_char(ic->ic_curchan),
- ieee80211_chan2ieee(ic, chan),
- ieee80211_channel_type_char(chan),
- (ss->ss_flags & IEEE80211_SCAN_ACTIVE) &&
- (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ?
- "active" : "passive",
- ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell));
+ /*
+ * Scan curchan. Drivers for "intelligent hardware"
+ * override ic_scan_curchan to tell the device to do
+ * the work. Otherwise we manage the work ourselves;
+ * 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).
+ */
+ ic->ic_scan_curchan(ss, maxdwell);
+ IEEE80211_LOCK(ic);
- /*
- * Potentially change channel and phy mode.
- */
- 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);
- ieee80211_radiotap_chan_change(ic);
+ /* XXX scan state can change! Re-validate scan state! */
- /*
- * Scan curchan. Drivers for "intelligent hardware"
- * override ic_scan_curchan to tell the device to do
- * 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);
+ ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell;
+ /* clear mindwell lock and initial channel change flush */
+ ss_priv->ss_iflags &= ~ISCAN_REP;
- /* XXX scan state can change! Re-validate scan state! */
+ if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))
+ goto end;
- SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell;
- /* clear mindwell lock and initial channel change flush */
- SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: waiting\n", __func__);
+ IEEE80211_UNLOCK(ic);
+}
- if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)))
- continue;
+static void
+scan_end(struct ieee80211_scan_state *ss, int scandone)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211vap *vap = ss->ss_vap;
+ struct ieee80211com *ic = ss->ss_ic;
- IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: waiting\n", __func__);
- /* Wait to be signalled to scan the next channel */
- cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic));
- }
+ IEEE80211_LOCK_ASSERT(ic);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__);
- if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)
- goto done;
+ if (ss_priv->ss_iflags & ISCAN_ABORT) {
+ scan_done(ss, scandone);
+ return;
+ }
IEEE80211_UNLOCK(ic);
ic->ic_scan_end(ic); /* notify driver */
@@ -750,8 +771,7 @@
* Since a cancellation may have occured during one of the
* driver calls (whilst unlocked), update scandone.
*/
- if (scandone == 0 &&
- ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) {
+ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
/* XXX printf? */
if_printf(vap->iv_ifp,
"%s: OOPS! scan cancelled during driver call (1)!\n",
@@ -776,7 +796,7 @@
IEEE80211_LOCK(ic);
}
/* clear internal flags and any indication of a pick */
- SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP;
+ ss_priv->ss_iflags &= ~ISCAN_REP;
ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK;
/*
@@ -786,15 +806,15 @@
* 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 &&
+ if ((ss_priv->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)) {
+ time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN,
"%s: done, restart "
"[ticks %u, dwell min %lu scanend %lu]\n",
__func__,
- ticks, ss->ss_mindwell, scanend);
+ ticks, ss->ss_mindwell, ss_priv->ss_scanend);
ss->ss_next = 0; /* reset to begining */
if (ss->ss_flags & IEEE80211_SCAN_ACTIVE)
vap->iv_stats.is_scan_active++;
@@ -802,7 +822,7 @@
vap->iv_stats.is_scan_passive++;
ss->ss_ops->scan_restart(ss, vap); /* XXX? */
- ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task);
+ ieee80211_runtask(ic, &ss_priv->ss_scan_start);
IEEE80211_UNLOCK(ic);
return;
}
@@ -814,14 +834,13 @@
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);
+ ticks, ss->ss_mindwell, ss_priv->ss_scanend);
/*
* Since a cancellation may have occured during one of the
* driver calls (whilst unlocked), update scandone.
*/
- if (scandone == 0 &&
- ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0)) {
+ if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) {
/* XXX printf? */
if_printf(vap->iv_ifp,
"%s: OOPS! scan cancelled during driver call (2)!\n",
@@ -829,6 +848,18 @@
scandone = 1;
}
+ scan_done(ss, scandone);
+}
+
+static void
+scan_done(struct ieee80211_scan_state *ss, int scandone)
+{
+ struct scan_state *ss_priv = SCAN_PRIVATE(ss);
+ struct ieee80211com *ic = ss->ss_ic;
+ struct ieee80211vap *vap = ss->ss_vap;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
/*
* Clear the SCAN bit first in case frames are
* pending on the station power save queue. If
@@ -835,8 +866,8 @@
* 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
@@ -853,7 +884,8 @@
ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN;
}
}
- SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
+ ss_priv->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT);
+ ss_priv->ss_scanend = 0;
ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST);
IEEE80211_UNLOCK(ic);
#undef ISCAN_REP
@@ -947,9 +979,9 @@
ic->ic_scan = NULL;
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);
+ TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss);
+ TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0,
+ scan_curchan_task, ss);
ic->ic_scan = &ss->base;
ss->base.ss_ic = ic;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?op.yc4mcrg5iew4ia>
