diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index a36abf1aa0d..391780ddab9 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -645,6 +645,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, ieee80211_scan_vattach(vap); ieee80211_regdomain_vattach(vap); ieee80211_radiotap_vattach(vap); + ieee80211_vap_reset_erp(vap); ieee80211_ratectl_set(vap, IEEE80211_RATECTL_NONE); return 0; @@ -2200,7 +2201,7 @@ ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); ic->ic_curmode = mode; - ieee80211_reset_erp(ic); /* reset ERP state */ + ieee80211_reset_erp(ic); /* reset global ERP state */ return 0; } diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index a1415d73ec6..f77be819f53 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -446,7 +446,7 @@ ieee80211_reset_bss(struct ieee80211vap *vap) ieee80211_node_table_reset(&ic->ic_sta, vap); /* XXX multi-bss: wrong */ - ieee80211_reset_erp(ic); + ieee80211_vap_reset_erp(vap); ni = ieee80211_alloc_node(&ic->ic_sta, vap, vap->iv_myaddr); KASSERT(ni != NULL, ("unable to setup initial BSS node")); @@ -682,7 +682,7 @@ ieee80211_ibss_merge(struct ieee80211_node *ni) "%s: new bssid %s: %s preamble, %s slot time%s\n", __func__, ether_sprintf(ni->ni_bssid), ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", - ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + vap->iv_flags&IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "" ); return ieee80211_sta_join1(ieee80211_ref_node(ni)); @@ -881,7 +881,7 @@ ieee80211_sta_join1(struct ieee80211_node *selbs) * the auto-select case; this should be redundant if the * mode is locked. */ - ieee80211_reset_erp(ic); + ieee80211_vap_reset_erp(vap); ieee80211_wme_initparams(vap); if (vap->iv_opmode == IEEE80211_M_STA) { @@ -2645,6 +2645,7 @@ static void ieee80211_node_join_11g(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; IEEE80211_LOCK_ASSERT(ic); @@ -2660,14 +2661,13 @@ ieee80211_node_join_11g(struct ieee80211_node *ni) IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni, "station needs long slot time, count %d", ic->ic_longslotsta); - /* XXX vap's w/ conflicting needs won't work */ if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) { /* * Don't force slot time when switched to turbo * mode as non-ERP stations won't be present; this * need only be done when on the normal G channel. */ - ieee80211_set_shortslottime(ic, 0); + ieee80211_vap_set_shortslottime(vap, 0); } } /* @@ -2757,7 +2757,7 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp) "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", IEEE80211_NODE_AID(ni), ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long", - ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", + vap->iv_flags & IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", /* XXX update for VHT string */ @@ -2810,6 +2810,7 @@ static void ieee80211_node_leave_11g(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; + struct ieee80211vap *vap = ni->ni_vap; IEEE80211_LOCK_ASSERT(ic); @@ -2838,7 +2839,7 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni) IEEE80211_MSG_ASSOC, "%s: re-enable use of short slot time\n", __func__); - ieee80211_set_shortslottime(ic, 1); + ieee80211_vap_set_shortslottime(vap, 1); } } } diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 4bb72c39b2a..22f0c58a944 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -2522,7 +2522,7 @@ ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan) if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && IEEE80211_IS_CHAN_2GHZ(chan)) capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; - if (ic->ic_flags & IEEE80211_F_SHSLOT) + if (vap->iv_flags & IEEE80211_F_SHSLOT) capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME; if (IEEE80211_IS_CHAN_5GHZ(chan) && (vap->iv_flags & IEEE80211_F_DOTH)) capinfo |= IEEE80211_CAPINFO_SPECTRUM_MGMT; diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h index 1539e879335..45ff6c6b6b0 100644 --- a/sys/net80211/ieee80211_phy.h +++ b/sys/net80211/ieee80211_phy.h @@ -55,10 +55,25 @@ #define IEEE80211_DUR_SHSLOT 9 /* ERP short slottime */ #define IEEE80211_DUR_OFDM_SLOT 9 /* OFDM slottime */ +/* + * For drivers that don't implement per-VAP slot time + * (ie, they rely on net80211 figuring out the union + * between VAPs to program a single radio) - return + * the current radio configured slot time. + */ #define IEEE80211_GET_SLOTTIME(ic) \ ((ic->ic_flags & IEEE80211_F_SHSLOT) ? \ IEEE80211_DUR_SHSLOT : IEEE80211_DUR_SLOT) +/* + * For drivers that implement per-VAP slot time; look + * at the per-VAP flags to determine whether this VAP + * is in short or long slot time. + */ +#define IEEE80211_VAP_GET_SLOTTIME(vap) \ + ((vap->iv_flags & IEEE80211_F_SHSLOT) ? \ + IEEE80211_DUR_SHSLOT : IEEE80211_DUR_SLOT) + /* * DIFS (microseconds). */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index c70a159adad..ea189cfae70 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -244,6 +244,7 @@ static void update_promisc(void *, int); static void update_channel(void *, int); static void update_chw(void *, int); static void vap_update_wme(void *, int); +static void vap_update_slot(void *, int); static void restart_vaps(void *, int); static void ieee80211_newstate_cb(void *, int); @@ -340,6 +341,7 @@ ieee80211_proto_vattach(struct ieee80211vap *vap) TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap); TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap); TASK_INIT(&vap->iv_wme_task, 0, vap_update_wme, vap); + TASK_INIT(&vap->iv_slot_task, 0, vap_update_slot, vap); /* * Install default tx rate handling: no fixed rate, lowest * supported rate for mgmt and multicast frames. Default @@ -748,6 +750,32 @@ ieee80211_fix_rate(struct ieee80211_node *ni, return IEEE80211_RV(okrate); } +/* + * Reset 11g-related state. + * + * This is for per-VAP ERP/11g state. + * + * Eventually everything in ieee80211_reset_erp() will be + * per-VAP and in here. + */ +void +ieee80211_vap_reset_erp(struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + + /* + * Short slot time is enabled only when operating in 11g + * and not in an IBSS. We must also honor whether or not + * the driver is capable of doing it. + */ + ieee80211_vap_set_shortslottime(vap, + IEEE80211_IS_CHAN_A(ic->ic_curchan) || + IEEE80211_IS_CHAN_HT(ic->ic_curchan) || + (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && + vap->iv_opmode == IEEE80211_M_HOSTAP && + (ic->ic_caps & IEEE80211_C_SHSLOT))); +} + /* * Reset 11g-related state. */ @@ -757,17 +785,6 @@ ieee80211_reset_erp(struct ieee80211com *ic) ic->ic_flags &= ~IEEE80211_F_USEPROT; ic->ic_nonerpsta = 0; ic->ic_longslotsta = 0; - /* - * Short slot time is enabled only when operating in 11g - * and not in an IBSS. We must also honor whether or not - * the driver is capable of doing it. - */ - ieee80211_set_shortslottime(ic, - IEEE80211_IS_CHAN_A(ic->ic_curchan) || - IEEE80211_IS_CHAN_HT(ic->ic_curchan) || - (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && - ic->ic_opmode == IEEE80211_M_HOSTAP && - (ic->ic_caps & IEEE80211_C_SHSLOT))); /* * Set short preamble and ERP barker-preamble flags. */ @@ -781,19 +798,95 @@ ieee80211_reset_erp(struct ieee80211com *ic) } } +/* + * Deferred slot time update. + * + * For per-VAP slot time configuration, call the VAP + * method if the VAP requires it. Otherwise, just call the + * older global method. + * + * If the per-VAP method is called then it's expected that + * the driver/firmware will take care of turning the per-VAP + * flags into slot time configuration. + * + * If the per-VAP method is not called then the global flags will be + * flipped into sync with the VAPs; ic_flags IEEE80211_F_SHSLOT will + * be set only if all of the vaps will have it set. + */ +static void +vap_update_slot(void *arg, int npending) +{ + struct ieee80211vap *vap = arg; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211vap *iv; + int num_shslot = 0, num_lgslot = 0; + + /* + * Per-VAP path - we've already had the flags updated; + * so just notify the driver and move on. + */ + if (vap->iv_updateslot != NULL) { + vap->iv_updateslot(vap); + return; + } + + /* + * Iterate over all of the VAP flags to update the + * global flag. + * + * If all vaps have short slot enabled then flip on + * short slot. If any vap has it disabled then + * we leave it globally disabled. This should provide + * correct behaviour in a multi-BSS scenario where + * at least one VAP has short slot disabled for some + * reason. + */ + IEEE80211_LOCK(ic); + TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) { + if (iv->iv_flags & IEEE80211_F_SHSLOT) + num_shslot++; + else + num_lgslot++; + } + IEEE80211_UNLOCK(ic); + + /* + * It looks backwards but - if the number of short slot VAPs + * is zero then we're not short slot. Else, we have one + * or more short slot VAPs and we're checking to see if ANY + * of them have short slot disabled. + */ + if (num_shslot == 0) + ic->ic_flags &= ~IEEE80211_F_SHSLOT; + else if (num_lgslot == 0) + ic->ic_flags |= IEEE80211_F_SHSLOT; + + /* + * Call the driver with our new global slot time flags. + */ + ic->ic_updateslot(ic); +} + /* * Set the short slot time state and notify the driver. + * + * This is the per-VAP slot time state. */ void -ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) +ieee80211_vap_set_shortslottime(struct ieee80211vap *vap, int onoff) { + struct ieee80211com *ic = vap->iv_ic; + + /* + * Only modify the per-VAP slot time. + */ if (onoff) - ic->ic_flags |= IEEE80211_F_SHSLOT; + vap->iv_flags |= IEEE80211_F_SHSLOT; else - ic->ic_flags &= ~IEEE80211_F_SHSLOT; - /* notify driver */ - if (ic->ic_updateslot != NULL) - ic->ic_updateslot(ic); + vap->iv_flags &= ~IEEE80211_F_SHSLOT; + + /* schedule the deferred slot flag update and update */ + ieee80211_runtask(ic, &vap->iv_slot_task); } /* diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 9d224b1f5c1..cd927458ef4 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -154,8 +154,9 @@ uint16_t ieee80211_getcapinfo(struct ieee80211vap *, struct ieee80211_wme_state; uint8_t * ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme); +void ieee80211_vap_reset_erp(struct ieee80211vap *); void ieee80211_reset_erp(struct ieee80211com *); -void ieee80211_set_shortslottime(struct ieee80211com *, int onoff); +void ieee80211_vap_set_shortslottime(struct ieee80211vap *, int onoff); int ieee80211_iserp_rateset(const struct ieee80211_rateset *); void ieee80211_setbasicrates(struct ieee80211_rateset *, enum ieee80211_phymode); diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 46ba73a9ecd..6daa4c23614 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -1435,7 +1435,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, * NB: we assume short preamble doesn't * change dynamically */ - ieee80211_set_shortslottime(ic, + ieee80211_vap_set_shortslottime(vap, IEEE80211_IS_CHAN_A(ic->ic_bsschan) || (scan.capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); ni->ni_capinfo = (ni->ni_capinfo &~ IEEE80211_CAPINFO_SHORT_SLOTTIME) @@ -1847,7 +1847,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; ic->ic_flags |= IEEE80211_F_USEBARKER; } - ieee80211_set_shortslottime(ic, + ieee80211_vap_set_shortslottime(vap, IEEE80211_IS_CHAN_A(ic->ic_curchan) || (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)); /* @@ -1866,7 +1866,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, ISREASSOC(subtype) ? "re" : "", IEEE80211_NODE_AID(ni), ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long", - ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long", + vap->iv_flags&IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "", ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", ni->ni_flags & IEEE80211_NODE_HT ? diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 24ffbe106ba..1fa596b2391 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -563,6 +563,10 @@ struct ieee80211vap { const struct wmeParams *wme_params); struct task iv_wme_task; /* deferred VAP WME update */ + /* update device state for 802.11 slot time change */ + void (*iv_updateslot)(struct ieee80211vap *); + struct task iv_slot_task; /* deferred slot time update */ + uint64_t iv_spare[6]; }; MALLOC_DECLARE(M_80211_VAP);