]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: mac80211: mlme: use NPCA chandef if capable
authorJohannes Berg <johannes.berg@intel.com>
Tue, 28 Apr 2026 09:25:38 +0000 (11:25 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 May 2026 12:49:03 +0000 (14:49 +0200)
If the device is capable, parse the AP chandef with NPCA.
Also advertise the other NPCA operational parameters to the
underlying driver and track if they change (though not with
BSS critical update etc. yet)

Since NPCA can only be enabled when the chanctx isn't shared,
the channel context code needs to clear/set npca.enabled in
the per-link configuration, except during association since
we can't enable NPCA before having completed association. In
this case, set npca.enabled during the association process.

Link: https://patch.msgid.link/20260428112708.eb1e42c0b6d7.I0acd8445d4600363afb8430922531450399d0fab@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/chan.c
net/mac80211/mlme.c
net/mac80211/util.c

index ae7fd44e17b5410cd50c066ea6436a7c8615f3a8..4fb579805e0fbf8fdb9c0e565911a33f4e49f2fd 100644 (file)
@@ -373,6 +373,7 @@ struct ieee80211_vif_chanctx_switch {
  * @BSS_CHANGED_MLD_TTLM: negotiated TID to link mapping was changed
  * @BSS_CHANGED_TPE: transmit power envelope changed
  * @BSS_CHANGED_NAN_LOCAL_SCHED: NAN local schedule changed (NAN mode only)
+ * @BSS_CHANGED_NPCA: NPCA parameters changed
  */
 enum ieee80211_bss_change {
        BSS_CHANGED_ASSOC               = 1<<0,
@@ -411,6 +412,7 @@ enum ieee80211_bss_change {
        BSS_CHANGED_MLD_TTLM            = BIT_ULL(34),
        BSS_CHANGED_TPE                 = BIT_ULL(35),
        BSS_CHANGED_NAN_LOCAL_SCHED     = BIT_ULL(36),
+       BSS_CHANGED_NPCA                = BIT_ULL(37),
 
        /* when adding here, make sure to change ieee80211_reconfig */
 };
@@ -596,6 +598,26 @@ struct ieee80211_parsed_tpe {
        struct ieee80211_parsed_tpe_psd psd_local[2], psd_reg_client[2];
 };
 
+/**
+ * struct ieee80211_bss_npca_params - NPCA parameters
+ * @min_dur_thresh: NPCA minimum duration threshold (512 + 128*n usec)
+ * @switch_delay: NPCA switch delay (units of 4 usec)
+ * @switch_back_delay: NPCA switch back delay (units of 4 usec)
+ * @init_qsrc: initial QSRC value
+ * @moplen: indicates MOPLEN NPCA is permitted in the BSS
+ * @enabled: NPCA is enabled for this link
+ *
+ * Note: the individual values (except @enabled) are in spec representation.
+ */
+struct ieee80211_bss_npca_params {
+       u32 min_dur_thresh:4,
+           switch_delay:6,
+           switch_back_delay:6,
+           init_qsrc:2,
+           moplen:1,
+           enabled:1;
+};
+
 /**
  * struct ieee80211_bss_conf - holds the BSS's changing parameters
  *
@@ -770,6 +792,7 @@ struct ieee80211_parsed_tpe {
  *     (as opposed to hearing its value from another link's beacon).
  * @s1g_long_beacon_period: number of beacon intervals between each long
  *     beacon transmission.
+ * @npca: NPCA parameters
  */
 struct ieee80211_bss_conf {
        struct ieee80211_vif *vif;
@@ -873,6 +896,8 @@ struct ieee80211_bss_conf {
        u8 bss_param_ch_cnt_link_id;
 
        u8 s1g_long_beacon_period;
+
+       struct ieee80211_bss_npca_params npca;
 };
 
 #define IEEE80211_NAN_MAX_CHANNELS 3
index 5e24988ef5614cd9511edc55c600f28e09bed776..b9d563f927daa3da28e38b1f5c83bce991367d86 100644 (file)
@@ -770,6 +770,38 @@ void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
        _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
 }
 
+static void
+ieee80211_chanctx_update_npca_links(struct ieee80211_local *local,
+                                   struct ieee80211_chanctx *ctx,
+                                   bool enable)
+{
+       struct ieee80211_chanctx_user_iter iter;
+
+       if (!!ctx->conf.def.npca_chan != enable)
+               return;
+
+       for_each_chanctx_user_assigned(local, ctx, &iter) {
+               if (!iter.link)
+                       continue;
+               if (!iter.sdata->vif.cfg.assoc)
+                       continue;
+
+               if (enable) {
+                       if (!iter.link->conf->chanreq.oper.npca_chan)
+                               continue;
+               } else {
+                       if (!iter.link->conf->npca.enabled)
+                               continue;
+               }
+
+               iter.link->conf->npca.enabled = enable;
+               drv_link_info_changed(local, iter.sdata,
+                                     iter.link->conf,
+                                     iter.link->link_id,
+                                     BSS_CHANGED_NPCA);
+       }
+}
+
 static void _ieee80211_change_chanctx(struct ieee80211_local *local,
                                      struct ieee80211_chanctx *ctx,
                                      struct ieee80211_chanctx *old_ctx,
@@ -845,10 +877,16 @@ static void _ieee80211_change_chanctx(struct ieee80211_local *local,
 
        ieee80211_add_wbrf(local, &ctx->conf.def);
 
+       /* disable NPCA on the link using it */
+       ieee80211_chanctx_update_npca_links(local, ctx, false);
+
        drv_change_chanctx(local, ctx, changed);
 
        /* check if BW is wider */
        ieee80211_chan_bw_change(local, old_ctx, false, false);
+
+       /* enable NPCA on the link that requested it */
+       ieee80211_chanctx_update_npca_links(local, ctx, true);
 }
 
 static void ieee80211_change_chanctx(struct ieee80211_local *local,
index 5be390de67564143c38d68ad8e1d3b61284daca6..3db8a499a1c8285dae4a45cee62ee0f5d3445485 100644 (file)
@@ -401,6 +401,7 @@ check_uhr:
                                       elems->uhr_operation_len,
                                       false)) {
                struct cfg80211_chan_def npca_chandef = *chandef;
+               const struct ieee80211_sta_uhr_cap *uhr_cap;
                const struct ieee80211_uhr_npca_info *npca;
 
                npca = ieee80211_uhr_npca_info(uhr_oper);
@@ -411,6 +412,14 @@ check_uhr:
                                   "AP UHR NPCA settings invalid, disabling UHR\n");
                        return IEEE80211_CONN_MODE_EHT;
                }
+
+               uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
+               /* can't happen since we must have UHR to parse the elems */
+               if (WARN_ON(!uhr_cap))
+                       return IEEE80211_CONN_MODE_EHT;
+
+               if (uhr_cap->mac.mac_cap[0] & IEEE80211_UHR_MAC_CAP0_NPCA_SUPP)
+                       *chandef = npca_chandef;
        }
 
        return IEEE80211_CONN_MODE_UHR;
@@ -1320,6 +1329,7 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
                .conn = &link->u.mgd.conn,
        };
        struct ieee80211_sub_if_data *sdata = link->sdata;
+       struct ieee80211_chanctx_conf *chanctx_conf;
        struct ieee80211_chan_req chanreq = {};
        enum ieee80211_conn_mode ap_mode;
        const char *frame;
@@ -1403,8 +1413,55 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
                }
        }
 
-       if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq))
+       /*
+        * Beacons don't have the full information - we need to track
+        * critical updates for NPCA parameters etc. For now only handle
+        * association and link reconfiguration response.
+        */
+       if (stype != IEEE80211_STYPE_BEACON &&
+           chanreq.oper.npca_chan && elems->uhr_operation &&
+           ieee80211_uhr_oper_size_ok((const void *)elems->uhr_operation,
+                                      elems->uhr_operation_len,
+                                      false)) {
+               const struct ieee80211_uhr_npca_info *npca;
+               struct ieee80211_bss_npca_params params = {};
+
+               npca = ieee80211_uhr_npca_info(elems->uhr_operation);
+               if (!npca) {
+                       chanreq.oper.npca_chan = NULL;
+                       chanreq.oper.npca_punctured = 0;
+               } else {
+                       params.min_dur_thresh =
+                               le32_get_bits(npca->params,
+                                             IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH);
+                       params.switch_delay =
+                               le32_get_bits(npca->params,
+                                             IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY);
+                       params.switch_back_delay =
+                               le32_get_bits(npca->params,
+                                             IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY);
+                       params.init_qsrc =
+                               le32_get_bits(npca->params,
+                                             IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC);
+                       params.moplen =
+                               le32_get_bits(npca->params,
+                                             IEEE80211_UHR_NPCA_PARAMS_MOPLEN);
+                       /* don't change the enabled bit yet */
+                       params.enabled = link->conf->npca.enabled;
+               }
+
+               if (memcmp(&params, &link->conf->npca, sizeof(params)) ||
+                   !update) {
+                       link->conf->npca = params;
+                       *changed |= BSS_CHANGED_NPCA;
+               }
+       }
+
+       if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq)) {
+               if (update)
+                       goto update_npca;
                return 0;
+       }
 
        link_info(link,
                  "AP %pM changed bandwidth in %s, new used config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n",
@@ -1451,6 +1508,24 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
        }
 
        cfg80211_schedule_channels_check(&sdata->wdev);
+
+update_npca:
+       chanctx_conf = sdata_dereference(link->conf->chanctx_conf, sdata);
+       /* must be non-NULL when update is true */
+       if (WARN_ON(!chanctx_conf))
+               return -EINVAL;
+
+       /*
+        * If we're not associated yet (i.e. in the process associating)
+        * then the chanctx code won't have enabled NPCA in the link, so
+        * if the channel context was set up with NPCA for us, enable it.
+        */
+       if (chanreq.oper.npca_chan && chanctx_conf->def.npca_chan &&
+           !link->conf->npca.enabled && !sdata->vif.cfg.assoc) {
+               link->conf->npca.enabled = true;
+               *changed |= BSS_CHANGED_NPCA;
+       }
+
        return 0;
 }
 
index 24537794256c30e496f0f0f3428763564eaf26c8..3184007f40c1ebf8195f4eda6d750ba857cbdb37 100644 (file)
@@ -2147,6 +2147,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
                                ieee80211_bss_info_change_notify(sdata,
                                                                 changed);
                        } else if (!WARN_ON(!link)) {
+                               if (link->conf->npca.enabled)
+                                       changed |= BSS_CHANGED_NPCA;
+
                                ieee80211_link_info_change_notify(sdata, link,
                                                                  changed);
                                changed = BSS_CHANGED_ASSOC |