summaryrefslogtreecommitdiffstats
path: root/sys/net80211
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
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')
-rw-r--r--sys/net80211/ieee80211.c23
-rw-r--r--sys/net80211/ieee80211_freebsd.h1
-rw-r--r--sys/net80211/ieee80211_ioctl.c4
-rw-r--r--sys/net80211/ieee80211_node.c18
-rw-r--r--sys/net80211/ieee80211_node.h2
-rw-r--r--sys/net80211/ieee80211_proto.c215
-rw-r--r--sys/net80211/ieee80211_scan.c407
-rw-r--r--sys/net80211/ieee80211_scan.h1
-rw-r--r--sys/net80211/ieee80211_scan_sta.c6
-rw-r--r--sys/net80211/ieee80211_var.h38
10 files changed, 454 insertions, 261 deletions
diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c
index eb40f61..2e75965 100644
--- a/sys/net80211/ieee80211.c
+++ b/sys/net80211/ieee80211.c
@@ -33,7 +33,7 @@ __FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
-#include <sys/systm.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/socket.h>
@@ -251,6 +251,12 @@ ieee80211_ifattach(struct ieee80211com *ic,
IEEE80211_LOCK_INIT(ic, ifp->if_xname);
TAILQ_INIT(&ic->ic_vaps);
+
+ /* Create a taskqueue for all state changes */
+ ic->ic_tq = taskqueue_create("ic_taskq", M_WAITOK | M_ZERO,
+ taskqueue_thread_enqueue, &ic->ic_tq);
+ taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s taskq",
+ ifp->if_xname);
/*
* Fill in 802.11 available channel set, mark all
* available channels as active, and pick a default
@@ -325,6 +331,7 @@ ieee80211_ifdetach(struct ieee80211com *ic)
ieee80211_node_detach(ic);
ifmedia_removeall(&ic->ic_media);
+ taskqueue_free(ic->ic_tq);
IEEE80211_LOCK_DESTROY(ic);
if_detach(ifp);
}
@@ -554,7 +561,17 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
* while we cleanup internal state but that is hard.
*/
ieee80211_stop_locked(vap);
+ IEEE80211_UNLOCK(ic);
+
+ /*
+ * Flush any deferred vap tasks.
+ * NB: must be before ether_ifdetach() and removal from ic_vaps list
+ */
+ ieee80211_draintask(ic, &vap->iv_nstate_task);
+ ieee80211_draintask(ic, &vap->iv_swbmiss_task);
+ IEEE80211_LOCK(ic);
+ KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running"));
TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next);
ieee80211_syncflag_locked(ic, IEEE80211_F_WME);
#ifdef IEEE80211_SUPPORT_SUPERG
@@ -627,9 +644,9 @@ ieee80211_syncifflag_locked(struct ieee80211com *ic, int flag)
/* XXX should we return 1/0 and let caller do this? */
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
if (flag == IFF_PROMISC)
- ic->ic_update_promisc(ifp);
+ ieee80211_runtask(ic, &ic->ic_promisc_task);
else if (flag == IFF_ALLMULTI)
- ic->ic_update_mcast(ifp);
+ ieee80211_runtask(ic, &ic->ic_mcast_task);
}
}
}
diff --git a/sys/net80211/ieee80211_freebsd.h b/sys/net80211/ieee80211_freebsd.h
index 2e0075e..3ed9ece 100644
--- a/sys/net80211/ieee80211_freebsd.h
+++ b/sys/net80211/ieee80211_freebsd.h
@@ -32,6 +32,7 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
+#include <sys/taskqueue.h>
/*
* Common state locking definitions.
diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c
index b28dffa..d40680a 100644
--- a/sys/net80211/ieee80211_ioctl.c
+++ b/sys/net80211/ieee80211_ioctl.c
@@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/systm.h>
-#include <sys/taskqueue.h>
#include <net/if.h>
#include <net/if_dl.h>
@@ -3196,8 +3195,7 @@ ieee80211_ioctl_updatemulti(struct ieee80211com *ic)
(void) if_addmulti(parent, ifma->ifma_addr, NULL);
}
parent->if_ioctl = ioctl;
-
- ic->ic_update_mcast(ic->ic_ifp);
+ ieee80211_runtask(ic, &ic->ic_mcast_task);
IEEE80211_UNLOCK(ic);
}
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c
index 2f39eb8..483af2d 100644
--- a/sys/net80211/ieee80211_node.c
+++ b/sys/net80211/ieee80211_node.c
@@ -629,16 +629,18 @@ ieee80211_sync_curchan(struct ieee80211com *ic)
ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
+ IEEE80211_UNLOCK(ic);
ic->ic_set_channel(ic);
+ IEEE80211_LOCK(ic);
}
}
/*
- * Change the current channel. The request channel may be
+ * Setup the current channel. The request channel may be
* promoted if other vap's are operating with HT20/HT40.
*/
void
-ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+ieee80211_setupcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
{
if (ic->ic_htcaps & IEEE80211_HTC_HT) {
int flags = gethtadjustflags(ic);
@@ -654,7 +656,17 @@ ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
ic->ic_bsschan = ic->ic_curchan = c;
ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan);
ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan);
- ic->ic_set_channel(ic);
+}
+
+/*
+ * Change the current channel. The channel change is guaranteed to have
+ * happened before the next state change.
+ */
+void
+ieee80211_setcurchan(struct ieee80211com *ic, struct ieee80211_channel *c)
+{
+ ieee80211_setupcurchan(ic, c);
+ ieee80211_runtask(ic, &ic->ic_chan_task);
}
/*
diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h
index 9e9d254..47f705f 100644
--- a/sys/net80211/ieee80211_node.h
+++ b/sys/net80211/ieee80211_node.h
@@ -286,6 +286,8 @@ void ieee80211_node_set_chan(struct ieee80211_node *,
void ieee80211_create_ibss(struct ieee80211vap*, struct ieee80211_channel *);
void ieee80211_reset_bss(struct ieee80211vap *);
void ieee80211_sync_curchan(struct ieee80211com *);
+void ieee80211_setupcurchan(struct ieee80211com *,
+ struct ieee80211_channel *);
void ieee80211_setcurchan(struct ieee80211com *, struct ieee80211_channel *);
int ieee80211_ibss_merge(struct ieee80211_node *);
struct ieee80211_scan_entry;
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index 572580f..98888d8 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -37,7 +37,6 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
-#include <sys/taskqueue.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@@ -96,7 +95,13 @@ const char *ieee80211_wme_acnames[] = {
"WME_UPSD",
};
+static void beacon_miss(void *, int);
+static void beacon_swmiss(void *, int);
static void parent_updown(void *, int);
+static void update_mcast(void *, int);
+static void update_promisc(void *, int);
+static void update_channel(void *, int);
+static void ieee80211_newstate_cb(void *, int);
static int ieee80211_new_state_locked(struct ieee80211vap *,
enum ieee80211_state, int);
@@ -131,6 +136,10 @@ ieee80211_proto_attach(struct ieee80211com *ic)
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp);
+ TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
+ TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic);
+ TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
+ TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
ic->ic_wme.wme_hipri_switch_hysteresis =
AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
@@ -176,6 +185,8 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
vap->iv_bmiss_max = IEEE80211_BMISS_MAX;
callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE);
callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE);
+ TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
+ TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
/*
* Install default tx rate handling: no fixed rate, lowest
* supported rate for mgmt and multicast frames. Default
@@ -1072,6 +1083,32 @@ parent_updown(void *arg, int npending)
parent->if_ioctl(parent, SIOCSIFFLAGS, NULL);
}
+static void
+update_mcast(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+ struct ifnet *parent = ic->ic_ifp;
+
+ ic->ic_update_mcast(parent);
+}
+
+static void
+update_promisc(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+ struct ifnet *parent = ic->ic_ifp;
+
+ ic->ic_update_promisc(parent);
+}
+
+static void
+update_channel(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ ic->ic_set_channel(ic);
+}
+
/*
* Block until the parent is in a known state. This is
* used after any operations that dispatch a task (e.g.
@@ -1080,7 +1117,13 @@ parent_updown(void *arg, int npending)
void
ieee80211_waitfor_parent(struct ieee80211com *ic)
{
- taskqueue_drain(taskqueue_thread, &ic->ic_parent_task);
+ taskqueue_block(ic->ic_tq);
+ ieee80211_draintask(ic, &ic->ic_parent_task);
+ ieee80211_draintask(ic, &ic->ic_mcast_task);
+ ieee80211_draintask(ic, &ic->ic_promisc_task);
+ ieee80211_draintask(ic, &ic->ic_chan_task);
+ ieee80211_draintask(ic, &ic->ic_bmiss_task);
+ taskqueue_unblock(ic->ic_tq);
}
/*
@@ -1121,7 +1164,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"%s: up parent %s\n", __func__, parent->if_xname);
parent->if_flags |= IFF_UP;
- taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ ieee80211_runtask(ic, &ic->ic_parent_task);
return;
}
}
@@ -1156,8 +1199,7 @@ ieee80211_start_locked(struct ieee80211vap *vap)
* preempted if the station is locked to a particular
* channel.
*/
- /* XXX needed? */
- ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0);
+ vap->iv_flags_ext |= IEEE80211_FEXT_REINIT;
if (vap->iv_opmode == IEEE80211_M_MONITOR ||
vap->iv_opmode == IEEE80211_M_WDS)
ieee80211_new_state_locked(vap,
@@ -1240,7 +1282,7 @@ ieee80211_stop_locked(struct ieee80211vap *vap)
IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
"down parent %s\n", parent->if_xname);
parent->if_flags &= ~IFF_UP;
- taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task);
+ ieee80211_runtask(ic, &ic->ic_parent_task);
}
}
}
@@ -1319,10 +1361,20 @@ ieee80211_resume_all(struct ieee80211com *ic)
void
ieee80211_beacon_miss(struct ieee80211com *ic)
{
+ IEEE80211_LOCK(ic);
+ if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
+ /* Process in a taskq, the handler may reenter the driver */
+ ieee80211_runtask(ic, &ic->ic_bmiss_task);
+ }
+ IEEE80211_UNLOCK(ic);
+}
+
+static void
+beacon_miss(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
struct ieee80211vap *vap;
- if (ic->ic_flags & IEEE80211_F_SCAN)
- return;
/* XXX locking */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
/*
@@ -1337,6 +1389,18 @@ ieee80211_beacon_miss(struct ieee80211com *ic)
}
}
+static void
+beacon_swmiss(void *arg, int npending)
+{
+ struct ieee80211vap *vap = arg;
+
+ if (vap->iv_state != IEEE80211_S_RUN)
+ return;
+
+ /* XXX Call multiple times if npending > zero? */
+ vap->iv_bmiss(vap);
+}
+
/*
* Software beacon miss handling. Check if any beacons
* were received in the last period. If not post a
@@ -1366,7 +1430,7 @@ ieee80211_swbmiss(void *arg)
vap->iv_swbmiss_count = 0;
} else if (vap->iv_swbmiss_count == 0) {
if (vap->iv_bmiss != NULL)
- vap->iv_bmiss(vap);
+ ieee80211_runtask(ic, &vap->iv_swbmiss_task);
if (vap->iv_bmiss_count == 0) /* don't re-arm timer */
return;
} else
@@ -1463,7 +1527,6 @@ ieee80211_cac_completeswitch(struct ieee80211vap *vap0)
* and mark them as waiting for a scan to complete. These vaps
* will be brought up when the scan completes and the scanning vap
* reaches RUN state by wakeupwaiting.
- * XXX if we do this in threads we can use sleep/wakeup.
*/
static void
markwaiting(struct ieee80211vap *vap0)
@@ -1473,10 +1536,16 @@ markwaiting(struct ieee80211vap *vap0)
IEEE80211_LOCK_ASSERT(ic);
+ /*
+ * A vap list entry can not disappear since we are running on the
+ * taskqueue and a vap destroy will queue and drain another state
+ * change task.
+ */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_state != IEEE80211_S_INIT) {
+ /* NB: iv_newstate may drop the lock */
vap->iv_newstate(vap, IEEE80211_S_INIT, 0);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
}
@@ -1487,6 +1556,7 @@ markwaiting(struct ieee80211vap *vap0)
* Wakeup all vap's waiting for a scan to complete. This is the
* companion to markwaiting (above) and is used to coordinate
* multiple vaps scanning.
+ * This is called from the state taskqueue.
*/
static void
wakeupwaiting(struct ieee80211vap *vap0)
@@ -1496,12 +1566,18 @@ wakeupwaiting(struct ieee80211vap *vap0)
IEEE80211_LOCK_ASSERT(ic);
+ /*
+ * A vap list entry can not disappear since we are running on the
+ * taskqueue and a vap destroy will queue and drain another state
+ * change task.
+ */
TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
if (vap == vap0)
continue;
if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) {
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT;
/* NB: sta's cannot go INIT->RUN */
+ /* NB: iv_newstate may drop the lock */
vap->iv_newstate(vap,
vap->iv_opmode == IEEE80211_M_STA ?
IEEE80211_S_SCAN : IEEE80211_S_RUN, 0);
@@ -1513,15 +1589,63 @@ wakeupwaiting(struct ieee80211vap *vap0)
* Handle post state change work common to all operating modes.
*/
static void
-ieee80211_newstate_cb(struct ieee80211vap *vap,
- enum ieee80211_state nstate, int arg)
+ieee80211_newstate_cb(void *xvap, int npending)
{
+ struct ieee80211vap *vap = xvap;
struct ieee80211com *ic = vap->iv_ic;
+ enum ieee80211_state nstate, ostate;
+ int arg, rc;
- IEEE80211_LOCK_ASSERT(ic);
+ IEEE80211_LOCK(ic);
+ nstate = vap->iv_nstate;
+ arg = vap->iv_nstate_arg;
+ if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) {
+ /*
+ * We have been requested to drop back to the INIT before
+ * proceeding to the new state.
+ */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s -> %s arg %d\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[IEEE80211_S_INIT], arg);
+ vap->iv_newstate(vap, IEEE80211_S_INIT, arg);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT;
+ }
+
+ ostate = vap->iv_state;
+ if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) {
+ /*
+ * SCAN was forced; e.g. on beacon miss. Force other running
+ * vap's to INIT state and mark them as waiting for the scan to
+ * complete. This insures they don't interfere with our
+ * scanning. Since we are single threaded the vaps can not
+ * transition again while we are executing.
+ *
+ * XXX not always right, assumes ap follows sta
+ */
+ markwaiting(vap);
+ }
IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
- "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg);
+ "%s: %s -> %s arg %d\n", __func__,
+ ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg);
+
+ rc = vap->iv_newstate(vap, nstate, arg);
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT;
+ if (rc != 0) {
+ /* State transition failed */
+ KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred"));
+ KASSERT(nstate != IEEE80211_S_INIT,
+ ("INIT state change failed"));
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: %s returned error %d\n", __func__,
+ ieee80211_state_name[nstate], rc);
+ goto done;
+ }
+
+ /* No actual transition, skip post processing */
+ if (ostate == nstate)
+ goto done;
if (nstate == IEEE80211_S_RUN) {
/*
@@ -1549,7 +1673,8 @@ ieee80211_newstate_cb(struct ieee80211vap *vap,
/* XXX NB: cast for altq */
ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap);
}
- vap->iv_newstate_cb = NULL;
+done:
+ IEEE80211_UNLOCK(ic);
}
/*
@@ -1586,10 +1711,32 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211vap *vp;
enum ieee80211_state ostate;
- int nrunning, nscanning, rc;
+ int nrunning, nscanning;
IEEE80211_LOCK_ASSERT(ic);
+ if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) {
+ if (vap->iv_nstate == IEEE80211_S_INIT) {
+ /*
+ * XXX The vap is being stopped, do no allow any other
+ * state changes until this is completed.
+ */
+ return -1;
+ }
+#if 0
+ /* Warn if the previous state hasn't completed. */
+ IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
+ "%s: pending %s -> %s transition lost\n", __func__,
+ ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[vap->iv_nstate]);
+#else
+ /* XXX temporarily enable to identify issues */
+ if_printf(vap->iv_ifp, "%s: pending %s -> %s transition lost\n",
+ __func__, ieee80211_state_name[vap->iv_state],
+ ieee80211_state_name[vap->iv_nstate]);
+#endif
+ }
+
nrunning = nscanning = 0;
/* XXX can track this state instead of calculating */
TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) {
@@ -1625,8 +1772,7 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
__func__, ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
- rc = 0;
- goto done;
+ return 0;
}
if (nrunning) {
/*
@@ -1650,16 +1796,6 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
}
#endif
}
- } else {
- /*
- * SCAN was forced; e.g. on beacon miss. Force
- * other running vap's to INIT state and mark
- * them as waiting for the scan to complete. This
- * insures they don't interfere with our scanning.
- *
- * XXX not always right, assumes ap follows sta
- */
- markwaiting(vap);
}
break;
case IEEE80211_S_RUN:
@@ -1676,8 +1812,7 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]);
vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT;
- rc = 0;
- goto done;
+ return 0;
}
if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
@@ -1706,20 +1841,12 @@ ieee80211_new_state_locked(struct ieee80211vap *vap,
default:
break;
}
- /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */
- if (ostate != nstate) {
- /*
- * Arrange for work to happen after state change completes.
- * If this happens asynchronously the caller must arrange
- * for the com lock to be held.
- */
- vap->iv_newstate_cb = ieee80211_newstate_cb;
- }
- rc = vap->iv_newstate(vap, nstate, arg);
- if (rc == 0 && vap->iv_newstate_cb != NULL)
- vap->iv_newstate_cb(vap, nstate, arg);
-done:
- return rc;
+ /* defer the state change to a thread */
+ vap->iv_nstate = nstate;
+ vap->iv_nstate_arg = arg;
+ vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT;
+ ieee80211_runtask(ic, &vap->iv_nstate_task);
+ return EINPROGRESS;
}
int
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
}
diff --git a/sys/net80211/ieee80211_scan.h b/sys/net80211/ieee80211_scan.h
index 78e5fe4..edba660 100644
--- a/sys/net80211/ieee80211_scan.h
+++ b/sys/net80211/ieee80211_scan.h
@@ -90,6 +90,7 @@ struct ieee80211_scan_ssid {
*/
struct ieee80211_scan_state {
struct ieee80211vap *ss_vap;
+ struct ieee80211com *ss_ic;
const struct ieee80211_scanner *ss_ops; /* policy hookup, see below */
void *ss_priv; /* scanner private state */
uint16_t ss_flags;
diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c
index 607c36d..cef3db4 100644
--- a/sys/net80211/ieee80211_scan_sta.c
+++ b/sys/net80211/ieee80211_scan_sta.c
@@ -32,10 +32,10 @@ __FBSDID("$FreeBSD$");
#include "opt_wlan.h"
#include <sys/param.h>
-#include <sys/systm.h>
+#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
-
+
#include <sys/socket.h>
#include <net/if.h>
@@ -1640,7 +1640,7 @@ ap_force_promisc(struct ieee80211com *ic)
IEEE80211_LOCK(ic);
/* set interface into promiscuous mode */
ifp->if_flags |= IFF_PROMISC;
- ic->ic_update_promisc(ifp);
+ ieee80211_runtask(ic, &ic->ic_promisc_task);
IEEE80211_UNLOCK(ic);
}
diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h
index 55204f8..6c4ee74 100644
--- a/sys/net80211/ieee80211_var.h
+++ b/sys/net80211/ieee80211_var.h
@@ -126,7 +126,12 @@ struct ieee80211com {
enum ieee80211_opmode ic_opmode; /* operation mode */
struct ifmedia ic_media; /* interface media config */
struct callout ic_inact; /* inactivity processing */
+ struct taskqueue *ic_tq; /* deferred state thread */
struct task ic_parent_task; /* deferred parent processing */
+ struct task ic_promisc_task;/* deferred promisc update */
+ struct task ic_mcast_task; /* deferred mcast update */
+ struct task ic_chan_task; /* deferred channel change */
+ struct task ic_bmiss_task; /* deferred beacon miss hndlr */
uint32_t ic_flags; /* state flags */
uint32_t ic_flags_ext; /* extended state flags */
@@ -325,8 +330,10 @@ struct ieee80211vap {
uint32_t iv_htcaps; /* HT capabilities */
enum ieee80211_opmode iv_opmode; /* operation mode */
enum ieee80211_state iv_state; /* state machine state */
- void (*iv_newstate_cb)(struct ieee80211vap *,
- enum ieee80211_state, int);
+ enum ieee80211_state iv_nstate; /* pending state */
+ int iv_nstate_arg; /* pending state arg */
+ struct task iv_nstate_task; /* deferred state processing */
+ struct task iv_swbmiss_task;/* deferred iv_bmiss call */
struct callout iv_mgtsend; /* mgmt frame response timer */
/* inactivity timer settings */
int iv_inact_init; /* setting for new station */
@@ -519,6 +526,8 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
#define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */
#define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */
+#define IEEE80211_FEXT_STATEWAIT 0x00002000 /* STATUS: awaiting state chg */
+#define IEEE80211_FEXT_REINIT 0x00004000 /* STATUS: INIT state first */
/* NB: immutable: should be set only when creating a vap */
#define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
@@ -536,9 +545,10 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FEXT_BITS \
"\20\1NONHT_PR\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \
- "\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\22WDSLEGACY\23PROBECHAN" \
- "\24HT\25AMDPU_TX\26AMPDU_TX\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN" \
- "\33SHORTGI20\34SHORTGI40\35HTCOMPAT\36RIFS"
+ "\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \
+ "\22WDSLEGACY\23PROBECHAN\24HT\25AMDPU_TX\26AMPDU_TX\27AMSDU_TX" \
+ "\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20\34SHORTGI40\35HTCOMPAT" \
+ "\36RIFS"
#define IEEE80211_FVEN_BITS "\20"
@@ -633,6 +643,24 @@ struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *,
int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode);
enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *);
+/*
+ * Enqueue a task on the state thread.
+ */
+static __inline void
+ieee80211_runtask(struct ieee80211com *ic, struct task *task)
+{
+ taskqueue_enqueue(ic->ic_tq, task);
+}
+
+/*
+ * Wait for a queued task to complete.
+ */
+static __inline void
+ieee80211_draintask(struct ieee80211com *ic, struct task *task)
+{
+ taskqueue_drain(ic->ic_tq, task);
+}
+
/*
* Key update synchronization methods. XXX should not be visible.
*/
OpenPOWER on IntegriCloud