svn commit: r311855 - head/sys/net80211

Adrian Chadd adrian at FreeBSD.org
Tue Jan 10 05:30:17 UTC 2017


Author: adrian
Date: Tue Jan 10 05:30:15 2017
New Revision: 311855
URL: https://svnweb.freebsd.org/changeset/base/311855

Log:
  [net80211] create a helper function to calculate the station facing VHT capabilities.
  
  This is needed for two reasons:
  
  * Drivers will need to know what the negotiated set of VHT capabilities
    and rates are in order to configure (and reconfigure for opmode/chanwidth
    changes) how to speak to a given peer; and
  * Because some vendors are "special", we should be careful in what we announce
    to them during peer association.
  
  This isn't the complete solution, as I still need to make sure that when
  sending out probe requests before we know what we want, we don't limit
  the capabilities being announced.  This is important for IBSS/mesh work
  later on as probe request/response exchanges are the first hint at what
  a peer supports.  I'll look at adding that to the API soon.

Modified:
  head/sys/net80211/ieee80211_vht.c
  head/sys/net80211/ieee80211_vht.h

Modified: head/sys/net80211/ieee80211_vht.c
==============================================================================
--- head/sys/net80211/ieee80211_vht.c	Tue Jan 10 04:50:26 2017	(r311854)
+++ head/sys/net80211/ieee80211_vht.c	Tue Jan 10 05:30:15 2017	(r311855)
@@ -71,6 +71,15 @@ __FBSDID("$FreeBSD$");
 } while (0)
 
 /*
+ * Immediate TODO:
+ *
+ * + handle WLAN_ACTION_VHT_OPMODE_NOTIF and other VHT action frames
+ * + ensure vhtinfo/vhtcap parameters correctly use the negotiated
+ *   capabilities and ratesets
+ * + group ID management operation
+ */
+
+/*
  * XXX TODO: handle WLAN_ACTION_VHT_OPMODE_NOTIF
  *
  * Look at mac80211/vht.c:ieee80211_vht_handle_opmode() for further details.
@@ -153,9 +162,9 @@ ieee80211_vht_announce(struct ieee80211c
 
 	/* Channel width */
 	ic_printf(ic, "[VHT] Channel Widths: 20MHz, 40MHz, 80MHz");
-	if (ic->ic_vhtcaps & IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+	if (MS(ic->ic_vhtcaps, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) == 2)
 		printf(" 80+80MHz");
-	if (ic->ic_vhtcaps & IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ)
+	if (MS(ic->ic_vhtcaps, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) >= 1)
 		printf(" 160MHz");
 	printf("\n");
 
@@ -280,38 +289,357 @@ ieee80211_vht_node_leave(struct ieee8021
 	    "%s: called", __func__);
 }
 
-uint8_t *
-ieee80211_add_vhtcap(uint8_t *frm, struct ieee80211_node *ni)
+/*
+ * Calculate the VHTCAP IE for a given node.
+ *
+ * This includes calculating the capability intersection based on the
+ * current operating mode and intersection of the TX/RX MCS maps.
+ *
+ * The standard only makes it clear about MCS rate negotiation
+ * and MCS basic rates (which must be a subset of the general
+ * negotiated rates).  It doesn't make it clear that the AP should
+ * figure out the minimum functional overlap with the STA and
+ * support that.
+ *
+ * Note: this is in host order, not in 802.11 endian order.
+ *
+ * TODO: ensure I re-read 9.7.11 Rate Selection for VHT STAs.
+ *
+ * TODO: investigate what we should negotiate for MU-MIMO beamforming
+ *       options.
+ *
+ * opmode is '1' for "vhtcap as if I'm a STA", 0 otherwise.
+ */
+void
+ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
+    struct ieee80211_ie_vhtcap *vhtcap, int opmode)
 {
-	uint32_t cap;
+	struct ieee80211vap *vap = ni->ni_vap;
+//	struct ieee80211com *ic = vap->iv_ic;
+	uint32_t val, val1, val2;
+	uint32_t new_vhtcap;
+	int i;
 
-	memset(frm, '\0', sizeof(struct ieee80211_ie_vhtcap));
+	vhtcap->ie = IEEE80211_ELEMID_VHT_CAP;
+	vhtcap->len = sizeof(struct ieee80211_ie_vhtcap) - 2;
 
-	frm[0] = IEEE80211_ELEMID_VHT_CAP;
-	frm[1] = sizeof(struct ieee80211_ie_vhtcap) - 2;
-	frm += 2;
+	/*
+	 * Capabilities - it depends on whether we are a station
+	 * or not.
+	 */
+	new_vhtcap = 0;
 
 	/*
-	 * For now, don't do any configuration.
-	 * Just populate the node configuration.
-	 * We can worry about making it configurable later.
+	 * Station - use our desired configuration based on
+	 * local config, local device bits and the already-learnt
+	 * vhtcap/vhtinfo IE in the node.
 	 */
 
-	cap = ni->ni_vhtcap;
+	/* Limit MPDU size to the smaller of the two */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+
+	/* Limit supp channel config */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+	}
+	if ((val2 == 2) &&
+	    ((vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80P80) == 0))
+		val2 = 1;
+	if ((val2 == 1) &&
+	    ((vap->iv_flags_vht & IEEE80211_FVHT_USEVHT160) == 0))
+		val2 = 0;
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+
+	/* RX LDPC */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RXLDPC);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RXLDPC);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_RXLDPC);
+
+	/* Short-GI 80 */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_SHORT_GI_80);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_80);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SHORT_GI_80);
+
+	/* Short-GI 160 */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_SHORT_GI_160);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_160);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SHORT_GI_160);
+
+	/*
+	 * STBC is slightly more complicated.
+	 *
+	 * In non-STA mode, we just announce our capabilities and that
+	 * is that.
+	 *
+	 * In STA mode, we should calculate our capabilities based on
+	 * local capabilities /and/ what the remote says. So:
+	 *
+	 * + Only TX STBC if we support it and the remote supports RX STBC;
+	 * + Only announce RX STBC if we support it and the remote supports
+	 *   TX STBC;
+	 * + RX STBC should be the minimum of local and remote RX STBC;
+	 */
+
+	/* TX STBC */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_TXSTBC);
+	if (opmode == 1) {
+		/* STA mode - enable it only if node RXSTBC is non-zero */
+		val2 = !! MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RXSTBC_MASK);
+	}
+	val = MIN(val1, val2);
+	/* XXX For now, use the 11n config flag */
+	if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_TX) == 0)
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_TXSTBC);
+
+	/* RX STBC1..4 */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RXSTBC_MASK);
+	if (opmode == 1) {
+		/* STA mode - enable it only if node TXSTBC is non-zero */
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_TXSTBC);
+	}
+	val = MIN(val1, val2);
+	/* XXX For now, use the 11n config flag */
+	if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_RXSTBC_MASK);
+
+	/*
+	 * Finally - if RXSTBC is 0, then don't enable TXSTBC.
+	 * Strictly speaking a device can TXSTBC and not RXSTBC, but
+	 * it would be silly.
+	 */
+	if (val == 0)
+		new_vhtcap &= ~IEEE80211_VHTCAP_TXSTBC;
+
+	/*
+	 * Some of these fields require other fields to exist.
+	 * So before using it, the parent field needs to be checked
+	 * otherwise the overridden value may be wrong.
+	 *
+	 * For example, if SU beamformee is set to 0, then BF STS
+	 * needs to be 0.
+	 */
+
+	/* SU Beamformer capable */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+
+	/* SU Beamformee capable */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+	}
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+
+	/* Beamformee STS capability - only if SU beamformee capable */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+	if (opmode == 1) {
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+	}
+	val = MIN(val1, val2);
+	if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0)
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+
+	/* Sounding dimensions - only if SU beamformer capable */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
+	val = MIN(val1, val2);
+	if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0)
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
 
 	/*
-	 * XXX TODO: any capability changes required by
-	 * configuration.
+	 * MU Beamformer capable - only if SU BFF capable, MU BFF capable
+	 * and STA (not AP)
 	 */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE);
+	val = MIN(val1, val2);
+	if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0)
+		val = 0;
+	if (opmode != 1)	/* Only enable for STA mode */
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+
+	/*
+	 * MU Beamformee capable - only if SU BFE capable, MU BFE capable
+	 * and AP (not STA)
+	 */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE);
+	val = MIN(val1, val2);
+	if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0)
+		val = 0;
+	if (opmode != 0)	/* Only enable for AP mode */
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+
+	/* VHT TXOP PS */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_VHT_TXOP_PS);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_VHT_TXOP_PS);
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_VHT_TXOP_PS);
+
+	/* HTC_VHT */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_HTC_VHT);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_HTC_VHT);
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_HTC_VHT);
+
+	/* A-MPDU length max */
+	/* XXX TODO: we need a userland config knob for this */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+	val = MIN(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+
+	/*
+	 * Link adaptation is only valid if HTC-VHT capable is 1.
+	 * Otherwise, always set it to 0.
+	 */
+	val2 = val1 = MS(vap->iv_vhtcaps,
+	    IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap,
+		    IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+	val = MIN(val1, val2);
+	if ((new_vhtcap & IEEE80211_VHTCAP_HTC_VHT) == 0)
+		val = 0;
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+
+	/*
+	 * The following two options are 0 if the pattern may change, 1 if it
+	 * does not change.  So, downgrade to the higher value.
+	 */
+
+	/* RX antenna pattern */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+	val = MAX(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+
+	/* TX antenna pattern */
+	val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+	if (opmode == 1)
+		val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+	val = MAX(val1, val2);
+	new_vhtcap |= SM(val, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+
+	/*
+	 * MCS set - again, we announce what we want to use
+	 * based on configuration, device capabilities and
+	 * already-learnt vhtcap/vhtinfo IE information.
+	 */
+
+	/* MCS set - start with whatever the device supports */
+	vhtcap->supp_mcs.rx_mcs_map = vap->iv_vht_mcsinfo.rx_mcs_map;
+	vhtcap->supp_mcs.rx_highest = 0;
+	vhtcap->supp_mcs.tx_mcs_map = vap->iv_vht_mcsinfo.tx_mcs_map;
+	vhtcap->supp_mcs.tx_highest = 0;
+
+	vhtcap->vht_cap_info = new_vhtcap;
+
+	/*
+	 * Now, if we're a STA, mask off whatever the AP doesn't support.
+	 * Ie, we continue to state we can receive whatever we can do,
+	 * but we only announce that we will transmit rates that meet
+	 * the AP requirement.
+	 *
+	 * Note: 0 - MCS0..7; 1 - MCS0..8; 2 - MCS0..9; 3 = not supported.
+	 * We can't just use MIN() because '3' means "no", so special case it.
+	 */
+	if (opmode) {
+		for (i = 0; i < 8; i++) {
+			val1 = (vhtcap->supp_mcs.tx_mcs_map >> (i*2)) & 0x3;
+			val2 = (ni->ni_vht_mcsinfo.tx_mcs_map >> (i*2)) & 0x3;
+			val = MIN(val1, val2);
+			if (val1 == 3 || val2 == 3)
+				val = 3;
+			vhtcap->supp_mcs.tx_mcs_map &= ~(0x3 << (i*2));
+			vhtcap->supp_mcs.tx_mcs_map |= (val << (i*2));
+		}
+	}
+}
+
+/*
+ * Add a VHTCAP field.
+ *
+ * If in station mode, we announce what we would like our
+ * desired configuration to be.
+ *
+ * Else, we announce our capabilities based on our current
+ * configuration.
+ */
+uint8_t *
+ieee80211_add_vhtcap(uint8_t *frm, struct ieee80211_node *ni)
+{
+	struct ieee80211_ie_vhtcap vhtcap;
+	int opmode;
+
+	opmode = 0;
+	if (ni->ni_vap->iv_opmode == IEEE80211_M_STA)
+		opmode = 1;
+
+	ieee80211_vht_get_vhtcap_ie(ni, &vhtcap, opmode);
+
+	memset(frm, '\0', sizeof(struct ieee80211_ie_vhtcap));
+
+	frm[0] = IEEE80211_ELEMID_VHT_CAP;
+	frm[1] = sizeof(struct ieee80211_ie_vhtcap) - 2;
+	frm += 2;
 
 	/* 32-bit VHT capability */
-	ADDWORD(frm, cap);
+	ADDWORD(frm, vhtcap.vht_cap_info);
 
 	/* suppmcs */
-	ADDSHORT(frm, ni->ni_vht_mcsinfo.rx_mcs_map);
-	ADDSHORT(frm, ni->ni_vht_mcsinfo.rx_highest);
-	ADDSHORT(frm, ni->ni_vht_mcsinfo.tx_mcs_map);
-	ADDSHORT(frm, ni->ni_vht_mcsinfo.tx_highest);
+	ADDSHORT(frm, vhtcap.supp_mcs.rx_mcs_map);
+	ADDSHORT(frm, vhtcap.supp_mcs.rx_highest);
+	ADDSHORT(frm, vhtcap.supp_mcs.tx_mcs_map);
+	ADDSHORT(frm, vhtcap.supp_mcs.tx_highest);
 
 	return (frm);
 }
@@ -370,17 +698,6 @@ ieee80211_add_vhtinfo(uint8_t *frm, stru
 	frm[1] = sizeof(struct ieee80211_ie_vht_operation) - 2;
 	frm += 2;
 
-	/*
-	 * XXX if it's a station, then see if we have a node
-	 * channel or ANYC.  If it's ANYC then assume we're
-	 * scanning, and announce our capabilities.
-	 *
-	 * This should set the "20/40/80/160MHz wide config";
-	 * the 80/80 or 160MHz wide config is done in VHTCAP.
-	 *
-	 * Other modes - just limit it to the channel.
-	 */
-
 	/* 8-bit chanwidth */
 	*frm++ = ieee80211_vht_get_chwidth_ie(ni->ni_chan);
 
@@ -475,3 +792,19 @@ ieee80211_vht_adjust_channel(struct ieee
 #endif
 	return (chan);
 }
+
+/*
+ * Calculate the VHT operation IE for a given node.
+ *
+ * This includes calculating the suitable channel width/parameters
+ * and basic MCS set.
+ *
+ * TODO: ensure I read 9.7.11 Rate Selection for VHT STAs.
+ * TODO: ensure I read 10.39.7 - BSS Basic VHT-MCS and NSS set operation.
+ */
+void
+ieee80211_vht_get_vhtinfo_ie(struct ieee80211_node *ni,
+    struct ieee80211_ie_vht_operation *vhtop, int opmode)
+{
+	printf("%s: called; TODO!\n", __func__);
+}

Modified: head/sys/net80211/ieee80211_vht.h
==============================================================================
--- head/sys/net80211/ieee80211_vht.h	Tue Jan 10 04:50:26 2017	(r311854)
+++ head/sys/net80211/ieee80211_vht.h	Tue Jan 10 05:30:15 2017	(r311855)
@@ -60,4 +60,9 @@ struct ieee80211_channel *
 	ieee80211_vht_adjust_channel(struct ieee80211com *,
 	    struct ieee80211_channel *, int);
 
+void	ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
+	    struct ieee80211_ie_vhtcap *, int);
+void	ieee80211_vht_get_vhtinfo_ie(struct ieee80211_node *ni,
+	    struct ieee80211_ie_vht_operation *, int);
+
 #endif	/* _NET80211_IEEE80211_VHT_H_ */


More information about the svn-src-all mailing list