memcpy(addr, sta->sta.addr, ETH_ALEN);
+ ieee80211_sta_init_nss(&sta->deflink);
+
ibss_dbg(sdata, "Adding new IBSS station %pM\n", addr);
sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
{
return _ieee80211_sta_cur_vht_bw(link_sta, NULL);
}
-void ieee80211_sta_init_nss(struct link_sta_info *link_sta);
+
void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
struct ieee80211_mgmt *mgmt);
elems->eht_cap, elems->eht_cap_len,
&sta->deflink);
+ ieee80211_sta_init_nss(&sta->deflink);
+
if (bw != sta->sta.deflink.bandwidth)
changed |= IEEE80211_RC_BW_CHANGED;
*/
/*
- * If an operating mode notification IE is present, override the
- * NSS calculation (that would be done in rate_control_rate_init())
- * and use the # of streams from that element.
+ * If an operating mode notification element is present, set the opmode
+ * NSS override to correct for the current number of spatial streams,
+ * overriding the capabilities. ieee80211_sta_init_nss() uses this.
*/
if (elems->opmode_notif &&
!(*elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_TYPE_BF)) {
nss = *elems->opmode_notif & IEEE80211_OPMODE_NOTIF_RX_NSS_MASK;
nss >>= IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT;
nss += 1;
- link_sta->pub->rx_nss = nss;
+ link_sta->op_mode_nss = nss;
}
+ ieee80211_sta_init_nss(link_sta);
+
/*
* Always handle WMM once after association regardless
* of the first value the AP uses. Setting -1 here has
if (add_links_data->link[link_id].status != WLAN_STATUS_SUCCESS)
goto disconnect;
- ieee80211_sta_init_nss(link_sta);
if (ieee80211_sta_activate_link(sta, link_id))
goto disconnect;
*
* Copyright: (c) 2014 Czech Technical University in Prague
* (c) 2014 Volkswagen Group Research
- * Copyright (C) 2022 - 2024 Intel Corporation
+ * Copyright (C) 2022 - 2024, 2026 Intel Corporation
* Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
* Funded by: Volkswagen Group Research
*/
memcpy(addr, sta->sta.addr, ETH_ALEN);
+ ieee80211_sta_init_nss(&sta->deflink);
+
ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
addr, sdata->name);
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
* Copyright 2017 Intel Deutschland GmbH
- * Copyright (C) 2019, 2022-2025 Intel Corporation
+ * Copyright (C) 2019, 2022-2026 Intel Corporation
*/
#include <linux/kernel.h>
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
- ieee80211_sta_init_nss(link_sta);
-
if (!ref)
return;
sta_remove_link(sta, link_id, true);
}
+void ieee80211_sta_init_nss(struct link_sta_info *link_sta)
+{
+ u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss;
+ bool support_160;
+
+ if (link_sta->pub->eht_cap.has_eht) {
+ int i;
+ const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp;
+
+ /* get the max nss for EHT over all possible bandwidths and mcs */
+ for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++)
+ eht_rx_nss = max_t(u8, eht_rx_nss,
+ u8_get_bits(rx_nss_mcs[i],
+ IEEE80211_EHT_MCS_NSS_RX));
+ }
+
+ if (link_sta->pub->he_cap.has_he) {
+ int i;
+ u8 rx_mcs_80 = 0, rx_mcs_160 = 0;
+ const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
+ u16 mcs_160_map =
+ le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
+ u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
+
+ for (i = 7; i >= 0; i--) {
+ u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
+
+ if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
+ rx_mcs_160 = i + 1;
+ break;
+ }
+ }
+ for (i = 7; i >= 0; i--) {
+ u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
+
+ if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
+ rx_mcs_80 = i + 1;
+ break;
+ }
+ }
+
+ support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
+
+ if (support_160)
+ he_rx_nss = min(rx_mcs_80, rx_mcs_160);
+ else
+ he_rx_nss = rx_mcs_80;
+ }
+
+ if (link_sta->pub->ht_cap.ht_supported) {
+ if (link_sta->pub->ht_cap.mcs.rx_mask[0])
+ ht_rx_nss++;
+ if (link_sta->pub->ht_cap.mcs.rx_mask[1])
+ ht_rx_nss++;
+ if (link_sta->pub->ht_cap.mcs.rx_mask[2])
+ ht_rx_nss++;
+ if (link_sta->pub->ht_cap.mcs.rx_mask[3])
+ ht_rx_nss++;
+ /* FIXME: consider rx_highest? */
+ }
+
+ if (link_sta->pub->vht_cap.vht_supported) {
+ int i;
+ u16 rx_mcs_map;
+
+ rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map);
+
+ for (i = 7; i >= 0; i--) {
+ u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
+
+ if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
+ vht_rx_nss = i + 1;
+ break;
+ }
+ }
+ /* FIXME: consider rx_highest? */
+ }
+
+ rx_nss = max(vht_rx_nss, ht_rx_nss);
+ rx_nss = max(he_rx_nss, rx_nss);
+ rx_nss = max(eht_rx_nss, rx_nss);
+ rx_nss = max_t(u8, 1, rx_nss);
+ link_sta->capa_nss = rx_nss;
+
+ if (link_sta->op_mode_nss)
+ link_sta->pub->rx_nss =
+ min_t(u8, rx_nss, link_sta->op_mode_nss);
+ else
+ link_sta->pub->rx_nss = rx_nss;
+}
+
void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
const u8 *ext_capab,
unsigned int ext_capab_len)
unsigned long ieee80211_sta_last_active(struct sta_info *sta, int link_id);
+void ieee80211_sta_init_nss(struct link_sta_info *link_sta);
void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
const u8 *ext_capab,
unsigned int ext_capab_len);
return bw;
}
-void ieee80211_sta_init_nss(struct link_sta_info *link_sta)
-{
- u8 ht_rx_nss = 0, vht_rx_nss = 0, he_rx_nss = 0, eht_rx_nss = 0, rx_nss;
- bool support_160;
-
- if (link_sta->pub->eht_cap.has_eht) {
- int i;
- const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp;
-
- /* get the max nss for EHT over all possible bandwidths and mcs */
- for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++)
- eht_rx_nss = max_t(u8, eht_rx_nss,
- u8_get_bits(rx_nss_mcs[i],
- IEEE80211_EHT_MCS_NSS_RX));
- }
-
- if (link_sta->pub->he_cap.has_he) {
- int i;
- u8 rx_mcs_80 = 0, rx_mcs_160 = 0;
- const struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
- u16 mcs_160_map =
- le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160);
- u16 mcs_80_map = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80);
-
- for (i = 7; i >= 0; i--) {
- u8 mcs_160 = (mcs_160_map >> (2 * i)) & 3;
-
- if (mcs_160 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
- rx_mcs_160 = i + 1;
- break;
- }
- }
- for (i = 7; i >= 0; i--) {
- u8 mcs_80 = (mcs_80_map >> (2 * i)) & 3;
-
- if (mcs_80 != IEEE80211_HE_MCS_NOT_SUPPORTED) {
- rx_mcs_80 = i + 1;
- break;
- }
- }
-
- support_160 = he_cap->he_cap_elem.phy_cap_info[0] &
- IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
-
- if (support_160)
- he_rx_nss = min(rx_mcs_80, rx_mcs_160);
- else
- he_rx_nss = rx_mcs_80;
- }
-
- if (link_sta->pub->ht_cap.ht_supported) {
- if (link_sta->pub->ht_cap.mcs.rx_mask[0])
- ht_rx_nss++;
- if (link_sta->pub->ht_cap.mcs.rx_mask[1])
- ht_rx_nss++;
- if (link_sta->pub->ht_cap.mcs.rx_mask[2])
- ht_rx_nss++;
- if (link_sta->pub->ht_cap.mcs.rx_mask[3])
- ht_rx_nss++;
- /* FIXME: consider rx_highest? */
- }
-
- if (link_sta->pub->vht_cap.vht_supported) {
- int i;
- u16 rx_mcs_map;
-
- rx_mcs_map = le16_to_cpu(link_sta->pub->vht_cap.vht_mcs.rx_mcs_map);
-
- for (i = 7; i >= 0; i--) {
- u8 mcs = (rx_mcs_map >> (2 * i)) & 3;
-
- if (mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) {
- vht_rx_nss = i + 1;
- break;
- }
- }
- /* FIXME: consider rx_highest? */
- }
-
- rx_nss = max(vht_rx_nss, ht_rx_nss);
- rx_nss = max(he_rx_nss, rx_nss);
- rx_nss = max(eht_rx_nss, rx_nss);
- rx_nss = max_t(u8, 1, rx_nss);
- link_sta->capa_nss = rx_nss;
-
- /* that shouldn't be set yet, but we can handle it anyway */
- if (link_sta->op_mode_nss)
- link_sta->pub->rx_nss =
- min_t(u8, rx_nss, link_sta->op_mode_nss);
- else
- link_sta->pub->rx_nss = rx_nss;
-}
-
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct link_sta_info *link_sta,
u8 opmode, enum nl80211_band band)