summaryrefslogtreecommitdiff
path: root/net/mac80211/chan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r--net/mac80211/chan.c95
1 files changed, 75 insertions, 20 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index cca6d14084d2..3aaf5abf1acc 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211 - channel management
- * Copyright 2020 - 2024 Intel Corporation
+ * Copyright 2020 - 2025 Intel Corporation
*/
#include <linux/nl80211.h>
@@ -247,6 +247,13 @@ static enum nl80211_chan_width ieee80211_get_sta_bw(struct sta_info *sta,
if (!link_sta)
return NL80211_CHAN_WIDTH_20_NOHT;
+ /*
+ * We assume that TX/RX might be asymmetric (so e.g. VHT operating
+ * mode notification changes what a STA wants to receive, but not
+ * necessarily what it will transmit to us), and therefore use the
+ * capabilities here. Calling it RX bandwidth capability is a bit
+ * wrong though, since capabilities are in fact symmetric.
+ */
width = ieee80211_sta_cap_rx_bw(link_sta);
switch (width) {
@@ -323,22 +330,34 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
continue;
switch (link->sdata->vif.type) {
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_AP_VLAN:
- width = ieee80211_get_max_required_bw(link);
- break;
case NL80211_IFTYPE_STATION:
+ if (!link->sdata->vif.cfg.assoc) {
+ /*
+ * The AP's sta->bandwidth may not yet be set
+ * at this point (pre-association), so simply
+ * take the width from the chandef. We cannot
+ * have TDLS peers yet (only after association).
+ */
+ width = link->conf->chanreq.oper.width;
+ break;
+ }
/*
- * The ap's sta->bandwidth is not set yet at this
- * point, so take the width from the chandef, but
- * account also for TDLS peers
+ * otherwise just use min_def like in AP, depending on what
+ * we currently think the AP STA (and possibly TDLS peers)
+ * require(s)
*/
- width = max(link->conf->chanreq.oper.width,
- ieee80211_get_max_required_bw(link));
+ fallthrough;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ width = ieee80211_get_max_required_bw(link);
break;
case NL80211_IFTYPE_P2P_DEVICE:
case NL80211_IFTYPE_NAN:
continue;
+ case NL80211_IFTYPE_MONITOR:
+ WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
+ NO_VIRTUAL_MONITOR));
+ fallthrough;
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_OCB:
@@ -347,7 +366,6 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_UNSPECIFIED:
case NUM_NL80211_IFTYPES:
- case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
WARN_ON_ONCE(1);
@@ -409,7 +427,7 @@ _ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
if (!ctx->driver_present)
return 0;
- return IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
+ return IEEE80211_CHANCTX_CHANGE_MIN_DEF;
}
static void ieee80211_chan_bw_change(struct ieee80211_local *local,
@@ -462,12 +480,12 @@ static void ieee80211_chan_bw_change(struct ieee80211_local *local,
continue;
/* vif changed to narrow BW and narrow BW for station wasn't
- * requested or vise versa */
+ * requested or vice versa */
if ((new_sta_bw < link_sta->pub->bandwidth) == !narrowed)
continue;
link_sta->pub->bandwidth = new_sta_bw;
- rate_control_rate_update(local, sband, sta, link_id,
+ rate_control_rate_update(local, sband, link_sta,
IEEE80211_RC_BW_CHANGED);
}
}
@@ -905,7 +923,7 @@ static int ieee80211_assign_link_chanctx(struct ieee80211_link_data *link,
}
if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) {
- ieee80211_recalc_txpower(sdata, false);
+ ieee80211_recalc_txpower(link, false);
ieee80211_recalc_chanctx_min_def(local, new_ctx, NULL, false);
}
@@ -956,6 +974,10 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
if (!link->sdata->u.mgd.associated)
continue;
break;
+ case NL80211_IFTYPE_MONITOR:
+ if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ continue;
+ break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
@@ -968,6 +990,11 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
continue;
+ if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ rx_chains_dynamic = rx_chains_static = local->rx_chains;
+ break;
+ }
+
switch (link->smps_mode) {
default:
WARN_ONCE(1, "Invalid SMPS mode %d\n",
@@ -1118,7 +1145,7 @@ ieee80211_replace_chanctx(struct ieee80211_local *local,
*
* Consider ctx1..3, link1..6, each ctx has 2 links. link1 and
* link2 from ctx1 request new different chandefs starting 2
- * in-place reserations with ctx4 and ctx5 replacing ctx1 and
+ * in-place reservations with ctx4 and ctx5 replacing ctx1 and
* ctx2 respectively. Next link5 and link6 from ctx3 reserve
* ctx4. If link3 and link4 remain on ctx2 as they are then this
* fails unless `replace_ctx` from ctx5 is replaced with ctx3.
@@ -1169,7 +1196,7 @@ ieee80211_replace_chanctx(struct ieee80211_local *local,
static bool
ieee80211_find_available_radio(struct ieee80211_local *local,
const struct ieee80211_chan_req *chanreq,
- int *radio_idx)
+ u32 radio_mask, int *radio_idx)
{
struct wiphy *wiphy = local->hw.wiphy;
const struct wiphy_radio *radio;
@@ -1180,6 +1207,9 @@ ieee80211_find_available_radio(struct ieee80211_local *local,
return true;
for (i = 0; i < wiphy->n_radio; i++) {
+ if (!(radio_mask & BIT(i)))
+ continue;
+
radio = &wiphy->radio[i];
if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
continue;
@@ -1213,7 +1243,9 @@ int ieee80211_link_reserve_chanctx(struct ieee80211_link_data *link,
new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
if (!new_ctx) {
if (ieee80211_can_create_new_chanctx(local, -1) &&
- ieee80211_find_available_radio(local, chanreq, &radio_idx))
+ ieee80211_find_available_radio(local, chanreq,
+ sdata->wdev.radio_mask,
+ &radio_idx))
new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
false, radio_idx);
else
@@ -1712,7 +1744,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
link,
changed);
- ieee80211_recalc_txpower(sdata, false);
+ ieee80211_recalc_txpower(link, false);
}
ieee80211_recalc_chanctx_chantype(local, ctx);
@@ -1883,7 +1915,9 @@ int _ieee80211_link_use_channel(struct ieee80211_link_data *link,
/* Note: context is now reserved */
if (ctx)
reserved = true;
- else if (!ieee80211_find_available_radio(local, chanreq, &radio_idx))
+ else if (!ieee80211_find_available_radio(local, chanreq,
+ sdata->wdev.radio_mask,
+ &radio_idx))
ctx = ERR_PTR(-EBUSY);
else
ctx = ieee80211_new_chanctx(local, chanreq, mode,
@@ -2097,6 +2131,9 @@ void ieee80211_link_release_channel(struct ieee80211_link_data *link)
{
struct ieee80211_sub_if_data *sdata = link->sdata;
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ return;
+
lockdep_assert_wiphy(sdata->local->hw.wiphy);
if (rcu_access_pointer(link->conf->chanctx_conf))
@@ -2144,3 +2181,21 @@ void ieee80211_iter_chan_contexts_atomic(
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
+
+void ieee80211_iter_chan_contexts_mtx(
+ struct ieee80211_hw *hw,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_wiphy(hw->wiphy);
+
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ if (ctx->driver_present)
+ iter(hw, &ctx->conf, iter_data);
+}
+EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_mtx);