diff --git a/sys/net80211/ieee80211_phy.h b/sys/net80211/ieee80211_phy.h --- a/sys/net80211/ieee80211_phy.h +++ b/sys/net80211/ieee80211_phy.h @@ -221,5 +221,12 @@ uint16_t rate, int streams, int isht40, int isShortGI); +enum ieee80211_sta_rx_bw; + +uint16_t ieee80211_phy_vht_get_mcs_mask(enum ieee80211_sta_rx_bw, + uint8_t); +bool ieee80211_phy_vht_validate_mcs(enum ieee80211_sta_rx_bw, + uint8_t, uint8_t); + #endif /* _KERNEL */ #endif /* !_NET80211_IEEE80211_PHY_H_ */ diff --git a/sys/net80211/ieee80211_phy.c b/sys/net80211/ieee80211_phy.c --- a/sys/net80211/ieee80211_phy.c +++ b/sys/net80211/ieee80211_phy.c @@ -621,3 +621,89 @@ #undef HT_L_LTF #undef HT_L_STF #undef OFDM_PLCP_BITS + + +/* + * A bitmask of allowable rates for each spatial stream + * and bandwidth. + * + * This is based on 802.11-2020 21.5 (Parameters for VHT-MCSs.) + * + * Not all MCS rate / channel widths are valid, as there needs + * to be an integer number of symbols and the number of tones + * available for each channel bandwidth doesn't result in + * an integer value. + */ +static uint16_t ieee80211_vht_mcs_allowed_list_20[] = { + 0x01ff, 0x01ff, 0x03ff, 0x01ff, 0x01ff, 0x03ff, 0x01ff, 0x01ff, +}; + +static uint16_t ieee80211_vht_mcs_allowed_list_40[] = { + 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, +}; + +static uint16_t ieee80211_vht_mcs_allowed_list_80[] = { + 0x03ff, 0x03ff, 0x03bf, 0x03ff, 0x03ff, 0x01ff, 0x03bf, 0x03ff, +}; + +static uint16_t ieee80211_vht_mcs_allowed_list_160[] = { + 0x03ff, 0x03ff, 0x01ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, 0x03ff, +}; + +/** + * @brief Fetch the allowable MCS mask for the given channel bandwidth and NSS + * + * Return a bitmask of valid MCS rates from 0..9 given a channel bandwith + * and number of spatial streams. + * + * See 802.11-2020 21.5 (Parameters for VHT-MCSs) for more details. + * + * @param bw channel bandwidth, via enum ieee80211_sta_rx_bw + * @param nss number of spatial streams, 1..8 + * @returns bitmask of valid MCS rates from 0..9 + */ +uint16_t +ieee80211_phy_vht_get_mcs_mask(enum ieee80211_sta_rx_bw bw, uint8_t nss) +{ + if (nss == 0 || nss > 8) + return (0); + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + return (ieee80211_vht_mcs_allowed_list_20[nss - 1]); + case IEEE80211_STA_RX_BW_40: + return (ieee80211_vht_mcs_allowed_list_40[nss - 1]); + case IEEE80211_STA_RX_BW_80: + return (ieee80211_vht_mcs_allowed_list_80[nss - 1]); + case IEEE80211_STA_RX_BW_160: + return (ieee80211_vht_mcs_allowed_list_160[nss - 1]); + case IEEE80211_STA_RX_BW_320: + /* invalid for VHT */ + return (0); + } +} + +/** + * @brief Check if the given NSS/MCS combination is valid for the given channel + * bandwidth + * + * See 802.11-2020 21.5 (Parameters for VHT-MCSs) for more details. + * + * @param bw channel bandwidth, via enum ieee80211_sta_rx_bw + * @param nss number of spatial streams, 1..8 + * @param mcs MCS rate, 0..9 + * @retval true if the NSS / MCS / bandwidth combination is valid + * @retval false if the NSS / MCS / bandwidth combination is not valid + */ +bool +ieee80211_phy_vht_validate_mcs(enum ieee80211_sta_rx_bw bw, uint8_t nss, + uint8_t mcs) +{ + uint16_t mask; + + mask = ieee80211_phy_vht_get_mcs_mask(bw, nss); + if (mask == 0) + return (false); + + return ((mask & (1 << mcs)) != 0); +}