diff options
Diffstat (limited to 'sys/net80211')
-rw-r--r-- | sys/net80211/ieee80211.c | 23 | ||||
-rw-r--r-- | sys/net80211/ieee80211_freebsd.h | 1 | ||||
-rw-r--r-- | sys/net80211/ieee80211_ioctl.c | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.c | 18 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 2 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 215 | ||||
-rw-r--r-- | sys/net80211/ieee80211_scan.c | 407 | ||||
-rw-r--r-- | sys/net80211/ieee80211_scan.h | 1 | ||||
-rw-r--r-- | sys/net80211/ieee80211_scan_sta.c | 6 | ||||
-rw-r--r-- | sys/net80211/ieee80211_var.h | 38 |
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. */ |