git: 07555b10469d - stable/15 - LinuxKPI: 802.11: when synching HT and VHT cap, mask rx_mcs
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 08 Dec 2025 15:44:15 UTC
The branch stable/15 has been updated by bz:
URL: https://cgit.FreeBSD.org/src/commit/?id=07555b10469d0cb613cfeedfd1239d5c4b6e507f
commit 07555b10469d0cb613cfeedfd1239d5c4b6e507f
Author: Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2025-11-28 23:10:45 +0000
Commit: Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2025-12-08 15:43:52 +0000
LinuxKPI: 802.11: when synching HT and VHT cap, mask rx_mcs
When we sync the sta data, mask the rx_mcs with what the hardware is
able to do so that we do not leave, e.g., a 2nd stream enabled on a 1x1
chipset.
iwlwifi(4) has a further check for the smps_mode to limit to NSS=1 but
I believe that is historic and not actually in use anymore.
This fixes firmware crashes on TLC updates with nss=1 but the nss=2 array
index also being populated (with HT/VHT80/160 mcs information):
data being populated:
iwlwifi0: 0x20101A0D | ADVANCED_SYSASSERT
iwlwifi0: 0x00000006 | umac data1
iwlwifi0: 0x00000001 | umac data2
iwlwifi0: 0x000003FF | umac data3
iwlwifi0: 0x____050F | last host cmd
Reported by: Claudio Zumbo (claudiozumbo gmail.com), Erik Power
Tested by: Claudio Zumbo, Erik Power (eppower umich.edu)
PR: 290622
Sponsored by: The FreeBSD Foundation
(cherry picked from commit adb4901ac9ae6c2ceb4194d139ed3176f6ed5e55)
---
sys/compat/linuxkpi/common/src/linux_80211.c | 99 ++++++++++++++++++++--------
1 file changed, 71 insertions(+), 28 deletions(-)
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 8b1f5f0e0399..8098d02da1a9 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -128,11 +128,11 @@ SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
#define UNIMPLEMENTED if (linuxkpi_debug_80211 & D80211_TODO) \
printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__)
-#define TRACEOK() if (linuxkpi_debug_80211 & D80211_TRACEOK) \
- printf("XXX-TODO %s:%d: TRACEPOINT\n", __func__, __LINE__)
+#define TRACEOK(_fmt, ...) if (linuxkpi_debug_80211 & D80211_TRACEOK) \
+ printf("%s:%d: TRACEPOINT " _fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
#else
#define UNIMPLEMENTED do { } while (0)
-#define TRACEOK() do { } while (0)
+#define TRACEOK(...) do { } while (0)
#endif
/* #define PREP_TX_INFO_DURATION (IEEE80211_TRANS_WAIT * 1000) */
@@ -474,12 +474,14 @@ lkpi_sync_chanctx_cw_from_rx_bw(struct ieee80211_hw *hw,
#if defined(LKPI_80211_HT)
static void
-lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_node *ni)
+lkpi_sta_sync_ht_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct ieee80211_node *ni)
{
struct ieee80211vap *vap;
uint8_t *ie;
struct ieee80211_ht_cap *htcap;
+ struct ieee80211_sta_ht_cap *ht_cap, *sta_ht_cap;
+ enum nl80211_band band;
int i, rx_nss;
if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) {
@@ -513,13 +515,23 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
* MCS sets from the Rx MCS Bitmask; then there is MCS 32 and
* MCS33.. is UEQM.
*/
+ band = vif->bss_conf.chanctx_conf->def.chan->band;
+ ht_cap = &hw->wiphy->bands[band]->ht_cap;
+ sta_ht_cap = &sta->deflink.ht_cap;
rx_nss = 0;
for (i = 0; i < 4; i++) {
- if (htcap->mcs.rx_mask[i] != 0)
+ TRACEOK("HT rx_mask[%d] sta %#04x & hw %#04x", i,
+ sta_ht_cap->mcs.rx_mask[i], ht_cap->mcs.rx_mask[i]);
+ sta_ht_cap->mcs.rx_mask[i] =
+ sta_ht_cap->mcs.rx_mask[i] & ht_cap->mcs.rx_mask[i];
+ /* XXX-BZ masking unequal modulation? */
+
+ if (sta_ht_cap->mcs.rx_mask[i] != 0)
rx_nss++;
}
if (rx_nss > 0) {
- sta->deflink.rx_nss = rx_nss;
+ TRACEOK("HT rx_nss = max(%d, %d)", rx_nss, sta->deflink.rx_nss);
+ sta->deflink.rx_nss = MAX(rx_nss, sta->deflink.rx_nss);
} else {
sta->deflink.ht_cap.ht_supported = false;
return;
@@ -548,14 +560,15 @@ lkpi_sta_sync_ht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
#if defined(LKPI_80211_VHT)
static void
-lkpi_sta_sync_vht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
- struct ieee80211_node *ni)
+lkpi_sta_sync_vht_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta, struct ieee80211_node *ni)
{
+ struct ieee80211_sta_vht_cap *vht_cap, *sta_vht_cap;;
enum ieee80211_sta_rx_bandwidth bw;
+ enum nl80211_band band;
uint32_t width;
int rx_nss;
- uint16_t rx_mcs_map;
- uint8_t mcs;
+ uint16_t rx_map, tx_map;
if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0 ||
!IEEE80211_IS_CHAN_VHT_5GHZ(ni->ni_chan)) {
@@ -609,18 +622,49 @@ lkpi_sta_sync_vht_from_ni(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
sta->deflink.bandwidth = bw;
skip_bw:
+ band = vif->bss_conf.chanctx_conf->def.chan->band;
+ vht_cap = &hw->wiphy->bands[band]->vht_cap;
+ sta_vht_cap = &sta->deflink.vht_cap;
+
rx_nss = 0;
- rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
+ rx_map = tx_map = 0;
for (int i = 7; i >= 0; i--) {
- mcs = rx_mcs_map >> (2 * i);
- mcs &= 0x3;
- if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
- rx_nss = i + 1;
- break;
+ uint8_t card, sta;
+
+ card = (vht_cap->vht_mcs.rx_mcs_map >> (2 * i)) & 0x3;
+ sta = (sta_vht_cap->vht_mcs.rx_mcs_map >> (2 * i)) & 0x3;
+ if (sta != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ if (card == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ sta = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ else {
+ sta = MIN(sta, card);
+ rx_nss = i + 1;
+ }
+ }
+ rx_map |= (sta << (2 * i));
+
+ card = (vht_cap->vht_mcs.tx_mcs_map >> (2 * i)) & 0x3;
+ sta = (sta_vht_cap->vht_mcs.tx_mcs_map >> (2 * i)) & 0x3;
+ if (sta != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ if (card == IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ sta = IEEE80211_VHT_MCS_NOT_SUPPORTED;
+ else
+ sta = MIN(sta, card);
}
+ tx_map |= (sta << (2 * i));
+ }
+ TRACEOK("VHT rx_mcs_map %#010x->%#010x, tx_mcs_map %#010x->%#010x, rx_nss = %d",
+ sta_vht_cap->vht_mcs.rx_mcs_map, rx_map,
+ sta_vht_cap->vht_mcs.tx_mcs_map, tx_map, rx_nss);
+ sta_vht_cap->vht_mcs.rx_mcs_map = rx_map;
+ sta_vht_cap->vht_mcs.tx_mcs_map = tx_map;
+ if (rx_nss > 0) {
+ TRACEOK("VHT rx_nss = max(%d, %d)", rx_nss, sta->deflink.rx_nss);
+ sta->deflink.rx_nss = MAX(rx_nss, sta->deflink.rx_nss);
+ } else {
+ sta->deflink.vht_cap.vht_supported = false;
+ return;
}
- if (rx_nss > 0)
- sta->deflink.rx_nss = rx_nss;
switch (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK) {
case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
@@ -642,18 +686,18 @@ lkpi_sta_sync_from_ni(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_node *ni, bool updchnctx)
{
-#if defined(LKPI_80211_HT)
- lkpi_sta_sync_ht_from_ni(vif, sta, ni);
-#endif
-#if defined(LKPI_80211_VHT)
- lkpi_sta_sync_vht_from_ni(vif, sta, ni);
-#endif
-
/*
* Ensure rx_nss is at least 1 as otherwise drivers run into
* unexpected problems.
*/
- sta->deflink.rx_nss = MAX(1, sta->deflink.rx_nss);
+ sta->deflink.rx_nss = 1;
+
+#if defined(LKPI_80211_HT)
+ lkpi_sta_sync_ht_from_ni(hw, vif, sta, ni);
+#endif
+#if defined(LKPI_80211_VHT)
+ lkpi_sta_sync_vht_from_ni(hw, vif, sta, ni);
+#endif
/*
* We are also called from node allocation which net80211
@@ -6237,7 +6281,6 @@ lkpi_ic_getradiocaps_ht(struct ieee80211com *ic, struct ieee80211_hw *hw,
#endif
IMPROVE("PS, ampdu_*, ht_cap.mcs.tx_params, ...");
- ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_OFF;
/* Only add HT40 channels if supported. */
if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0 &&