]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mac80211_hwsim: split NAN handling into separate file
authorBenjamin Berg <benjamin.berg@intel.com>
Mon, 4 May 2026 07:20:50 +0000 (10:20 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 5 May 2026 10:10:15 +0000 (12:10 +0200)
Having everything in one file for mac80211_hwsim is starting to get a
lot and it will be even worse if we implement more parts of NAN. Split
the NAN implementation into separate files to improve the code
structuring.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260504101829.541371b35f02.I9484d746286eb2ab71ac987dfb907497d213c2bb@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
MAINTAINERS
drivers/net/wireless/virtual/Makefile
drivers/net/wireless/virtual/mac80211_hwsim_i.h [new file with mode: 0644]
drivers/net/wireless/virtual/mac80211_hwsim_main.c [moved from drivers/net/wireless/virtual/mac80211_hwsim.c with 96% similarity]
drivers/net/wireless/virtual/mac80211_hwsim_nan.c [new file with mode: 0644]
drivers/net/wireless/virtual/mac80211_hwsim_nan.h [new file with mode: 0644]

index 27a073f53cea63f44ec8eeff7c6c8169833cd707..cb0f2bf6fe2ec56a6cc2260f687d75284b3c7e98 100644 (file)
@@ -15344,7 +15344,7 @@ T:      git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git
 F:     Documentation/networking/mac80211-injection.rst
 F:     Documentation/networking/mac80211_hwsim/mac80211_hwsim.rst
-F:     drivers/net/wireless/virtual/mac80211_hwsim.[ch]
+F:     drivers/net/wireless/virtual/mac80211_hwsim*.[ch]
 F:     include/net/mac80211.h
 F:     net/mac80211/
 
index 5773cc6d643ec3b38af428d99f8dc5417f13820a..6ad860dd76439245ee58a8d5bb8a213f9e9c212b 100644 (file)
@@ -1,3 +1,5 @@
 obj-$(CONFIG_MAC80211_HWSIM)   += mac80211_hwsim.o
+mac80211_hwsim-objs            += mac80211_hwsim_main.o
+mac80211_hwsim-objs            += mac80211_hwsim_nan.o
 
 obj-$(CONFIG_VIRT_WIFI)        += virt_wifi.o
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
new file mode 100644 (file)
index 0000000..741eb08
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
+ * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 - 2025 Intel Corporation
+ */
+
+#ifndef __MAC80211_HWSIM_I_H
+#define __MAC80211_HWSIM_I_H
+
+#include <net/mac80211.h>
+#include "mac80211_hwsim.h"
+#include "mac80211_hwsim_nan.h"
+
+struct mac80211_hwsim_link_data {
+       u32 link_id;
+       u64 beacon_int  /* beacon interval in us */;
+       struct hrtimer beacon_timer;
+};
+
+#define HWSIM_NUM_CHANNELS_2GHZ                14
+#define HWSIM_NUM_CHANNELS_5GHZ                40
+#define HWSIM_NUM_CHANNELS_6GHZ                59
+#define HWSIM_NUM_S1G_CHANNELS_US      51
+#define HWSIM_NUM_RATES                        12
+#define HWSIM_NUM_CIPHERS              11
+
+struct mac80211_hwsim_data {
+       struct list_head list;
+       struct rhash_head rht;
+       struct ieee80211_hw *hw;
+       struct device *dev;
+       struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
+       struct ieee80211_channel channels_2ghz[HWSIM_NUM_CHANNELS_2GHZ];
+       struct ieee80211_channel channels_5ghz[HWSIM_NUM_CHANNELS_5GHZ];
+       struct ieee80211_channel channels_6ghz[HWSIM_NUM_CHANNELS_6GHZ];
+       struct ieee80211_channel channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
+       struct ieee80211_rate rates[HWSIM_NUM_RATES];
+       struct ieee80211_iface_combination if_combination;
+       struct ieee80211_iface_limit if_limits[4];
+       int n_if_limits;
+       /* Storage space for channels, etc. */
+       struct mac80211_hwsim_phy_data *phy_data;
+
+       struct ieee80211_iface_combination if_combination_radio;
+       struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
+       struct wiphy_radio radio[NUM_NL80211_BANDS];
+
+       u32 ciphers[HWSIM_NUM_CIPHERS];
+
+       struct mac_address addresses[3];
+       int channels, idx;
+       bool use_chanctx;
+       bool destroy_on_close;
+       u32 portid;
+       char alpha2[2];
+       const struct ieee80211_regdomain *regd;
+
+       struct ieee80211_channel *tmp_chan;
+       struct ieee80211_channel *roc_chan;
+       u32 roc_duration;
+       struct delayed_work roc_start;
+       struct delayed_work roc_done;
+       struct delayed_work hw_scan;
+       struct cfg80211_scan_request *hw_scan_request;
+       struct ieee80211_vif *hw_scan_vif;
+       int scan_chan_idx;
+       u8 scan_addr[ETH_ALEN];
+       struct {
+               struct ieee80211_channel *channel;
+               unsigned long next_start, start, end;
+       } survey_data[HWSIM_NUM_CHANNELS_2GHZ +
+                     HWSIM_NUM_CHANNELS_5GHZ +
+                     HWSIM_NUM_CHANNELS_6GHZ];
+
+       struct ieee80211_channel *channel;
+       enum nl80211_chan_width bw;
+       unsigned int rx_filter;
+       bool started, idle, scanning;
+       struct mutex mutex;
+       enum ps_mode {
+               PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
+       } ps;
+       bool ps_poll_pending;
+       struct dentry *debugfs;
+       struct cfg80211_chan_def radar_background_chandef;
+
+       atomic_t pending_cookie;
+       struct sk_buff_head pending;    /* packets pending */
+       /*
+        * Only radios in the same group can communicate together (the
+        * channel has to match too). Each bit represents a group. A
+        * radio can be in more than one group.
+        */
+       u64 group;
+
+       /* group shared by radios created in the same netns */
+       int netgroup;
+       /* wmediumd portid responsible for netgroup of this radio */
+       u32 wmediumd;
+
+       /* difference between this hw's clock and the real clock, in usecs */
+       s64 tsf_offset;
+       s64 bcn_delta;
+       /* absolute beacon transmission time. Used to cover up "tx" delay. */
+       u64 abs_bcn_ts;
+
+       /* Stats */
+       u64 tx_pkts;
+       u64 rx_pkts;
+       u64 tx_bytes;
+       u64 rx_bytes;
+       u64 tx_dropped;
+       u64 tx_failed;
+
+       /* RSSI in rx status of the receiver */
+       int rx_rssi;
+
+       /* only used when pmsr capability is supplied */
+       struct cfg80211_pmsr_capabilities pmsr_capa;
+       struct cfg80211_pmsr_request *pmsr_request;
+       struct wireless_dev *pmsr_request_wdev;
+
+       struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
+
+       struct mac80211_hwsim_nan_data nan;
+};
+
+extern spinlock_t hwsim_radio_lock;
+extern struct list_head hwsim_radios;
+
+u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
+                          struct ieee80211_vif *vif);
+
+#endif /* __MAC80211_HWSIM_I_H */
similarity index 96%
rename from drivers/net/wireless/virtual/mac80211_hwsim.c
rename to drivers/net/wireless/virtual/mac80211_hwsim_main.c
index d5b9170f690cf9ab3c6870a782002a0f19b7aa8a..4ad2c6f386634436a9936b9d98328bd466470747 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/uaccess.h>
 #include <linux/string.h>
 #include "mac80211_hwsim.h"
+#include "mac80211_hwsim_i.h"
 
 #define WARN_QUEUE 100
 #define MAX_QUEUE 200
@@ -379,6 +380,8 @@ static const struct ieee80211_channel hwsim_channels_2ghz[] = {
        CHAN2G(2472), /* Channel 13 */
        CHAN2G(2484), /* Channel 14 */
 };
+static_assert(HWSIM_NUM_CHANNELS_2GHZ == ARRAY_SIZE(hwsim_channels_2ghz),
+             "Inconsistent 2 GHz channel count");
 
 static const struct ieee80211_channel hwsim_channels_5ghz[] = {
        CHAN5G(5180), /* Channel 36 */
@@ -428,6 +431,8 @@ static const struct ieee80211_channel hwsim_channels_5ghz[] = {
        CHAN5G(5920), /* Channel 184 */
        CHAN5G(5925), /* Channel 185 */
 };
+static_assert(HWSIM_NUM_CHANNELS_5GHZ == ARRAY_SIZE(hwsim_channels_5ghz),
+             "Inconsistent 5 GHz channel count");
 
 static const struct ieee80211_channel hwsim_channels_6ghz[] = {
        CHAN6G(5955), /* Channel 1 */
@@ -490,9 +495,10 @@ static const struct ieee80211_channel hwsim_channels_6ghz[] = {
        CHAN6G(7095), /* Channel 229 */
        CHAN6G(7115), /* Channel 233 */
 };
+static_assert(HWSIM_NUM_CHANNELS_6GHZ == ARRAY_SIZE(hwsim_channels_6ghz),
+             "Inconsistent 6 GHz channel count");
 
-#define NUM_S1G_CHANS_US 51
-static struct ieee80211_channel hwsim_channels_s1g[NUM_S1G_CHANS_US];
+static struct ieee80211_channel hwsim_channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
 
 static const struct ieee80211_sta_s1g_cap hwsim_s1g_cap = {
        .s1g = true,
@@ -525,7 +531,7 @@ static void hwsim_init_s1g_channels(struct ieee80211_channel *chans)
 {
        int ch, freq;
 
-       for (ch = 0; ch < NUM_S1G_CHANS_US; ch++) {
+       for (ch = 0; ch < ARRAY_SIZE(hwsim_channels_s1g); ch++) {
                freq = 902000 + (ch + 1) * 500;
                chans[ch].band = NL80211_BAND_S1GHZ;
                chans[ch].center_freq = KHZ_TO_MHZ(freq);
@@ -548,6 +554,8 @@ static const struct ieee80211_rate hwsim_rates[] = {
        { .bitrate = 480 },
        { .bitrate = 540 }
 };
+static_assert(HWSIM_NUM_RATES == ARRAY_SIZE(hwsim_rates),
+             "Inconsistent rates count");
 
 #define DEFAULT_RX_RSSI -50
 
@@ -564,6 +572,8 @@ static const u32 hwsim_ciphers[] = {
        WLAN_CIPHER_SUITE_BIP_GMAC_128,
        WLAN_CIPHER_SUITE_BIP_GMAC_256,
 };
+static_assert(HWSIM_NUM_CIPHERS == ARRAY_SIZE(hwsim_ciphers),
+             "Inconsistent cipher count");
 
 #define OUI_QCA 0x001374
 #define QCA_NL80211_SUBCMD_TEST 1
@@ -644,12 +654,11 @@ static const struct nl80211_vendor_cmd_info mac80211_hwsim_vendor_events[] = {
        { .vendor_id = OUI_QCA, .subcmd = 1 },
 };
 
-static DEFINE_SPINLOCK(hwsim_radio_lock);
-static LIST_HEAD(hwsim_radios);
+DEFINE_SPINLOCK(hwsim_radio_lock);
+LIST_HEAD(hwsim_radios);
 static struct rhashtable hwsim_radios_rht;
 static int hwsim_radio_idx;
 static int hwsim_radios_generation = 1;
-static u8 hwsim_nan_cluster_id[ETH_ALEN];
 
 static struct platform_driver mac80211_hwsim_driver = {
        .driver = {
@@ -657,120 +666,6 @@ static struct platform_driver mac80211_hwsim_driver = {
        },
 };
 
-struct mac80211_hwsim_link_data {
-       u32 link_id;
-       u64 beacon_int  /* beacon interval in us */;
-       struct hrtimer beacon_timer;
-};
-
-struct mac80211_hwsim_nan_data {
-       struct ieee80211_vif *device_vif;
-       u8 bands;
-
-       enum nl80211_band curr_dw_band;
-       struct hrtimer timer;
-       bool notify_dw;
-};
-
-struct mac80211_hwsim_data {
-       struct list_head list;
-       struct rhash_head rht;
-       struct ieee80211_hw *hw;
-       struct device *dev;
-       struct ieee80211_supported_band bands[NUM_NL80211_BANDS];
-       struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)];
-       struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)];
-       struct ieee80211_channel channels_6ghz[ARRAY_SIZE(hwsim_channels_6ghz)];
-       struct ieee80211_channel channels_s1g[ARRAY_SIZE(hwsim_channels_s1g)];
-       struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)];
-       struct ieee80211_iface_combination if_combination;
-       struct ieee80211_iface_limit if_limits[4];
-       int n_if_limits;
-
-       struct ieee80211_iface_combination if_combination_radio;
-       struct wiphy_radio_freq_range radio_range[NUM_NL80211_BANDS];
-       struct wiphy_radio radio[NUM_NL80211_BANDS];
-
-       u32 ciphers[ARRAY_SIZE(hwsim_ciphers)];
-
-       struct mac_address addresses[3];
-       int channels, idx;
-       bool use_chanctx;
-       bool destroy_on_close;
-       u32 portid;
-       char alpha2[2];
-       const struct ieee80211_regdomain *regd;
-
-       struct ieee80211_channel *tmp_chan;
-       struct ieee80211_channel *roc_chan;
-       u32 roc_duration;
-       struct delayed_work roc_start;
-       struct delayed_work roc_done;
-       struct delayed_work hw_scan;
-       struct cfg80211_scan_request *hw_scan_request;
-       struct ieee80211_vif *hw_scan_vif;
-       int scan_chan_idx;
-       u8 scan_addr[ETH_ALEN];
-       struct {
-               struct ieee80211_channel *channel;
-               unsigned long next_start, start, end;
-       } survey_data[ARRAY_SIZE(hwsim_channels_2ghz) +
-                     ARRAY_SIZE(hwsim_channels_5ghz) +
-                     ARRAY_SIZE(hwsim_channels_6ghz)];
-
-       struct ieee80211_channel *channel;
-       enum nl80211_chan_width bw;
-       unsigned int rx_filter;
-       bool started, idle, scanning;
-       struct mutex mutex;
-       enum ps_mode {
-               PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL
-       } ps;
-       bool ps_poll_pending;
-       struct dentry *debugfs;
-       struct cfg80211_chan_def radar_background_chandef;
-
-       atomic_t pending_cookie;
-       struct sk_buff_head pending;    /* packets pending */
-       /*
-        * Only radios in the same group can communicate together (the
-        * channel has to match too). Each bit represents a group. A
-        * radio can be in more than one group.
-        */
-       u64 group;
-
-       /* group shared by radios created in the same netns */
-       int netgroup;
-       /* wmediumd portid responsible for netgroup of this radio */
-       u32 wmediumd;
-
-       /* difference between this hw's clock and the real clock, in usecs */
-       s64 tsf_offset;
-       s64 bcn_delta;
-       /* absolute beacon transmission time. Used to cover up "tx" delay. */
-       u64 abs_bcn_ts;
-
-       /* Stats */
-       u64 tx_pkts;
-       u64 rx_pkts;
-       u64 tx_bytes;
-       u64 rx_bytes;
-       u64 tx_dropped;
-       u64 tx_failed;
-
-       /* RSSI in rx status of the receiver */
-       int rx_rssi;
-
-       /* only used when pmsr capability is supplied */
-       struct cfg80211_pmsr_capabilities pmsr_capa;
-       struct cfg80211_pmsr_request *pmsr_request;
-       struct wireless_dev *pmsr_request_wdev;
-
-       struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
-
-       struct mac80211_hwsim_nan_data nan;
-};
-
 static const struct rhashtable_params hwsim_rht_params = {
        .nelem_hint = 2,
        .automatic_shrinking = true,
@@ -1327,8 +1222,8 @@ static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
        return cpu_to_le64(now + data->tsf_offset);
 }
 
-static u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
-                                 struct ieee80211_vif *vif)
+u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
+                          struct ieee80211_vif *vif)
 {
        struct mac80211_hwsim_data *data = hw->priv;
        return le64_to_cpu(__mac80211_hwsim_get_tsf(data));
@@ -4130,168 +4025,6 @@ out:
        return err;
 }
 
-static enum hrtimer_restart
-mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
-{
-       struct mac80211_hwsim_data *data =
-               container_of(timer, struct mac80211_hwsim_data,
-                            nan.timer);
-       struct ieee80211_hw *hw = data->hw;
-       u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
-       u32 dw_int = 512 * 1024;
-       u64 until_dw;
-
-       if (!data->nan.device_vif)
-               return HRTIMER_NORESTART;
-
-       if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
-               if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
-                       dw_int = 128 * 1024;
-                       data->nan.curr_dw_band = NL80211_BAND_5GHZ;
-               } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
-                       data->nan.curr_dw_band = NL80211_BAND_2GHZ;
-               }
-       }
-
-       until_dw = dw_int - do_div(tsf, dw_int);
-
-       /* The timer might fire just before the actual DW, in which case
-        * update the timeout to the actual next DW
-        */
-       if (until_dw < dw_int / 2)
-               until_dw += dw_int;
-
-       /* The above do_div() call directly modifies the 'tsf' variable, thus,
-        * use a copy so that the print below would show the original TSF.
-        */
-       wiphy_debug(hw->wiphy,
-                   "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
-                   __func__, orig_tsf, data->nan.curr_dw_band,
-                   until_dw);
-
-       hrtimer_forward_now(&data->nan.timer,
-                           ns_to_ktime(until_dw * NSEC_PER_USEC));
-
-       if (data->nan.notify_dw) {
-               struct ieee80211_channel *ch;
-               struct wireless_dev *wdev =
-                       ieee80211_vif_to_wdev(data->nan.device_vif);
-
-               if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
-                       ch = ieee80211_get_channel(hw->wiphy, 5745);
-               else
-                       ch = ieee80211_get_channel(hw->wiphy, 2437);
-
-               cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC);
-       }
-
-       return HRTIMER_RESTART;
-}
-
-static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw,
-                                   struct ieee80211_vif *vif,
-                                   struct cfg80211_nan_conf *conf)
-{
-       struct mac80211_hwsim_data *data = hw->priv;
-       u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
-       u32 dw_int = 512 * 1000;
-       u64 until_dw = dw_int - do_div(tsf, dw_int);
-       struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
-
-       if (vif->type != NL80211_IFTYPE_NAN)
-               return -EINVAL;
-
-       if (data->nan.device_vif)
-               return -EALREADY;
-
-       /* set this before starting the timer, as preemption might occur */
-       data->nan.device_vif = vif;
-       data->nan.bands = conf->bands;
-       data->nan.curr_dw_band = NL80211_BAND_2GHZ;
-
-       wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
-                   until_dw);
-
-       hrtimer_start(&data->nan.timer,
-                     ns_to_ktime(until_dw * NSEC_PER_USEC),
-                     HRTIMER_MODE_REL_SOFT);
-
-       if (!is_zero_ether_addr(conf->cluster_id) &&
-           is_zero_ether_addr(hwsim_nan_cluster_id)) {
-               memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN);
-       } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) {
-               hwsim_nan_cluster_id[0] = 0x50;
-               hwsim_nan_cluster_id[1] = 0x6f;
-               hwsim_nan_cluster_id[2] = 0x9a;
-               hwsim_nan_cluster_id[3] = 0x01;
-               hwsim_nan_cluster_id[4] = get_random_u8();
-               hwsim_nan_cluster_id[5] = get_random_u8();
-       }
-
-       data->nan.notify_dw = conf->enable_dw_notification;
-
-       cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
-                                   GFP_KERNEL);
-
-       return 0;
-}
-
-static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw,
-                                  struct ieee80211_vif *vif)
-{
-       struct mac80211_hwsim_data *data = hw->priv;
-       struct mac80211_hwsim_data *data2;
-       bool nan_cluster_running = false;
-
-       if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
-           data->nan.device_vif != vif)
-               return -EINVAL;
-
-       hrtimer_cancel(&data->nan.timer);
-       data->nan.device_vif = NULL;
-
-       spin_lock_bh(&hwsim_radio_lock);
-       list_for_each_entry(data2, &hwsim_radios, list) {
-               if (data2->nan.device_vif) {
-                       nan_cluster_running = true;
-                       break;
-               }
-       }
-       spin_unlock_bh(&hwsim_radio_lock);
-
-       if (!nan_cluster_running)
-               memset(hwsim_nan_cluster_id, 0, ETH_ALEN);
-
-       return 0;
-}
-
-static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw,
-                                           struct ieee80211_vif *vif,
-                                           struct cfg80211_nan_conf *conf,
-                                           u32 changes)
-{
-       struct mac80211_hwsim_data *data = hw->priv;
-
-       if (vif->type != NL80211_IFTYPE_NAN)
-               return -EINVAL;
-
-       if (!data->nan.device_vif)
-               return -EINVAL;
-
-       wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
-
-       /* Handle only the changes we care about for simulation purposes */
-       if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
-               data->nan.bands = conf->bands;
-               data->nan.curr_dw_band = NL80211_BAND_2GHZ;
-       }
-
-       if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
-               data->nan.notify_dw = conf->enable_dw_notification;
-
-       return 0;
-}
-
 static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw,
                                               struct cfg80211_chan_def *chan)
 {
@@ -4342,11 +4075,11 @@ static int mac80211_hwsim_set_radar_background(struct ieee80211_hw *hw,
        .get_et_strings = mac80211_hwsim_get_et_strings,        \
        .start_pmsr = mac80211_hwsim_start_pmsr,                \
        .abort_pmsr = mac80211_hwsim_abort_pmsr,                \
-       .start_nan = mac80211_hwsim_start_nan,                  \
-       .stop_nan = mac80211_hwsim_stop_nan,                    \
-       .nan_change_conf = mac80211_hwsim_change_nan_config,    \
        .set_radar_background = mac80211_hwsim_set_radar_background, \
        .set_key = mac80211_hwsim_set_key,                      \
+       .start_nan = mac80211_hwsim_nan_start,                  \
+       .stop_nan = mac80211_hwsim_nan_stop,                    \
+       .nan_change_conf = mac80211_hwsim_nan_change_config,    \
        HWSIM_DEBUGFS_OPS
 
 #define HWSIM_NON_MLO_OPS                                      \
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
new file mode 100644 (file)
index 0000000..393ea70
--- /dev/null
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#include "mac80211_hwsim_i.h"
+
+static u8 hwsim_nan_cluster_id[ETH_ALEN];
+
+enum hrtimer_restart
+mac80211_hwsim_nan_dw_start(struct hrtimer *timer)
+{
+       struct mac80211_hwsim_data *data =
+               container_of(timer, struct mac80211_hwsim_data,
+                            nan.timer);
+       struct ieee80211_hw *hw = data->hw;
+       u64 orig_tsf = mac80211_hwsim_get_tsf(hw, NULL), tsf = orig_tsf;
+       u32 dw_int = 512 * 1024;
+       u64 until_dw;
+
+       if (!data->nan.device_vif)
+               return HRTIMER_NORESTART;
+
+       if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
+               if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) {
+                       dw_int = 128 * 1024;
+                       data->nan.curr_dw_band = NL80211_BAND_5GHZ;
+               } else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) {
+                       data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+               }
+       }
+
+       until_dw = dw_int - do_div(tsf, dw_int);
+
+       /* The timer might fire just before the actual DW, in which case
+        * update the timeout to the actual next DW
+        */
+       if (until_dw < dw_int / 2)
+               until_dw += dw_int;
+
+       /* The above do_div() call directly modifies the 'tsf' variable, thus,
+        * use a copy so that the print below would show the original TSF.
+        */
+       wiphy_debug(hw->wiphy,
+                   "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n",
+                   __func__, orig_tsf, data->nan.curr_dw_band,
+                   until_dw);
+
+       hrtimer_forward_now(&data->nan.timer,
+                           ns_to_ktime(until_dw * NSEC_PER_USEC));
+
+       if (data->nan.notify_dw) {
+               struct ieee80211_channel *ch;
+               struct wireless_dev *wdev =
+                       ieee80211_vif_to_wdev(data->nan.device_vif);
+
+               if (data->nan.curr_dw_band == NL80211_BAND_5GHZ)
+                       ch = ieee80211_get_channel(hw->wiphy, 5745);
+               else
+                       ch = ieee80211_get_channel(hw->wiphy, 2437);
+
+               cfg80211_next_nan_dw_notif(wdev, ch, GFP_ATOMIC);
+       }
+
+       return HRTIMER_RESTART;
+}
+
+int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct cfg80211_nan_conf *conf)
+{
+       struct mac80211_hwsim_data *data = hw->priv;
+       u64 tsf = mac80211_hwsim_get_tsf(hw, NULL);
+       u32 dw_int = 512 * 1000;
+       u64 until_dw = dw_int - do_div(tsf, dw_int);
+       struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
+
+       if (vif->type != NL80211_IFTYPE_NAN)
+               return -EINVAL;
+
+       if (data->nan.device_vif)
+               return -EALREADY;
+
+       /* set this before starting the timer, as preemption might occur */
+       data->nan.device_vif = vif;
+       data->nan.bands = conf->bands;
+       data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+
+       wiphy_debug(hw->wiphy, "nan_started, next_dw=%llu\n",
+                   until_dw);
+
+       hrtimer_start(&data->nan.timer,
+                     ns_to_ktime(until_dw * NSEC_PER_USEC),
+                     HRTIMER_MODE_REL_SOFT);
+
+       if (!is_zero_ether_addr(conf->cluster_id) &&
+           is_zero_ether_addr(hwsim_nan_cluster_id)) {
+               memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN);
+       } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) {
+               hwsim_nan_cluster_id[0] = 0x50;
+               hwsim_nan_cluster_id[1] = 0x6f;
+               hwsim_nan_cluster_id[2] = 0x9a;
+               hwsim_nan_cluster_id[3] = 0x01;
+               hwsim_nan_cluster_id[4] = get_random_u8();
+               hwsim_nan_cluster_id[5] = get_random_u8();
+       }
+
+       data->nan.notify_dw = conf->enable_dw_notification;
+
+       cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
+                                   GFP_KERNEL);
+
+       return 0;
+}
+
+int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif)
+{
+       struct mac80211_hwsim_data *data = hw->priv;
+       struct mac80211_hwsim_data *data2;
+       bool nan_cluster_running = false;
+
+       if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
+           data->nan.device_vif != vif)
+               return -EINVAL;
+
+       hrtimer_cancel(&data->nan.timer);
+       data->nan.device_vif = NULL;
+
+       spin_lock_bh(&hwsim_radio_lock);
+       list_for_each_entry(data2, &hwsim_radios, list) {
+               if (data2->nan.device_vif) {
+                       nan_cluster_running = true;
+                       break;
+               }
+       }
+       spin_unlock_bh(&hwsim_radio_lock);
+
+       if (!nan_cluster_running)
+               memset(hwsim_nan_cluster_id, 0, ETH_ALEN);
+
+       return 0;
+}
+
+int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct cfg80211_nan_conf *conf,
+                                    u32 changes)
+{
+       struct mac80211_hwsim_data *data = hw->priv;
+
+       if (vif->type != NL80211_IFTYPE_NAN)
+               return -EINVAL;
+
+       if (!data->nan.device_vif)
+               return -EINVAL;
+
+       wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
+
+       /* Handle only the changes we care about for simulation purposes */
+       if (changes & CFG80211_NAN_CONF_CHANGED_BANDS) {
+               data->nan.bands = conf->bands;
+               data->nan.curr_dw_band = NL80211_BAND_2GHZ;
+       }
+
+       if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
+               data->nan.notify_dw = conf->enable_dw_notification;
+
+       return 0;
+}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
new file mode 100644 (file)
index 0000000..eac64ac
--- /dev/null
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
+ * Copyright (C) 2025 Intel Corporation
+ */
+
+#ifndef __MAC80211_HWSIM_NAN_H
+#define __MAC80211_HWSIM_NAN_H
+
+struct mac80211_hwsim_nan_data {
+       struct ieee80211_vif *device_vif;
+       u8 bands;
+
+       enum nl80211_band curr_dw_band;
+       struct hrtimer timer;
+       bool notify_dw;
+};
+
+enum hrtimer_restart
+mac80211_hwsim_nan_dw_start(struct hrtimer *timer);
+
+int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
+                            struct ieee80211_vif *vif,
+                            struct cfg80211_nan_conf *conf);
+
+int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
+                           struct ieee80211_vif *vif);
+
+int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
+                                    struct ieee80211_vif *vif,
+                                    struct cfg80211_nan_conf *conf,
+                                    u32 changes);
+
+#endif /* __MAC80211_HWSIM_NAN_H */