From: Ilan Peer ilan.peer@intel.com
[ Upstream commit 8f79d2f13dd3b0af00a5303d4ff913767dd7684e ]
In case that NAN is started, mark the device as non idle, and set LED triggering similar to scan and ROC. Set the device to idle once NAN is stopped.
Signed-off-by: Ilan Peer ilan.peer@intel.com Reviewed-by: Andrei Otcheretianski andrei.otcheretianski@intel.com Reviewed-by: Johannes Berg johannes.berg@intel.com Signed-off-by: Miri Korenblit miriam.rachel.korenblit@intel.com Link: https://patch.msgid.link/20250908140015.2711d62fce22.I9b9f826490e50967a66788... Signed-off-by: Johannes Berg johannes.berg@intel.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES - `ieee80211_start_nan()` now flips a dedicated `started` flag and immediately calls `ieee80211_recalc_idle()` once NAN activation succeeds (`net/mac80211/cfg.c:323-345`). Without this, the hw stays flagged idle (`IEEE80211_CONF_IDLE`) after mac80211 opens the interface, so drivers may power down the radio and NAN discovery/advertisement stops working despite reporting success. The new `-EALREADY` guard also prevents double starts that would otherwise re-run the driver callback on an already-open interface. - `ieee80211_stop_nan()` symmetrically checks the flag, calls the driver stop hook, clears `started`, and recalculates idle (`net/mac80211/cfg.c:349-360`). This guarantees the device returns to idle only after NAN really terminates, fixing the stale “busy”/LED state left behind today. - `__ieee80211_recalc_idle()` now marks the device “working” whenever any interface of type `NL80211_IFTYPE_NAN` is flagged as started (`net/mac80211/iface.c:105-146`). This ties NAN activity into the existing idle/LED machinery instead of relying on ROC/scan bookkeeping, which never covered NAN and is why the hw incorrectly entered idle before this change. - A new `bool started` in `struct ieee80211_if_nan` (`net/mac80211/ieee80211_i.h:985-999`) is the only state carried across, fits the existing zero-initialized lifetime of `sdata->u.nan`, and does not affect other interface types sharing the union. The rest of the logic and cfg80211 NAN APIs stay untouched, so drivers see no interface changes.
The regression being fixed is user-visible (NAN sessions silently stall because hw is left in idle), the patch is small and self-contained in mac80211, and the new state bit is initialized automatically. No architectural churn or external dependencies are introduced, making this a low-risk, high-value candidate for stable backporting.
net/mac80211/cfg.c | 20 +++++++++++++++++--- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/iface.c | 9 +++++++++ 3 files changed, 28 insertions(+), 3 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 7609c7c31df74..42539c3b4f282 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -320,6 +320,9 @@ static int ieee80211_start_nan(struct wiphy *wiphy,
lockdep_assert_wiphy(sdata->local->hw.wiphy);
+ if (sdata->u.nan.started) + return -EALREADY; + ret = ieee80211_check_combinations(sdata, NULL, 0, 0, -1); if (ret < 0) return ret; @@ -329,12 +332,18 @@ static int ieee80211_start_nan(struct wiphy *wiphy, return ret;
ret = drv_start_nan(sdata->local, sdata, conf); - if (ret) + if (ret) { ieee80211_sdata_stop(sdata); + return ret; + }
- sdata->u.nan.conf = *conf; + sdata->u.nan.started = true; + ieee80211_recalc_idle(sdata->local);
- return ret; + sdata->u.nan.conf.master_pref = conf->master_pref; + sdata->u.nan.conf.bands = conf->bands; + + return 0; }
static void ieee80211_stop_nan(struct wiphy *wiphy, @@ -342,8 +351,13 @@ static void ieee80211_stop_nan(struct wiphy *wiphy, { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (!sdata->u.nan.started) + return; + drv_stop_nan(sdata->local, sdata); + sdata->u.nan.started = false; ieee80211_sdata_stop(sdata); + ieee80211_recalc_idle(sdata->local); }
static int ieee80211_nan_change_conf(struct wiphy *wiphy, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 140dc7e32d4aa..7d1e93f51a67b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -977,11 +977,13 @@ struct ieee80211_if_mntr { * struct ieee80211_if_nan - NAN state * * @conf: current NAN configuration + * @started: true iff NAN is started * @func_lock: lock for @func_inst_ids * @function_inst_ids: a bitmap of available instance_id's */ struct ieee80211_if_nan { struct cfg80211_nan_conf conf; + bool started;
/* protects function_inst_ids */ spinlock_t func_lock; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index abc8cca54f4e1..a7873832d4fa6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -107,6 +107,7 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local, { bool working, scanning, active; unsigned int led_trig_start = 0, led_trig_stop = 0; + struct ieee80211_sub_if_data *iter;
lockdep_assert_wiphy(local->hw.wiphy);
@@ -117,6 +118,14 @@ static u32 __ieee80211_recalc_idle(struct ieee80211_local *local, working = !local->ops->remain_on_channel && !list_empty(&local->roc_list);
+ list_for_each_entry(iter, &local->interfaces, list) { + if (iter->vif.type == NL80211_IFTYPE_NAN && + iter->u.nan.started) { + working = true; + break; + } + } + scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);