summaryrefslogtreecommitdiffstats
path: root/sys/net80211
diff options
context:
space:
mode:
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