From: Johannes Berg Implement peer station management for NAN, i.e. support for adding, removing, and updating NMI and NDI stations. Signed-off-by: Johannes Berg Co-developed-by: Miri Korenblit Signed-off-by: Miri Korenblit --- .../net/wireless/intel/iwlwifi/mld/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mld/nan.c | 35 +++++-- drivers/net/wireless/intel/iwlwifi/mld/sta.c | 96 +++++++++++++++---- drivers/net/wireless/intel/iwlwifi/mld/sta.h | 4 +- 4 files changed, 109 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c index 3c84c6b0faaa..6b4b2683cd1e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -1841,7 +1841,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, new_state == IEEE80211_STA_AUTHORIZED) { ret = 0; - if (!sta->tdls) { + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { mld_vif->authorized = true; /* Ensure any block due to a non-BSS link is synced */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c index eba79aca8c06..53d39717deab 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c @@ -464,19 +464,17 @@ static int iwl_mld_nan_link_set_active(struct iwl_mld *mld, } static void iwl_mld_nan_link_remove(struct iwl_mld *mld, - struct iwl_mld_nan_link *nan_link) + struct iwl_mld_nan_link *nan_link, + u32 link_id) { struct iwl_link_config_cmd cmd = { - .link_id = cpu_to_le32(nan_link->fw_id), + .link_id = cpu_to_le32(link_id), .phy_id = cpu_to_le32(FW_CTXT_ID_INVALID), }; - if (WARN_ON_ONCE(nan_link->fw_id == FW_CTXT_ID_INVALID)) - return; - iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); - RCU_INIT_POINTER(mld->fw_id_to_bss_conf[nan_link->fw_id], NULL); + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link_id], NULL); nan_link->fw_id = FW_CTXT_ID_INVALID; nan_link->active = false; nan_link->chanctx = NULL; @@ -518,6 +516,8 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld, struct ieee80211_nan_channel **slots = sched_cfg->schedule; bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {}; struct iwl_mld_nan_link *nan_link; + unsigned long remove_link_ids = 0; + bool added_links = false; bool empty_schedule = true; int ret, i; @@ -588,6 +588,7 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld, /* we have a link, activate it */ if (links[i]) { + added_links = true; link_used[links[i]->fw_id] = true; iwl_mld_nan_link_set_active(mld, links[i], true); } @@ -626,14 +627,32 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld, if (ret) IWL_ERR(mld, "NAN: failed to update schedule (%d)\n", ret); - /* delete unused links */ + /* 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_remove(mld, nan_link); + remove_link_ids |= BIT(nan_link->fw_id); + /* mark unused for STA updates */ + nan_link->fw_id = FW_CTXT_ID_INVALID; + } + } + + if (added_links || remove_link_ids) { + struct ieee80211_sta *sta; + + 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_NMI || + mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) + iwl_mld_add_modify_sta_cmd(mld, &sta->deflink); } } + /* delete unused links */ + for_each_set_bit(i, &remove_link_ids, ARRAY_SIZE(mld_vif->nan.links)) + iwl_mld_nan_link_remove(mld, &mld_vif->nan.links[i], i); + /* remove MAC if needed */ if (!previously_empty_schedule && empty_schedule) { /* must have been added */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c index 4c97d12ce2d0..f794f80b0fdd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #include @@ -13,6 +13,7 @@ #include "key.h" #include "agg.h" #include "tlc.h" +#include "nan.h" #include "fw/api/sta.h" #include "fw/api/mac.h" #include "fw/api/rx.h" @@ -43,13 +44,13 @@ int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, static void iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, - struct ieee80211_bss_conf *link, + bool is_6ghz, __le32 *tx_ampdu_max_size, __le32 *tx_ampdu_spacing) { u32 agg_size = 0, mpdu_dens = 0; - if (WARN_ON(!link_sta || !link)) + if (WARN_ON(!link_sta)) return; /* Note that we always use only legacy & highest supported PPDUs, so @@ -63,7 +64,7 @@ iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, mpdu_dens = link_sta->ht_cap.ampdu_density; } - if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { + if (is_6ghz) { /* overwrite HT values on 6 GHz */ mpdu_dens = le16_get_bits(link_sta->he_6ghz_capa.capa, @@ -439,29 +440,56 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, return ret; } -static int -iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, - struct ieee80211_link_sta *link_sta) +int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) { struct ieee80211_sta *sta = link_sta->sta; struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); - struct ieee80211_bss_conf *link; - struct iwl_mld_link *mld_link; struct iwl_sta_cfg_cmd cmd = {}; int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + bool is_6ghz, uora_exists; + u32 link_mask; lockdep_assert_wiphy(mld->wiphy); - link = link_conf_dereference_protected(mld_sta->vif, - link_sta->link_id); + if (WARN_ON(fw_id < 0)) + return -EINVAL; + + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI || + mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) { + struct iwl_mld_nan_link *nan_link; + struct iwl_mld_vif *nan_dev; - mld_link = iwl_mld_link_from_mac80211(link); + is_6ghz = false; + uora_exists = false; - if (WARN_ON(!link || !mld_link) || fw_id < 0) - return -EINVAL; + if (WARN_ON(!mld->nan_device_vif)) + return -EINVAL; + + nan_dev = iwl_mld_vif_from_mac80211(mld->nan_device_vif); + + link_mask = 0; + + for_each_mld_nan_valid_link(nan_dev, nan_link) + link_mask |= BIT(nan_link->fw_id); + } else { + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!link || !mld_link)) + return -EINVAL; + + link_mask = BIT(mld_link->fw_id); + is_6ghz = link->chanreq.oper.chan->band == NL80211_BAND_6GHZ; + uora_exists = link->uora_exists; + } cmd.sta_id = cpu_to_le32(fw_id); - cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id)); + cmd.link_mask = cpu_to_le32(link_mask); cmd.station_type = cpu_to_le32(mld_sta->sta_type); memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN); @@ -499,7 +527,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, break; } - iwl_mld_fill_ampdu_size_and_dens(link_sta, link, + iwl_mld_fill_ampdu_size_and_dens(link_sta, is_6ghz, &cmd.tx_ampdu_max_size, &cmd.tx_ampdu_spacing); @@ -511,7 +539,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, if (link_sta->he_cap.has_he) { cmd.trig_rnd_alloc = - cpu_to_le32(link->uora_exists ? 1 : 0); + cpu_to_le32(uora_exists ? 1 : 0); /* PPE Thresholds */ iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext); @@ -525,6 +553,25 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, cmd.ack_enabled = cpu_to_le32(1); } + if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) { + struct ieee80211_sta *nmi_sta = + wiphy_dereference(mld->wiphy, sta->nmi); + int nmi_fw_id; + + /* copy the local NDI address */ + ether_addr_copy(cmd.ndi_local_addr, mld_sta->vif->addr); + + if (WARN_ON(!nmi_sta)) + return -EINVAL; + + nmi_fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, + &nmi_sta->deflink); + if (nmi_fw_id < 0) + return -EINVAL; + + cmd.nmi_sta_id = (u8) nmi_fw_id; + } + return iwl_mld_send_sta_cmd(mld, &cmd); } @@ -759,10 +806,23 @@ int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, { struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct ieee80211_link_sta *link_sta; + enum iwl_fw_sta_type type; int link_id; int ret; - ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER); + switch (vif->type) { + case NL80211_IFTYPE_NAN: + type = STATION_TYPE_NAN_PEER_NMI; + break; + case NL80211_IFTYPE_NAN_DATA: + type = STATION_TYPE_NAN_PEER_NDI; + break; + default: + type = STATION_TYPE_PEER; + break; + } + + ret = iwl_mld_init_sta(mld, sta, vif, type); if (ret) return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h index 36288c2fb38c..13644ffd185d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024-2025 Intel Corporation + * Copyright (C) 2024-2026 Intel Corporation */ #ifndef __iwl_mld_sta_h__ @@ -195,6 +195,8 @@ void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta); int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta); u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta); +int iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta); int iwl_mld_update_all_link_stations(struct iwl_mld *mld, struct ieee80211_sta *sta); void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta); -- 2.34.1