wifi: iwlwifi: mld: implement NAN peer station management

Implement peer station management for NAN, i.e. support for adding,
removing, and updating NMI and NDI stations.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Co-developed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260510234534.cbf6bac4744f.I3bd01266e47f24fb7f1240db62ac3bd47c479127@changeid
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
This commit is contained in:
Johannes Berg 2026-05-10 23:48:32 +03:00 committed by Miri Korenblit
parent cfc84d1710
commit 03779b6966
4 changed files with 110 additions and 29 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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 <linux/ieee80211.h>
@ -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);
mld_link = iwl_mld_link_from_mac80211(link);
if (WARN_ON(!link || !mld_link) || fw_id < 0)
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;
is_6ghz = false;
uora_exists = false;
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;

View File

@ -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);