diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c --- a/sys/compat/linuxkpi/common/src/linux_80211.c +++ b/sys/compat/linuxkpi/common/src/linux_80211.c @@ -547,19 +547,15 @@ width = (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK); switch (width) { -#if 0 case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; break; -#endif default: /* Check if we do support 160Mhz somehow after all. */ -#if 0 if ((sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) != 0) sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; else -#endif sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80; } skip_bw: @@ -2112,14 +2108,11 @@ #endif #ifdef LKPI_80211_VHT if (IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) { -#ifdef __notyet__ if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) chanctx_conf->def.width = NL80211_CHAN_WIDTH_80P80; else if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan)) chanctx_conf->def.width = NL80211_CHAN_WIDTH_160; - else -#endif - if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) + else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan)) chanctx_conf->def.width = NL80211_CHAN_WIDTH_80; } #endif diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -945,12 +945,15 @@ struct ieee80211_vht_mcs_info supp_mcs; } __packed; -/* 802.11ac-2013, Table 8-183x-VHT Operation Information subfields */ +/* + * 802.11ac-2013, Table 8-183x-VHT Operation Information subfields. + * 802.11-2020, Table 9-274-VHT Operation Information subfields (for deprecations) + */ enum ieee80211_vht_chanwidth { IEEE80211_VHT_CHANWIDTH_USE_HT = 0, /* 20 MHz or 40 MHz */ IEEE80211_VHT_CHANWIDTH_80MHZ = 1, /* 80MHz */ - IEEE80211_VHT_CHANWIDTH_160MHZ = 2, /* 160MHz */ - IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3, /* 80+80MHz */ + IEEE80211_VHT_CHANWIDTH_160MHZ = 2, /* 160MHz (deprecated) */ + IEEE80211_VHT_CHANWIDTH_80P80MHZ = 3, /* 80+80MHz (deprecated) */ /* 4..255 reserved. */ }; diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1931,66 +1931,132 @@ static uint32_t ieee80211_vht_get_vhtflags(struct ieee80211_node *ni, uint32_t htflags) { - struct ieee80211vap *vap = ni->ni_vap; - uint32_t vhtflags = 0; +#define _RETURN_CHAN_BITS(_cb) \ +do { \ + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, \ + "%s:%d: selected %b", __func__, __LINE__, \ + (_cb), IEEE80211_CHAN_BITS); \ + return (_cb); \ +} while(0) + struct ieee80211vap *vap; + const struct ieee80211_ie_htinfo *htinfo; + uint32_t vhtflags; + bool can_vht160, can_vht80p80, can_vht80; + bool ht40; + + vap = ni->ni_vap; + + /* If we do not support VHT or VHT is disabled just return. */ + if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0 || + (vap->iv_vht_flags & IEEE80211_FVHT_VHT) == 0) + _RETURN_CHAN_BITS(0); + + /* + * The original code was based on + * 802.11ac-2013, Table 8-183x-VHT Operation Information subfields. + * 802.11-2020, Table 9-274-VHT Operation Information subfields + * has IEEE80211_VHT_CHANWIDTH_160MHZ and + * IEEE80211_VHT_CHANWIDTH_80P80MHZ deprecated. + * For current logic see + * 802.11-2020, 11.38.1 Basic VHT BSS functionality. + */ + + htinfo = (const struct ieee80211_ie_htinfo *)ni->ni_ies.htinfo_ie; + ht40 = ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH) == + IEEE80211_HTINFO_TXWIDTH_2040); + can_vht160 = can_vht80p80 = can_vht80 = false; + + /* 20 Mhz */ + if (!ht40) { + /* Check for the full valid combination -- other fields be 0. */ + if (ni->ni_vht_chanwidth != IEEE80211_VHT_CHANWIDTH_USE_HT || + ni->ni_vht_chan2 != 0) + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, + "%s: invalid VHT BSS bandwidth 0/%d/%d/%d", + __func__, ni->ni_vht_chanwidth, + ni->ni_vht_chan1, ni->ni_vht_chan2); + + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT20 | IEEE80211_CHAN_HT20); + } vhtflags = 0; - if (ni->ni_flags & IEEE80211_NODE_VHT && vap->iv_vht_flags & IEEE80211_FVHT_VHT) { - if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) && - IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(vap->iv_vht_cap.vht_cap_info) && - (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160)) { - vhtflags = IEEE80211_CHAN_VHT160; - /* Mirror the HT40 flags */ - if (htflags == IEEE80211_CHAN_HT40U) { - vhtflags |= IEEE80211_CHAN_HT40U; - } else if (htflags == IEEE80211_CHAN_HT40D) { - vhtflags |= IEEE80211_CHAN_HT40D; - } - } else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80P80MHZ) && - IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(vap->iv_vht_cap.vht_cap_info) && - (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80)) { - vhtflags = IEEE80211_CHAN_VHT80P80; - /* Mirror the HT40 flags */ - if (htflags == IEEE80211_CHAN_HT40U) { - vhtflags |= IEEE80211_CHAN_HT40U; - } else if (htflags == IEEE80211_CHAN_HT40D) { - vhtflags |= IEEE80211_CHAN_HT40D; - } - } else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80MHZ) && - (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80)) { - vhtflags = IEEE80211_CHAN_VHT80; - /* Mirror the HT40 flags */ - if (htflags == IEEE80211_CHAN_HT40U) { - vhtflags |= IEEE80211_CHAN_HT40U; - } else if (htflags == IEEE80211_CHAN_HT40D) { - vhtflags |= IEEE80211_CHAN_HT40D; - } - } else if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT) { - /* Mirror the HT40 flags */ - /* - * XXX TODO: if ht40 is disabled, but vht40 isn't - * disabled then this logic will get very, very sad. - * It's quite possible the only sane thing to do is - * to not have vht40 as an option, and just obey - * 'ht40' as that flag. - */ - if ((htflags == IEEE80211_CHAN_HT40U) && - (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) { - vhtflags = IEEE80211_CHAN_VHT40U - | IEEE80211_CHAN_HT40U; - } else if (htflags == IEEE80211_CHAN_HT40D && - (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) { - vhtflags = IEEE80211_CHAN_VHT40D - | IEEE80211_CHAN_HT40D; - } else if (htflags == IEEE80211_CHAN_HT20) { - vhtflags = IEEE80211_CHAN_VHT20 - | IEEE80211_CHAN_HT20; - } - } else { - vhtflags = IEEE80211_CHAN_VHT20; + + /* We know we can at least do 40Mhz, so mirror the HT40 flags. */ + if (htflags == IEEE80211_CHAN_HT40U) + vhtflags |= IEEE80211_CHAN_HT40U; + else if (htflags == IEEE80211_CHAN_HT40D) + vhtflags |= IEEE80211_CHAN_HT40D; + + /* 40 MHz */ + if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT) { + if (ni->ni_vht_chan2 != 0) + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, + "%s: invalid VHT BSS bandwidth 1/%d/%d/%d", + __func__, ni->ni_vht_chanwidth, + ni->ni_vht_chan1, ni->ni_vht_chan2); + + if ((vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40) != 0) { + if (htflags == IEEE80211_CHAN_HT40U) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT40U | vhtflags); + if (htflags == IEEE80211_CHAN_HT40D) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT40D | vhtflags); } + + /* If we get here VHT40 is not supported or disabled. */ + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT20 | IEEE80211_CHAN_HT20); + } + + /* Deprecated check for 160. */ + if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) && + IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(vap->iv_vht_cap.vht_cap_info) && + (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160) != 0) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT160 | vhtflags); + + /* Deprecated check for 80P80. */ + if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80P80MHZ) && + IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(vap->iv_vht_cap.vht_cap_info) && + (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80) != 0) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT80P80 | vhtflags); + + if (ni->ni_vht_chanwidth != IEEE80211_VHT_CHANWIDTH_80MHZ) { + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, + "%s: invalid VHT BSS bandwidth %d/%d/%d", __func__, + ni->ni_vht_chanwidth, ni->ni_vht_chan2); + + _RETURN_CHAN_BITS(0); } - return (vhtflags); + + /* CCFS1 > 0 and | CCFS1 - CCFS0 | = 8 */ + if (ni->ni_vht_chan2 > 0 && (ni->ni_vht_chan2 - ni->ni_vht_chan1) == 8) + can_vht160 = can_vht80 = true; + + /* CCFS1 > 0 and | CCFS1 - CCFS0 | > 16 */ + if (ni->ni_vht_chan2 > 0 && (ni->ni_vht_chan2 - ni->ni_vht_chan1) > 16) + can_vht80p80 = can_vht80 = true; + + /* CFFS1 == 0 */ + if (ni->ni_vht_chan2 == 0) + can_vht80 = true; + + if (can_vht160 && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160) != 0) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT160 | vhtflags); + + if (can_vht80p80 && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80) != 0) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT80P80 | vhtflags); + + if (can_vht80 && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80) != 0) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT80 | vhtflags); + + if (ht40 && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40) != 0) { + if (htflags == IEEE80211_CHAN_HT40U) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT40U | vhtflags); + if (htflags == IEEE80211_CHAN_HT40D) + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT40D | vhtflags); + } + + /* Either we disabled support or got an invalid setting. */ + _RETURN_CHAN_BITS(IEEE80211_CHAN_VHT20 | IEEE80211_CHAN_HT20); +#undef _RETURN_CHAN_BITS } /*