Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: set RX_FLAG_RADIOTAP_TLV_AT_END generically
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

Instead of setting this flag in the iwl_mld_radiotap_put_tlv()
users, and not even all of them, set it inside the function.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/rx.c | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 6f40d6e47083..a2e586c6ea67 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.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 <net/mac80211.h>
@@ -791,6 +791,9 @@ static void *
 iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
 {
 	struct ieee80211_radiotap_tlv *tlv;
+	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
 
 	tlv = skb_put(skb, sizeof(*tlv));
 	tlv->type = cpu_to_le16(type);
@@ -1234,8 +1237,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
 
 	eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
 
-	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
-
 	switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
 	case 0:
 		if (he_type == RATE_MCS_HE_TYPE_TRIG) {
@@ -1329,7 +1330,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
 static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
 					    struct sk_buff *skb)
 {
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_radiotap_vendor_content *radiotap;
 	const u16 vendor_data_len = sizeof(mld->monitor.cur_aid);
 
@@ -1353,8 +1353,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
 	/* fill the data now */
 	memcpy(radiotap->data, &mld->monitor.cur_aid,
 	       sizeof(mld->monitor.cur_aid));
-
-	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
 }
 #endif
 
@@ -1362,7 +1360,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
 					      struct sk_buff *skb,
 					      struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
 {
-	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_radiotap_vendor_content *radiotap;
 	const u16 vendor_data_len = sizeof(*ntfy);
 
@@ -1382,8 +1379,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
 
 	/* fill the data now */
 	memcpy(radiotap->data, ntfy, vendor_data_len);
-
-	rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
 }
 
 static void
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 14/15] wifi: iwlwifi: reduce the number of prints upon firmware crash
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach, Eilon Rinat
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

When the firmware crashes, we print data to be able to know what
happened. The problem is that those prints became excessive as during
the course of the years, we added more data without ever removing the
prints that were no longer useful.
Instead of spamming the log with data that will not help anyone, limit
the prints to what is really needed.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Eilon Rinat <eilon.rinat@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/dump.c | 69 +-------------------
 1 file changed, 1 insertion(+), 68 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
index ddd714cff2f4..c2af66899a78 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2014 Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017 Intel Deutschland GmbH
  */
@@ -128,19 +128,11 @@ static void iwl_fwrt_dump_umac_error_log(struct iwl_fw_runtime *fwrt)
 
 	IWL_ERR(fwrt, "0x%08X | %s\n", table.error_id,
 		iwl_fw_lookup_assert_desc(table.error_id));
-	IWL_ERR(fwrt, "0x%08X | umac branchlink1\n", table.blink1);
-	IWL_ERR(fwrt, "0x%08X | umac branchlink2\n", table.blink2);
-	IWL_ERR(fwrt, "0x%08X | umac interruptlink1\n", table.ilink1);
 	IWL_ERR(fwrt, "0x%08X | umac interruptlink2\n", table.ilink2);
 	IWL_ERR(fwrt, "0x%08X | umac data1\n", table.data1);
 	IWL_ERR(fwrt, "0x%08X | umac data2\n", table.data2);
 	IWL_ERR(fwrt, "0x%08X | umac data3\n", table.data3);
-	IWL_ERR(fwrt, "0x%08X | umac major\n", table.umac_major);
-	IWL_ERR(fwrt, "0x%08X | umac minor\n", table.umac_minor);
-	IWL_ERR(fwrt, "0x%08X | frame pointer\n", table.frame_pointer);
-	IWL_ERR(fwrt, "0x%08X | stack pointer\n", table.stack_pointer);
 	IWL_ERR(fwrt, "0x%08X | last host cmd\n", table.cmd_header);
-	IWL_ERR(fwrt, "0x%08X | isr status reg\n", table.nic_isr_pref);
 }
 
 static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_num)
@@ -200,39 +192,10 @@ static void iwl_fwrt_dump_lmac_error_log(struct iwl_fw_runtime *fwrt, u8 lmac_nu
 
 	IWL_ERR(fwrt, "0x%08X | %-28s\n", table.error_id,
 		iwl_fw_lookup_assert_desc(table.error_id));
-	IWL_ERR(fwrt, "0x%08X | trm_hw_status0\n", table.trm_hw_status0);
-	IWL_ERR(fwrt, "0x%08X | trm_hw_status1\n", table.trm_hw_status1);
-	IWL_ERR(fwrt, "0x%08X | branchlink2\n", table.blink2);
-	IWL_ERR(fwrt, "0x%08X | interruptlink1\n", table.ilink1);
 	IWL_ERR(fwrt, "0x%08X | interruptlink2\n", table.ilink2);
 	IWL_ERR(fwrt, "0x%08X | data1\n", table.data1);
 	IWL_ERR(fwrt, "0x%08X | data2\n", table.data2);
 	IWL_ERR(fwrt, "0x%08X | data3\n", table.data3);
-	IWL_ERR(fwrt, "0x%08X | beacon time\n", table.bcon_time);
-	IWL_ERR(fwrt, "0x%08X | tsf low\n", table.tsf_low);
-	IWL_ERR(fwrt, "0x%08X | tsf hi\n", table.tsf_hi);
-	IWL_ERR(fwrt, "0x%08X | time gp1\n", table.gp1);
-	IWL_ERR(fwrt, "0x%08X | time gp2\n", table.gp2);
-	IWL_ERR(fwrt, "0x%08X | uCode revision type\n", table.fw_rev_type);
-	IWL_ERR(fwrt, "0x%08X | uCode version major\n", table.major);
-	IWL_ERR(fwrt, "0x%08X | uCode version minor\n", table.minor);
-	IWL_ERR(fwrt, "0x%08X | hw version\n", table.hw_ver);
-	IWL_ERR(fwrt, "0x%08X | board version\n", table.brd_ver);
-	IWL_ERR(fwrt, "0x%08X | hcmd\n", table.hcmd);
-	IWL_ERR(fwrt, "0x%08X | isr0\n", table.isr0);
-	IWL_ERR(fwrt, "0x%08X | isr1\n", table.isr1);
-	IWL_ERR(fwrt, "0x%08X | isr2\n", table.isr2);
-	IWL_ERR(fwrt, "0x%08X | isr3\n", table.isr3);
-	IWL_ERR(fwrt, "0x%08X | isr4\n", table.isr4);
-	IWL_ERR(fwrt, "0x%08X | last cmd Id\n", table.last_cmd_id);
-	IWL_ERR(fwrt, "0x%08X | wait_event\n", table.wait_event);
-	IWL_ERR(fwrt, "0x%08X | l2p_control\n", table.l2p_control);
-	IWL_ERR(fwrt, "0x%08X | l2p_duration\n", table.l2p_duration);
-	IWL_ERR(fwrt, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
-	IWL_ERR(fwrt, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
-	IWL_ERR(fwrt, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
-	IWL_ERR(fwrt, "0x%08X | timestamp\n", table.u_timestamp);
-	IWL_ERR(fwrt, "0x%08X | flow_handler\n", table.flow_handler);
 }
 
 /*
@@ -264,7 +227,6 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
 	struct iwl_trans *trans = fwrt->trans;
 	struct iwl_tcm_error_event_table table = {};
 	u32 base = fwrt->trans->dbg.tcm_error_event_table[idx];
-	int i;
 	u32 flag = idx ? IWL_ERROR_EVENT_TABLE_TCM2 :
 			 IWL_ERROR_EVENT_TABLE_TCM1;
 
@@ -275,23 +237,10 @@ static void iwl_fwrt_dump_tcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
 
 	IWL_ERR(fwrt, "TCM%d status:\n", idx + 1);
 	IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
-	IWL_ERR(fwrt, "0x%08X | tcm branchlink2\n", table.blink2);
-	IWL_ERR(fwrt, "0x%08X | tcm interruptlink1\n", table.ilink1);
 	IWL_ERR(fwrt, "0x%08X | tcm interruptlink2\n", table.ilink2);
 	IWL_ERR(fwrt, "0x%08X | tcm data1\n", table.data1);
 	IWL_ERR(fwrt, "0x%08X | tcm data2\n", table.data2);
 	IWL_ERR(fwrt, "0x%08X | tcm data3\n", table.data3);
-	IWL_ERR(fwrt, "0x%08X | tcm log PC\n", table.logpc);
-	IWL_ERR(fwrt, "0x%08X | tcm frame pointer\n", table.frame_pointer);
-	IWL_ERR(fwrt, "0x%08X | tcm stack pointer\n", table.stack_pointer);
-	IWL_ERR(fwrt, "0x%08X | tcm msg ID\n", table.msgid);
-	IWL_ERR(fwrt, "0x%08X | tcm ISR status\n", table.isr);
-	for (i = 0; i < ARRAY_SIZE(table.hw_status); i++)
-		IWL_ERR(fwrt, "0x%08X | tcm HW status[%d]\n",
-			table.hw_status[i], i);
-	for (i = 0; i < ARRAY_SIZE(table.sw_status); i++)
-		IWL_ERR(fwrt, "0x%08X | tcm SW status[%d]\n",
-			table.sw_status[i], i);
 }
 
 /*
@@ -338,26 +287,10 @@ static void iwl_fwrt_dump_rcm_error_log(struct iwl_fw_runtime *fwrt, int idx)
 
 	IWL_ERR(fwrt, "RCM%d status:\n", idx + 1);
 	IWL_ERR(fwrt, "0x%08X | error ID\n", table.error_id);
-	IWL_ERR(fwrt, "0x%08X | rcm branchlink2\n", table.blink2);
-	IWL_ERR(fwrt, "0x%08X | rcm interruptlink1\n", table.ilink1);
 	IWL_ERR(fwrt, "0x%08X | rcm interruptlink2\n", table.ilink2);
 	IWL_ERR(fwrt, "0x%08X | rcm data1\n", table.data1);
 	IWL_ERR(fwrt, "0x%08X | rcm data2\n", table.data2);
 	IWL_ERR(fwrt, "0x%08X | rcm data3\n", table.data3);
-	IWL_ERR(fwrt, "0x%08X | rcm log PC\n", table.logpc);
-	IWL_ERR(fwrt, "0x%08X | rcm frame pointer\n", table.frame_pointer);
-	IWL_ERR(fwrt, "0x%08X | rcm stack pointer\n", table.stack_pointer);
-	IWL_ERR(fwrt, "0x%08X | rcm msg ID\n", table.msgid);
-	IWL_ERR(fwrt, "0x%08X | rcm ISR status\n", table.isr);
-	IWL_ERR(fwrt, "0x%08X | frame HW status\n", table.frame_hw_status);
-	IWL_ERR(fwrt, "0x%08X | LMAC-to-RCM request mbox\n",
-		table.mbx_lmac_to_rcm_req);
-	IWL_ERR(fwrt, "0x%08X | RCM-to-LMAC request mbox\n",
-		table.mbx_rcm_to_lmac_req);
-	IWL_ERR(fwrt, "0x%08X | MAC header control\n", table.mh_ctl);
-	IWL_ERR(fwrt, "0x%08X | MAC header addr1 low\n", table.mh_addr1_lo);
-	IWL_ERR(fwrt, "0x%08X | MAC header info\n", table.mh_info);
-	IWL_ERR(fwrt, "0x%08X | MAC header error\n", table.mh_err);
 }
 
 static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt)
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 13/15] wifi: iwlwifi: fix the description of SESSION_PROTECTION_CMD
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

The struct has been renamed to iwl_session_prot_cmd.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index 25c57753ff34..b398c582b867 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
@@ -26,7 +26,7 @@ enum iwl_mac_conf_subcmd_ids {
 	 */
 	MISSED_VAP_NOTIF = 0xFA,
 	/**
-	 * @SESSION_PROTECTION_CMD: &struct iwl_mvm_session_prot_cmd
+	 * @SESSION_PROTECTION_CMD: &struct iwl_session_prot_cmd
 	 */
 	SESSION_PROTECTION_CMD = 0x5,
 	/**
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 12/15] wifi: iwlwifi: mld: introduce iwl_mld_vif_fw_id_valid
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

Introduce a helper function that checks if a vif fw id is valid, and warns
if it isn't.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/iface.h      | 11 ++++++++++-
 .../net/wireless/intel/iwlwifi/mld/low_latency.c    | 13 +++++++++----
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 0857ae28be8e..8dfc79fed253 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.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_iface_h__
 #define __iwl_mld_iface_h__
@@ -203,6 +203,15 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
 	return container_of((void *)mld_vif, struct ieee80211_vif, drv_priv);
 }
 
+/* Call only for interfaces that were added to the driver! */
+static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
+{
+	if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif)))
+		return false;
+
+	return true;
+}
+
 #define iwl_mld_link_dereference_check(mld_vif, link_id)		\
 	rcu_dereference_check((mld_vif)->link[link_id],			\
 			      lockdep_is_held(&mld_vif->mld->wiphy->mtx))
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
index d39dd36b08e3..a4ddc32e2860 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.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 "mld.h"
 #include "iface.h"
@@ -77,9 +77,12 @@ static void iwl_mld_low_latency_iter(void *_data, u8 *mac,
 	bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC;
 	bool low_latency;
 
-	if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result)))
+	if (!iwl_mld_vif_fw_id_valid(mld_vif))
 		return;
 
+	BUILD_BUG_ON(ARRAY_SIZE(mld->fw_id_to_vif) !=
+		     ARRAY_SIZE(mld->low_latency.result));
+
 	low_latency = mld->low_latency.result[mld_vif->fw_id];
 
 	if (prev != low_latency)
@@ -272,8 +275,10 @@ void iwl_mld_low_latency_update_counters(struct iwl_mld *mld,
 	if (WARN_ON_ONCE(!mld->low_latency.pkts_counters))
 		return;
 
-	if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) ||
-			 queue >= mld->trans->info.num_rxqs))
+	if (!iwl_mld_vif_fw_id_valid(mld_vif))
+		return;
+
+	if (WARN_ON_ONCE(queue >= mld->trans->info.num_rxqs))
 		return;
 
 	if (mld->low_latency.stopped)
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 11/15] wifi: iwlwifi: mld: block EMLSR during TDLS connections
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Avinash Bhatt, Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Avinash Bhatt <avinash.bhatt@intel.com>

TDLS (Tunneled Direct Link Setup) requires single-link operation
for direct peer-to-peer communication, which is incompatible with
EMLSR (Enhanced Multi-Link Single Radio) mode where the radio
switches between multiple links.

Block EMLSR when the first TDLS peer is added and unblock when
the last TDLS peer is removed. The block/unblock APIs handle
exiting EMLSR and triggering link selection automatically.

Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../net/wireless/intel/iwlwifi/mld/iface.h    |  2 ++
 .../net/wireless/intel/iwlwifi/mld/mac80211.c | 21 ++++++++++++++++---
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c  |  3 ++-
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 3e106c93f0db..0857ae28be8e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -33,6 +33,7 @@ enum iwl_mld_cca_40mhz_wa_status {
  *      there is an indication that a non-BSS interface is to be added.
  * @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile
  * @IWL_MLD_EMLSR_BLOCKED_NAN: NAN is preventing EMLSR.
+ * @IWL_MLD_EMLSR_BLOCKED_TDLS: TDLS connection is preventing EMLSR.
  */
 enum iwl_mld_emlsr_blocked {
 	IWL_MLD_EMLSR_BLOCKED_PREVENTION	= 0x1,
@@ -42,6 +43,7 @@ enum iwl_mld_emlsr_blocked {
 	IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS	= 0x10,
 	IWL_MLD_EMLSR_BLOCKED_TPT		= 0x20,
 	IWL_MLD_EMLSR_BLOCKED_NAN		= 0x40,
+	IWL_MLD_EMLSR_BLOCKED_TDLS		= 0x80,
 };
 
 /**
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 897602e5f9e7..da6fd7471568 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1759,10 +1759,19 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
 		if (ret)
 			return ret;
 
-		/* just added first TDLS STA, so disable PM */
-		if (sta->tdls && tdls_count == 0)
+		/* just added first TDLS STA, so disable PM and block EMLSR */
+		if (sta->tdls && tdls_count == 0) {
 			iwl_mld_update_mac_power(mld, vif, false);
 
+			/* TDLS requires single-link operation with
+			 * direct peer communication.
+			 * Block and exit EMLSR when TDLS is established.
+			 */
+			iwl_mld_block_emlsr(mld, vif,
+					    IWL_MLD_EMLSR_BLOCKED_TDLS,
+					    iwl_mld_get_primary_link(vif));
+		}
+
 		if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls)
 			mld_vif->ap_sta = sta;
 
@@ -1908,8 +1917,14 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
 		iwl_mld_remove_sta(mld, sta);
 
 		if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) {
-			/* just removed last TDLS STA, so enable PM */
+			/* just removed last TDLS STA, so enable PM
+			 * and unblock EMLSR
+			 */
 			iwl_mld_update_mac_power(mld, vif, false);
+
+			/* Unblock EMLSR when TDLS connection is torn down */
+			iwl_mld_unblock_emlsr(mld, vif,
+					      IWL_MLD_EMLSR_BLOCKED_TDLS);
 		}
 
 		if (sta->tdls) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index f693f92e42b4..9362e02d9e76 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -13,7 +13,8 @@
 	HOW(NON_BSS)			\
 	HOW(TMP_NON_BSS)		\
 	HOW(TPT)			\
-	HOW(NAN)
+	HOW(NAN)			\
+	HOW(TDLS)
 
 static const char *
 iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked)
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 10/15] wifi: iwlwifi: TLC_MNG_CONFIG_CMD can use several structures
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

Depending on the firmware API version, we can use different version of
the command. Mention them all in the description.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>x-iwlwifi-stack-dev: 324cf6bdf925d4c2d772df044c7e0b19bede0c4b
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index 6a6e11a57dbf..06370c161fe4 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -56,7 +56,8 @@ enum iwl_data_path_subcmd_ids {
 	RFH_QUEUE_CONFIG_CMD = 0xD,
 
 	/**
-	 * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4
+	 * @TLC_MNG_CONFIG_CMD: &struct iwl_tlc_config_cmd_v4 or
+	 *	&struct iwl_tlc_config_cmd_v5 or &struct iwl_tlc_config_cmd.
 	 */
 	TLC_MNG_CONFIG_CMD = 0xF,
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 09/15] wifi: iwlwifi: mld: update the TLC when we deactivate a link
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach, Miriam Rachel Korenblit
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

We hit a problem in the channel switch flow.
We had link 0 using PHY 0, so the TLC object in the firmware is using
PHY 0.
Then we switched channel, so mac80211 / iwlmld:
* deactivated link 0
* removed PHY 0
* added PHY 1
* modified link 0 to use PHY 1
* activated link 0.

The TLC object was not updated and the firmware was unhappy that the TLC
was still trying to use PHY 0.

Fix that by letting the TLC know about the PHY context before the link
activation.
When we are de-activating a link, let the TLC know so that it'll send a
TLC configuration command with an invalid PHY context to remove the
relationship between the TLC object and the PHY that is going to be
removed.

That last part is not implemented yet in the firmware, so leave this as
a TODO for now.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Tested-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
tested: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../net/wireless/intel/iwlwifi/mld/mac80211.c |  4 ++
 drivers/net/wireless/intel/iwlwifi/mld/tlc.c  | 50 ++++++++++++++++++-
 drivers/net/wireless/intel/iwlwifi/mld/tlc.h  |  3 ++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
index 358320051d1e..897602e5f9e7 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c
@@ -1148,6 +1148,8 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
 
 	/* Now activate the link */
 	if (iwl_mld_can_activate_link(mld, vif, link)) {
+		iwl_mld_tlc_update_phy(mld, vif, link);
+
 		ret = iwl_mld_activate_link(mld, link);
 		if (ret)
 			goto err;
@@ -1209,6 +1211,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
 
 	RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
 
+	iwl_mld_tlc_update_phy(mld, vif, link);
+
 	/* in the non-MLO case, remove/re-add the link to clean up FW state.
 	 * In MLO, it'll be done in drv_change_vif_link
 	 */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
index ede385909e38..78d6162d9297 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c
@@ -9,6 +9,7 @@
 #include "hcmd.h"
 #include "sta.h"
 #include "phy.h"
+#include "iface.h"
 
 #include "fw/api/rs.h"
 #include "fw/api/context.h"
@@ -530,6 +531,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
 				 struct ieee80211_bss_conf *link)
 {
 	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);
 	enum nl80211_band band = link->chanreq.oper.chan->band;
 	struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
 	const struct ieee80211_sta_he_cap *own_he_cap =
@@ -566,7 +568,10 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
 
 	cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
 
-	chan_ctx = rcu_dereference_wiphy(mld->wiphy, link->chanctx_conf);
+	if (WARN_ON_ONCE(!mld_link))
+		return;
+
+	chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
 	if (WARN_ON(!chan_ctx))
 		return;
 
@@ -658,6 +663,49 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
 	iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf);
 }
 
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+			    struct ieee80211_bss_conf *link_conf)
+{
+	struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+	struct ieee80211_chanctx_conf *chan_ctx;
+	int link_id = link_conf->link_id;
+	struct ieee80211_sta *sta;
+
+	lockdep_assert_wiphy(mld->wiphy);
+
+	if (WARN_ON(!mld_link))
+		return;
+
+	chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
+
+	for_each_station(sta, mld->hw) {
+		struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+		struct iwl_mld_link_sta *mld_link_sta;
+		struct ieee80211_link_sta *link_sta;
+
+		if (mld_sta->vif != vif)
+			continue;
+
+		link_sta = link_sta_dereference_protected(sta, link_id);
+		if (!link_sta)
+			continue;
+
+		mld_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
+								  link_id);
+
+		/* In recovery flow, the station may not be (yet) in the
+		 * firmware, don't send a TLC command for a station the
+		 * firmware does not know.
+		 */
+		if (!mld_link_sta || !mld_link_sta->in_fw)
+			continue;
+
+		if (chan_ctx)
+			iwl_mld_config_tlc_link(mld, vif, link_conf, link_sta);
+		/* TODO: else, remove the TLC object in the firmware */
+	}
+}
+
 void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
 			struct ieee80211_sta *sta)
 {
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
index c32f42e8840b..c7ff209c9ab6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h
@@ -20,4 +20,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
 
 int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data);
 
+void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
+			    struct ieee80211_bss_conf *link_conf);
+
 #endif /* __iwl_mld_tlc_h__ */
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 08/15] wifi: iwlwifi: add a macro for max FW links
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

Currently we use IWL_FW_MAX_LINK_ID + 1 to indicate the maximum number
of link that the fw supports. This is a bit confusing.
Add a macro that indicates the number if maximum links that the FW
supports and use it instead.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h  | 1 +
 drivers/net/wireless/intel/iwlwifi/fw/api/stats.h    | 5 ++---
 drivers/net/wireless/intel/iwlwifi/iwl-drv.c         | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/mld.h         | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.c         | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.h         | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c | 2 +-
 7 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index 180eb8227582..25c57753ff34 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -709,6 +709,7 @@ struct iwl_link_config_cmd {
  */
 #define IWL_FW_MAX_ACTIVE_LINKS_NUM 2
 #define IWL_FW_MAX_LINK_ID 3
+#define IWL_FW_MAX_LINKS IWL_FW_MAX_LINK_ID + 1
 
 /**
  * enum iwl_fw_sta_type - FW station types
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
index 8d9a5058d5a5..68983f6a0026 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h
@@ -598,7 +598,6 @@ struct iwl_stats_ntfy_per_sta {
 } __packed; /* STATISTICS_NTFY_PER_STA_API_S_VER_1 */
 
 #define IWL_STATS_MAX_PHY_OPERATIONAL 3
-#define IWL_STATS_MAX_FW_LINKS	(IWL_FW_MAX_LINK_ID + 1)
 
 /**
  * struct iwl_system_statistics_notif_oper - statistics notification
@@ -610,7 +609,7 @@ struct iwl_stats_ntfy_per_sta {
  */
 struct iwl_system_statistics_notif_oper {
 	__le32 time_stamp;
-	struct iwl_stats_ntfy_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+	struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
 	struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
 	struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
 } __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
@@ -624,7 +623,7 @@ struct iwl_system_statistics_notif_oper {
  */
 struct iwl_system_statistics_part1_notif_oper {
 	__le32 time_stamp;
-	struct iwl_stats_ntfy_part1_per_link per_link[IWL_STATS_MAX_FW_LINKS];
+	struct iwl_stats_ntfy_part1_per_link per_link[IWL_FW_MAX_LINKS];
 	__le32 per_phy_crc_error_stats[IWL_STATS_MAX_PHY_OPERATIONAL];
 } __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_PART1_API_S_VER_4 */
 
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
index 4cdd0fe1b788..d5ded4d3a30b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c
@@ -1315,7 +1315,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
 			if (tlv_len != sizeof(u32))
 				goto invalid_tlv_len;
 			if (le32_to_cpup((const __le32 *)tlv_data) >
-			    IWL_FW_MAX_LINK_ID + 1) {
+			    IWL_FW_MAX_LINKS) {
 				IWL_ERR(drv,
 					"%d is an invalid number of links\n",
 					le32_to_cpup((const __le32 *)tlv_data));
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index ea3d1fab6f46..606cb64f8ea4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -205,7 +205,7 @@
 struct iwl_mld {
 	/* Add here fields that need clean up on restart */
 	struct_group(zeroed_on_hw_restart,
-		struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1];
+		struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINKS];
 		struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER];
 		struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES];
 		u8 used_phy_ids: NUM_PHY_CTX;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index eda2cbbb3b30..4c97d12ce2d0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -938,7 +938,7 @@ static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue,
 	if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT))
 		goto unlock;
 
-	for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++)
+	for (int i = 0; i < IWL_FW_MAX_LINKS; i++)
 		total_mpdus += tx ? queue_counter->per_link[i].tx :
 				    queue_counter->per_link[i].rx;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index 5f6c440bf058..36288c2fb38c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -89,7 +89,7 @@ struct iwl_mld_per_link_mpdu_counter {
  */
 struct iwl_mld_per_q_mpdu_counter {
 	spinlock_t lock;
-	struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1];
+	struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINKS];
 	unsigned long window_start_time;
 } ____cacheline_aligned_in_smp;
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
index 176dbbf4c643..dce747270167 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
@@ -42,7 +42,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
 	iwl_construct_mld(mld, trans, cfg, fw, hw, NULL);
 
 	fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
-	fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1;
+	fw->ucode_capa.num_links = IWL_FW_MAX_LINKS;
 
 	mld->fwrt.trans = trans;
 	mld->fwrt.fw = fw;
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 07/15] wifi: iwlwifi: mld: always assign a fw id to a vif
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

We used to have a fw id assignment in iwl_mld_init_vif since all interface
types that were added to the driver was immediately added to the FW as
well.
Since NAN was introduced, this is no longer the case - the NAN interface
is not added to the fw until a local schedule is configured.

For this vif we don't assign a fw id so it is 0 by default.
But later, when the vif is removed from the driver, we think that it has
a valid fw id (0) and we point fw_id_to_vif[0] to NULL.
fw_id_to_vif[0] might actually point to another vif with a valid fw id
0. In this case, we end up messing fw_id_to_vif.

Fix this by initializing a vif with a special invalid fw id, and by
exiting iwl_mld_rm_vif early for NAN interfaces.

Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/iface.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index d3b850259569..5c435d6ffa90 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -445,6 +445,7 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
 	lockdep_assert_wiphy(mld->wiphy);
 
 	mld_vif->mld = mld;
+	mld_vif->fw_id = IWL_MLD_INVALID_FW_ID;
 	mld_vif->roc_activity = ROC_NUM_ACTIVITIES;
 
 	if (!mld->fw_status.in_hw_restart) {
@@ -492,6 +493,10 @@ void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
 
 	lockdep_assert_wiphy(mld->wiphy);
 
+	/* NAN interface type is not known to FW */
+	if (vif->type == NL80211_IFTYPE_NAN)
+		return;
+
 	iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE);
 
 	if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif)))
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 06/15] wifi: iwlwifi: mld: use the dedicated helper to extract a link
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

There is a helper, iwl_mld_fw_id_to_link_conf, that converts a fw link
id into the bss_conf structure. Use it in two more places instead of
retrieving the bss_conf directly from the fw-id-to-bss_conf mapping array.

This required changing the loop bound in iwl_mld_process_per_link_stats()
to ucode_capa.num_links, to avoid hitting a IWL_FW_CHECK for link ids
> ucode_capa.num_links and < ARRAY_SIZE(fw_id_to_bss_conf), but this
change makes sense anyway (there is no reason to iterate links that
cannot be valid).

Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/scan.c  | 4 +---
 drivers/net/wireless/intel/iwlwifi/mld/stats.c | 5 ++---
 2 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
index 17e0b13b5ce8..7ed107fb0e8d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/scan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c
@@ -2055,9 +2055,7 @@ void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld,
 		struct ieee80211_bss_conf *link_conf = NULL;
 
 		if (fw_link_id != IWL_MLD_INVALID_FW_ID)
-			link_conf =
-				wiphy_dereference(mld->wiphy,
-						  mld->fw_id_to_bss_conf[fw_link_id]);
+			link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id);
 
 		/* It is possible that by the time the scan is complete the
 		 * link was already removed and is not valid.
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 9b3149b9d2c2..54eb0ead78ee 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -431,14 +431,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
 	u32 total_airtime_usec = 0;
 
 	for (u32 fw_id = 0;
-	     fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf);
+	     fw_id < mld->fw->ucode_capa.num_links;
 	     fw_id++) {
 		const struct iwl_stats_ntfy_per_link *link_stats;
 		struct ieee80211_bss_conf *bss_conf;
 		int sig;
 
-		bss_conf = wiphy_dereference(mld->wiphy,
-					     mld->fw_id_to_bss_conf[fw_id]);
+		bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id);
 		if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION)
 			continue;
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 05/15] wifi: iwlwifi: add MAC context command version 4
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg, Emmanuel Grumbach
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

Due to NAN additions, this command needs to grow. In iwlmvm
we just need to use the old _v3 (or v2) version, but iwlmld
needs to handle the difference and send both.  Do that as a
first step towards adding NAN support.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../wireless/intel/iwlwifi/fw/api/mac-cfg.h   | 64 +++++++++++++++++--
 .../net/wireless/intel/iwlwifi/fw/api/mac.h   |  6 +-
 .../net/wireless/intel/iwlwifi/mld/iface.c    | 13 +++-
 .../net/wireless/intel/iwlwifi/mvm/mld-mac.c  | 18 +++---
 4 files changed, 81 insertions(+), 20 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index 2e3f437686b9..180eb8227582 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -34,7 +34,8 @@ enum iwl_mac_conf_subcmd_ids {
 	 */
 	CANCEL_CHANNEL_SWITCH_CMD = 0x6,
 	/**
-	 * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd
+	 * @MAC_CONFIG_CMD: &struct iwl_mac_config_cmd_v3 or
+	 *	&struct iwl_mac_config_cmd
 	 */
 	MAC_CONFIG_CMD = 0x8,
 	/**
@@ -357,7 +358,7 @@ struct iwl_mac_wifi_gen_support {
 } __packed;
 
 /**
- * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ * struct iwl_mac_config_cmd_v3 - command structure to configure MAC contexts in
  *	MLD API for versions 2 and 3
  * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
  *
@@ -376,7 +377,7 @@ struct iwl_mac_wifi_gen_support {
  * @client: client mac data
  * @p2p_dev: mac data for p2p device
  */
-struct iwl_mac_config_cmd {
+struct iwl_mac_config_cmd_v3 {
 	__le32 id_and_color;
 	__le32 action;
 	/* MAC_CONTEXT_TYPE_API_E */
@@ -394,7 +395,62 @@ struct iwl_mac_config_cmd {
 		struct iwl_mac_client_data client;
 		struct iwl_mac_p2p_dev_data p2p_dev;
 	};
-} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2, _VER_3 */
+
+/**
+ * struct iwl_mac_nan_data - NAN specific MAC data
+ * @ndi_addrs: extra NDI addresses being used
+ * @ndi_addrs_count: number of extra NDI addresses
+ */
+struct iwl_mac_nan_data {
+	struct {
+		u8 addr[ETH_ALEN];
+		__le16 reserved;
+	} __packed ndi_addrs[2];
+	__le32 ndi_addrs_count;
+} __packed; /* MAC_CONTEXT_CONFIG_NAN_DATA_API_S_VER_1 */
+
+/**
+ * struct iwl_mac_config_cmd - command structure to configure MAC contexts in
+ *	MLD API for versions 4
+ * ( MAC_CONTEXT_CONFIG_CMD = 0x8 )
+ *
+ * @id_and_color: ID and color of the MAC
+ * @action: action to perform, see &enum iwl_ctxt_action
+ * @mac_type: one of &enum iwl_mac_types
+ * @local_mld_addr: mld address
+ * @reserved_for_local_mld_addr: reserved
+ * @filter_flags: combination of &enum iwl_mac_config_filter_flags
+ * @wifi_gen_v2: he/eht parameters as in cmd version 2
+ * @wifi_gen: he/eht/uhr parameters as in cmd version 3
+ * @nic_not_ack_enabled: mark that the NIC doesn't support receiving
+ *	ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG).
+ *	If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0
+ *	len delim to determine if AGG or single.
+ * @client: client mac data
+ * @p2p_dev: mac data for p2p device
+ * @nan: NAN specific data (NAN data interface addresses)
+ */
+struct iwl_mac_config_cmd {
+	__le32 id_and_color;
+	__le32 action;
+	/* MAC_CONTEXT_TYPE_API_E */
+	__le32 mac_type;
+	u8 local_mld_addr[6];
+	__le16 reserved_for_local_mld_addr;
+	__le32 filter_flags;
+	union {
+		struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2;
+		struct iwl_mac_wifi_gen_support wifi_gen;
+	};
+	__le32 nic_not_ack_enabled;
+	/* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_3 */
+	union {
+		struct iwl_mac_client_data client;
+		struct iwl_mac_p2p_dev_data p2p_dev;
+		struct iwl_mac_nan_data nan;
+	};
+} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_4 */
 
 /**
  * enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
index 2a174c00b712..439a4530ec9f 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h
@@ -57,8 +57,7 @@ enum iwl_mac_protection_flags {
  * @FW_MAC_TYPE_P2P_DEVICE: P2P Device
  * @FW_MAC_TYPE_P2P_STA: P2P client
  * @FW_MAC_TYPE_GO: P2P GO
- * @FW_MAC_TYPE_TEST: ?
- * @FW_MAC_TYPE_MAX: highest support MAC type
+ * @FW_MAC_TYPE_NAN: NAN (since version 4)
  */
 enum iwl_mac_types {
 	FW_MAC_TYPE_FIRST = 1,
@@ -70,8 +69,7 @@ enum iwl_mac_types {
 	FW_MAC_TYPE_P2P_DEVICE,
 	FW_MAC_TYPE_P2P_STA,
 	FW_MAC_TYPE_GO,
-	FW_MAC_TYPE_TEST,
-	FW_MAC_TYPE_MAX = FW_MAC_TYPE_TEST
+	FW_MAC_TYPE_NAN,
 }; /* MAC_CONTEXT_TYPE_API_E_VER_1 */
 
 /**
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 4d8052e65f93..d3b850259569 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -61,13 +61,20 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
 static int iwl_mld_send_mac_cmd(struct iwl_mld *mld,
 				struct iwl_mac_config_cmd *cmd)
 {
+	u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD);
+	int len = sizeof(*cmd);
 	int ret;
 
 	lockdep_assert_wiphy(mld->wiphy);
 
-	ret = iwl_mld_send_cmd_pdu(mld,
-				   WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
-				   cmd);
+	if (iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) < 4) {
+		if (WARN_ON(cmd->mac_type == cpu_to_le32(FW_MAC_TYPE_NAN)))
+			return -EINVAL;
+
+		len = sizeof(struct iwl_mac_config_cmd_v3);
+	}
+
+	ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, len);
 	if (ret)
 		IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret);
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
index bf54b90a7c51..b65825747b9d 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c
@@ -6,7 +6,7 @@
 
 static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
 				       struct ieee80211_vif *vif,
-				       struct iwl_mac_config_cmd *cmd,
+				       struct iwl_mac_config_cmd_v3 *cmd,
 				       int cmd_ver)
 {
 	if (vif->type == NL80211_IFTYPE_AP) {
@@ -24,7 +24,7 @@ static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm,
 
 static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
 					    struct ieee80211_vif *vif,
-					    struct iwl_mac_config_cmd *cmd,
+					    struct iwl_mac_config_cmd_v3 *cmd,
 					    u32 action)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -83,7 +83,7 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
 }
 
 static int iwl_mvm_mld_mac_ctxt_send_cmd(struct iwl_mvm *mvm,
-					 struct iwl_mac_config_cmd *cmd)
+					 struct iwl_mac_config_cmd_v3 *cmd)
 {
 	int ret = iwl_mvm_send_cmd_pdu(mvm,
 				       WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD),
@@ -98,7 +98,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_sta(struct iwl_mvm *mvm,
 					struct ieee80211_vif *vif,
 					u32 action, bool force_assoc_off)
 {
-	struct iwl_mac_config_cmd cmd = {};
+	struct iwl_mac_config_cmd_v3 cmd = {};
 
 	WARN_ON(vif->type != NL80211_IFTYPE_STATION);
 
@@ -151,7 +151,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_listener(struct iwl_mvm *mvm,
 					     struct ieee80211_vif *vif,
 					     u32 action)
 {
-	struct iwl_mac_config_cmd cmd = {};
+	struct iwl_mac_config_cmd_v3 cmd = {};
 
 	WARN_ON(vif->type != NL80211_IFTYPE_MONITOR);
 
@@ -170,7 +170,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm,
 					 struct ieee80211_vif *vif,
 					 u32 action)
 {
-	struct iwl_mac_config_cmd cmd = {};
+	struct iwl_mac_config_cmd_v3 cmd = {};
 
 	WARN_ON(vif->type != NL80211_IFTYPE_ADHOC);
 
@@ -187,7 +187,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm,
 					       struct ieee80211_vif *vif,
 					       u32 action)
 {
-	struct iwl_mac_config_cmd cmd = {};
+	struct iwl_mac_config_cmd_v3 cmd = {};
 
 	WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE);
 
@@ -210,7 +210,7 @@ static int iwl_mvm_mld_mac_ctxt_cmd_ap_go(struct iwl_mvm *mvm,
 					  u32 action)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	struct iwl_mac_config_cmd cmd = {};
+	struct iwl_mac_config_cmd_v3 cmd = {};
 
 	WARN_ON(vif->type != NL80211_IFTYPE_AP);
 
@@ -286,7 +286,7 @@ int iwl_mvm_mld_mac_ctxt_changed(struct iwl_mvm *mvm,
 int iwl_mvm_mld_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
 	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-	struct iwl_mac_config_cmd cmd = {
+	struct iwl_mac_config_cmd_v3 cmd = {
 		.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
 		.id_and_color = cpu_to_le32(mvmvif->id),
 	};
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 04/15] wifi: iwlwifi: handle NULL/ERR returns from ptp_clock_register()
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Avinash Bhatt, Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Avinash Bhatt <avinash.bhatt@intel.com>

ptp_clock_register() returns NULL when PTP support is disabled and may
return an ERR_PTR() on other failures. Reduce Log severity for NULL
return cases to avoid misleading errors when PTP is unavailable.

Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/ptp.c | 4 +++-
 drivers/net/wireless/intel/iwlwifi/mvm/ptp.c | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
index 231920425c06..c65f4b56a327 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c
@@ -301,10 +301,12 @@ void iwl_mld_ptp_init(struct iwl_mld *mld)
 	mld->ptp_data.ptp_clock =
 		ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev);
 
-	if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) {
+	if (IS_ERR(mld->ptp_data.ptp_clock)) {
 		IWL_ERR(mld, "Failed to register PHC clock (%ld)\n",
 			PTR_ERR(mld->ptp_data.ptp_clock));
 		mld->ptp_data.ptp_clock = NULL;
+	} else if (!mld->ptp_data.ptp_clock) {
+		IWL_DEBUG_INFO(mld, "PTP module unavailable on this kernel\n");
 	} else {
 		IWL_DEBUG_INFO(mld, "Registered PHC clock: %s, with index: %d\n",
 			       mld->ptp_data.ptp_clock_info.name,
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
index ad156b82eaa9..f7b620136c85 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/ptp.c
@@ -304,7 +304,9 @@ void iwl_mvm_ptp_init(struct iwl_mvm *mvm)
 		IWL_ERR(mvm, "Failed to register PHC clock (%ld)\n",
 			PTR_ERR(mvm->ptp_data.ptp_clock));
 		mvm->ptp_data.ptp_clock = NULL;
-	} else if (mvm->ptp_data.ptp_clock) {
+	} else if (!mvm->ptp_data.ptp_clock) {
+		IWL_DEBUG_INFO(mvm, "PTP module unavailable on this kernel\n");
+	} else {
 		IWL_DEBUG_INFO(mvm, "Registered PHC clock: %s, with index: %d\n",
 			       mvm->ptp_data.ptp_clock_info.name,
 			       ptp_clock_index(mvm->ptp_data.ptp_clock));
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 03/15] wifi: iwlwifi: mld: add double-include guards to nan.h
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

This is missing, but needed when we want to add data structures
to this file.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/nan.h | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
index c9c83d1012f0..c04d77208971 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
@@ -2,7 +2,8 @@
 /*
  * Copyright (C) 2025 Intel Corporation
  */
-
+#ifndef __iwl_mld_nan_h__
+#define __iwl_mld_nan_h__
 #include <net/cfg80211.h>
 #include <linux/etherdevice.h>
 
@@ -26,3 +27,5 @@ bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
 bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
 				     struct iwl_rx_packet *pkt,
 				     u32 obj_id);
+
+#endif /* __iwl_mld_nan_h__ */
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 02/15] wifi: iwlwifi: mld: make alloc functions not forced static
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

In preparation for NAN needing the link ID allocation, have
the macro not automatically make the ID allocation functions
static so we can remove that later from the link allocation
function.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/iface.c       | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/link.c        | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/mld.h         | 4 ++--
 drivers/net/wireless/intel/iwlwifi/mld/sta.c         | 2 +-
 drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c | 6 +++---
 5 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 472592aa97fd..4d8052e65f93 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -427,7 +427,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
 	iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
 }
 
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
 
 /* Constructor function for struct iwl_mld_vif */
 static void
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index b5430e8a73d6..b66e84d2365f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -437,7 +437,7 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
 	iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
 }
 
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
 
 /* Constructor function for struct iwl_mld_link */
 static int
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
index 66c7a7d31409..ea3d1fab6f46 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h
@@ -530,9 +530,9 @@ void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,
 #define IWL_MLD_INVALID_FW_ID 0xff
 
 #define IWL_MLD_ALLOC_FN(_type, _mac80211_type)						\
-static int										\
+int											\
 iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld,					\
-				 u8 *fw_id,				\
+				 u8 *fw_id,						\
 				 struct ieee80211_##_mac80211_type *mac80211_ptr)	\
 {											\
 	u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8();			\
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index 619f302076ad..eda2cbbb3b30 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -528,7 +528,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
 	return iwl_mld_send_sta_cmd(mld, &cmd);
 }
 
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
 
 static int
 iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
index 26cf27be762d..176dbbf4c643 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c
@@ -68,7 +68,7 @@ int iwlmld_kunit_test_init(struct kunit *test)
 	return 0;
 }
 
-IWL_MLD_ALLOC_FN(link, bss_conf)
+static IWL_MLD_ALLOC_FN(link, bss_conf)
 
 static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
 				   struct ieee80211_bss_conf *link,
@@ -94,7 +94,7 @@ static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
 	rcu_assign_pointer(vif->link_conf[link_id], link);
 }
 
-IWL_MLD_ALLOC_FN(vif, vif)
+static IWL_MLD_ALLOC_FN(vif, vif)
 
 /* Helper function to add and initialize a VIF for KUnit tests */
 struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type)
@@ -199,7 +199,7 @@ void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif,
 		vif->active_links |= BIT(link->link_id);
 }
 
-IWL_MLD_ALLOC_FN(link_sta, link_sta)
+static IWL_MLD_ALLOC_FN(link_sta, link_sta)
 
 static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta,
 				      struct ieee80211_link_sta *link_sta,
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 01/15] wifi: iwlwifi: mld: rename iwl_mld_phy_from_mac80211() argument
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>

From: Johannes Berg <johannes.berg@intel.com>

Calling the channel context just "channel" is confusing since it's
a different struct, rename it to the more appropriate "chanctx".

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/phy.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
index 0deaf179f07c..6887f9feaa5c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/phy.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h
@@ -32,9 +32,9 @@ struct iwl_mld_phy {
 };
 
 static inline struct iwl_mld_phy *
-iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel)
+iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *chanctx)
 {
-	return (void *)channel->drv_priv;
+	return (void *)chanctx->drv_priv;
 }
 
 /* Cleanup function for struct iwl_mld_phy, will be called in restart */
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-03-21
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
  To: linux-wireless

Hi,

features and cleanups from our internal tree.

Thanks,
Miri
---

Avinash Bhatt (2):
  wifi: iwlwifi: handle NULL/ERR returns from ptp_clock_register()
  wifi: iwlwifi: mld: block EMLSR during TDLS connections

Emmanuel Grumbach (4):
  wifi: iwlwifi: mld: update the TLC when we deactivate a link
  wifi: iwlwifi: TLC_MNG_CONFIG_CMD can use several structures
  wifi: iwlwifi: fix the description of SESSION_PROTECTION_CMD
  wifi: iwlwifi: reduce the number of prints upon firmware crash

Johannes Berg (5):
  wifi: iwlwifi: mld: rename iwl_mld_phy_from_mac80211() argument
  wifi: iwlwifi: mld: make alloc functions not forced static
  wifi: iwlwifi: mld: add double-include guards to nan.h
  wifi: iwlwifi: add MAC context command version 4
  wifi: iwlwifi: mld: set RX_FLAG_RADIOTAP_TLV_AT_END generically

Miri Korenblit (4):
  wifi: iwlwifi: mld: use the dedicated helper to extract a link
  wifi: iwlwifi: mld: always assign a fw id to a vif
  wifi: iwlwifi: add a macro for max FW links
  wifi: iwlwifi: mld: introduce iwl_mld_vif_fw_id_valid

 .../wireless/intel/iwlwifi/fw/api/datapath.h  |  3 +-
 .../wireless/intel/iwlwifi/fw/api/mac-cfg.h   | 69 +++++++++++++++++--
 .../net/wireless/intel/iwlwifi/fw/api/mac.h   |  6 +-
 .../net/wireless/intel/iwlwifi/fw/api/stats.h |  5 +-
 drivers/net/wireless/intel/iwlwifi/fw/dump.c  | 69 +------------------
 drivers/net/wireless/intel/iwlwifi/iwl-drv.c  |  2 +-
 .../net/wireless/intel/iwlwifi/mld/iface.c    | 20 ++++--
 .../net/wireless/intel/iwlwifi/mld/iface.h    | 13 +++-
 drivers/net/wireless/intel/iwlwifi/mld/link.c |  2 +-
 .../wireless/intel/iwlwifi/mld/low_latency.c  | 13 ++--
 .../net/wireless/intel/iwlwifi/mld/mac80211.c | 25 ++++++-
 drivers/net/wireless/intel/iwlwifi/mld/mld.h  |  6 +-
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c  |  3 +-
 drivers/net/wireless/intel/iwlwifi/mld/nan.h  |  5 +-
 drivers/net/wireless/intel/iwlwifi/mld/phy.h  |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/ptp.c  |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/rx.c   | 13 ++--
 drivers/net/wireless/intel/iwlwifi/mld/scan.c |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.c  |  4 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.h  |  2 +-
 .../net/wireless/intel/iwlwifi/mld/stats.c    |  5 +-
 .../wireless/intel/iwlwifi/mld/tests/utils.c  |  8 +--
 drivers/net/wireless/intel/iwlwifi/mld/tlc.c  | 50 +++++++++++++-
 drivers/net/wireless/intel/iwlwifi/mld/tlc.h  |  3 +
 .../net/wireless/intel/iwlwifi/mvm/mld-mac.c  | 18 ++---
 drivers/net/wireless/intel/iwlwifi/mvm/ptp.c  |  4 +-
 26 files changed, 223 insertions(+), 137 deletions(-)

-- 
2.34.1


^ permalink raw reply

* Re: [PATCH 1/3] carlfw: add stability fixes for AR9170 USB adapters
From: Christian Lamparter @ 2026-03-21 13:29 UTC (permalink / raw)
  To: iamdevnull, Christian Lamparter; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B6784EE6B1CE5A663476329441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>

On 3/17/26 10:11 AM, iamdevnull wrote:
> From: Masi Osmani <mas-i@hotmail.de>
> 
> Five targeted fixes for stability issues observed on AR9170 USB hardware
> (AVM Fritz!WLAN USB Stick N) during extended operation:
> 
> 1. cam.c: Add timeout to CAM busy-wait loops. The original infinite
>     loops block the entire SH-2 processor if the MAC CAM engine stalls,
>     making the firmware unresponsive to USB commands. Add a 10000-cycle
>     timeout to prevent firmware lockup.

Hmm. The firmware sets up a watchdog (hardware timer). It barks (as in: it triggers
the reset vector) when not serviced.

Have you observed that the CAM busy-wait loops can prevent even the watchdog
to go off?


> 2. main.c (tally): Use hardware cycle counter AR9170_MAC_REG_CHANNEL_BUSY
>     for CCA busy time instead of single-bit polling via AR9170_MAC_BACKOFF_CCA.
>     The register is a read-and-clear counter like AR9170_MAC_REG_RX_TOTAL,
>     giving accurate channel utilization data to the driver's survey.


There were reasons why this was chosen. But sure, we can decide if it was a good one
or not.

You see, the problem with AR9170_MAC_REG_CHANNEL_BUSY... that there's also EXT_BUSY
#define AR9170_MAC_REG_CHANNEL_BUSY             (AR9170_MAC_REG_BASE + 0x6e8)
#define AR9170_MAC_REG_EXT_BUSY                 (AR9170_MAC_REG_BASE + 0x6ec)

Don't you need to do something (add it to the other channel struct) when in HT40 mode?
What would this look like?

The other reason was, that I don't know of any hardware counter that keeps track of
the tx-time (counter when hardware is transmitting).

I don't think we should let the hardware run the busy tally but let the software trying
to keep up and do the tx-transmit time tally (from my test back then, it couldn't).

So, we would need to remove the AR9170_MAC_BACKOFF_TX_PE together with fw.tally.tx_time.
And then you loose hostapd's automatic channel selection:
https://wireless.docs.kernel.org/en/latest/en/users/documentation/acs.html
(as it needs tx_time)

> 3. rf.c: On AGC calibration timeout, disable the baseband via
>     AR9170_PHY_ACTIVE_DIS so the driver sees a clean failure instead of
>     operating with a half-initialized PHY that produces corrupted frames.

Sure, if you can make this in a separate commit. I'll merge it.

> 
> 4. wlan.c (RX overrun): Lower the MAC reset threshold from 100% frame
>     loss to >50% frame loss. The original check (overruns == total) only
>     triggered at complete RX blindness, leaving the adapter nearly
>     non-functional at 95% loss without any recovery.

Sure, why not.

> 
> 5. wlan.c (PSM wake): After rf_psm() transitions from PHY_OFF to
>     PHY_ON, re-trigger TX DMA for queued frames. While the PHY was off,
>     hardware could not transmit and DMA trigger bits were consumed
>     without effect.

Yeah, no real surprise here. That said, I to this day disable PS

> 
> 6. wlantx.c: Move wlan_tx_ampdu_reset() after retry queue drain to
>     prevent clearing AMPDU state before retransmission completes.

Sure.

> 
> 7. usb/main.c: Implement usb_warm_reset() for USB bus reset handling.
>     Unlike reboot() which destroys USB PHY state requiring re-plug,
>     warm reset preserves the USB connection and jumps to start() for
>     full firmware re-init. Includes WLAN MAC, DMA, baseband, and PTA
>     reset with BB_WARM_RESET to prevent PHY lockups after repeated
>     warm resets.

The reason why reboot get called was that there seems to be some special
sauce in the sticks bootcode that could get wedged/stalled endpoints to
function again. I believe there's more magic involved which is not implemented
in the carl9170fw, that is necessary for this. One hint might be that I removed the
code that did usb1.1 <-> usb2.0 switching. This was because the original ar9170 was
such a #define nest that I didn't want to touch and the stick seemed fine without it.
(But then, I never observed usb1.1 <-> usb2.0 switching with the original otus driver
+firmware)


Cheers,
Christian

> Tested on AVM Fritz!WLAN N (AR9170, AR9001U) with kernel 6.18.12.
> 
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
>   carlfw/src/cam.c    | 17 +++++++----
>   carlfw/src/main.c   |  7 ++++-
>   carlfw/src/rf.c     |  9 ++++++
>   carlfw/src/wlan.c   | 33 +++++++++++++++------
>   carlfw/src/wlantx.c |  3 +-
>   carlfw/usb/main.c   | 63 +++++++++++++++++++++++++++++++++++++++--
>   6 files changed, 113 insertions(+), 19 deletions(-)
> 
> diff --git a/carlfw/src/cam.c b/carlfw/src/cam.c
> index 44cbddd..5273031 100644
> --- a/carlfw/src/cam.c
> +++ b/carlfw/src/cam.c
> @@ -42,31 +42,36 @@ static void enable_cam_user(const uint16_t userId)
>   		orl(AR9170_MAC_REG_CAM_ROLL_CALL_TBL_H, (((uint32_t) 1) << (userId - 32)));
>   }
> 
> +#define CAM_TIMEOUT	10000
> +
>   static void wait_for_cam_read_ready(void)
>   {
> -	while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) {
> -		/*
> -		 * wait
> -		 */
> +	unsigned int timeout = CAM_TIMEOUT;
> +
> +	while (((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_READ_PENDING) == 0) &&
> +	       timeout--) {
> +		/* wait */
>   	}
>   }
> 
>   static void wait_for_cam_write_ready(void)
>   {
> -	while ((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) {
> -		/*
> -		 * wait some more
> -		 */
> +	unsigned int timeout = CAM_TIMEOUT;
> +
> +	while (((get(AR9170_MAC_REG_CAM_STATE) & AR9170_MAC_CAM_STATE_WRITE_PENDING) == 0) &&
> +	       timeout--) {
> +		/* wait */
>   	}
>   }
> 
>   static void HW_CAM_Avail(void)
>   {
> +	unsigned int timeout = CAM_TIMEOUT;
>   	uint32_t tmpValue;
> 
>   	do {
>   		tmpValue = get(AR9170_MAC_REG_CAM_MODE);
> -	} while (tmpValue & AR9170_MAC_CAM_HOST_PENDING);
> +	} while ((tmpValue & AR9170_MAC_CAM_HOST_PENDING) && timeout--);
>   }
> 
>   static void HW_CAM_Write128(const uint32_t address, const uint32_t *data)
> diff --git a/carlfw/src/main.c b/carlfw/src/main.c
> index 8c13bf8..addc883 100644
> --- a/carlfw/src/main.c
> +++ b/carlfw/src/main.c
> @@ -94,11 +94,16 @@ static void tally_update(void)
> 
>   		fw.tally.active += delta;
> 
> +		/*
> +		 * Use HW cycle counter for CCA busy time instead of
> +		 * single-bit polling. AR9170_MAC_REG_CHANNEL_BUSY is
> +		 * a read-and-clear counter like AR9170_MAC_REG_RX_TOTAL.
> +		 */
> +		fw.tally.cca += get(AR9170_MAC_REG_CHANNEL_BUSY);
> +
>   		boff = get(AR9170_MAC_REG_BACKOFF_STATUS);
>   		if (boff & AR9170_MAC_BACKOFF_TX_PE)
>   			fw.tally.tx_time += delta;
> -		if (boff & AR9170_MAC_BACKOFF_CCA)
> -			fw.tally.cca += delta;
>   	}
>   #endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
>   	fw.tally_clock = time;
> diff --git a/carlfw/src/rf.c b/carlfw/src/rf.c
> index 5e8d3d8..d695742 100644
> --- a/carlfw/src/rf.c
> +++ b/carlfw/src/rf.c
> @@ -190,6 +190,15 @@ static uint32_t rf_init(const uint32_t delta_slope_coeff_exp,
> 
>   	ret = AGC_calibration(finiteLoopCount);
> 
> +	if (ret) {
> +		/*
> +		 * Calibration timed out — PHY is in an undefined state.
> +		 * Disable baseband so the driver sees a clean failure
> +		 * instead of operating with a half-initialized PHY.
> +		 */
> +		set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_DIS);
> +	}
> +
>   	set_channel_end();
>   	return ret;
>   }
> diff --git a/carlfw/src/wlan.c b/carlfw/src/wlan.c
> index 4e73a2b..7a01b09 100644
> --- a/carlfw/src/wlan.c
> +++ b/carlfw/src/wlan.c
> @@ -77,7 +77,13 @@ static void wlan_check_rx_overrun(void)
>   	fw.tally.rx_total += total = get(AR9170_MAC_REG_RX_TOTAL);
>   	fw.tally.rx_overrun += overruns = get(AR9170_MAC_REG_RX_OVERRUN);
>   	if (unlikely(overruns)) {
> -		if (overruns == total) {
> +		/*
> +		 * Trigger MAC reset when more than half of received
> +		 * frames are dropped.  The original check (overruns ==
> +		 * total) only fired at 100 % loss, leaving the adapter
> +		 * nearly blind at 95 % loss without any recovery.
> +		 */
> +		if (total && overruns > (total >> 1)) {
>   			DBG("RX Overrun");
>   			fw.wlan.mac_reset++;
>   		}
> @@ -100,10 +106,33 @@ static void handle_pretbtt(void)
>   	fw.wlan.cab_flush_time = get_clock_counter();
> 
>   #ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
> -	rf_psm();
> +	{
> +		unsigned int prev_phy_state = fw.phy.state;
> +
> +		rf_psm();
> +
> +		/*
> +		 * After PSM wake, re-trigger TX DMA for queued frames.
> +		 * While the PHY was off, the hardware could not transmit
> +		 * and DMA trigger bits were consumed without effect.
> +		 */
> +		if (prev_phy_state == CARL9170_PHY_OFF &&
> +		    fw.phy.state == CARL9170_PHY_ON) {
> +			int i;
> +			uint32_t trigger = 0;
> +
> +			for (i = AR9170_TXQ0; i <= AR9170_TXQ_SPECIAL; i++) {
> +				if (!queue_empty(&fw.wlan.tx_queue[i]))
> +					trigger |= BIT(i);
> +			}
> +
> +			if (trigger)
> +				wlan_trigger(trigger);
> +		}
> 
> -	send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00,
> -			 (uint8_t *) &fw.phy.psm.state);
> +		send_cmd_to_host(4, CARL9170_RSP_PRETBTT, 0x00,
> +				 (uint8_t *) &fw.phy.psm.state);
> +	}
>   #endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
>   }
> 
> diff --git a/carlfw/src/wlantx.c b/carlfw/src/wlantx.c
> index a8d0952..2db111e 100644
> --- a/carlfw/src/wlantx.c
> +++ b/carlfw/src/wlantx.c
> @@ -471,11 +471,10 @@ void handle_wlan_tx_completion(void)
>   			}
>   		}
> 
> -		wlan_tx_ampdu_reset(i);
> -
>   		for_each_desc(desc, &fw.wlan.tx_retry)
>   			__wlan_tx(desc);
> 
> +		wlan_tx_ampdu_reset(i);
>   		wlan_tx_ampdu_end(i);
>   		if (!queue_empty(&fw.wlan.tx_queue[i]))
>   			wlan_trigger(BIT(i));
> diff --git a/carlfw/usb/main.c b/carlfw/usb/main.c
> index 4199a21..9147c80 100644
> --- a/carlfw/usb/main.c
> +++ b/carlfw/usb/main.c
> @@ -295,6 +295,63 @@ static void disable_watchdog(void)
>   	set(AR9170_TIMER_REG_WATCH_DOG, 0xffff);
>   }
> 
> +/*
> + * Warm reset: re-initialize firmware without destroying USB PHY state.
> + * This allows the host to re-enumerate the device after a USB bus reset
> + * without requiring a physical re-plug.
> + *
> + * Unlike reboot() which calls turn_power_off() and jump_to_bootcode(),
> + * this preserves the USB connection and jumps directly to start().
> + */
> +static void __noreturn usb_warm_reset(void)
> +{
> +	disable_watchdog();
> +
> +	/* Disable baseband to stop PHY activity */
> +	set(AR9170_PHY_REG_ACTIVE, AR9170_PHY_ACTIVE_DIS);
> +
> +	/* Stop WLAN DMA */
> +	set(AR9170_MAC_REG_DMA_TRIGGER, 0);
> +
> +	/* Stop USB DMA without full power-off */
> +	andl(AR9170_USB_REG_DMA_CTL, ~(AR9170_USB_DMA_CTL_ENABLE_TO_DEVICE |
> +					AR9170_USB_DMA_CTL_ENABLE_FROM_DEVICE));
> +
> +	/* Reset PTA component */
> +	orl(AR9170_PTA_REG_DMA_MODE_CTRL, AR9170_PTA_DMA_MODE_CTRL_RESET);
> +	andl(AR9170_PTA_REG_DMA_MODE_CTRL, ~AR9170_PTA_DMA_MODE_CTRL_RESET);
> +
> +	/* Reset MAC power state */
> +	set(AR9170_MAC_REG_POWER_STATE_CTRL,
> +	    AR9170_MAC_POWER_STATE_CTRL_RESET);
> +
> +	/*
> +	 * Hardware reset: WLAN MAC, DMA engine, and baseband.
> +	 * Without this, the PHY/RF can lock up after repeated
> +	 * warm resets, causing -ETIMEDOUT on register writes
> +	 * and cascading driver reloads (phy0 -> phy29 -> crash).
> +	 *
> +	 * BB_WARM_RESET resets PHY logic while preserving
> +	 * calibration-friendly state. start() -> init() will
> +	 * reconfigure everything via the driver anyway.
> +	 */
> +	set(AR9170_PWR_REG_RESET, AR9170_PWR_RESET_COMMIT_RESET_MASK |
> +				  AR9170_PWR_RESET_WLAN_MASK |
> +				  AR9170_PWR_RESET_DMA_MASK |
> +				  AR9170_PWR_RESET_BB_WARM_RESET);
> +	set(AR9170_PWR_REG_RESET, 0x0);
> +
> +	/* Clean DMA memory */
> +	memset(&dma_mem, 0, sizeof(dma_mem));
> +
> +	/* Clear firmware state */
> +	memset(&fw, 0, sizeof(fw));
> +
> +	/* Re-enter firmware from start() which does full init
> +	 * and sends CARL9170_RSP_BOOT to the host. */
> +	start();
> +}
> +
>   void __noreturn reboot(void)
>   {
>   	disable_watchdog();
> @@ -377,7 +434,7 @@ static void usb_handler(uint8_t usb_interrupt_level1)
>   		if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_RESET) {
>   			usb_reset_ack();
>   			usb_reset_eps();
> -			reboot();
> +			usb_warm_reset();
>   		}
> 
>   		if (usb_interrupt_level2 & AR9170_USB_INTR_SRC7_USB_SUSPEND) {
> @@ -409,7 +466,7 @@ static void usb_handler(uint8_t usb_interrupt_level1)
>   			fw.suspend_mode = CARL9170_HOST_AWAKE;
>   			set(AR9170_USB_REG_WAKE_UP, 0);
> 
> -			reboot();
> +			usb_warm_reset();
>   		}
>   	}
>   }


^ permalink raw reply

* Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
From: LB F @ 2026-03-21 12:07 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <a8e187e1b40e4a35bbeb3bc3a3d21821@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:
> I'll send formal patch (Cc you) for the invalid VHT NSS=0, but not
> to handle "unused phy status page". Please give me Tested-by tag on
> the patch after I send it.

Hi Ping-Ke,

Just a quick update to keep you informed -- no rush on anything.

My kernel updated from 6.19.7 to 6.19.9, which wiped the previously
installed out-of-tree modules. I rebuilt and reinstalled both patches:

  1. The v2 DMI quirk (main.h + pci.c) disabling ASPM and LPS Deep
     Mode for the HP P3S95EA#ACB SKU.
  2. The rate validation patch (rx.c) clamping out-of-bounds rate
     values before they reach mac80211.

Both patches apply cleanly and the system remains fully stable on
6.19.9. The DMI quirk is confirmed active via sysfs (disable_aspm=Y,
disable_lps_deep=Y) with no manual modprobe overrides.

I am looking forward to your formal patch for the VHT NSS=0 issue and
will provide a Tested-by tag as soon as it arrives. Thank you again
for all your work and patience throughout this process.

Best regards,
Oleksandr Havrylov

^ permalink raw reply

* [PATCH] wifi: ath10k: update outdated comment for renamed ieee80211_tx_status()
From: Kexin Sun @ 2026-03-21 11:00 UTC (permalink / raw)
  To: jjohnson, linux-wireless, ath10k, linux-kernel
  Cc: julia.lawall, xutong.ma, kexinsun, yunbolyu, ratnadiraw

The function ieee80211_tx_status() was renamed to
ieee80211_tx_status_skb() by commit 2703bc851399
("wifi: mac80211: rename ieee80211_tx_status() to
ieee80211_tx_status_skb()").  Update the stale reference
in ath10k_htt_tx_hl().

Assisted-by: unnamed:deepseek-v3.2 coccinelle
Signed-off-by: Kexin Sun <kexinsun@smail.nju.edu.cn>
---
 drivers/net/wireless/ath/ath10k/htt_tx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index d6f1d85ba871..29e99fbf36fd 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -1353,7 +1353,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm
 		msdu_id = res;
 	}
 
-	/* As msdu is freed by mac80211 (in ieee80211_tx_status()) and by
+	/* As msdu is freed by mac80211 (in ieee80211_tx_status_skb()) and by
 	 * ath10k (in ath10k_htt_htc_tx_complete()) we have to increase
 	 * reference by one to avoid a use-after-free case and a double
 	 * free.
-- 
2.25.1


^ permalink raw reply related

* [PATCH] wifi: iwlwifi: mvm: update outdated comment for renamed ieee80211_tx_status()
From: Kexin Sun @ 2026-03-21 11:00 UTC (permalink / raw)
  To: miriam.rachel.korenblit, johannes.berg, ilan.peer, linux-wireless,
	linux-kernel
  Cc: julia.lawall, xutong.ma, kexinsun, yunbolyu, ratnadiraw

The function ieee80211_tx_status() was renamed to
ieee80211_tx_status_skb() by commit 2703bc851399
("wifi: mac80211: rename ieee80211_tx_status() to
ieee80211_tx_status_skb()").  Update the stale reference
in rs_set_lq_ss_params().

Assisted-by: unnamed:deepseek-v3.2 coccinelle
Signed-off-by: Kexin Sun <kexinsun@smail.nju.edu.cn>
---
 drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
index d1619a229d8f..2216a91cdc8f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c
@@ -3553,7 +3553,7 @@ static void rs_set_lq_ss_params(struct iwl_mvm *mvm,
 
 	/* This code is safe as it doesn't run concurrently for different
 	 * stations. This is guaranteed by the fact that calls to
-	 * ieee80211_tx_status wouldn't run concurrently for a single HW.
+	 * ieee80211_tx_status_skb wouldn't run concurrently for a single HW.
 	 */
 	if (!bfer_mvmsta) {
 		IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n");
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH v4] wifi: mt76: mt7921: fix txpower reporting from rate power configuration
From: Sean Wang @ 2026-03-21  8:58 UTC (permalink / raw)
  To: Lucid Duck
  Cc: Felix Fietkau, Lorenzo Bianconi, linux-wireless, linux-mediatek,
	morrownr, stable
In-Reply-To: <20260319203854.30479-1-lucid_duck@justthetip.ca>

Hi,

On Thu, Mar 19, 2026 at 3:39 PM Lucid Duck <lucid_duck@justthetip.ca> wrote:
>
> The mt7921 driver never updates phy->txpower_cur from the rate power
> configuration sent to firmware, causing mt76_get_txpower() to report
> bogus values to userspace (typically 3 dBm) regardless of actual
> regulatory or SAR limits.
>
> Two issues are addressed:
>
> 1. The rate power loop in mt76_connac_mcu_rate_txpower_band() computes
>    the correct bounded TX power per channel via
>    mt76_get_rate_power_limits() but discards the return value. Capture
>    it and store to phy->txpower_cur when processing the current
>    channel, matching how mt7915 handles this in
>    mt7915_mcu_set_txpower_sku(). Subtract the multi-chain path delta
>    before storing, since mt76_get_txpower() adds it back when
>    reporting -- consistent with mt7915's use of mt76_get_power_bound()
>    which performs the same subtraction.
>
> 2. mt7921 uses the chanctx model but its add_chanctx callback does not
>    update phy->chandef, leaving it stale after association. The rate
>    power loop's channel comparison then fails silently. Sync
>    phy->chandef from ctx->def in add_chanctx and change_chanctx, and
>    recompute txpower_cur via a lightweight helper that performs the
>    same bounded power calculation for the current channel without
>    reprogramming firmware rate tables.
>
> Tested on Alfa AWUS036AXML (MT7921AU), kernel 6.19.8, Canada:
>
>   Before: iw dev wlan0 info shows "txpower 3.00 dBm" (wrong)
>   After:  2.4GHz 36 dBm, 5GHz 23 dBm, 6GHz 12 dBm (match regulatory)
>
> Cc: stable@vger.kernel.org
> Fixes: 1c099ab44727 ("mt76: mt7921: add MAC support")
> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
> ---
> Changes since v3:
> - Removed mt7921_set_tx_sar_pwr() from add_chanctx and change_chanctx.
>   Channel transitions don't change underlying power constraints, so
>   reprogramming the full rate table is unnecessary. Replaced with a
>   lightweight helper that recomputes txpower_cur locally.
> - Removed IEEE80211_CONF_CHANGE_CHANNEL trigger from config().
> - Removed BSS_CHANGED_TXPOWER handler from bss_info_changed(). Writing
>   per-vif txpower into per-HW hw->conf.power_level breaks multi-vif
>   semantics. User txpower limits need a different approach (follow-up).
> - Subtracted path delta before storing txpower_cur. The connac rate
>   loop stores total bounded power, but mt76_get_txpower() adds the
>   multi-chain path delta when reporting. mt7915 accounts for this via
>   mt76_get_power_bound(), which subtracts the delta before storing.
>   Without the same subtraction, reported values were inflated by 3 dBm
>   on 2x2 devices.
>
>  .../wireless/mediatek/mt76/mt76_connac_mcu.c  | 14 +++++++--
>  .../net/wireless/mediatek/mt76/mt7921/main.c  | 30 +++++++++++++++++++
>  2 files changed, 41 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> index 16db0f208..e26a2cb39 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
> @@ -2193,14 +2193,22 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>                                 .hw_value = ch_list[idx],
>                                 .band = band,
>                         };
> -                       s8 reg_power, sar_power;
> +                       s8 reg_power, sar_power, max_power;
>
>                         reg_power = mt76_connac_get_ch_power(phy, &chan,
>                                                              tx_power);
>                         sar_power = mt76_get_sar_power(phy, &chan, reg_power);
>
> -                       mt76_get_rate_power_limits(phy, &chan, limits,
> -                                                  sar_power);
> +                       max_power = mt76_get_rate_power_limits(phy, &chan,
> +                                                              limits,
> +                                                              sar_power);
> +
> +                       if (phy->chandef.chan &&
> +                           phy->chandef.chan->hw_value == ch_list[idx] &&
> +                           phy->chandef.chan->band == band)
> +                               phy->txpower_cur = max_power -
> +                                       mt76_tx_power_path_delta(
> +                                               hweight16(phy->chainmask));
>
>                         tx_power_tlv.last_msg = ch_list[idx] == last_ch;
>                         sku_tlbv.channel = ch_list[idx];
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 5881040ac..a77ae5791 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -1355,13 +1355,39 @@ mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
>         mt792x_mutex_release(dev);
>  }
>
> +static void mt7921_update_txpower_cur(struct mt76_phy *phy)
> +{
> +       struct mt76_power_limits limits;
> +       struct ieee80211_channel *chan = phy->chandef.chan;
> +       int n_chains = hweight16(phy->chainmask);
> +       s8 reg_power, sar_power, max_power;
> +       int tx_power;
> +
> +       if (!chan)
> +               return;
> +
> +       tx_power = 2 * phy->hw->conf.power_level;
> +       if (!tx_power)
> +               tx_power = 127;
> +
> +       reg_power = mt76_connac_get_ch_power(phy, chan, tx_power);
> +       sar_power = mt76_get_sar_power(phy, chan, reg_power);
> +       max_power = mt76_get_rate_power_limits(phy, chan, &limits, sar_power);
> +
> +       phy->txpower_cur = max_power - mt76_tx_power_path_delta(n_chains);
> +}
> +
>  static int
>  mt7921_add_chanctx(struct ieee80211_hw *hw,
>                    struct ieee80211_chanctx_conf *ctx)
>  {
>         struct mt792x_dev *dev = mt792x_hw_dev(hw);
> +       struct mt76_phy *mphy = hw->priv;
>
>         dev->new_ctx = ctx;
> +       mphy->chandef = ctx->def;
> +       mt7921_update_txpower_cur(mphy);
> +

I don't think this is the right fix.

This mixes multiple things in one patch and duplicates the same power
limit calculation in shared connac code and mt7921-specific code.

The issue is in txpower reporting. A channel context is just a view of
the current PHY configuration and should not be used to overwrite PHY
state.

I think this needs to be reworked in a cleaner way, with the shared
logic kept in shared code and the reporting side fixed in the common
mt792x path so it can apply cleanly to both mt7921 and mt7925.

Given the points above, I'd prefer not to keep iterating on the current
approach for v5. I'll try to rework this along the lines above and send
an updated version.

>         return 0;
>  }
>
> @@ -1396,6 +1422,10 @@ mt7921_change_chanctx(struct ieee80211_hw *hw,
>                 mt7921_mcu_config_sniffer(mvif, ctx);
>         else
>                 mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx);
> +
> +       phy->mt76->chandef = ctx->def;
> +       mt7921_update_txpower_cur(phy->mt76);
> +
>         mt792x_mutex_release(phy->dev);
>  }
>
> --
> 2.53.0
>

^ permalink raw reply

* [PATCH v2] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs
From: Lucid Duck @ 2026-03-21  3:37 UTC (permalink / raw)
  To: linux-wireless; +Cc: Ping-Ke Shih, Bitterblue Smith
In-Reply-To: <20260125221943.36001-1-lucid_duck@justthetip.ca>

From: Lucid Duck <lucid_duck@justthetip.ca>
Date: Thu, 20 Mar 2026 20:00:00 -0700
Subject: [PATCH v2] wifi: rtw89: usb: fix TX flow control by tracking
 in-flight URBs

rtw89_usb_ops_check_and_reclaim_tx_resource() returns a hardcoded
placeholder value (42) instead of actual TX resource availability.
This violates mac80211's flow control contract, preventing backpressure
and causing uncontrolled URB accumulation under sustained TX load.

Fix by adding per-channel atomic counters (tx_inflight[]) that track
in-flight URBs:

- Increment before usb_submit_urb() with rollback on failure
- Decrement in completion callback
- Return (MAX_URBS - inflight) to mac80211, or 0 when at capacity
- Exclude firmware command channel (CH12) from tracking

The pre-increment pattern prevents a race where the USB core completes
the URB (possibly on another CPU) before the submitting code increments
the counter.

Tested on D-Link DWA-X1850 (RTL8832AU), kernel 6.18.3:

                     Unpatched -> Patched
  USB3 5GHz DL:      494 -> 709 Mbps (+44%)
  USB3 5GHz retx:    8 -> 1 (-88%)
  USB3 2.4GHz DL:    54 -> 68 Mbps (+25%)
  USB2 5GHz DL:      196 -> 225 Mbps (+15%)
  USB2 2.4GHz DL:    123 -> 131 Mbps (+6%)

Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
---
Resending v2. This was prepared in late January after addressing v1
review feedback, but the send failed silently (SMTP misconfiguration)
and never appeared on lore.kernel.org. Apologies for the delay.

Changes since v1:
- Removed duplicate "TX flow control" comment (Ping-Ke Shih)
- Added test results to commit message (Ping-Ke Shih)

Bitterblue's CH12 question from v1: the CH12 guards in tx_kick_off()
and write_port_complete() are a matched pair. tx_kick_off() skips
atomic_inc for CH12, so the completion handler must skip atomic_dec
to match. Removing only the completion side causes counter underflow.

Additional validation: 100-iteration stress test, 50-iteration
teardown (rmmod/modprobe under load), 10x hot-unplug during active
TX, and 30-minute soak -- all pass with counters balanced at idle.

The 32-URB-per-channel limit is based on similar USB wireless drivers
(mt76, ath9k_htc). The fixed value works well for both USB2 and USB3.

 drivers/net/wireless/realtek/rtw89/usb.c | 26 ++++++++++++++++++-----
 drivers/net/wireless/realtek/rtw89/usb.h |  6 ++++++
 2 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index eb489df..faafa3c 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -161,16 +161,25 @@ static u32
 rtw89_usb_ops_check_and_reclaim_tx_resource(struct rtw89_dev *rtwdev,
 					    u8 txch)
 {
+	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+	int inflight;
+
+	/* Firmware command channel is not tracked */
 	if (txch == RTW89_TXCH_CH12)
 		return 1;

-	return 42; /* TODO some kind of calculation? */
+	inflight = atomic_read(&rtwusb->tx_inflight[txch]);
+	if (inflight >= RTW89_USB_MAX_TX_URBS_PER_CH)
+		return 0;
+
+	return RTW89_USB_MAX_TX_URBS_PER_CH - inflight;
 }

 static void rtw89_usb_write_port_complete(struct urb *urb)
 {
 	struct rtw89_usb_tx_ctrl_block *txcb = urb->context;
 	struct rtw89_dev *rtwdev = txcb->rtwdev;
+	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 	struct ieee80211_tx_info *info;
 	struct rtw89_txwd_body *txdesc;
 	struct sk_buff *skb;
@@ -229,6 +238,10 @@ static void rtw89_usb_write_port_complete(struct urb *urb)
 		break;
 	}

+	/* Decrement in-flight counter (skip firmware command channel) */
+	if (txcb->txch != RTW89_TXCH_CH12)
+		atomic_dec(&rtwusb->tx_inflight[txcb->txch]);
+
 	kfree(txcb);
 }

@@ -306,9 +319,17 @@ static void rtw89_usb_ops_tx_kick_off(struct rtw89_dev *rtwdev, u8 txch)

 		skb_queue_tail(&txcb->tx_ack_queue, skb);

+		/* Increment BEFORE submit to avoid race with completion */
+		if (txch != RTW89_TXCH_CH12)
+			atomic_inc(&rtwusb->tx_inflight[txch]);
+
 		ret = rtw89_usb_write_port(rtwdev, txch, skb->data, skb->len,
 					   txcb);
 		if (ret) {
+			/* Rollback increment on failure */
+			if (txch != RTW89_TXCH_CH12)
+				atomic_dec(&rtwusb->tx_inflight[txch]);
+
 			if (ret != -ENODEV)
 				rtw89_err(rtwdev, "write port txch %d failed: %d\n",
 					  txch, ret);
@@ -666,8 +687,10 @@ static void rtw89_usb_init_tx(struct rtw89_dev *rtwdev)
 	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 	int i;

-	for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++)
+	for (i = 0; i < ARRAY_SIZE(rtwusb->tx_queue); i++) {
 		skb_queue_head_init(&rtwusb->tx_queue[i]);
+		atomic_set(&rtwusb->tx_inflight[i], 0);
+	}
 }

 static void rtw89_usb_deinit_tx(struct rtw89_dev *rtwdev)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.h b/drivers/net/wireless/realtek/rtw89/usb.h
index 9f554b5..1459122 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.h
+++ b/drivers/net/wireless/realtek/rtw89/usb.h
@@ -20,6 +20,9 @@
 #define RTW89_MAX_ENDPOINT_NUM		9
 #define RTW89_MAX_BULKOUT_NUM		7

+/* TX flow control: max in-flight URBs per channel */
+#define RTW89_USB_MAX_TX_URBS_PER_CH	32
+
 struct rtw89_usb_info {
 	u32 usb_host_request_2;
 	u32 usb_wlan0_1;
@@ -63,6 +66,9 @@ struct rtw89_usb {
 	struct usb_anchor tx_submitted;

 	struct sk_buff_head tx_queue[RTW89_TXCH_NUM];
+
+	/* TX flow control: track in-flight URBs per channel */
+	atomic_t tx_inflight[RTW89_TXCH_NUM];
 };

 static inline struct rtw89_usb *rtw89_usb_priv(struct rtw89_dev *rtwdev)
--
2.53.0

^ permalink raw reply related

* Re: [PATCH wireless-next v3 10/15] wifi: cfg80211: add ingress/egress distance thresholds for FTM
From: Peddolla Harshavardhan Reddy @ 2026-03-20 19:18 UTC (permalink / raw)
  To: Stern, Avraham, johannes@sipsolutions.net
  Cc: linux-wireless@vger.kernel.org, kavita.kavita@oss.qualcomm.com
In-Reply-To: <CH3PR11MB8383572815565E924067C267FF44A@CH3PR11MB8383.namprd11.prod.outlook.com>


On 13-Mar-26 2:44 AM, Stern, Avraham wrote:
>
>> From: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com> 
>> Sent: Thursday, March 5, 2026 6:07 PM
>> To: johannes@sipsolutions.net
>> Cc: linux-wireless@vger.kernel.org; kavita.kavita@oss.qualcomm.com
>> Subject: [PATCH wireless-next v3 10/15] wifi: cfg80211: add ingress/egress distance thresholds for FTM
>> + * @egress_distancemm: the measurement result of the peer needs
>> + *	to be indicated in case the device moves out of this range.
>> + *	(units mm, u64). measurement results need to be sent on a burst index
>> + *	basis in this case.
> Not sure it makes sense to keep reporting results if the device keeps moving away.
>
This is the real-time usecase.

>> + * @NL80211_PMSR_FTM_REQ_ATTR_INGRESS: the measurement result of the peer needs
>> + *	to be indicated in case the device moves into this range.(units mm, u64)
> Need to add "measurement results need to be sent on a burst index basis in this case"
Updated in the next version.
> ---------------------------------------------------------------------
> A member of the Intel Corporation group of companies
>
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.
>

^ permalink raw reply

* Re: [PATCH wireless-next v3 14/15] wifi: cfg80211: add LTF keyseed support for secure ranging
From: Peddolla Harshavardhan Reddy @ 2026-03-20 19:10 UTC (permalink / raw)
  To: Stern, Avraham, johannes@sipsolutions.net
  Cc: linux-wireless@vger.kernel.org, kavita.kavita@oss.qualcomm.com
In-Reply-To: <CH3PR11MB8383484D69A62802D1CC6898FF43A@CH3PR11MB8383.namprd11.prod.outlook.com>


On 15-Mar-26 1:52 PM, Stern, Avraham wrote:
>
>> Sent: Thursday, March 5, 2026 6:07 PM
>> To: johannes@sipsolutions.net
>> Cc: linux-wireless@vger.kernel.org; kavita.kavita@oss.qualcomm.com
>> Subject: [PATCH wireless-next v3 14/15] wifi: cfg80211: add LTF keyseed support for secure ranging
>> Currently there is no way to install an LTF key seed that can be used in non-trigger-based (NTB) and trigger-based (TB) FTM ranging to protect NDP frames. Without this, drivers cannot enable PHY-layer security for peer measurement sessions, leaving ranging measurements vulnerable to eavesdropping and manipulation.
> Installing keys when there is no station (PASN keys) is something new (not supported by mac80211, is it?). so currently even installing the TK from PASN is not clearly defined?


Yes correct.

>
>   
>>  struct key_params {
>>  	const u8 *key;
>> @@ -839,6 +841,8 @@ struct key_params {
>>  	u16 vlan_id;
>>  	u32 cipher;
>>  	enum nl80211_key_mode mode;
>> +	const u8 *ltf_keyseed;
>> +	size_t ltf_keyseed_len;
>  
> Since the mode is NL80211_KEY_LTF_SEED, can use key and key_len, like any other key.
> On the other hand, LTF key seed is not really a key... maybe add the KDK (from which the LTF key seed is derived) instead?


Yes LTF key seed is not a key but it is a pairwise key material to generate LTF keys. So we are configuring these LTF key materials with pairwise TK using single new key command 

>  
>> + * @NL80211_KEY_LTF_SEED: LTF key seed is used by the driver to generate
>> + *	secure LTF keys used in case of peer measurement request with FTM
>> + *	request type as either %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
>> + *	or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED, secure LTF key seeds will
>> + *	help enable PHY security in peer measurement session. The corresponding
>> + *	keys need to be configured beforehand to ensure peer measurement
>> + *	session is secure. Only valid if %NL80211_EXT_FEATURE_SET_KEY_LTF_SEED
> "the corresponding keys" - refers to the TK?
> "beforehand" - before installing the LTF key seed or before the measurement?
> This is not clear. 


Correct, TK is the corresponding keys. 
and yes we are referring to before the PMSR request is sent.

>
>
> ---------------------------------------------------------------------
> A member of the Intel Corporation group of companies
>
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.
>

^ permalink raw reply

* Re: [PATCH wireless-next v3 13/15] wifi: cfg80211: add result reporting control for PD requests
From: Peddolla Harshavardhan Reddy @ 2026-03-20 19:04 UTC (permalink / raw)
  To: Stern, Avraham, johannes@sipsolutions.net
  Cc: linux-wireless@vger.kernel.org, kavita.kavita@oss.qualcomm.com
In-Reply-To: <CH3PR11MB8383F6C4E5840702AF996AB9FF44A@CH3PR11MB8383.namprd11.prod.outlook.com>


On 13-Mar-26 2:46 AM, Stern, Avraham wrote:
>
>> From: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com> 
>> Sent: Thursday, March 5, 2026 6:07 PM
>> To: johannes@sipsolutions.net
>> Cc: linux-wireless@vger.kernel.org; kavita.kavita@oss.qualcomm.com
>> Subject: [PATCH wireless-next v3 13/15] wifi: cfg80211: add result reporting control for PD requests
>> Proximity detection applications may not need detailed ranging measurements for every request, yet currently receive all results causing unnecessary data transfer, host wakeups, and processing overhead. Currently, there is no mechanism for applications to suppress result reporting when only proximity detection is needed.
>> Introduce optional result suppression control that drivers can use to implement selective result reporting. Add a flag allowing applications to disable ranging reports when only proximity detection is needed, enabling drivers to reduce unnecessary data transfer and host wakeups. This flag cannot be combined > with range report or LMR feedback requests in RSTA mode as these require result reporting.
> As commented on previous patch, LMR feedback and range report are mandatory for PD ranging. Results can be not reported regardless.

yes you are right, fixed in the next version.


>
>> + * @pd_suppress_range_results: flag to suppress ranging results for PD
>> + *	requests. When set, ranging measurements are performed but results
>> + *	are not reported to userspace, regardless of ranging role or type.
>> + *	Only valid when @pd_request is set. Cannot be used with @range_report
> The title said "applications may not need detailed ranging measurements for every request", but here it says no results at all...
> So this is intended for providing ranging services to another peer?


Updated in the next version.

>
>
> ---------------------------------------------------------------------
> A member of the Intel Corporation group of companies
>
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.
>

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox