svn commit: r362815 - head/sys/net80211

Adrian Chadd adrian at FreeBSD.org
Wed Jul 1 00:23:51 UTC 2020


Author: adrian
Date: Wed Jul  1 00:23:49 2020
New Revision: 362815
URL: https://svnweb.freebsd.org/changeset/base/362815

Log:
  [net80211] Migrate HT/legacy protection mode and preamble calculation to per-VAP flags
  
  The later firmware devices (including iwn!) support multiple configuration
  contexts for a lot of things, leaving it up to the firmware to decide
  which channel and vap is active.  This allows for things like off-channel
  p2p sta/ap operation and other weird things.
  
  However, net80211 is still focused on a "net80211 drives all" when it comes to driving
  the NIC, and as part of this history a lot of these options are global and not per-VAP.
  This is fine when net80211 drives things and all VAPs share a single channel - these
  parameters importantly really reflect the state of the channel! - but it will increasingly
  be not fine when we start supporting more weird configurations and more recent NICs.
  Yeah, recent like iwn/iwm.
  
  Anyway - so, migrate all of the HT protection, legacy protection and preamble
  stuff to be per-VAP.  The global flags are still there; they're now calculated
  in a deferred taskqueue that mirrors the old behaviour.  Firmware based drivers
  which have per-VAP configuration of these parameters can now just listen to the
  per-VAP options.
  
  What do I mean by per-channel? Well, the above configuration parameters really
  are about interoperation with other devices on the same channel. Eg, HT protection
  mode will flip to legacy/mixed if it hears ANY BSS that supports non-HT stations or
  indicates it has non-HT stations associated.  So, these flags really should be
  per-channel rather than per-VAP, and then for things like "do i need short preamble
  or long preamble?" turn into a "do I need it for this current operating channel".
  Then any VAP using it can query the channel that it's on, reflecting the real
  required state.
  
  This patch does none of the above paragraph just yet.
  
  I'm also cheating a bit - I'm currently not using separate taskqueues for
  the beacon updates and the per-VAP configuration updates.  I can always further
  split it later if I need to but I didn't think it was SUPER important here.
  
  So:
  
  * Create vap taskqueue entries for ERP/protection, HT protection and short/long
    preamble;
  * Migrate the HT station count, short/long slot station count, etc - into per-VAP
    variables rather than global;
  * Fix a bug with my WME work from a while ago which made it per-VAP - do the WME
    beacon update /after/ the WME update taskqueue runs, not before;
  * Any time the HT protmode configuration changes or the ERP protection mode
    config changes - schedule the task, which will call the driver without the
    net80211 lock held and all correctly serialised;
  * Use the global flags for beacon IEs and VAP flags for probe responses and
    other IE situations.
  
  The primary consumer of this is ath10k.  iwn could use it when sending RXON,
  but we don't support IBSS or AP modes on it yet, and I'm not yet sure whether
  it's required in STA mode (ie whether the firmware parses beacons to change
  protection mode or whether we need to.)
  
  Tested:
  
  * AR9280, STA/AP
  * AR9380, DWDS STA+STA/AP
  * ath10k work, STA/AP
  * Intel 6235, STA
  * Various rtwn / run NICs, DWDS STA and STA configurations

Modified:
  head/sys/net80211/ieee80211_ddb.c
  head/sys/net80211/ieee80211_hostap.c
  head/sys/net80211/ieee80211_ht.c
  head/sys/net80211/ieee80211_ioctl.c
  head/sys/net80211/ieee80211_node.c
  head/sys/net80211/ieee80211_node.h
  head/sys/net80211/ieee80211_output.c
  head/sys/net80211/ieee80211_power.c
  head/sys/net80211/ieee80211_proto.c
  head/sys/net80211/ieee80211_proto.h
  head/sys/net80211/ieee80211_sta.c
  head/sys/net80211/ieee80211_var.h

Modified: head/sys/net80211/ieee80211_ddb.c
==============================================================================
--- head/sys/net80211/ieee80211_ddb.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_ddb.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -483,6 +483,17 @@ _db_show_vap(const struct ieee80211vap *vap, int showm
 	if (vap->iv_tdma != NULL)
 		_db_show_tdma("\t", vap->iv_tdma, showprocs);
 #endif /* IEEE80211_SUPPORT_TDMA */
+
+	db_printf("\tsta_assoc %u", vap->iv_sta_assoc);
+	db_printf(" ht_sta_assoc %u", vap->iv_ht_sta_assoc);
+	db_printf(" ht40_sta_assoc %u", vap->iv_ht40_sta_assoc);
+	db_printf("\n");
+	db_printf(" nonerpsta %u", vap->iv_nonerpsta);
+	db_printf(" longslotsta %u", vap->iv_longslotsta);
+	db_printf(" lastnonerp %d", vap->iv_lastnonerp);
+	db_printf(" lastnonht %d", vap->iv_lastnonht);
+	db_printf("\n");
+
 	if (showprocs) {
 		DB_PRINTSYM("\t", "iv_key_alloc", vap->iv_key_alloc);
 		DB_PRINTSYM("\t", "iv_key_delete", vap->iv_key_delete);
@@ -608,17 +619,8 @@ _db_show_com(const struct ieee80211com *ic, int showva
 		_db_show_node_table("\t", &ic->ic_sta);
 
 	db_printf("\tprotmode %d", ic->ic_protmode);
-	db_printf(" nonerpsta %u", ic->ic_nonerpsta);
-	db_printf(" longslotsta %u", ic->ic_longslotsta);
-	db_printf(" lastnonerp %d", ic->ic_lastnonerp);
-	db_printf("\n");
-	db_printf("\tsta_assoc %u", ic->ic_sta_assoc);
-	db_printf(" ht_sta_assoc %u", ic->ic_ht_sta_assoc);
-	db_printf(" ht40_sta_assoc %u", ic->ic_ht40_sta_assoc);
-	db_printf("\n");
 	db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode);
 	db_printf(" htprotmode %d", ic->ic_htprotmode);
-	db_printf(" lastnonht %d", ic->ic_lastnonht);
 	db_printf("\n");
 
 	db_printf("\tsuperg %p\n", ic->ic_superg);

Modified: head/sys/net80211/ieee80211_hostap.c
==============================================================================
--- head/sys/net80211/ieee80211_hostap.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_hostap.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -206,8 +206,9 @@ hostap_newstate(struct ieee80211vap *vap, enum ieee802
 			 * state and the timeout routines check if the flag
 			 * is set before doing anything so this is sufficient.
 			 */
-			ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
-			ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
+			vap->iv_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+			vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
+			/* XXX TODO: schedule deferred update? */
 			/* fall thru... */
 		case IEEE80211_S_CAC:
 			/*
@@ -1812,10 +1813,13 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbu
 		    scan.status == 0 &&			/* NB: on-channel */
 		    ((scan.erp & 0x100) == 0 ||		/* NB: no ERP, 11b sta*/
 		     (scan.erp & IEEE80211_ERP_NON_ERP_PRESENT))) {
-			ic->ic_lastnonerp = ticks;
-			ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
-			if (ic->ic_protmode != IEEE80211_PROT_NONE &&
-			    (ic->ic_flags & IEEE80211_F_USEPROT) == 0) {
+			vap->iv_lastnonerp = ticks;
+			vap->iv_flags_ext |= IEEE80211_FEXT_NONERP_PR;
+			/*
+			 * XXX TODO: this may need to check all VAPs?
+			 */
+			if (vap->iv_protmode != IEEE80211_PROT_NONE &&
+			    (vap->iv_flags & IEEE80211_F_USEPROT) == 0) {
 				IEEE80211_NOTE_FRAME(vap,
 				    IEEE80211_MSG_ASSOC, wh,
 				    "non-ERP present on channel %d "
@@ -1823,8 +1827,8 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbu
 				    "enable use of protection",
 				    ic->ic_curchan->ic_ieee,
 				    scan.erp, scan.chan);
-				ic->ic_flags |= IEEE80211_F_USEPROT;
-				ieee80211_notify_erp(ic);
+				vap->iv_flags |= IEEE80211_F_USEPROT;
+				ieee80211_vap_update_erp_protmode(vap);
 			}
 		}
 		/* 
@@ -1844,12 +1848,12 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbu
 					break;
 			}
 			if (scan.htinfo == NULL) {
-				ieee80211_htprot_update(ic,
+				ieee80211_htprot_update(vap,
 				    IEEE80211_HTINFO_OPMODE_PROTOPT |
 				    IEEE80211_HTINFO_NONHT_PRESENT);
 			} else if (ishtmixed(scan.htinfo)) {
 				/* XXX? take NONHT_PRESENT from beacon? */
-				ieee80211_htprot_update(ic,
+				ieee80211_htprot_update(vap,
 				    IEEE80211_HTINFO_OPMODE_MIXED |
 				    IEEE80211_HTINFO_NONHT_PRESENT);
 			}

Modified: head/sys/net80211/ieee80211_ht.c
==============================================================================
--- head/sys/net80211/ieee80211_ht.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_ht.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -270,6 +270,9 @@ ieee80211_ht_vattach(struct ieee80211vap *vap)
 	vap->iv_ampdu_mintraffic[WME_AC_VO] = 32;
 	vap->iv_ampdu_mintraffic[WME_AC_VI] = 32;
 
+	vap->iv_htprotmode = IEEE80211_PROT_RTSCTS;
+	vap->iv_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+
 	if (vap->iv_htcaps & IEEE80211_HTC_HT) {
 		/*
 		 * Device is HT capable; enable all HT-related
@@ -1509,38 +1512,36 @@ ieee80211_ht_wds_init(struct ieee80211_node *ni)
 }
 
 /*
- * Notify hostap vaps of a change in the HTINFO ie.
+ * Notify a VAP of a change in the HTINFO ie if it's a hostap VAP.
+ *
+ * This is to be called from the deferred HT protection update
+ * task once the flags are updated.
  */
-static void
-htinfo_notify(struct ieee80211com *ic)
+void
+ieee80211_htinfo_notify(struct ieee80211vap *vap)
 {
-	struct ieee80211vap *vap;
-	int first = 1;
 
-	IEEE80211_LOCK_ASSERT(ic);
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
 
-	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
-		if (vap->iv_opmode != IEEE80211_M_HOSTAP)
-			continue;
-		if (vap->iv_state != IEEE80211_S_RUN ||
-		    !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
-			continue;
-		if (first) {
-			IEEE80211_NOTE(vap,
-			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
-			    vap->iv_bss,
-			    "HT bss occupancy change: %d sta, %d ht, "
-			    "%d ht40%s, HT protmode now 0x%x"
-			    , ic->ic_sta_assoc
-			    , ic->ic_ht_sta_assoc
-			    , ic->ic_ht40_sta_assoc
-			    , (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) ?
-				 ", non-HT sta present" : ""
-			    , ic->ic_curhtprotmode);
-			first = 0;
-		}
-		ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
-	}
+	if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+		return;
+	if (vap->iv_state != IEEE80211_S_RUN ||
+	    !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan))
+		return;
+
+	IEEE80211_NOTE(vap,
+	    IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N,
+	    vap->iv_bss,
+	    "HT bss occupancy change: %d sta, %d ht, "
+	    "%d ht40%s, HT protmode now 0x%x"
+	    , vap->iv_sta_assoc
+	    , vap->iv_ht_sta_assoc
+	    , vap->iv_ht40_sta_assoc
+	    , (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) ?
+		 ", non-HT sta present" : ""
+	    , vap->iv_curhtprotmode);
+
+	ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO);
 }
 
 /*
@@ -1548,26 +1549,28 @@ htinfo_notify(struct ieee80211com *ic)
  * state and handle updates.
  */
 static void
-htinfo_update(struct ieee80211com *ic)
+htinfo_update(struct ieee80211vap *vap)
 {
+	struct ieee80211com *ic = vap->iv_ic;
 	uint8_t protmode;
 
-	if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+	if (vap->iv_sta_assoc != vap->iv_ht_sta_assoc) {
 		protmode = IEEE80211_HTINFO_OPMODE_MIXED
 			 | IEEE80211_HTINFO_NONHT_PRESENT;
-	} else if (ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) {
+	} else if (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) {
 		protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
 			 | IEEE80211_HTINFO_NONHT_PRESENT;
 	} else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
 	    IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && 
-	    ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
+	    vap->iv_sta_assoc != vap->iv_ht40_sta_assoc) {
 		protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
 	} else {
 		protmode = IEEE80211_HTINFO_OPMODE_PURE;
 	}
-	if (protmode != ic->ic_curhtprotmode) {
-		ic->ic_curhtprotmode = protmode;
-		htinfo_notify(ic);
+	if (protmode != vap->iv_curhtprotmode) {
+		vap->iv_curhtprotmode = protmode;
+		/* Update VAP with new protection mode */
+		ieee80211_vap_update_ht_protmode(vap);
 	}
 }
 
@@ -1577,16 +1580,16 @@ htinfo_update(struct ieee80211com *ic)
 void
 ieee80211_ht_node_join(struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211vap *vap = ni->ni_vap;
 
-	IEEE80211_LOCK_ASSERT(ic);
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
 
 	if (ni->ni_flags & IEEE80211_NODE_HT) {
-		ic->ic_ht_sta_assoc++;
+		vap->iv_ht_sta_assoc++;
 		if (ni->ni_chw == 40)
-			ic->ic_ht40_sta_assoc++;
+			vap->iv_ht40_sta_assoc++;
 	}
-	htinfo_update(ic);
+	htinfo_update(vap);
 }
 
 /*
@@ -1595,16 +1598,16 @@ ieee80211_ht_node_join(struct ieee80211_node *ni)
 void
 ieee80211_ht_node_leave(struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211vap *vap = ni->ni_vap;
 
-	IEEE80211_LOCK_ASSERT(ic);
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
 
 	if (ni->ni_flags & IEEE80211_NODE_HT) {
-		ic->ic_ht_sta_assoc--;
+		vap->iv_ht_sta_assoc--;
 		if (ni->ni_chw == 40)
-			ic->ic_ht40_sta_assoc--;
+			vap->iv_ht40_sta_assoc--;
 	}
-	htinfo_update(ic);
+	htinfo_update(vap);
 }
 
 /*
@@ -1618,25 +1621,27 @@ ieee80211_ht_node_leave(struct ieee80211_node *ni)
  * a higher precedence than PROTOPT (i.e. we will not change
  * change PROTOPT -> MIXED; only MIXED -> PROTOPT).  This
  * corresponds to how we handle things in htinfo_update.
+ *
  */
 void
-ieee80211_htprot_update(struct ieee80211com *ic, int protmode)
+ieee80211_htprot_update(struct ieee80211vap *vap, int protmode)
 {
+	struct ieee80211com *ic = vap->iv_ic;
 #define	OPMODE(x)	SM(x, IEEE80211_HTINFO_OPMODE)
 	IEEE80211_LOCK(ic);
 
 	/* track non-HT station presence */
 	KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT,
 	    ("protmode 0x%x", protmode));
-	ic->ic_flags_ht |= IEEE80211_FHT_NONHT_PR;
-	ic->ic_lastnonht = ticks;
+	vap->iv_flags_ht |= IEEE80211_FHT_NONHT_PR;
+	vap->iv_lastnonht = ticks;
 
-	if (protmode != ic->ic_curhtprotmode &&
-	    (OPMODE(ic->ic_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
+	if (protmode != vap->iv_curhtprotmode &&
+	    (OPMODE(vap->iv_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED ||
 	     OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) {
-		/* push beacon update */
-		ic->ic_curhtprotmode = protmode;
-		htinfo_notify(ic);
+		vap->iv_curhtprotmode = protmode;
+		/* Update VAP with new protection mode */
+		ieee80211_vap_update_ht_protmode(vap);
 	}
 	IEEE80211_UNLOCK(ic);
 #undef OPMODE
@@ -1651,18 +1656,17 @@ ieee80211_htprot_update(struct ieee80211com *ic, int p
  * gone we time out this condition.
  */
 void
-ieee80211_ht_timeout(struct ieee80211com *ic)
+ieee80211_ht_timeout(struct ieee80211vap *vap)
 {
-	IEEE80211_LOCK_ASSERT(ic);
 
-	if ((ic->ic_flags_ht & IEEE80211_FHT_NONHT_PR) &&
-	    ieee80211_time_after(ticks, ic->ic_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
-#if 0
-		IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni,
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+	if ((vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) &&
+	    ieee80211_time_after(ticks, vap->iv_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) {
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N,
 		    "%s", "time out non-HT STA present on channel");
-#endif
-		ic->ic_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
-		htinfo_update(ic);
+		vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR;
+		htinfo_update(vap);
 	}
 }
 
@@ -3507,6 +3511,12 @@ ieee80211_ht_update_beacon(struct ieee80211vap *vap,
 		ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
 
 	/* protection mode */
+	/*
+	 * XXX TODO: this uses the global flag, not the per-VAP flag.
+	 * Eventually (once the protection modes are done per-channel
+	 * rather than per-VAP) we can flip this over to be per-VAP but
+	 * using the channel protection mode.
+	 */
 	ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
 
 	ieee80211_free_node(ni);
@@ -3547,7 +3557,11 @@ ieee80211_add_htinfo_body(uint8_t *frm, struct ieee802
 	if (IEEE80211_IS_CHAN_HT40(ni->ni_chan))
 		frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
 
-	frm[1] = ic->ic_curhtprotmode;
+	/*
+	 * Add current protection mode.  Unlike for beacons,
+	 * this will respect the per-VAP flags.
+	 */
+	frm[1] = vap->iv_curhtprotmode;
 
 	frm += 5;
 

Modified: head/sys/net80211/ieee80211_ioctl.c
==============================================================================
--- head/sys/net80211/ieee80211_ioctl.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_ioctl.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -851,7 +851,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_l
 		ireq->i_val = vap->iv_rtsthreshold;
 		break;
 	case IEEE80211_IOC_PROTMODE:
-		ireq->i_val = ic->ic_protmode;
+		ireq->i_val = vap->iv_protmode;
 		break;
 	case IEEE80211_IOC_TXPOWER:
 		/*
@@ -1097,7 +1097,7 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_l
 		error = ieee80211_ioctl_getdevcaps(ic, ireq);
 		break;
 	case IEEE80211_IOC_HTPROTMODE:
-		ireq->i_val = ic->ic_htprotmode;
+		ireq->i_val = vap->iv_htprotmode;
 		break;
 	case IEEE80211_IOC_HTCONF:
 		if (vap->iv_flags_ht & IEEE80211_FHT_HT) {
@@ -2912,11 +2912,13 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_l
 	case IEEE80211_IOC_PROTMODE:
 		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
 			return EINVAL;
-		ic->ic_protmode = (enum ieee80211_protmode)ireq->i_val;
+		vap->iv_protmode = (enum ieee80211_protmode)ireq->i_val;
 		/* NB: if not operating in 11g this can wait */
 		if (ic->ic_bsschan != IEEE80211_CHAN_ANYC &&
 		    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan))
 			error = ERESTART;
+		/* driver callback for protection mode update */
+		ieee80211_vap_update_erp_protmode(vap);
 		break;
 	case IEEE80211_IOC_TXPOWER:
 		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
@@ -3384,11 +3386,13 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_l
 	case IEEE80211_IOC_HTPROTMODE:
 		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
 			return EINVAL;
-		ic->ic_htprotmode = ireq->i_val ?
+		vap->iv_htprotmode = ireq->i_val ?
 		    IEEE80211_PROT_RTSCTS : IEEE80211_PROT_NONE;
 		/* NB: if not operating in 11n this can wait */
 		if (isvapht(vap))
 			error = ERESTART;
+		/* Notify driver layer of HT protmode changes */
+		ieee80211_vap_update_ht_protmode(vap);
 		break;
 	case IEEE80211_IOC_STA_VLAN:
 		error = ieee80211_ioctl_setstavlan(vap, ireq);

Modified: head/sys/net80211/ieee80211_node.c
==============================================================================
--- head/sys/net80211/ieee80211_node.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_node.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -99,7 +99,7 @@ static void ieee80211_node_table_init(struct ieee80211
 static void ieee80211_node_table_reset(struct ieee80211_node_table *,
 	struct ieee80211vap *);
 static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
-static void ieee80211_erp_timeout(struct ieee80211com *);
+static void ieee80211_vap_erp_timeout(struct ieee80211vap *);
 
 MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
 MALLOC_DEFINE(M_80211_NODE_IE, "80211nodeie", "802.11 node ie");
@@ -674,7 +674,6 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
 {
 #ifdef IEEE80211_DEBUG
 	struct ieee80211vap *vap = ni->ni_vap;
-	struct ieee80211com *ic = ni->ni_ic;
 #endif
 
 	if (! ieee80211_ibss_merge_check(ni))
@@ -683,9 +682,9 @@ ieee80211_ibss_merge(struct ieee80211_node *ni)
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
 		"%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",
+		vap->iv_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
 		vap->iv_flags&IEEE80211_F_SHSLOT ? "short" : "long",
-		ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
+		vap->iv_flags&IEEE80211_F_USEPROT ? ", protection" : ""
 	);
 	return ieee80211_sta_join1(ieee80211_ref_node(ni));
 }
@@ -2508,12 +2507,27 @@ ieee80211_drain(struct ieee80211com *ic)
 }
 
 /*
+ * Per-ieee80211vap inactivity timer callback.
+ */
+static void
+ieee80211_vap_timeout(struct ieee80211vap *vap)
+{
+
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
+
+	ieee80211_vap_erp_timeout(vap);
+	ieee80211_ht_timeout(vap);
+	ieee80211_vht_timeout(vap);
+}
+
+/*
  * Per-ieee80211com inactivity timer callback.
  */
 void
 ieee80211_node_timeout(void *arg)
 {
 	struct ieee80211com *ic = arg;
+	struct ieee80211vap *vap;
 
 	/*
 	 * Defer timeout processing if a channel switch is pending.
@@ -2530,9 +2544,8 @@ ieee80211_node_timeout(void *arg)
 		ieee80211_ageq_age(&ic->ic_stageq, IEEE80211_INACT_WAIT);
 
 		IEEE80211_LOCK(ic);
-		ieee80211_erp_timeout(ic);
-		ieee80211_ht_timeout(ic);
-		ieee80211_vht_timeout(ic);
+		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+			ieee80211_vap_timeout(vap);
 		IEEE80211_UNLOCK(ic);
 	}
 	callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz,
@@ -2645,7 +2658,12 @@ ieee80211_dump_nodes(struct ieee80211_node_table *nt)
 		(ieee80211_iter_func *) ieee80211_dump_node, nt);
 }
 
-static void
+/*
+ * Iterate over the VAPs and update their ERP beacon IEs.
+ *
+ * Note this must be called from the deferred ERP update task paths.
+ */
+void
 ieee80211_notify_erp_locked(struct ieee80211com *ic)
 {
 	struct ieee80211vap *vap;
@@ -2657,14 +2675,6 @@ ieee80211_notify_erp_locked(struct ieee80211com *ic)
 			ieee80211_beacon_notify(vap, IEEE80211_BEACON_ERP);
 }
 
-void
-ieee80211_notify_erp(struct ieee80211com *ic)
-{
-	IEEE80211_LOCK(ic);
-	ieee80211_notify_erp_locked(ic);
-	IEEE80211_UNLOCK(ic);
-}
-
 /*
  * Handle a station joining an 11g network.
  */
@@ -2684,10 +2694,13 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
 	 * next beacon transmission (per sec. 7.3.1.4 of 11g).
 	 */
 	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
-		ic->ic_longslotsta++;
+		vap->iv_longslotsta++;
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
 		    "station needs long slot time, count %d",
-		    ic->ic_longslotsta);
+		    vap->iv_longslotsta);
+		/*
+		 * XXX TODO: this may need all VAPs checked!
+		 */
 		if (!IEEE80211_IS_CHAN_108G(ic->ic_bsschan)) {
 			/*
 			 * Don't force slot time when switched to turbo
@@ -2703,10 +2716,10 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
 	 * if configured.
 	 */
 	if (!ieee80211_iserp_rateset(&ni->ni_rates)) {
-		ic->ic_nonerpsta++;
+		vap->iv_nonerpsta++;
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
 		    "station is !ERP, %d non-ERP stations associated",
-		    ic->ic_nonerpsta);
+		    vap->iv_nonerpsta);
 		/*
 		 * If station does not support short preamble
 		 * then we must enable use of Barker preamble.
@@ -2714,20 +2727,21 @@ ieee80211_node_join_11g(struct ieee80211_node *ni)
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
 			IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
 			    "%s", "station needs long preamble");
-			ic->ic_flags |= IEEE80211_F_USEBARKER;
-			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+			vap->iv_flags |= IEEE80211_F_USEBARKER;
+			vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
+			ieee80211_vap_update_preamble(vap);
 		}
 		/*
 		 * If protection is configured and this is the first
 		 * indication we should use protection, enable it.
 		 */
-		if (ic->ic_protmode != IEEE80211_PROT_NONE &&
-		    ic->ic_nonerpsta == 1 &&
-		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
+		if (vap->iv_protmode != IEEE80211_PROT_NONE &&
+		    vap->iv_nonerpsta == 1 &&
+		    (vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
 			IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
 			    "%s: enable use of protection\n", __func__);
-			ic->ic_flags |= IEEE80211_F_USEPROT;
-			ieee80211_notify_erp_locked(ic);
+			vap->iv_flags |= IEEE80211_F_USEPROT;
+			ieee80211_vap_update_erp_protmode(vap);
 		}
 	} else
 		ni->ni_flags |= IEEE80211_NODE_ERP;
@@ -2762,7 +2776,6 @@ ieee80211_node_join(struct ieee80211_node *ni, int res
 		IEEE80211_LOCK(ic);
 		IEEE80211_AID_SET(vap, ni->ni_associd);
 		vap->iv_sta_assoc++;
-		ic->ic_sta_assoc++;
 
 		if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan))
 			ieee80211_ht_node_join(ni);
@@ -2783,9 +2796,9 @@ ieee80211_node_join(struct ieee80211_node *ni, int res
 	IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni,
 	    "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s%s",
 	    IEEE80211_NODE_AID(ni),
-	    ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
+	    vap->iv_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
 	    vap->iv_flags & IEEE80211_F_SHSLOT ? "short" : "long",
-	    ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "",
+	    vap->iv_flags & IEEE80211_F_USEPROT ? ", protection" : "",
 	    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "",
 	    /* XXX update for VHT string */
 	    ni->ni_flags & IEEE80211_NODE_HT ?
@@ -2815,20 +2828,23 @@ ieee80211_node_join(struct ieee80211_node *ni, int res
 }
 
 static void
-disable_protection(struct ieee80211com *ic)
+disable_protection(struct ieee80211vap *vap)
 {
-	KASSERT(ic->ic_nonerpsta == 0 &&
-	    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
-	   ("%d non ERP stations, flags 0x%x", ic->ic_nonerpsta,
-	   ic->ic_flags_ext));
+	struct ieee80211com *ic = vap->iv_ic;
 
-	ic->ic_flags &= ~IEEE80211_F_USEPROT;
+	KASSERT(vap->iv_nonerpsta == 0 &&
+	    (vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0,
+	   ("%d non ERP stations, flags 0x%x", vap->iv_nonerpsta,
+	   vap->iv_flags_ext));
+
+	vap->iv_flags &= ~IEEE80211_F_USEPROT;
 	/* XXX verify mode? */
 	if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
-		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
-		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+		vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
+		vap->iv_flags &= ~IEEE80211_F_USEBARKER;
 	}
-	ieee80211_notify_erp_locked(ic);
+	ieee80211_vap_update_erp_protmode(vap);
+	ieee80211_vap_update_preamble(vap);
 }
 
 /*
@@ -2850,13 +2866,16 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
 	 * If a long slot station do the slot time bookkeeping.
 	 */
 	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
-		KASSERT(ic->ic_longslotsta > 0,
-		    ("bogus long slot station count %d", ic->ic_longslotsta));
-		ic->ic_longslotsta--;
+		KASSERT(vap->iv_longslotsta > 0,
+		    ("bogus long slot station count %d", vap->iv_longslotsta));
+		vap->iv_longslotsta--;
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
 		    "long slot time station leaves, count now %d",
-		    ic->ic_longslotsta);
-		if (ic->ic_longslotsta == 0) {
+		    vap->iv_longslotsta);
+		/*
+		 * XXX TODO: this may need all VAPs checked!
+		 */
+		if (vap->iv_longslotsta == 0) {
 			/*
 			 * Re-enable use of short slot time if supported
 			 * and not operating in IBSS mode (per spec).
@@ -2875,18 +2894,18 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
 	 * If a non-ERP station do the protection-related bookkeeping.
 	 */
 	if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
-		KASSERT(ic->ic_nonerpsta > 0,
-		    ("bogus non-ERP station count %d", ic->ic_nonerpsta));
-		ic->ic_nonerpsta--;
+		KASSERT(vap->iv_nonerpsta > 0,
+		    ("bogus non-ERP station count %d", vap->iv_nonerpsta));
+		vap->iv_nonerpsta--;
 		IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC, ni,
-		    "non-ERP station leaves, count now %d%s", ic->ic_nonerpsta,
-		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
+		    "non-ERP station leaves, count now %d%s", vap->iv_nonerpsta,
+		    (vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) ?
 			" (non-ERP sta present)" : "");
-		if (ic->ic_nonerpsta == 0 &&
-		    (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
+		if (vap->iv_nonerpsta == 0 &&
+		    (vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) == 0) {
 			IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_ASSOC,
 				"%s: disable use of protection\n", __func__);
-			disable_protection(ic);
+			disable_protection(vap);
 		}
 	}
 }
@@ -2900,20 +2919,18 @@ ieee80211_node_leave_11g(struct ieee80211_node *ni)
  * condition.
  */
 static void
-ieee80211_erp_timeout(struct ieee80211com *ic)
+ieee80211_vap_erp_timeout(struct ieee80211vap *vap)
 {
 
-	IEEE80211_LOCK_ASSERT(ic);
+	IEEE80211_LOCK_ASSERT(vap->iv_ic);
 
-	if ((ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
-	    ieee80211_time_after(ticks, ic->ic_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
-#if 0
-		IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC, ni,
+	if ((vap->iv_flags_ext & IEEE80211_FEXT_NONERP_PR) &&
+	    ieee80211_time_after(ticks, vap->iv_lastnonerp + IEEE80211_NONERP_PRESENT_AGE)) {
+		IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC,
 		    "%s", "age out non-ERP sta present on channel");
-#endif
-		ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
-		if (ic->ic_nonerpsta == 0)
-			disable_protection(ic);
+		vap->iv_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;
+		if (vap->iv_nonerpsta == 0)
+			disable_protection(vap);
 	}
 }
 
@@ -2952,7 +2969,6 @@ ieee80211_node_leave(struct ieee80211_node *ni)
 	IEEE80211_LOCK(ic);
 	IEEE80211_AID_CLR(vap, ni->ni_associd);
 	vap->iv_sta_assoc--;
-	ic->ic_sta_assoc--;
 
 	if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan))
 		ieee80211_vht_node_leave(ni);

Modified: head/sys/net80211/ieee80211_node.h
==============================================================================
--- head/sys/net80211/ieee80211_node.h	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_node.h	Wed Jul  1 00:23:49 2020	(r362815)
@@ -479,7 +479,7 @@ int	ieee80211_iterate_nodes_vap(struct ieee80211_node_
 void	ieee80211_iterate_nodes(struct ieee80211_node_table *,
 		ieee80211_iter_func *, void *);
 
-void	ieee80211_notify_erp(struct ieee80211com *);
+void	ieee80211_notify_erp_locked(struct ieee80211com *);
 void	ieee80211_dump_node(struct ieee80211_node_table *,
 		struct ieee80211_node *);
 void	ieee80211_dump_nodes(struct ieee80211_node_table *);

Modified: head/sys/net80211/ieee80211_output.c
==============================================================================
--- head/sys/net80211/ieee80211_output.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_output.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -2100,15 +2100,34 @@ ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, 
  * Add an erp element to a frame.
  */
 static uint8_t *
-ieee80211_add_erp(uint8_t *frm, struct ieee80211com *ic)
+ieee80211_add_erp(uint8_t *frm, struct ieee80211vap *vap)
 {
+	struct ieee80211com *ic = vap->iv_ic;
 	uint8_t erp;
 
 	*frm++ = IEEE80211_ELEMID_ERP;
 	*frm++ = 1;
 	erp = 0;
-	if (ic->ic_nonerpsta != 0)
+
+	/*
+	 * TODO:  This uses the global flags for now because
+	 * the per-VAP flags are fine for per-VAP, but don't
+	 * take into account which VAPs share the same channel
+	 * and which are on different channels.
+	 *
+	 * ERP and HT/VHT protection mode is a function of
+	 * how many stations are on a channel, not specifically
+	 * the VAP or global.  But, until we grow that status,
+	 * the global flag will have to do.
+	 */
+	if (ic->ic_flags_ext & IEEE80211_FEXT_NONERP_PR)
 		erp |= IEEE80211_ERP_NON_ERP_PRESENT;
+
+	/*
+	 * TODO: same as above; these should be based not
+	 * on the vap or ic flags, but instead on a combination
+	 * of per-VAP and channels.
+	 */
 	if (ic->ic_flags & IEEE80211_F_USEPROT)
 		erp |= IEEE80211_ERP_USE_PROTECTION;
 	if (ic->ic_flags & IEEE80211_F_USEBARKER)
@@ -2569,7 +2588,6 @@ ieee80211_send_probereq(struct ieee80211_node *ni,
 uint16_t
 ieee80211_getcapinfo(struct ieee80211vap *vap, struct ieee80211_channel *chan)
 {
-	struct ieee80211com *ic = vap->iv_ic;
 	uint16_t capinfo;
 
 	KASSERT(vap->iv_opmode != IEEE80211_M_STA, ("station mode"));
@@ -2582,7 +2600,7 @@ ieee80211_getcapinfo(struct ieee80211vap *vap, struct 
 		capinfo = 0;
 	if (vap->iv_flags & IEEE80211_F_PRIVACY)
 		capinfo |= IEEE80211_CAPINFO_PRIVACY;
-	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) &&
 	    IEEE80211_IS_CHAN_2GHZ(chan))
 		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
 	if (vap->iv_flags & IEEE80211_F_SHSLOT)
@@ -2761,7 +2779,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int typ
 		 * NB: Some 11a AP's reject the request when
 		 *     short preamble is set.
 		 */
-		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+		if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) &&
 		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
 		if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
@@ -3098,7 +3116,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, 
 		}
 	}
 	if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
-		frm = ieee80211_add_erp(frm, ic);
+		frm = ieee80211_add_erp(frm, vap);
 	frm = ieee80211_add_xrates(frm, rs);
 	frm = ieee80211_add_rsn(frm, vap);
 	/*
@@ -3268,6 +3286,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const 
     uint8_t rate, int prot)
 {
 	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211vap *vap = ni->ni_vap;
 	const struct ieee80211_frame *wh;
 	struct mbuf *mprot;
 	uint16_t dur;
@@ -3279,7 +3298,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const 
 
 	wh = mtod(m, const struct ieee80211_frame *);
 	pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
-	isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
+	isshort = (vap->iv_flags & IEEE80211_F_SHPREAMBLE) != 0;
 	dur = ieee80211_compute_duration(ic->ic_rt, pktlen, rate, isshort)
 	    + ieee80211_ack_duration(ic->ic_rt, rate, isshort);
 
@@ -3288,7 +3307,7 @@ ieee80211_alloc_prot(struct ieee80211_node *ni, const 
 		dur += ieee80211_ack_duration(ic->ic_rt, rate, isshort);
 		mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
 	} else
-		mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
+		mprot = ieee80211_alloc_cts(ic, vap->iv_myaddr, dur);
 
 	return (mprot);
 }
@@ -3496,7 +3515,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *fr
 
 	if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
 		bo->bo_erp = frm;
-		frm = ieee80211_add_erp(frm, ic);
+		frm = ieee80211_add_erp(frm, vap);
 	}
 	frm = ieee80211_add_xrates(frm, rs);
 	frm = ieee80211_add_rsn(frm, vap);
@@ -3981,7 +4000,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, str
 			/*
 			 * ERP element needs updating.
 			 */
-			(void) ieee80211_add_erp(bo->bo_erp, ic);
+			(void) ieee80211_add_erp(bo->bo_erp, vap);
 			clrbit(bo->bo_flags, IEEE80211_BEACON_ERP);
 		}
 #ifdef IEEE80211_SUPPORT_SUPERG

Modified: head/sys/net80211/ieee80211_power.c
==============================================================================
--- head/sys/net80211/ieee80211_power.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_power.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -335,7 +335,7 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct mb
 	if (psq->psq_len >= psq->psq_maxlen) {
 		psq->psq_drops++;
 		IEEE80211_PSQ_UNLOCK(psq);
-		IEEE80211_NOTE(vap, IEEE80211_MSG_ANY, ni,
+		IEEE80211_NOTE(vap, IEEE80211_MSG_POWER, ni,
 		    "pwr save q overflow, drops %d (size %d)",
 		    psq->psq_drops, psq->psq_len);
 #ifdef IEEE80211_DEBUG

Modified: head/sys/net80211/ieee80211_proto.c
==============================================================================
--- head/sys/net80211/ieee80211_proto.c	Tue Jun 30 22:01:21 2020	(r362814)
+++ head/sys/net80211/ieee80211_proto.c	Wed Jul  1 00:23:49 2020	(r362815)
@@ -246,6 +246,9 @@ 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 vap_update_erp_protmode(void *, int);
+static void vap_update_preamble(void *, int);
+static void vap_update_ht_protmode(void *, int);
 static void ieee80211_newstate_cb(void *, int);
 
 static int
@@ -275,7 +278,7 @@ ieee80211_proto_attach(struct ieee80211com *ic)
 		max_hdr = max_linkhdr + max_protohdr;
 		max_datalen = MHLEN - max_hdr;
 	}
-	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+	//ic->ic_protmode = IEEE80211_PROT_CTSONLY;
 
 	TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ic);
 	TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic);
@@ -342,6 +345,9 @@ ieee80211_proto_vattach(struct ieee80211vap *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);
+	TASK_INIT(&vap->iv_erp_protmode_task, 0, vap_update_erp_protmode, vap);
+	TASK_INIT(&vap->iv_ht_protmode_task, 0, vap_update_ht_protmode, vap);
+	TASK_INIT(&vap->iv_preamble_task, 0, vap_update_preamble, vap);
 	/*
 	 * Install default tx rate handling: no fixed rate, lowest
 	 * supported rate for mgmt and multicast frames.  Default
@@ -388,6 +394,7 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
 
 	vap->iv_update_beacon = null_update_beacon;
 	vap->iv_deliver_data = ieee80211_deliver_data;
+	vap->iv_protmode = IEEE80211_PROT_CTSONLY;
 
 	/* attach support for operating mode */
 	ic->ic_vattach[vap->iv_opmode](vap);
@@ -763,7 +770,23 @@ ieee80211_vap_reset_erp(struct ieee80211vap *vap)
 {
 	struct ieee80211com *ic = vap->iv_ic;
 
+	vap->iv_nonerpsta = 0;
+	vap->iv_longslotsta = 0;
+
+	vap->iv_flags &= ~IEEE80211_F_USEPROT;
 	/*
+	 * Set short preamble and ERP barker-preamble flags.
+	 */
+	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
+	    (vap->iv_caps & IEEE80211_C_SHPREAMBLE)) {
+		vap->iv_flags |= IEEE80211_F_SHPREAMBLE;
+		vap->iv_flags &= ~IEEE80211_F_USEBARKER;
+	} else {
+		vap->iv_flags &= ~IEEE80211_F_SHPREAMBLE;
+		vap->iv_flags |= IEEE80211_F_USEBARKER;
+	}
+
+	/*
 	 * 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.
@@ -778,13 +801,15 @@ ieee80211_vap_reset_erp(struct ieee80211vap *vap)
 
 /*
  * Reset 11g-related state.
+ *
+ * Note this resets the global state and a caller should schedule
+ * a re-check of all the VAPs after setup to update said state.
  */
 void
 ieee80211_reset_erp(struct ieee80211com *ic)
 {
+#if 0
 	ic->ic_flags &= ~IEEE80211_F_USEPROT;
-	ic->ic_nonerpsta = 0;
-	ic->ic_longslotsta = 0;
 	/*
 	 * Set short preamble and ERP barker-preamble flags.
 	 */
@@ -796,6 +821,8 @@ ieee80211_reset_erp(struct ieee80211com *ic)
 		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
 		ic->ic_flags |= IEEE80211_F_USEBARKER;
 	}
+#endif
+	/* XXX TODO: schedule a new per-VAP ERP calculation */
 }
 
 /*
@@ -812,6 +839,9 @@ ieee80211_reset_erp(struct ieee80211com *ic)
  * 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.
+ *
+ * Look at the comments for vap_update_erp_protmode() for more
+ * background; this assumes all VAPs are on the same channel.
  */
 static void
 vap_update_slot(void *arg, int npending)
@@ -848,7 +878,6 @@ vap_update_slot(void *arg, int npending)
 		else
 			num_lgslot++;
 	}
-	IEEE80211_UNLOCK(ic);
 
 	/*
 	 * It looks backwards but - if the number of short slot VAPs
@@ -860,6 +889,7 @@ vap_update_slot(void *arg, int npending)
 		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
 	else if (num_lgslot == 0)
 		ic->ic_flags |= IEEE80211_F_SHSLOT;
+	IEEE80211_UNLOCK(ic);
 
 	/*
 	 * Call the driver with our new global slot time flags.
@@ -869,6 +899,293 @@ vap_update_slot(void *arg, int npending)
 }
 
 /*
+ * Deferred ERP protmode update.
+ *
+ * This currently calculates the global ERP protection mode flag
+ * based on each of the VAPs.  Any VAP with it enabled is enough
+ * for the global flag to be enabled.  All VAPs with it disabled
+ * is enough for it to be disabled.
+ *
+ * This may make sense right now for the supported hardware where
+ * net80211 is controlling the single channel configuration, but
+ * offload firmware that's doing channel changes (eg off-channel
+ * TDLS, off-channel STA, off-channel P2P STA/AP) may get some
+ * silly looking flag updates.
+ *
+ * Ideally the protection mode calculation is done based on the
+ * channel, and all VAPs using that channel will inherit it.
+ * But until that's what net80211 does, this wil have to do.
+ */
+static void
+vap_update_erp_protmode(void *arg, int npending)
+{
+	struct ieee80211vap *vap = arg;
+	struct ieee80211com *ic = vap->iv_ic;
+	struct ieee80211vap *iv;
+	int enable_protmode = 0;
+	int non_erp_present = 0;
+
+	/*
+	 * Iterate over all of the VAPs to calculate the overlapping
+	 * ERP protection mode configuration and ERP present math.
+	 *
+	 * For now we assume that if a driver can handle this per-VAP
+	 * then it'll ignore the ic->ic_protmode variant and instead
+	 * will look at the vap related flags.
+	 */
+	IEEE80211_LOCK(ic);
+	TAILQ_FOREACH(iv, &ic->ic_vaps, iv_next) {
+		if (iv->iv_flags & IEEE80211_F_USEPROT)
+			enable_protmode = 1;
+		if (iv->iv_flags_ext & IEEE80211_FEXT_NONERP_PR)
+			non_erp_present = 1;
+	}
+
+	if (enable_protmode)
+		ic->ic_flags |= IEEE80211_F_USEPROT;
+	else
+		ic->ic_flags &= ~IEEE80211_F_USEPROT;
+
+	if (non_erp_present)
+		ic->ic_flags_ext |= IEEE80211_FEXT_NONERP_PR;
+	else
+		ic->ic_flags_ext &= ~IEEE80211_FEXT_NONERP_PR;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list