]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211: update NAN data path state on schedule changes
authorMiri Korenblit <miriam.rachel.korenblit@intel.com>
Thu, 26 Mar 2026 10:14:40 +0000 (12:14 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 7 Apr 2026 13:36:03 +0000 (15:36 +0200)
A carrier of an NDI interface is turned on when there is at least one NDI
station that: (1) correlates to this interface (2) is authorized (3) the
NAN peer to which this station belongs has at least one common slot with
the local schedule. Otherwise, it is turned off.
(common slots are slots where both schedules are active on compatible
 channels.)

Implement the calculation of the carrier state and trigger it when
needed.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260326121156.98ff4115406f.Ie796487ab9eb23cda819b0afac57e7267b134911@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/nan.c
net/mac80211/sta_info.c

index 0c1439d31c93e800a61ca712e1c5294a93b6e182..e47197fe6d1b3736130f4f707674764599aa6b87 100644 (file)
@@ -2502,7 +2502,15 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct wireless_dev *wdev,
            test_sta_flag(sta, WLAN_STA_ASSOC))
                rate_control_rate_init_all_links(sta);
 
-       return sta_info_insert(sta);
+       err = sta_info_insert(sta);
+
+       /*
+        * ieee80211_nan_update_ndi_carrier was called from sta_apply_parameters,
+        * but then we did not have the STA in the list.
+        */
+       if (!err && sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
+               ieee80211_nan_update_ndi_carrier(sta->sdata);
+       return err;
 }
 
 static int ieee80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev,
index 23bf00472915014f29bf1598339644b0f9c9bcf3..2a693406294bc1fd7295a2bef24b63a89a905772 100644 (file)
@@ -2045,6 +2045,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
 int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
                                 struct cfg80211_nan_peer_sched *sched);
 void ieee80211_nan_free_peer_sched(struct ieee80211_nan_peer_sched *sched);
+void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata);
 
 /* scan/BSS handling */
 void ieee80211_scan_work(struct wiphy *wiphy, struct wiphy_work *work);
index 5e1f9bb7c49d8451bba700e8167171d3ded83901..4e262b624521d21e001148cce3a58a1c39b0d1da 100644 (file)
@@ -220,6 +220,23 @@ ieee80211_nan_remove_channel(struct ieee80211_sub_if_data *sdata,
                ieee80211_free_chanctx(sdata->local, ctx, false);
 }
 
+static void
+ieee80211_nan_update_all_ndi_carriers(struct ieee80211_local *local)
+{
+       struct ieee80211_sub_if_data *sdata;
+
+       lockdep_assert_wiphy(local->hw.wiphy);
+
+       /* Iterate all interfaces and update carrier for NDI interfaces */
+       list_for_each_entry(sdata, &local->interfaces, list) {
+               if (!ieee80211_sdata_running(sdata) ||
+                   sdata->vif.type != NL80211_IFTYPE_NAN_DATA)
+                       continue;
+
+               ieee80211_nan_update_ndi_carrier(sdata);
+       }
+}
+
 static struct ieee80211_nan_channel *
 ieee80211_nan_find_free_channel(struct ieee80211_nan_sched_cfg *sched_cfg)
 {
@@ -347,6 +364,7 @@ int ieee80211_nan_set_local_sched(struct ieee80211_sub_if_data *sdata,
        if (sched_cfg->deferred)
                return 0;
 
+       ieee80211_nan_update_all_ndi_carriers(sdata->local);
        bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
 
        return 0;
@@ -409,6 +427,7 @@ err:
        bitmap_zero(sdata->u.nan.removed_channels, IEEE80211_NAN_MAX_CHANNELS);
 
        drv_vif_cfg_changed(sdata->local, sdata, BSS_CHANGED_NAN_LOCAL_SCHED);
+       ieee80211_nan_update_all_ndi_carriers(sdata->local);
        return ret;
 }
 
@@ -423,6 +442,8 @@ void ieee80211_nan_sched_update_done(struct ieee80211_vif *vif)
        if (WARN_ON(!sched_cfg->deferred))
                return;
 
+       ieee80211_nan_update_all_ndi_carriers(sdata->local);
+
        /*
         * Clear the deferred flag before removing channels. Removing channels
         * will trigger another schedule update to the driver, and there is no
@@ -531,6 +552,91 @@ ieee80211_nan_init_peer_map(struct ieee80211_nan_peer_sched *peer_sched,
        }
 }
 
+/*
+ * Check if the local schedule and a peer schedule have at least one common
+ * slot - a slot where both schedules are active on compatible channels.
+ */
+static bool
+ieee80211_nan_has_common_slots(struct ieee80211_sub_if_data *sdata,
+                              struct ieee80211_nan_peer_sched *peer_sched)
+{
+       for (int slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; slot++) {
+               struct ieee80211_nan_channel *local_chan =
+                       sdata->vif.cfg.nan_sched.schedule[slot];
+
+               if (!local_chan || !local_chan->chanctx_conf)
+                       continue;
+
+               /* Check all peer maps for this slot */
+               for (int m = 0; m < CFG80211_NAN_MAX_PEER_MAPS; m++) {
+                       struct ieee80211_nan_peer_map *map = &peer_sched->maps[m];
+                       struct ieee80211_nan_channel *peer_chan;
+
+                       if (map->map_id == CFG80211_NAN_INVALID_MAP_ID)
+                               continue;
+
+                       peer_chan = map->slots[slot];
+                       if (!peer_chan)
+                               continue;
+
+                       if (local_chan->chanctx_conf == peer_chan->chanctx_conf)
+                               return true;
+               }
+       }
+
+       return false;
+}
+
+void ieee80211_nan_update_ndi_carrier(struct ieee80211_sub_if_data *ndi_sdata)
+{
+       struct ieee80211_local *local = ndi_sdata->local;
+       struct ieee80211_sub_if_data *nmi_sdata;
+       struct sta_info *sta;
+
+       lockdep_assert_wiphy(local->hw.wiphy);
+
+       if (WARN_ON(ndi_sdata->vif.type != NL80211_IFTYPE_NAN_DATA ||
+                   !ndi_sdata->dev) || !ieee80211_sdata_running(ndi_sdata))
+               return;
+
+       nmi_sdata = wiphy_dereference(local->hw.wiphy, ndi_sdata->u.nan_data.nmi);
+       if (WARN_ON(!nmi_sdata))
+               return;
+
+       list_for_each_entry(sta, &local->sta_list, list) {
+               struct ieee80211_sta *nmi_sta;
+
+               if (sta->sdata != ndi_sdata ||
+                   !test_sta_flag(sta, WLAN_STA_AUTHORIZED))
+                       continue;
+
+               nmi_sta = wiphy_dereference(local->hw.wiphy, sta->sta.nmi);
+               if (WARN_ON(!nmi_sta) || !nmi_sta->nan_sched)
+                       continue;
+
+               if (ieee80211_nan_has_common_slots(nmi_sdata, nmi_sta->nan_sched)) {
+                       netif_carrier_on(ndi_sdata->dev);
+                       return;
+               }
+       }
+
+       netif_carrier_off(ndi_sdata->dev);
+}
+
+static void
+ieee80211_nan_update_peer_ndis_carrier(struct ieee80211_local *local,
+                                      struct sta_info *nmi_sta)
+{
+       struct sta_info *sta;
+
+       lockdep_assert_wiphy(local->hw.wiphy);
+
+       list_for_each_entry(sta, &local->sta_list, list) {
+               if (rcu_access_pointer(sta->sta.nmi) == &nmi_sta->sta)
+                       ieee80211_nan_update_ndi_carrier(sta->sdata);
+       }
+}
+
 int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
                                 struct cfg80211_nan_peer_sched *sched)
 {
@@ -592,6 +698,8 @@ int ieee80211_nan_set_peer_sched(struct ieee80211_sub_if_data *sdata,
                goto out;
        }
 
+       ieee80211_nan_update_peer_ndis_carrier(sdata->local, sta);
+
        /* Success - free old schedule */
        to_free = old_sched;
        ret = 0;
index fb0cfd4d16d38994b964eb156ad3e2cd55b71051..4c31ef8817ce07a559ce589e826ac726427e0645 100644 (file)
@@ -1454,6 +1454,8 @@ static int _sta_info_move_state(struct sta_info *sta,
                } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
                        ieee80211_vif_dec_num_mcast(sta->sdata);
                        clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
+                       if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
+                               ieee80211_nan_update_ndi_carrier(sta->sdata);
 
                        /*
                         * If we have encryption offload, flush (station) queues
@@ -1482,6 +1484,8 @@ static int _sta_info_move_state(struct sta_info *sta,
                        set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
                        ieee80211_check_fast_xmit(sta);
                        ieee80211_check_fast_rx(sta);
+                       if (sta->sdata->vif.type == NL80211_IFTYPE_NAN_DATA)
+                               ieee80211_nan_update_ndi_carrier(sta->sdata);
                }
                if (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
                    sta->sdata->vif.type == NL80211_IFTYPE_AP)