wifi: iwlwifi: mld: add TLC support for NAN stations

In order to support NAN, TLC now has a station bitmap. Use this
and update TLC for the NAN stations accordingly whenever links
(and thus PHYs) change, and whenever else mac80211 might update
the rate scale information.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Co-developed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260511203428.ea4112d28331.Ie11049c12a4765a97f3e6dfd1ea4afb265c93817@changeid
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
This commit is contained in:
Johannes Berg 2026-05-11 20:36:24 +03:00 committed by Miri Korenblit
parent 2b7a3019b9
commit bc2f28ead2
2 changed files with 182 additions and 36 deletions

View File

@ -7,6 +7,7 @@
#include "iface.h"
#include "link.h"
#include "mlo.h"
#include "tlc.h"
#include "fw/api/mac-cfg.h"
#include "fw/api/mac.h"
#include "fw/api/rs.h"
@ -437,15 +438,26 @@ iwl_mld_nan_link_add(struct iwl_mld *mld,
}
static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct iwl_mld_nan_link *nan_link,
bool active)
{
struct iwl_link_config_cmd cmd;
struct ieee80211_sta *sta;
int ret;
if (nan_link->active == active)
return 0;
if (active) {
for_each_station(sta, mld->hw) {
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
iwl_mld_config_tlc(mld, mld_sta->vif, sta);
}
}
nan_link->active = active;
iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
@ -457,8 +469,10 @@ static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
return ret;
}
if (!active)
if (!active) {
nan_link->chanctx = NULL;
/* TODO: when FW is ready, Update phy in TLC to invalid after */
}
return 0;
}
@ -590,7 +604,7 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
if (links[i]) {
added_links = true;
link_used[links[i]->fw_id] = true;
iwl_mld_nan_link_set_active(mld, links[i], true);
iwl_mld_nan_link_set_active(mld, vif, links[i], true);
}
}
@ -630,7 +644,7 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
/* prepare stations for links we'll remove */
for_each_mld_nan_valid_link(mld_vif, nan_link) {
if (!link_used[nan_link->fw_id]) {
iwl_mld_nan_link_set_active(mld, nan_link, false);
iwl_mld_nan_link_set_active(mld, vif, nan_link, false);
remove_link_ids |= BIT(nan_link->fw_id);
/* mark unused for STA updates */
nan_link->fw_id = FW_CTXT_ID_INVALID;
@ -694,6 +708,15 @@ int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
.len[1] = sched->ulw_size,
.dataflags[1] = IWL_HCMD_DFL_DUP,
};
struct ieee80211_sta *iter;
/* Update TLC in case peer channels were added/removed/updated */
for_each_station(iter, mld->hw) {
struct iwl_mld_sta *tmp = iwl_mld_sta_from_mac80211(iter);
if (tmp->sta_type == STATION_TYPE_NAN_PEER_NDI)
iwl_mld_config_tlc(mld, tmp->vif, iter);
}
for (int i = 0; i < ARRAY_SIZE(sched->maps); i++) {
if (sched->maps[i].map_id == CFG80211_NAN_INVALID_MAP_ID)

View File

@ -529,7 +529,7 @@ static int iwl_mld_convert_tlc_cmd_to_v4(struct iwl_tlc_config_cmd *cmd,
static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct iwl_mld_sta *mld_sta,
int fw_sta_id, int phy_id,
int fw_sta_mask, int phy_id,
struct iwl_mld_tlc_sta_capa *capa)
{
struct iwl_tlc_config_cmd cmd = {
@ -550,10 +550,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
u8 cmd_size;
int ret;
if (fw_sta_id < 0)
return;
cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
cmd.sta_mask = cpu_to_le32(fw_sta_mask);
cmd.phy_id = cpu_to_le32(phy_id);
iwl_mld_fill_supp_rates(mld, vif, capa, &cmd);
@ -617,25 +614,27 @@ int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data)
return ret;
}
void iwl_mld_config_tlc_link(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta)
static void _iwl_mld_config_tlc_link(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_chanctx_conf *chan_ctx,
u32 fw_sta_mask,
struct ieee80211_link_sta *link_sta,
struct iwl_mld_tlc_sta_capa *capa)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
struct ieee80211_chanctx_conf *chan_ctx;
struct ieee80211_supported_band *sband;
struct iwl_mld_tlc_sta_capa capa = {};
unsigned long rates_bitmap;
enum nl80211_band band;
int fw_sta_id, i;
int i;
if (WARN_ON_ONCE(!mld_link))
return;
/*
* Note: Due to NAN, the chan_ctx here might not be the same as the
* vif->links[link_sta->link_id] one (which is NULL in NAN), so some
* care is needed in this function (and the 'capa' exists to make it
* less error-prone, the other functions need not worry about it.)
*/
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
if (WARN_ON_ONCE(!chan_ctx))
if (WARN_ON(!chan_ctx))
return;
band = chan_ctx->def.chan->band;
@ -653,25 +652,18 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
/* non HT rates */
rates_bitmap = link_sta->supp_rates[sband->band];
for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG)
capa.non_ht_rates |= BIT(sband->bitrates[i].hw_value);
capa->non_ht_rates |= BIT(sband->bitrates[i].hw_value);
capa.rx_nss = link_sta->rx_nss;
capa.smps_mode = link_sta->smps_mode;
capa.bandwidth = link_sta->bandwidth;
capa.max_amsdu_len = link_sta->agg.max_amsdu_len;
capa.ht_cap = &link_sta->ht_cap;
capa.vht_cap = &link_sta->vht_cap;
capa.he_cap = &link_sta->he_cap;
capa.eht_cap = &link_sta->eht_cap;
capa.uhr_cap = &link_sta->uhr_cap;
capa.own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
capa.own_eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif);
capa.own_uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, vif);
capa->max_amsdu_len = link_sta->agg.max_amsdu_len;
capa->ht_cap = &link_sta->ht_cap;
capa->vht_cap = &link_sta->vht_cap;
capa->he_cap = &link_sta->he_cap;
capa->eht_cap = &link_sta->eht_cap;
capa->uhr_cap = &link_sta->uhr_cap;
fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
iwl_mld_send_tlc_cmd(mld, vif, mld_sta, fw_sta_id,
iwl_mld_send_tlc_cmd(mld, vif, mld_sta, fw_sta_mask,
iwl_mld_phy_from_mac80211(chan_ctx)->fw_id,
&capa);
capa);
}
void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
@ -717,6 +709,136 @@ void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
}
}
static void iwl_mld_config_tlc_nan(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_link_sta *link_sta,
int fw_sta_id)
{
struct ieee80211_nan_peer_sched *sched;
struct iwl_mld_nan_link *nan_link;
struct ieee80211_sta *nmi, *iter;
struct iwl_mld_vif *nan_mld_vif;
u32 fw_sta_mask;
nmi = wiphy_dereference(mld->wiphy, link_sta->sta->nmi);
if (WARN_ON(!nmi))
return;
sched = nmi->nan_sched;
/* This sta might not be in the list yet as it is just getting added */
fw_sta_mask = BIT(fw_sta_id);
for_each_station(iter, mld->hw) {
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(iter);
struct ieee80211_sta *iter_nmi;
iter_nmi = wiphy_dereference(mld->wiphy, iter->nmi);
if (iter_nmi == nmi)
fw_sta_mask |= BIT(mld_sta->deflink.fw_id);
}
if (WARN_ON(!mld->nan_device_vif))
return;
nan_mld_vif = iwl_mld_vif_from_mac80211(mld->nan_device_vif);
for_each_mld_nan_valid_link(nan_mld_vif, nan_link) {
struct ieee80211_chanctx_conf *chan_ctx;
struct iwl_mld_tlc_sta_capa capa = {};
struct cfg80211_chan_def *chandef;
bool common_channel = false;
chan_ctx = nan_link->chanctx;
if (!chan_ctx)
continue;
chandef = iwl_mld_get_chandef_from_chanctx(mld, chan_ctx);
capa.smps_mode = IEEE80211_SMPS_OFF; /* always off */
capa.rx_nss = 2; /* maximum we support */
capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width);
for (int j = 0; j < (sched ? sched->n_channels : 0); j++) {
enum ieee80211_sta_rx_bandwidth rx_bw;
enum nl80211_chan_width width;
int chains;
if (sched->channels[j].chanctx_conf != chan_ctx)
continue;
common_channel = true;
width = sched->channels[j].chanreq.oper.width;
rx_bw = ieee80211_chan_width_to_rx_bw(width);
capa.bandwidth = min(capa.bandwidth, rx_bw);
chains = sched->channels[j].needed_rx_chains;
capa.rx_nss = min(capa.rx_nss, chains);
}
/* Apply minimum parameters if there is no common channel */
if (!common_channel) {
capa.bandwidth = IEEE80211_STA_RX_BW_20;
capa.rx_nss = 1;
}
capa.own_he_cap = &mld->wiphy->nan_capa.phy.he;
/* no EHT/UHR for NAN */
_iwl_mld_config_tlc_link(mld, vif, chan_ctx, fw_sta_mask,
link_sta, &capa);
}
}
void iwl_mld_config_tlc_link(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf,
struct ieee80211_link_sta *link_sta)
{
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta);
struct ieee80211_chanctx_conf *chan_ctx;
struct ieee80211_supported_band *sband;
struct iwl_mld_tlc_sta_capa capa = {};
struct cfg80211_chan_def *chandef;
enum nl80211_band band;
if (fw_sta_id < 0)
return;
if (vif->type == NL80211_IFTYPE_NAN)
return;
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
iwl_mld_config_tlc_nan(mld, vif, link_sta, fw_sta_id);
return;
}
if (WARN_ON_ONCE(!mld_link))
return;
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
if (WARN_ON_ONCE(!chan_ctx))
return;
chandef = &iwl_mld_phy_from_mac80211(chan_ctx)->chandef;
band = chandef->chan->band;
sband = mld->hw->wiphy->bands[band];
capa.smps_mode = link_sta->smps_mode;
capa.rx_nss = link_sta->rx_nss;
capa.bandwidth = ieee80211_chan_width_to_rx_bw(chandef->width);
capa.bandwidth = min(capa.bandwidth, link_sta->bandwidth);
capa.own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif);
capa.own_eht_cap = ieee80211_get_eht_iftype_cap_vif(sband, vif);
capa.own_uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, vif);
_iwl_mld_config_tlc_link(mld, vif, chan_ctx, BIT(fw_sta_id),
link_sta, &capa);
}
void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
@ -725,6 +847,7 @@ void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
lockdep_assert_wiphy(mld->wiphy);
/* Note: for NAN this is only the mac80211-level deflink */
for_each_vif_active_link(vif, link, link_id) {
struct ieee80211_link_sta *link_sta =
link_sta_dereference_check(sta, link_id);