From owner-svn-src-head@freebsd.org Tue Jan 10 05:30:17 2017 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 0388BCA8955; Tue, 10 Jan 2017 05:30:17 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id BFE4E145D; Tue, 10 Jan 2017 05:30:16 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id v0A5UGJu094899; Tue, 10 Jan 2017 05:30:16 GMT (envelope-from adrian@FreeBSD.org) Received: (from adrian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id v0A5UF3H094897; Tue, 10 Jan 2017 05:30:15 GMT (envelope-from adrian@FreeBSD.org) Message-Id: <201701100530.v0A5UF3H094897@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: adrian set sender to adrian@FreeBSD.org using -f From: Adrian Chadd Date: Tue, 10 Jan 2017 05:30:15 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r311855 - head/sys/net80211 X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 10 Jan 2017 05:30:17 -0000 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_ */