wifi: mac80211: mlme: use NPCA chandef if capable

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>
This commit is contained in:
Johannes Berg 2026-04-28 11:25:38 +02:00
parent a731e2fc26
commit 5af8f06349
4 changed files with 142 additions and 1 deletions

View 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

View 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,

View File

@ -401,6 +401,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
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 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
"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;
}

View 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 |