[PATCH ath-next 2/2] wifi: ath11k: advertise a spatial stream for every chain
Christopher Griffin via B4 Relay
devnull+christophergriffin05.gmail.com at kernel.org
Wed Oct 28 12:37:39 PDT 2026
From: Christopher Griffin <christophergriffin05 at gmail.com>
ath11k counts stream i as supported only when bit i of the radio's
chainmask is set: in the HT RX MCS mask (ath11k_create_ht_cap()), the
VHT MCS maps (ath11k_create_vht_cap()) and the HE MCS maps
(ath11k_mac_set_hemcsmap()). A chainmask need not be contiguous. The
QCN9072 in an Arista C-360 reports 0x5, chains 0 and 2: it has two
chains, and ar->num_tx_chains and ar->num_rx_chains say so, but bit 1
is clear, so its HT, VHT and HE capabilities offer one stream ("2
streams: not supported" in iw phy, on 2.4, 5 and 6 GHz) and it
associates as a 1x1 client.
A radio can use as many streams as it has chains, whichever chains
they are. Count the streams up to ar->num_tx_chains and
ar->num_rx_chains, the chainmasks' weights, and take TX STBC away when
there is one TX chain rather than when the shifted TX chainmask is at
most 1. The rate-cap chainmasks that only fed these tests go. This also
ends the HE RX map's use of tx_chain_mask_shift for the RX chainmask.
With this the QCN9072 shows HT MCS 0-15 and two VHT and HE streams on
every band, and as a client on 5 GHz at 40 MHz it associated at
HE-MCS 10, 2 streams, and ran 310 Mbit/s down and 438 up to a local
server. Contiguous chainmasks get the same capabilities as before: the
IPQ8074's radios (0xf, and 0xf0 shifted by 4) are unchanged.
Tested-on: QCN9074 hw1.0 PCI fw_version 0x250affff (vendor build)
Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.9.0.1-02146-QCAHKSWPL_SILICONZ-1
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Fixes: ebf82988f844 ("wifi: ath11k: generate rx and tx mcs maps for supported HE mcs")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Christopher Griffin <christophergriffin05 at gmail.com>
---
drivers/net/wireless/ath/ath11k/mac.c | 38 ++++++++++++-----------------------
1 file changed, 13 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index af01ef1cf4c9..62c3a6a7cd20 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5556,7 +5556,7 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw,
}
static struct ieee80211_sta_ht_cap
-ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask)
+ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap)
{
int i;
struct ieee80211_sta_ht_cap ht_cap = {};
@@ -5611,10 +5611,9 @@ ath11k_create_ht_cap(struct ath11k *ar, u32 ar_ht_cap, u32 rate_cap_rx_chainmask
if (ar_vht_cap & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
- for (i = 0; i < ar->num_rx_chains; i++) {
- if (rate_cap_rx_chainmask & BIT(i))
- ht_cap.mcs.rx_mask[i] = 0xFF;
- }
+ /* a stream for every chain, whichever chains the chainmask names */
+ for (i = 0; i < ar->num_rx_chains; i++)
+ ht_cap.mcs.rx_mask[i] = 0xFF;
ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
@@ -5723,8 +5722,7 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap)
}
static struct ieee80211_sta_vht_cap
-ath11k_create_vht_cap(struct ath11k *ar, u32 rate_cap_tx_chainmask,
- u32 rate_cap_rx_chainmask)
+ath11k_create_vht_cap(struct ath11k *ar)
{
struct ieee80211_sta_vht_cap vht_cap = {};
u16 txmcs_map, rxmcs_map;
@@ -5742,18 +5740,18 @@ ath11k_create_vht_cap(struct ath11k *ar, u32 rate_cap_tx_chainmask,
rxmcs_map = 0;
txmcs_map = 0;
for (i = 0; i < 8; i++) {
- if (i < ar->num_tx_chains && rate_cap_tx_chainmask & BIT(i))
+ if (i < ar->num_tx_chains)
txmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
else
txmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
- if (i < ar->num_rx_chains && rate_cap_rx_chainmask & BIT(i))
+ if (i < ar->num_rx_chains)
rxmcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2);
else
rxmcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2);
}
- if (rate_cap_tx_chainmask <= 1)
+ if (ar->num_tx_chains <= 1)
vht_cap.cap &= ~IEEE80211_VHT_CAP_TXSTBC;
vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_map);
@@ -5767,20 +5765,14 @@ static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar,
u32 *ht_cap_info)
{
struct ieee80211_supported_band *band;
- u32 rate_cap_tx_chainmask;
- u32 rate_cap_rx_chainmask;
u32 ht_cap;
- rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift;
- rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift;
-
if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) {
band = &ar->mac.sbands[NL80211_BAND_2GHZ];
ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info;
if (ht_cap_info)
*ht_cap_info = ht_cap;
- band->ht_cap = ath11k_create_ht_cap(ar, ht_cap,
- rate_cap_rx_chainmask);
+ band->ht_cap = ath11k_create_ht_cap(ar, ht_cap);
}
/* A 6 GHz capable radio may cover 5 GHz as well, as a QCN9074 does:
@@ -5793,10 +5785,8 @@ static void ath11k_mac_setup_ht_vht_cap(struct ath11k *ar,
ht_cap = cap->band[NL80211_BAND_5GHZ].ht_cap_info;
if (ht_cap_info)
*ht_cap_info = ht_cap;
- band->ht_cap = ath11k_create_ht_cap(ar, ht_cap,
- rate_cap_rx_chainmask);
- band->vht_cap = ath11k_create_vht_cap(ar, rate_cap_tx_chainmask,
- rate_cap_rx_chainmask);
+ band->ht_cap = ath11k_create_ht_cap(ar, ht_cap);
+ band->vht_cap = ath11k_create_vht_cap(ar);
}
}
@@ -5949,14 +5939,12 @@ static void ath11k_mac_set_hemcsmap(struct ath11k *ar,
rxmcs_map = 0;
txmcs_map = 0;
for (i = 0; i < 8; i++) {
- if (i < ar->num_tx_chains &&
- (ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
+ if (i < ar->num_tx_chains)
txmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
else
txmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
- if (i < ar->num_rx_chains &&
- (ar->cfg_rx_chainmask >> cap->tx_chain_mask_shift) & BIT(i))
+ if (i < ar->num_rx_chains)
rxmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2);
else
rxmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2);
--
2.43.0
More information about the ath11k
mailing list