From: Benjamin Berg The NAN notification is for 5745 MHz which corresponds to channel 149 and not 5475 which is not actually a valid channel. This could result in a NULL pointer dereference in cfg80211_next_nan_dw_notif. Fixes: a37a6f54439b ("wifi: mac80211_hwsim: Add simulation support for NAN device") Signed-off-by: Benjamin Berg Reviewed-by: Ilan Peer Reviewed-by: Johannes Berg Reviewed-by: Miriam Rachel Korenblit --- drivers/net/wireless/virtual/mac80211_hwsim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 551f5eb4e747..92427f527286 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4040,7 +4040,7 @@ mac80211_hwsim_nan_dw_start(struct hrtimer *timer) ieee80211_vif_to_wdev(data->nan_device_vif); if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) - ch = ieee80211_get_channel(hw->wiphy, 5475); + ch = ieee80211_get_channel(hw->wiphy, 5745); else ch = ieee80211_get_channel(hw->wiphy, 2437); -- 2.51.1 From: Benjamin Berg The hwsim_radio_lock spinlock expects bottom-half to be disabled, fix the call in mac80211_hwsim_nan_stop to ensure BHs are disabled. Fixes: a37a6f54439b ("wifi: mac80211_hwsim: Add simulation support for NAN device") Signed-off-by: Benjamin Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 92427f527286..79cc63272134 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4112,14 +4112,14 @@ static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw, hrtimer_cancel(&data->nan_timer); data->nan_device_vif = NULL; - spin_lock(&hwsim_radio_lock); + 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(&hwsim_radio_lock); + spin_unlock_bh(&hwsim_radio_lock); if (!nan_cluster_running) memset(hwsim_nan_cluster_id, 0, ETH_ALEN); -- 2.51.1 From: Benjamin Berg The struct also contains nan_device_vif and that is the member that is being used. Signed-off-by: Benjamin Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index 79cc63272134..dc0ef30f96b1 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -760,7 +760,6 @@ struct mac80211_hwsim_data { enum nl80211_band nan_curr_dw_band; struct hrtimer nan_timer; bool notify_dw; - struct ieee80211_vif *nan_vif; }; static const struct rhashtable_params hwsim_rht_params = { -- 2.51.1 From: Benjamin Berg Move it all into a common struct to better segment the code. Signed-off-by: Benjamin Berg --- drivers/net/wireless/virtual/mac80211_hwsim.c | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index dc0ef30f96b1..05e27c2fbdc9 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -659,6 +659,15 @@ struct mac80211_hwsim_link_data { 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; @@ -754,12 +763,7 @@ struct mac80211_hwsim_data { struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; - struct ieee80211_vif *nan_device_vif; - u8 nan_bands; - - enum nl80211_band nan_curr_dw_band; - struct hrtimer nan_timer; - bool notify_dw; + struct mac80211_hwsim_nan_data nan; }; static const struct rhashtable_params hwsim_rht_params = { @@ -1979,9 +1983,9 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, * on channel 6 or channel 149, unless a ROC is in progress (for * USD use cases). */ - if (data->nan_curr_dw_band == NL80211_BAND_2GHZ) + if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) channel = ieee80211_get_channel(hw->wiphy, 2437); - else if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) + else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) channel = ieee80211_get_channel(hw->wiphy, 5745); else channel = NULL; @@ -3996,21 +4000,21 @@ mac80211_hwsim_nan_dw_start(struct hrtimer *timer) { struct mac80211_hwsim_data *data = container_of(timer, struct mac80211_hwsim_data, - nan_timer); + 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) + 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) { + 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; + 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; } } @@ -4027,18 +4031,18 @@ mac80211_hwsim_nan_dw_start(struct hrtimer *timer) */ wiphy_debug(hw->wiphy, "%s: tsf=%llx, curr_dw_band=%u, next_dw=%llu\n", - __func__, orig_tsf, data->nan_curr_dw_band, + __func__, orig_tsf, data->nan.curr_dw_band, until_dw); - hrtimer_forward_now(&data->nan_timer, + hrtimer_forward_now(&data->nan.timer, ns_to_ktime(until_dw * NSEC_PER_USEC)); - if (data->notify_dw) { + if (data->nan.notify_dw) { struct ieee80211_channel *ch; struct wireless_dev *wdev = - ieee80211_vif_to_wdev(data->nan_device_vif); + ieee80211_vif_to_wdev(data->nan.device_vif); - if (data->nan_curr_dw_band == NL80211_BAND_5GHZ) + if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) ch = ieee80211_get_channel(hw->wiphy, 5745); else ch = ieee80211_get_channel(hw->wiphy, 2437); @@ -4062,18 +4066,18 @@ static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw, if (vif->type != NL80211_IFTYPE_NAN) return -EINVAL; - if (data->nan_device_vif) + 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; + 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, + hrtimer_start(&data->nan.timer, ns_to_ktime(until_dw * NSEC_PER_USEC), HRTIMER_MODE_REL_SOFT); @@ -4089,7 +4093,7 @@ static int mac80211_hwsim_start_nan(struct ieee80211_hw *hw, hwsim_nan_cluster_id[5] = get_random_u8(); } - data->notify_dw = conf->enable_dw_notification; + data->nan.notify_dw = conf->enable_dw_notification; cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true, GFP_KERNEL); @@ -4104,16 +4108,16 @@ static int mac80211_hwsim_stop_nan(struct ieee80211_hw *hw, 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) + 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; + 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) { + if (data2->nan.device_vif) { nan_cluster_running = true; break; } @@ -4136,19 +4140,19 @@ static int mac80211_hwsim_change_nan_config(struct ieee80211_hw *hw, if (vif->type != NL80211_IFTYPE_NAN) return -EINVAL; - if (!data->nan_device_vif) + 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; + data->nan.bands = conf->bands; + data->nan.curr_dw_band = NL80211_BAND_2GHZ; } if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG) - data->notify_dw = conf->enable_dw_notification; + data->nan.notify_dw = conf->enable_dw_notification; return 0; } @@ -5519,7 +5523,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED | NAN_DEV_CAPA_NDPE_SUPPORTED; - hrtimer_setup(&data->nan_timer, mac80211_hwsim_nan_dw_start, + hrtimer_setup(&data->nan.timer, mac80211_hwsim_nan_dw_start, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT); } -- 2.51.1 From: Benjamin Berg 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 --- drivers/net/wireless/virtual/Makefile | 2 + .../net/wireless/virtual/mac80211_hwsim_i.h | 136 ++++++++ ...mac80211_hwsim.c => mac80211_hwsim_main.c} | 307 ++---------------- .../net/wireless/virtual/mac80211_hwsim_nan.c | 171 ++++++++++ .../net/wireless/virtual/mac80211_hwsim_nan.h | 34 ++ 5 files changed, 363 insertions(+), 287 deletions(-) create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_i.h rename drivers/net/wireless/virtual/{mac80211_hwsim.c => mac80211_hwsim_main.c} (96%) create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.c create mode 100644 drivers/net/wireless/virtual/mac80211_hwsim_nan.h diff --git a/drivers/net/wireless/virtual/Makefile b/drivers/net/wireless/virtual/Makefile index 5773cc6d643e..6ad860dd7643 100644 --- a/drivers/net/wireless/virtual/Makefile +++ b/drivers/net/wireless/virtual/Makefile @@ -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 index 000000000000..e1a36eb7a3d5 --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211 + * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2011, Javier Lopez + * Copyright (c) 2016 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 - 2025 Intel Corporation + */ + +#ifndef __MAC80211_HWSIM_I_H +#define __MAC80211_HWSIM_I_H + +#include +#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; + + 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 */ diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c similarity index 96% rename from drivers/net/wireless/virtual/mac80211_hwsim.c rename to drivers/net/wireless/virtual/mac80211_hwsim_main.c index 05e27c2fbdc9..dc2e2a2ece7b 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -36,7 +36,7 @@ #include #include #include -#include "mac80211_hwsim.h" +#include "mac80211_hwsim_i.h" #define WARN_QUEUE 100 #define MAX_QUEUE 200 @@ -375,6 +375,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 */ @@ -424,6 +426,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 */ @@ -486,9 +490,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, @@ -521,7 +526,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); @@ -544,6 +549,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 @@ -560,6 +567,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 @@ -640,12 +649,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 = { @@ -653,119 +661,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; - - 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, @@ -1227,8 +1122,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)); @@ -3995,168 +3890,6 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info) 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 (conf->cluster_id && !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; -} - #ifdef CONFIG_MAC80211_DEBUGFS #define HWSIM_DEBUGFS_OPS \ .link_add_debugfs = mac80211_hwsim_link_add_debugfs, @@ -4189,9 +3922,9 @@ static int mac80211_hwsim_change_nan_config(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, \ + .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 index 000000000000..5d7c736d7972 --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -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 (conf->cluster_id && !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 index 000000000000..eac64ac37589 --- /dev/null +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -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 */ -- 2.51.1 From: Benjamin Berg The mac80211_hwsim base time for the simulation of the TSF was based on the real time of the system. This clock is subject to unexpected changes. Switch it to use boottime which is always monotonic and also continues to run through times where the system is suspended. Also change the function name from tsf_raw to sim_tsf to better differentiate between the TSF of the mac and the TSF base of the simulation. Signed-off-by: Benjamin Berg --- .../wireless/virtual/mac80211_hwsim_main.c | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index dc2e2a2ece7b..75dfb4e51612 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1111,22 +1111,25 @@ static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static inline u64 mac80211_hwsim_get_tsf_raw(void) +static inline u64 mac80211_hwsim_get_sim_tsf(void) { - return ktime_to_us(ktime_get_real()); -} - -static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) -{ - u64 now = mac80211_hwsim_get_tsf_raw(); - return cpu_to_le64(now + data->tsf_offset); + return ktime_to_us(ktime_get_boottime()); } 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)); + u64 sim_time = mac80211_hwsim_get_sim_tsf(); + + return sim_time + data->tsf_offset; +} + +static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) +{ + u64 sim_time = mac80211_hwsim_get_sim_tsf(); + + return cpu_to_le64(sim_time + data->tsf_offset); } static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, @@ -1678,7 +1681,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; - u64 now; + u64 sim_tsf; memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_START; @@ -1731,9 +1734,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (ieee80211_is_beacon(hdr->frame_control) || ieee80211_is_probe_resp(hdr->frame_control)) { rx_status.boottime_ns = ktime_get_boottime_ns(); - now = data->abs_bcn_ts; + sim_tsf = data->abs_bcn_ts; } else { - now = mac80211_hwsim_get_tsf_raw(); + sim_tsf = mac80211_hwsim_get_sim_tsf(); } /* Copy skb to all enabled radios that are on the current frequency */ @@ -1794,7 +1797,7 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, if (mac80211_hwsim_addr_match(data2, hdr->addr1)) ack = true; - rx_status.mactime = now + data2->tsf_offset; + rx_status.mactime = sim_tsf + data2->tsf_offset; mac80211_hwsim_rx(data2, &rx_status, nskb); } @@ -2011,7 +2014,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, txrate = ieee80211_get_tx_rate(hw, txi); if (txrate) bitrate = txrate->bitrate; - ts = mac80211_hwsim_get_tsf_raw(); + ts = mac80211_hwsim_get_sim_tsf(); mgmt->u.probe_resp.timestamp = cpu_to_le64(ts + data->tsf_offset + 24 * 8 * 10 / bitrate); @@ -2194,7 +2197,7 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, mgmt = (struct ieee80211_mgmt *) skb->data; /* fake header transmission time */ - data->abs_bcn_ts = mac80211_hwsim_get_tsf_raw(); + data->abs_bcn_ts = mac80211_hwsim_get_sim_tsf(); if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { struct ieee80211_ext *ext = (void *) mgmt; -- 2.51.1 From: Benjamin Berg By delegating writing the timestamp into beacons and probe responses, we can remove the abs_bcn_ts from the global data and still avoid any time offset issues. This also seems conceptually closer to "real" hardware where the timestamp will be written late in the TX path. Move sending the SKB to the monitor interface to happen later, so that the frame timestamp has the value filled in by mac80211_hwsim. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 2 - .../wireless/virtual/mac80211_hwsim_main.c | 110 ++++++++---------- 2 files changed, 49 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index e1a36eb7a3d5..5aae80cee8ec 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -103,8 +103,6 @@ struct mac80211_hwsim_data { /* 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; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 75dfb4e51612..118c4d92d94f 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1423,6 +1423,43 @@ static inline u16 trans_tx_rate_flags_ieee2hwsim(struct ieee80211_tx_rate *rate) return result; } +static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data, + struct sk_buff *skb, u64 sim_time) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_rate *txrate; + /* TODO: get MCS */ + int bitrate = 100; + + txrate = ieee80211_get_tx_rate(data->hw, info); + if (txrate) + bitrate = txrate->bitrate; + + if (skb->len >= offsetofend(typeof(*mgmt), u.probe_resp.timestamp) && + ieee80211_is_probe_resp(hdr->frame_control)) { + mgmt->u.probe_resp.timestamp = + cpu_to_le64(sim_time + data->tsf_offset + + 24 * 8 * 10 / bitrate); + } else if (skb->len >= offsetofend(typeof(*mgmt), u.beacon.timestamp) && + ieee80211_is_beacon(mgmt->frame_control)) { + mgmt->u.beacon.timestamp = cpu_to_le64(sim_time + + data->tsf_offset + + 24 * 8 * 10 / + bitrate); + } else if (skb->len >= offsetofend(struct ieee80211_ext, + u.s1g_beacon.timestamp) && + ieee80211_is_s1g_beacon(mgmt->frame_control)) { + struct ieee80211_ext *ext = (void *)mgmt; + + ext->u.s1g_beacon.timestamp = cpu_to_le32(sim_time + + data->tsf_offset + + 10 * 8 * 10 / + bitrate); + } +} + static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct sk_buff *my_skb, int dst_portid, @@ -1438,6 +1475,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; struct hwsim_tx_rate_flag tx_attempts_flags[IEEE80211_TX_MAX_RATES]; uintptr_t cookie; + u64 sim_tsf; if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); @@ -1450,6 +1488,9 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, } } + sim_tsf = mac80211_hwsim_get_sim_tsf(); + mac80211_hwsim_write_tsf(data, my_skb, sim_tsf); + skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (skb == NULL) goto nla_put_failure; @@ -1681,7 +1722,11 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_rx_status rx_status; - u64 sim_tsf; + u64 sim_tsf = mac80211_hwsim_get_sim_tsf(); + + mac80211_hwsim_write_tsf(data, skb, sim_tsf); + + mac80211_hwsim_monitor_rx(hw, skb, chan); memset(&rx_status, 0, sizeof(rx_status)); rx_status.flag |= RX_FLAG_MACTIME_START; @@ -1724,20 +1769,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, skb_ext_reset(skb); nf_reset_ct(skb); - /* - * Get absolute mactime here so all HWs RX at the "same time", and - * absolute TX time for beacon mactime so the timestamp matches. - * Giving beacons a different mactime than non-beacons looks messy, but - * it helps the Toffset be exact and a ~10us mactime discrepancy - * probably doesn't really matter. - */ if (ieee80211_is_beacon(hdr->frame_control) || - ieee80211_is_probe_resp(hdr->frame_control)) { + ieee80211_is_probe_resp(hdr->frame_control)) rx_status.boottime_ns = ktime_get_boottime_ns(); - sim_tsf = data->abs_bcn_ts; - } else { - sim_tsf = mac80211_hwsim_get_sim_tsf(); - } /* Copy skb to all enabled radios that are on the current frequency */ spin_lock(&hwsim_radio_lock); @@ -2001,27 +2035,6 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (skb->len >= 24 + 8 && - ieee80211_is_probe_resp(hdr->frame_control)) { - /* fake header transmission time */ - struct ieee80211_mgmt *mgmt; - struct ieee80211_rate *txrate; - /* TODO: get MCS */ - int bitrate = 100; - u64 ts; - - mgmt = (struct ieee80211_mgmt *)skb->data; - txrate = ieee80211_get_tx_rate(hw, txi); - if (txrate) - bitrate = txrate->bitrate; - ts = mac80211_hwsim_get_sim_tsf(); - mgmt->u.probe_resp.timestamp = - cpu_to_le64(ts + data->tsf_offset + - 24 * 8 * 10 / bitrate); - } - - mac80211_hwsim_monitor_rx(hw, skb, channel); - /* wmediumd mode check */ _portid = READ_ONCE(data->wmediumd); @@ -2155,8 +2168,6 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, ARRAY_SIZE(txi->control.rates)); } - mac80211_hwsim_monitor_rx(hw, skb, chan); - if (_portid || hwsim_virtio_enabled) return mac80211_hwsim_tx_frame_nl(hw, skb, _portid, chan); @@ -2174,10 +2185,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct ieee80211_tx_info *info; - struct ieee80211_rate *txrate; - struct ieee80211_mgmt *mgmt; - /* TODO: get MCS */ - int bitrate = 100; if (vp->skip_beacons[link_conf->link_id]) { vp->skip_beacons[link_conf->link_id]--; @@ -2191,27 +2198,6 @@ static void __mac80211_hwsim_beacon_tx(struct ieee80211_bss_conf *link_conf, info->control.rates, ARRAY_SIZE(info->control.rates)); - txrate = ieee80211_get_tx_rate(hw, info); - if (txrate) - bitrate = txrate->bitrate; - - mgmt = (struct ieee80211_mgmt *) skb->data; - /* fake header transmission time */ - data->abs_bcn_ts = mac80211_hwsim_get_sim_tsf(); - if (ieee80211_is_s1g_beacon(mgmt->frame_control)) { - struct ieee80211_ext *ext = (void *) mgmt; - - ext->u.s1g_beacon.timestamp = cpu_to_le32(data->abs_bcn_ts + - data->tsf_offset + - 10 * 8 * 10 / - bitrate); - } else { - mgmt->u.beacon.timestamp = cpu_to_le64(data->abs_bcn_ts + - data->tsf_offset + - 24 * 8 * 10 / - bitrate); - } - mac80211_hwsim_tx_frame(hw, skb, rcu_dereference(link_conf->chanctx_conf)->def.chan); } @@ -5814,6 +5800,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, if (!found) goto out; + mac80211_hwsim_monitor_rx(data2->hw, skb, data2->channel); + /* Tx info received because the frame was broadcasted on user space, so we get all the necessary info: tx attempts and skb control buff */ -- 2.51.1 From: Benjamin Berg It is easy to calculate the next target beacon transmission time (TBTT) based on the current TSF and the beacon interval. Use this method to calculate the time to the next beacon. With this, the bcn_delta variable can be removed and drift over time due to the timer firing late is fully avoided. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 1 - .../wireless/virtual/mac80211_hwsim_main.c | 37 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index 5aae80cee8ec..87d5e7c2b643 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -102,7 +102,6 @@ struct mac80211_hwsim_data { /* difference between this hw's clock and the real clock, in usecs */ s64 tsf_offset; - s64 bcn_delta; /* Stats */ u64 tx_pkts; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 118c4d92d94f..43b969592d44 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1116,6 +1116,12 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void) return ktime_to_us(ktime_get_boottime()); } +static ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, + u64 tsf) +{ + return us_to_ktime(tsf - data->tsf_offset); +} + u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1137,8 +1143,6 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, { struct mac80211_hwsim_data *data = hw->priv; u64 now = mac80211_hwsim_get_tsf(hw, vif); - /* MLD not supported here */ - u32 bcn_int = data->link_data[0].beacon_int; u64 delta = abs(tsf - now); struct ieee80211_bss_conf *conf; @@ -1147,13 +1151,10 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, return; /* adjust after beaconing with new timestamp at old TBTT */ - if (tsf > now) { + if (tsf > now) data->tsf_offset += delta; - data->bcn_delta = do_div(delta, bcn_int); - } else { + else data->tsf_offset -= delta; - data->bcn_delta = -(s64)do_div(delta, bcn_int); - } } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, @@ -2274,7 +2275,8 @@ mac80211_hwsim_beacon(struct hrtimer *timer) container_of(link_data, struct mac80211_hwsim_data, link_data[link_data->link_id]); struct ieee80211_hw *hw = data->hw; - u64 bcn_int = link_data->beacon_int; + u64 tsf_now; + u64 tbtt; if (!data->started) return HRTIMER_NORESTART; @@ -2283,13 +2285,18 @@ mac80211_hwsim_beacon(struct hrtimer *timer) hw, IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_beacon_tx, link_data); - /* beacon at new TBTT + beacon interval */ - if (data->bcn_delta) { - bcn_int -= data->bcn_delta; - data->bcn_delta = 0; - } - hrtimer_forward_now(&link_data->beacon_timer, - ns_to_ktime(bcn_int * NSEC_PER_USEC)); + /* TSF is the same for all VIFs, parameter is unused */ + tsf_now = mac80211_hwsim_get_tsf(hw, NULL); + + /* Wrap value to be after the next TBTT */ + tbtt = tsf_now + link_data->beacon_int; + + /* Round TBTT down to the correct time */ + tbtt = tbtt - tbtt % link_data->beacon_int; + + hrtimer_set_expires(&link_data->beacon_timer, + mac80211_hwsim_tsf_to_boottime(data, tbtt)); + return HRTIMER_RESTART; } -- 2.51.1 From: Benjamin Berg Refactor the NAN timer to more closely track the where in the NAN schedule the device currently is. Do this by having an hrtimer that fires at the start of every slot. For now continue to update the current channel at the start of the DW. In the future, the correct channel according to the schedule should be used everywhere. This is in preparation to more accurately simulate more of the NAN logic. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 5 + .../wireless/virtual/mac80211_hwsim_main.c | 22 +-- .../net/wireless/virtual/mac80211_hwsim_nan.c | 160 ++++++++++++------ .../net/wireless/virtual/mac80211_hwsim_nan.h | 8 +- 4 files changed, 133 insertions(+), 62 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index 87d5e7c2b643..890bf8ac064a 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -127,6 +127,11 @@ struct mac80211_hwsim_data { extern spinlock_t hwsim_radio_lock; extern struct list_head hwsim_radios; +ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, + u64 tsf); +u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, + ktime_t ts); + u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 43b969592d44..d6b4a80452b5 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1116,12 +1116,18 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void) return ktime_to_us(ktime_get_boottime()); } -static ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, - u64 tsf) +ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, + u64 tsf) { return us_to_ktime(tsf - data->tsf_offset); } +u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, + ktime_t ts) +{ + return ktime_to_us(ts + data->tsf_offset); +} + u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1916,12 +1922,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, * on channel 6 or channel 149, unless a ROC is in progress (for * USD use cases). */ - if (data->nan.curr_dw_band == NL80211_BAND_2GHZ) - channel = ieee80211_get_channel(hw->wiphy, 2437); - else if (data->nan.curr_dw_band == NL80211_BAND_5GHZ) - channel = ieee80211_get_channel(hw->wiphy, 5745); - else - channel = NULL; + channel = data->nan.channel; if (WARN_ON(!channel)) { ieee80211_free_txskb(hw, skb); @@ -5252,8 +5253,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED | NAN_DEV_CAPA_NDPE_SUPPORTED; - hrtimer_setup(&data->nan.timer, mac80211_hwsim_nan_dw_start, - CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT); + hrtimer_setup(&data->nan.slot_timer, + mac80211_hwsim_nan_slot_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); } data->if_combination.radar_detect_widths = diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index 5d7c736d7972..f4191954ca9d 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -6,63 +6,132 @@ #include "mac80211_hwsim_i.h" +/* Defined as the lower 23 bits being zero */ +#define DW0_TSF_MASK GENMASK(22, 0) + +/* DWs are repeated every 512 TUs */ +#define DWST_TU 512 +#define DWST_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1) + +#define SLOT_TU 16 +#define SLOT_TSF_MASK (ieee80211_tu_to_usec(DWST_TU) - 1) + +/* The 2.4 GHz DW is at the start, the 5 GHz is in slot 8 (after 128 TUs) */ +#define DW_5G_OFFSET_TU 128 + +#define SLOT_24GHZ_DW 0 +#define SLOT_5GHZ_DW (DW_5G_OFFSET_TU / SLOT_TU) + +/* The special DW0 happens every 16 DWSTs (8192 TUs) */ +static_assert(16 * DWST_TU * 1024 == 8192 * 1024); +static_assert(DW0_TSF_MASK + 1 == 8192 * 1024); + static u8 hwsim_nan_cluster_id[ETH_ALEN]; +static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data) +{ + ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer); + + return mac80211_hwsim_boottime_to_tsf(data, expires); +} + +static u8 hwsim_nan_slot_from_tsf(u64 tsf) +{ + return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU); +} + +static void +mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot) +{ + u64 tsf = hwsim_nan_get_timer_tsf(data); + + /* Only called by mac80211_hwsim_nan_dw_timer from softirq context */ + lockdep_assert_in_softirq(); + + tsf &= ~DWST_TSF_MASK; + tsf += ieee80211_tu_to_usec(slot * SLOT_TU); + + hrtimer_set_expires(&data->nan.slot_timer, + mac80211_hwsim_tsf_to_boottime(data, tsf)); +} + +static void +mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data) +{ + /* + * Handle NAN role and state transitions at the end of the DW period + * in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e. + * end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW. + * + * TODO: Implement + */ +} + enum hrtimer_restart -mac80211_hwsim_nan_dw_start(struct hrtimer *timer) +mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) { struct mac80211_hwsim_data *data = container_of(timer, struct mac80211_hwsim_data, - nan.timer); + nan.slot_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; + struct ieee80211_channel *notify_dw_chan = NULL; + u64 tsf = hwsim_nan_get_timer_tsf(data); + u8 slot = hwsim_nan_slot_from_tsf(tsf); + bool dwst_of_dw0 = false; + bool dw_end = false; 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; - } - } + if ((tsf & DW0_TSF_MASK & ~DWST_TSF_MASK) == 0) + dwst_of_dw0 = true; - 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; + switch (slot) { + case SLOT_24GHZ_DW: + wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n", + dwst_of_dw0); + data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437); + break; - /* 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); + case SLOT_24GHZ_DW + 1: + if (!(data->nan.bands & BIT(NL80211_BAND_5GHZ))) { + notify_dw_chan = ieee80211_get_channel(hw->wiphy, 2437); + dw_end = true; + } else { + notify_dw_chan = ieee80211_get_channel(hw->wiphy, 5745); + } + break; - hrtimer_forward_now(&data->nan.timer, - ns_to_ktime(until_dw * NSEC_PER_USEC)); + case SLOT_5GHZ_DW: + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { + wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n"); + data->nan.channel = + ieee80211_get_channel(hw->wiphy, 5745); + } + break; + + case SLOT_5GHZ_DW + 1: + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { + notify_dw_chan = + ieee80211_get_channel(hw->wiphy, 2437); + dw_end = true; + } + break; + } + + if (dw_end) + mac80211_hwsim_nan_exec_state_transitions(data); - if (data->nan.notify_dw) { - struct ieee80211_channel *ch; + if (data->nan.notify_dw && notify_dw_chan) { 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); + cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC); } + mac80211_hwsim_nan_schedule_slot(data, slot + 1); + return HRTIMER_RESTART; } @@ -71,9 +140,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, 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) @@ -85,13 +151,11 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, /* 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; + data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437); - 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), + /* Just run this "soon" and start in a random schedule position */ + hrtimer_start(&data->nan.slot_timer, + ns_to_ktime(10 * NSEC_PER_USEC), HRTIMER_MODE_REL_SOFT); if (conf->cluster_id && !is_zero_ether_addr(conf->cluster_id) && @@ -125,7 +189,7 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw, data->nan.device_vif != vif) return -EINVAL; - hrtimer_cancel(&data->nan.timer); + hrtimer_cancel(&data->nan.slot_timer); data->nan.device_vif = NULL; spin_lock_bh(&hwsim_radio_lock); @@ -159,10 +223,8 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, 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) { + 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; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index eac64ac37589..e86e7f9e9a3c 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -11,13 +11,15 @@ struct mac80211_hwsim_nan_data { struct ieee80211_vif *device_vif; u8 bands; - enum nl80211_band curr_dw_band; - struct hrtimer timer; + /* Current channel of the NAN device */ + struct ieee80211_channel *channel; + + struct hrtimer slot_timer; bool notify_dw; }; enum hrtimer_restart -mac80211_hwsim_nan_dw_start(struct hrtimer *timer); +mac80211_hwsim_nan_slot_timer(struct hrtimer *timer); int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, -- 2.51.1 From: Benjamin Berg Simply immediately TX all available packets on the corresponding queue. This removes the HWSIM_TM_CMD_STOP_QUEUES/HWSIM_TM_CMD_RESUME_QUEUES feature for now. It should be reasonably simple to add it back if it is needed. Signed-off-by: Benjamin Berg --- .../wireless/virtual/mac80211_hwsim_main.c | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index d6b4a80452b5..8bfd6f984f1c 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -2062,6 +2062,17 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, ieee80211_tx_status_irqsafe(hw, skb); } +static void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_tx_control control = { + .sta = txq->sta, + }; + struct sk_buff *skb; + + while ((skb = ieee80211_tx_dequeue(hw, txq))) + mac80211_hwsim_tx(hw, &control, skb); +} static int mac80211_hwsim_start(struct ieee80211_hw *hw) { @@ -2805,11 +2816,7 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, goto nla_put_failure; return cfg80211_testmode_reply(skb); case HWSIM_TM_CMD_STOP_QUEUES: - ieee80211_stop_queues(hw); - return 0; case HWSIM_TM_CMD_WAKE_QUEUES: - ieee80211_wake_queues(hw); - return 0; default: return -EOPNOTSUPP; } @@ -3896,7 +3903,7 @@ static int hwsim_pmsr_report_nl(struct sk_buff *msg, struct genl_info *info) #define HWSIM_COMMON_OPS \ .tx = mac80211_hwsim_tx, \ - .wake_tx_queue = ieee80211_handle_wake_tx_queue, \ + .wake_tx_queue = ieee80211_hwsim_wake_tx_queue, \ .start = mac80211_hwsim_start, \ .stop = mac80211_hwsim_stop, \ .add_interface = mac80211_hwsim_add_interface, \ @@ -5331,6 +5338,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, TDLS_WIDER_BW); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); ieee80211_hw_set(hw, STRICT); + ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); + ieee80211_hw_set(hw, STA_MMPDU_TXQ); if (param->mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; @@ -5380,6 +5389,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->vif_data_size = sizeof(struct hwsim_vif_priv); hw->sta_data_size = sizeof(struct hwsim_sta_priv); hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + hw->txq_data_size = 0; memcpy(data->channels_2ghz, hwsim_channels_2ghz, sizeof(hwsim_channels_2ghz)); -- 2.51.1 From: Benjamin Berg Currently there is no TXQ for non-data frames. Add a new txq_mgmt for this purpose and create one of these on NAN devices. On NAN devices, these frames may only be transmitted during the discovery window and it is therefore helpful to schedule them using a queue. type=feature ticket=none Signed-off-by: Benjamin Berg --- include/net/mac80211.h | 2 ++ net/mac80211/iface.c | 28 ++++++++++++++++++++++++++-- net/mac80211/tx.c | 16 +++++++++++++--- net/mac80211/util.c | 33 +++++++++++++++++++++++++-------- 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index c2e49542626c..8874e5eeae7d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2034,6 +2034,7 @@ enum ieee80211_neg_ttlm_res { * @drv_priv: data area for driver use, will always be aligned to * sizeof(void \*). * @txq: the multicast data TX queue + * @txq_mgmt: the mgmt frame TX queue, currently only exists for NAN devices * @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see * &enum ieee80211_offload_flags. */ @@ -2052,6 +2053,7 @@ struct ieee80211_vif { u8 hw_queue[IEEE80211_NUM_ACS]; struct ieee80211_txq *txq; + struct ieee80211_txq *txq_mgmt; netdev_features_t netdev_features; u32 driver_flags; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 4f04d95c19d4..764f8acaacd8 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -678,6 +678,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do if (sdata->vif.txq) ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq)); + if (sdata->vif.txq_mgmt) + ieee80211_txq_purge(sdata->local, + to_txq_info(sdata->vif.txq_mgmt)); + sdata->bss = NULL; if (local->open_count == 0) @@ -2200,10 +2204,16 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, lockdep_assert_wiphy(local->hw.wiphy); if (type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN) { + int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, + sizeof(void *)); struct wireless_dev *wdev; + int txq_size = 0; + + if (type == NL80211_IFTYPE_NAN) + txq_size = sizeof(struct txq_info) + + local->hw.txq_data_size; - sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, - GFP_KERNEL); + sdata = kzalloc(size + txq_size, GFP_KERNEL); if (!sdata) return -ENOMEM; wdev = &sdata->wdev; @@ -2213,6 +2223,16 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_assign_perm_addr(local, wdev->address, type); memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr); + + /* + * Add a management TXQ for NAN devices which includes frames + * that will only be transmitted during discovery windows (DWs) + */ + if (type == NL80211_IFTYPE_NAN) { + txqi = (struct txq_info *)((unsigned long)sdata + size); + ieee80211_txq_init(sdata, NULL, txqi, + IEEE80211_NUM_TIDS); + } } else { int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, sizeof(void *)); @@ -2363,6 +2383,10 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata) if (sdata->vif.txq) ieee80211_txq_purge(sdata->local, to_txq_info(sdata->vif.txq)); + if (sdata->vif.txq_mgmt) + ieee80211_txq_purge(sdata->local, + to_txq_info(sdata->vif.txq_mgmt)); + synchronize_rcu(); cfg80211_unregister_wdev(&sdata->wdev); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9d8b0a25f73c..99b4fd51e28c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1321,6 +1321,10 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, * opt-in hardware flag. */ txq = sta->sta.txq[IEEE80211_NUM_TIDS]; + } else if ((!ieee80211_is_mgmt(hdr->frame_control) || + ieee80211_is_bufferable_mmpdu(skb)) && + !sta) { + txq = vif->txq_mgmt; } } else if (sta) { u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; @@ -1513,9 +1517,15 @@ void ieee80211_txq_init(struct ieee80211_sub_if_data *sdata, txqi->txq.vif = &sdata->vif; if (!sta) { - sdata->vif.txq = &txqi->txq; - txqi->txq.tid = 0; - txqi->txq.ac = IEEE80211_AC_BE; + txqi->txq.tid = tid; + + if (tid == IEEE80211_NUM_TIDS) { + sdata->vif.txq_mgmt = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_VO; + } else { + sdata->vif.txq = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_BE; + } return; } diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0c46009a3d63..ef7ea78da736 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -326,7 +326,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) struct ieee80211_vif *vif = &sdata->vif; struct fq *fq = &local->fq; struct ps_data *ps = NULL; - struct txq_info *txqi; + struct txq_info *to_wake[2] = {}; struct sta_info *sta; int i; @@ -345,6 +345,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { struct ieee80211_txq *txq = sta->sta.txq[i]; + struct txq_info *txqi; if (!txq) continue; @@ -364,18 +365,34 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac) } } - if (!vif->txq) - goto out; + if (vif->txq) { + struct txq_info *txqi; - txqi = to_txq_info(vif->txq); + txqi = to_txq_info(vif->txq); - if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) || - (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac) - goto out; + if (test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) && + !(ps && atomic_read(&ps->num_sta_ps)) && + ac == vif->txq->ac) + to_wake[0] = txqi; + } + + if (vif->txq_mgmt) { + struct txq_info *txqi; + + txqi = to_txq_info(vif->txq_mgmt); + + if (test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) && + ac == vif->txq->ac) + to_wake[1] = txqi; + } spin_unlock(&fq->lock); - drv_wake_tx_queue(local, txqi); + if (to_wake[0]) + drv_wake_tx_queue(local, to_wake[0]); + if (to_wake[1]) + drv_wake_tx_queue(local, to_wake[0]); + local_bh_enable(); return; out: -- 2.51.1 From: Benjamin Berg Frames submitted on the NAN device interface should only be transmitted during one of the discovery windows (DWs). It is assumed that software submits frames from the DW end notifications for the next DW period. Simulate this behaviour by checking that we are currently in a DW before transmitting from ieee80211_hwsim_wake_tx_queue. As frames will be queued up at the start of a DW, wake the management TX queue every time a DW is started. Do so with a randomized offset just to avoid every client transmitting at the same time. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 3 + .../wireless/virtual/mac80211_hwsim_main.c | 11 ++- .../net/wireless/virtual/mac80211_hwsim_nan.c | 79 +++++++++++++++++++ .../net/wireless/virtual/mac80211_hwsim_nan.h | 6 ++ 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index 890bf8ac064a..0cc87205554e 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -135,4 +135,7 @@ u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + #endif /* __MAC80211_HWSIM_I_H */ diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 8bfd6f984f1c..0f8769ccfd9a 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -2062,14 +2062,18 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, ieee80211_tx_status_irqsafe(hw, skb); } -static void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, - struct ieee80211_txq *txq) +void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) { struct ieee80211_tx_control control = { .sta = txq->sta, }; struct sk_buff *skb; + if (txq->vif->type == NL80211_IFTYPE_NAN && + !mac80211_hwsim_nan_txq_transmitting(hw, txq)) + return; + while ((skb = ieee80211_tx_dequeue(hw, txq))) mac80211_hwsim_tx(hw, &control, skb); } @@ -5263,6 +5267,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hrtimer_setup(&data->nan.slot_timer, mac80211_hwsim_nan_slot_timer, CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + hrtimer_setup(&data->nan.resume_txqs_timer, + mac80211_hwsim_nan_resume_txqs_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); } data->if_combination.radar_detect_widths = diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index f4191954ca9d..029499bf965f 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -26,8 +26,13 @@ static_assert(16 * DWST_TU * 1024 == 8192 * 1024); static_assert(DW0_TSF_MASK + 1 == 8192 * 1024); +/* Quiet time at the end of each slot where TX is suppressed */ +#define NAN_CHAN_SWITCH_TIME_US 256 + static u8 hwsim_nan_cluster_id[ETH_ALEN]; +static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data); + static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data) { ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer); @@ -130,6 +135,8 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC); } + mac80211_hwsim_nan_resume_txqs(data); + mac80211_hwsim_nan_schedule_slot(data, slot + 1); return HRTIMER_RESTART; @@ -190,6 +197,7 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw, return -EINVAL; hrtimer_cancel(&data->nan.slot_timer); + hrtimer_cancel(&data->nan.resume_txqs_timer); data->nan.device_vif = NULL; spin_lock_bh(&hwsim_radio_lock); @@ -231,3 +239,74 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, return 0; } + +static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data) +{ + u32 timeout_ns; + + /* Nothing to do if we are not in a DW */ + if (!mac80211_hwsim_nan_txq_transmitting(data->hw, + data->nan.device_vif->txq_mgmt)) + return; + + /* + * Wait a bit and also randomize things so that not everyone is TXing + * at the same time. Each slot is 16 TU long, this waits between 100 us + * and 5 ms before starting to TX (unless a new frame arrives). + */ + timeout_ns = get_random_u32_inclusive(100 * NSEC_PER_USEC, + 5 * NSEC_PER_MSEC); + + hrtimer_start(&data->nan.resume_txqs_timer, + ns_to_ktime(timeout_ns), + HRTIMER_MODE_REL_SOFT); +} + +enum hrtimer_restart +mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer) +{ + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + nan.resume_txqs_timer); + + guard(rcu)(); + + /* Wake TX queue for management frames on the NAN device interface */ + if (mac80211_hwsim_nan_txq_transmitting(data->hw, + data->nan.device_vif->txq_mgmt)) + ieee80211_hwsim_wake_tx_queue(data->hw, + data->nan.device_vif->txq_mgmt); + + return HRTIMER_NORESTART; +} + +bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct mac80211_hwsim_data *data = hw->priv; + u64 tsf; + u8 slot; + + if (WARN_ON_ONCE(!data->nan.device_vif)) + return true; + + tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif); + slot = hwsim_nan_slot_from_tsf(tsf); + + /* Enforce a maximum channel switch time and guard against TX delays */ + if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US)) + return false; + + /* Check NAN device interface management frame transmission */ + if (!txq->sta) { + /* Only transmit these during one of the DWs */ + if (slot == SLOT_24GHZ_DW || + (slot == SLOT_5GHZ_DW && + (data->nan.bands & BIT(NL80211_BAND_5GHZ)))) + return true; + + return false; + } + + return true; +} diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index e86e7f9e9a3c..6a0780797273 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -15,11 +15,14 @@ struct mac80211_hwsim_nan_data { struct ieee80211_channel *channel; struct hrtimer slot_timer; + struct hrtimer resume_txqs_timer; bool notify_dw; }; enum hrtimer_restart mac80211_hwsim_nan_slot_timer(struct hrtimer *timer); +enum hrtimer_restart +mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer); int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -33,4 +36,7 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, struct cfg80211_nan_conf *conf, u32 changes); +bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, + struct ieee80211_txq *txq); + #endif /* __MAC80211_HWSIM_NAN_H */ -- 2.51.1 From: Benjamin Berg Move the TX channel selection into the NAN specific file and select the channel based on the current slot. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_main.c | 6 +----- .../net/wireless/virtual/mac80211_hwsim_nan.c | 18 ++++++++++++++++++ .../net/wireless/virtual/mac80211_hwsim_nan.h | 3 +++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 0f8769ccfd9a..6903f6044a10 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1918,11 +1918,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, } if (vif && vif->type == NL80211_IFTYPE_NAN && !data->tmp_chan) { - /* For NAN Device simulation purposes, assume that NAN is always - * on channel 6 or channel 149, unless a ROC is in progress (for - * USD use cases). - */ - channel = data->nan.channel; + channel = mac80211_hwsim_nan_get_tx_channel(hw); if (WARN_ON(!channel)) { ieee80211_free_txskb(hw, skb); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index 029499bf965f..ce8bb73f9dd6 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -310,3 +310,21 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, return true; } + +struct ieee80211_channel * +mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw) +{ + struct mac80211_hwsim_data *data = hw->priv; + u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + u8 slot = hwsim_nan_slot_from_tsf(tsf); + + if (slot == SLOT_24GHZ_DW) + return ieee80211_get_channel(hw->wiphy, 2437); + + if (slot == SLOT_5GHZ_DW && + data->nan.bands & BIT(NL80211_BAND_5GHZ)) + return ieee80211_get_channel(hw->wiphy, 5745); + + /* drop frame and warn, NAN_CHAN_SWITCH_TIME_US should avoid races */ + return NULL; +} diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index 6a0780797273..796cc17d194e 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -39,4 +39,7 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, struct ieee80211_txq *txq); +struct ieee80211_channel * +mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw); + #endif /* __MAC80211_HWSIM_NAN_H */ -- 2.51.1 From: Benjamin Berg This moves the NAN receive into the main code and changes it so that frame RX only happens when the device is active on the channel. This limits RX to the DW slots as there is currently no datapath. With this the globally stored channel is obsolete, remove it. Signed-off-by: Benjamin Berg --- .../wireless/virtual/mac80211_hwsim_main.c | 18 ++++---- .../net/wireless/virtual/mac80211_hwsim_nan.c | 41 +++++++++++++++++-- .../net/wireless/virtual/mac80211_hwsim_nan.h | 7 ++-- 3 files changed, 52 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 6903f6044a10..4e836289d58e 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1594,6 +1594,8 @@ static bool hwsim_chans_compat(struct ieee80211_channel *c1, struct tx_iter_data { struct ieee80211_channel *channel; + struct ieee80211_rx_status *rx_status; + struct ieee80211_hw *hw; bool receive; }; @@ -1603,13 +1605,10 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, struct tx_iter_data *data = _data; int i; - /* For NAN Device simulation purposes, assume that NAN is always - * on channel 6 or channel 149. - */ if (vif->type == NL80211_IFTYPE_NAN) { - data->receive = (data->channel && - (data->channel->center_freq == 2437 || - data->channel->center_freq == 5745)); + data->receive = mac80211_hwsim_nan_receive(data->hw, + data->channel, + data->rx_status); return; } @@ -1786,7 +1785,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct sk_buff *nskb; struct tx_iter_data tx_iter_data = { .receive = false, + .hw = data2->hw, .channel = chan, + .rx_status = &rx_status, }; if (data == data2) @@ -5924,7 +5925,10 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); if (info->attrs[HWSIM_ATTR_FREQ]) { - struct tx_iter_data iter_data = {}; + struct tx_iter_data iter_data = { + .hw = data2->hw, + .rx_status = &rx_status, + }; /* throw away off-channel packets, but allow both the temporary * ("hw" scan/remain-on-channel), regular channels and links, diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index ce8bb73f9dd6..acc1cd6e24ac 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -96,7 +96,6 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) case SLOT_24GHZ_DW: wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n", dwst_of_dw0); - data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437); break; case SLOT_24GHZ_DW + 1: @@ -111,8 +110,6 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) case SLOT_5GHZ_DW: if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n"); - data->nan.channel = - ieee80211_get_channel(hw->wiphy, 5745); } break; @@ -158,7 +155,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, /* set this before starting the timer, as preemption might occur */ data->nan.device_vif = vif; data->nan.bands = conf->bands; - data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437); /* Just run this "soon" and start in a random schedule position */ hrtimer_start(&data->nan.slot_timer, @@ -328,3 +324,40 @@ mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw) /* drop frame and warn, NAN_CHAN_SWITCH_TIME_US should avoid races */ return NULL; } + +bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + struct ieee80211_rx_status *rx_status) +{ + struct mac80211_hwsim_data *data = hw->priv; + u8 slot; + + if (WARN_ON_ONCE(!data->nan.device_vif)) + return false; + + if (rx_status->rx_flags & RX_FLAG_MACTIME) { + slot = hwsim_nan_slot_from_tsf(rx_status->mactime); + } else { + u64 tsf; + + /* + * This is not perfect, but that should be fine. + * + * Assume the frame might be a bit early in relation to our + * own TSF. This is largely because the TSF sync is going to be + * pretty bad when the frame was RXed via NL and the beacon as + * well as RX timestamps are not accurate. + */ + tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + slot = hwsim_nan_slot_from_tsf(tsf + 128); + } + + if (slot == SLOT_24GHZ_DW && channel->center_freq == 2437) + return true; + + if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ) && + channel->center_freq == 5745) + return true; + + return false; +} diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index 796cc17d194e..af8dd7ff00cc 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -11,9 +11,6 @@ struct mac80211_hwsim_nan_data { struct ieee80211_vif *device_vif; u8 bands; - /* Current channel of the NAN device */ - struct ieee80211_channel *channel; - struct hrtimer slot_timer; struct hrtimer resume_txqs_timer; bool notify_dw; @@ -42,4 +39,8 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw, struct ieee80211_channel * mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw); +bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + struct ieee80211_rx_status *rx_status); + #endif /* __MAC80211_HWSIM_NAN_H */ -- 2.51.1 From: Benjamin Berg To implement NAN synchronization in hwsim, the TSF needs to be adjusted regularly from the RX path. Add a spinlock so that this can be done in a safe manner. Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 1 + .../wireless/virtual/mac80211_hwsim_main.c | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index 0cc87205554e..ee566aff4785 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -101,6 +101,7 @@ struct mac80211_hwsim_data { u32 wmediumd; /* difference between this hw's clock and the real clock, in usecs */ + spinlock_t tsf_offset_lock; s64 tsf_offset; /* Stats */ diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 4e836289d58e..78f31921d8d7 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -1119,13 +1119,17 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void) ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data, u64 tsf) { - return us_to_ktime(tsf - data->tsf_offset); + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return us_to_ktime(tsf - data->tsf_offset); + } } u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, ktime_t ts) { - return ktime_to_us(ts + data->tsf_offset); + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return ktime_to_us(ts) + data->tsf_offset; + } } u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, @@ -1134,14 +1138,18 @@ u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct mac80211_hwsim_data *data = hw->priv; u64 sim_time = mac80211_hwsim_get_sim_tsf(); - return sim_time + data->tsf_offset; + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return sim_time + data->tsf_offset; + } } static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data) { u64 sim_time = mac80211_hwsim_get_sim_tsf(); - return cpu_to_le64(sim_time + data->tsf_offset); + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + return cpu_to_le64(sim_time + data->tsf_offset); + } } static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, @@ -1156,11 +1164,13 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, if (conf && !conf->enable_beacon) return; - /* adjust after beaconing with new timestamp at old TBTT */ - if (tsf > now) - data->tsf_offset += delta; - else - data->tsf_offset -= delta; + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + /* adjust after beaconing with new timestamp at old TBTT */ + if (tsf > now) + data->tsf_offset += delta; + else + data->tsf_offset -= delta; + } } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, @@ -1440,6 +1450,8 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data, /* TODO: get MCS */ int bitrate = 100; + spin_lock_bh(&data->tsf_offset_lock); + txrate = ieee80211_get_tx_rate(data->hw, info); if (txrate) bitrate = txrate->bitrate; @@ -1465,6 +1477,8 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data, 10 * 8 * 10 / bitrate); } + + spin_unlock_bh(&data->tsf_offset_lock); } static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, @@ -5320,6 +5334,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hw->wiphy->mbssid_max_interfaces = 8; hw->wiphy->ema_max_profile_periodicity = 3; + spin_lock_init(&data->tsf_offset_lock); + data->rx_rssi = DEFAULT_RX_RSSI; INIT_DELAYED_WORK(&data->roc_start, hw_roc_start); -- 2.51.1 From: Benjamin Berg These will be needed to implement NAN synchronization in mac80211_hwsim. Signed-off-by: Benjamin Berg --- include/linux/ieee80211-nan.h | 37 +++++++++++++++++++++++++++++++++++ include/linux/ieee80211.h | 1 + 2 files changed, 38 insertions(+) diff --git a/include/linux/ieee80211-nan.h b/include/linux/ieee80211-nan.h index d07959bf8a90..a6b0c7473233 100644 --- a/include/linux/ieee80211-nan.h +++ b/include/linux/ieee80211-nan.h @@ -32,4 +32,41 @@ #define NAN_DEV_CAPA_NDPE_SUPPORTED 0x08 #define NAN_DEV_CAPA_S3_SUPPORTED 0x10 +/* NAN attributes, as defined in Wi-Fi Aware (TM) specification 4.0 Table 42 */ +#define NAN_ATTR_MASTER_INDICATION 0x00 +#define NAN_ATTR_CLUSTER_INFO 0x01 + +struct ieee80211_nan_attr { + u8 attr; + __le16 length; + u8 data[]; +} __packed; + +struct ieee80211_nan_master_indication { + u8 master_pref; + u8 random_factor; +} __packed; + +struct ieee80211_nan_anchor_master_info { + union { + __le64 master_rank; + struct { + u8 master_addr[ETH_ALEN]; + u8 random_factor; + u8 master_pref; + } __packed; + } __packed; + u8 hop_count; + __le32 ambtt; +} __packed; + +#define for_each_nan_attr(_attr, _data, _datalen) \ + for (_attr = (const struct ieee80211_nan_attr *)(_data); \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_attr >= \ + (int)sizeof(*_attr) && \ + (const u8 *)(_data) + (_datalen) - (const u8 *)_attr >= \ + (int)sizeof(*_attr) + le16_to_cpu(_attr->length); \ + _attr = (const struct ieee80211_nan_attr *) \ + (_attr->data + le16_to_cpu(_attr->length))) + #endif /* LINUX_IEEE80211_NAN_H */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 96439de55f07..773b1e3888a0 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2231,6 +2231,7 @@ struct ieee80211_multiple_bssid_configuration { #define WLAN_OUI_WFA 0x506f9a #define WLAN_OUI_TYPE_WFA_P2P 9 +#define WLAN_OUI_TYPE_WFA_NAN 0x13 #define WLAN_OUI_TYPE_WFA_DPP 0x1A #define WLAN_OUI_MICROSOFT 0x0050f2 #define WLAN_OUI_TYPE_MICROSOFT_WPA 1 -- 2.51.1 From: Benjamin Berg The function is quite useful when handling beacon timestamps. Export it so that it can be used by mac80211_hwsim and others. Signed-off-by: Benjamin Berg --- include/net/mac80211.h | 18 ++++++++++++++++++ net/mac80211/ibss.c | 2 +- net/mac80211/ieee80211_i.h | 4 ---- net/mac80211/mesh_sync.c | 2 +- net/mac80211/rx.c | 2 +- net/mac80211/scan.c | 2 +- net/mac80211/util.c | 18 +++--------------- 7 files changed, 25 insertions(+), 23 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8874e5eeae7d..66f27b0c04a9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -7273,6 +7273,24 @@ void ieee80211_disable_rssi_reports(struct ieee80211_vif *vif); */ int ieee80211_ave_rssi(struct ieee80211_vif *vif, int link_id); +/** + * ieee80211_calculate_rx_timestamp - calculate timestamp in frame + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @status: RX status + * @mpdu_len: total MPDU length (including FCS) + * @mpdu_offset: offset into MPDU to calculate timestamp at + * + * This function calculates the RX timestamp at the given MPDU offset, taking + * into account what the RX timestamp was. An offset of 0 will just normalize + * the timestamp to TSF at beginning of MPDU reception. + * + * Returns: the calculated timestamp + */ +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_hw *hw, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset); + /** * ieee80211_report_wowlan_wakeup - report WoWLAN wakeup * @vif: virtual interface diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 168f84a1353b..3ce7412b4fcd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1133,7 +1133,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (ieee80211_have_rx_timestamp(rx_status)) { /* time when timestamp field was received */ rx_timestamp = - ieee80211_calculate_rx_timestamp(local, rx_status, + ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); } else { /* diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9d9313eee59f..12d9c10ef773 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1914,10 +1914,6 @@ ieee80211_vif_get_num_mcast_if(struct ieee80211_sub_if_data *sdata) return -1; } -u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, - struct ieee80211_rx_status *status, - unsigned int mpdu_len, - unsigned int mpdu_offset); int ieee80211_hw_config(struct ieee80211_local *local, int radio_idx, u32 changed); int ieee80211_hw_conf_chan(struct ieee80211_local *local); diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 3a66b4cefca7..24a68eef7db8 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -103,7 +103,7 @@ mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, u16 stype, * section. */ if (ieee80211_have_rx_timestamp(rx_status)) - t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + t_r = ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); else t_r = drv_get_tsf(local, sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6a1899512d07..b82657f79a6e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -404,7 +404,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, while ((pos - (u8 *)rthdr) & 7) *pos++ = 0; put_unaligned_le64( - ieee80211_calculate_rx_timestamp(local, status, + ieee80211_calculate_rx_timestamp(&local->hw, status, mpdulen, 0), pos); rthdr->it_present |= cpu_to_le32(BIT(IEEE80211_RADIOTAP_TSFT)); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 5ef315ed3b0f..a536f535e315 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -216,7 +216,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, if (link_conf) { bss_meta.parent_tsf = - ieee80211_calculate_rx_timestamp(local, + ieee80211_calculate_rx_timestamp(&local->hw, rx_status, len + FCS_LEN, 24); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ef7ea78da736..b333ebd73d86 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3370,20 +3370,7 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) return 1; } -/** - * ieee80211_calculate_rx_timestamp - calculate timestamp in frame - * @local: mac80211 hw info struct - * @status: RX status - * @mpdu_len: total MPDU length (including FCS) - * @mpdu_offset: offset into MPDU to calculate timestamp at - * - * This function calculates the RX timestamp at the given MPDU offset, taking - * into account what the RX timestamp was. An offset of 0 will just normalize - * the timestamp to TSF at beginning of MPDU reception. - * - * Returns: the calculated timestamp - */ -u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_hw *hw, struct ieee80211_rx_status *status, unsigned int mpdu_len, unsigned int mpdu_offset) @@ -3502,7 +3489,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, case RX_ENC_LEGACY: { struct ieee80211_supported_band *sband; - sband = local->hw.wiphy->bands[status->band]; + sband = hw->wiphy->bands[status->band]; ri.legacy = sband->bitrates[status->rate_idx].bitrate; if (mactime_plcp_start) { @@ -3534,6 +3521,7 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, return ts; } +EXPORT_SYMBOL_GPL(ieee80211_calculate_rx_timestamp); /* Cancel CAC for the interfaces under the specified @local. If @ctx is * also provided, only the interfaces using that ctx will be canceled. -- 2.51.1 From: Benjamin Berg Add all the handling to do NAN synchronization on 2.4 GHz including sending out beacons. With this, the mac80211_hwsim NAN device also works when used in conjunction with an external medium simulation. Note that the TSF sync is not ideal in case of an external medium simulation. This is because the mactime for received frames needs to be estimated and the simulation may not update the timestamp of beacons to the actual time that the frame was transmitted. The implementation has an initial short phase where it scans for clusters. This facilitates cluster joining and avoids creating a new cluster immediately, which would result in two cluster join notifications. It does not scan otherwise and will only see another cluster appearing if a discovery beacon happens to be sent during the 2.4 GHz discovery window (DW). Signed-off-by: Benjamin Berg --- .../net/wireless/virtual/mac80211_hwsim_i.h | 4 + .../wireless/virtual/mac80211_hwsim_main.c | 18 +- .../net/wireless/virtual/mac80211_hwsim_nan.c | 721 +++++++++++++++++- .../net/wireless/virtual/mac80211_hwsim_nan.h | 43 ++ 4 files changed, 743 insertions(+), 43 deletions(-) diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h index ee566aff4785..a42f94dd6d6a 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h @@ -136,6 +136,10 @@ u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data, u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan); + void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c index 78f31921d8d7..78d658bc6478 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c @@ -914,10 +914,6 @@ static int hwsim_get_chanwidth(enum nl80211_chan_width bw) return INT_MAX; } -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan); - /* sysfs attributes */ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) { @@ -1728,6 +1724,9 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data, mac80211_hwsim_add_vendor_rtap(skb); + if (data->nan.device_vif) + mac80211_hwsim_nan_rx(data->hw, skb); + data->rx_pkts++; data->rx_bytes += skb->len; ieee80211_rx_irqsafe(data->hw, skb); @@ -2182,9 +2181,9 @@ static void mac80211_hwsim_remove_interface( mac80211_hwsim_config_mac_nl(hw, vif->addr, false); } -static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, - struct sk_buff *skb, - struct ieee80211_channel *chan) +void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan) { struct mac80211_hwsim_data *data = hw->priv; u32 _portid = READ_ONCE(data->wmediumd); @@ -5281,6 +5280,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, hrtimer_setup(&data->nan.resume_txqs_timer, mac80211_hwsim_nan_resume_txqs_timer, CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + hrtimer_setup(&data->nan.discovery_beacon_timer, + mac80211_hwsim_nan_discovery_beacon_timer, + CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT); + + spin_lock_init(&data->nan.state_lock); } data->if_combination.radar_detect_widths = diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c index acc1cd6e24ac..ded629f0d4af 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c @@ -4,6 +4,7 @@ * Copyright (C) 2025 Intel Corporation */ +#include #include "mac80211_hwsim_i.h" /* Defined as the lower 23 bits being zero */ @@ -26,11 +27,15 @@ static_assert(16 * DWST_TU * 1024 == 8192 * 1024); static_assert(DW0_TSF_MASK + 1 == 8192 * 1024); +/* warmup phase should be 120 seconds, which is approximately 225 DWSTs */ +#define NAN_WARMUP_DWST 225 + +#define NAN_RSSI_CLOSE (-60) +#define NAN_RSSI_MIDDLE (-75) + /* Quiet time at the end of each slot where TX is suppressed */ #define NAN_CHAN_SWITCH_TIME_US 256 -static u8 hwsim_nan_cluster_id[ETH_ALEN]; - static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data); static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data) @@ -45,10 +50,43 @@ static u8 hwsim_nan_slot_from_tsf(u64 tsf) return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU); } +static u64 hwsim_nan_encode_master_rank(u8 master_pref, u8 random_factor, + const u8 *addr) +{ + return ((u64)master_pref << 56) + + ((u64)random_factor << 48) + + ((u64)addr[5] << 40) + + ((u64)addr[4] << 32) + + ((u64)addr[3] << 24) + + ((u64)addr[2] << 16) + + ((u64)addr[1] << 8) + + ((u64)addr[0] << 0); +} + +static u64 hwsim_nan_get_master_rank(struct mac80211_hwsim_data *data) +{ + u8 master_pref = 0; + u8 random_factor = 0; + + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) { + master_pref = data->nan.master_pref; + random_factor = data->nan.random_factor; + } + + return hwsim_nan_encode_master_rank(master_pref, random_factor, + data->nan.device_vif->addr); +} + static void -mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot) +mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot, + bool discontinuity) { - u64 tsf = hwsim_nan_get_timer_tsf(data); + u64 tsf; + + if (!discontinuity) + tsf = hwsim_nan_get_timer_tsf(data); + else + tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); /* Only called by mac80211_hwsim_nan_dw_timer from softirq context */ lockdep_assert_in_softirq(); @@ -60,16 +98,564 @@ mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot) mac80211_hwsim_tsf_to_boottime(data, tsf)); } +void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct mac80211_hwsim_data *data = hw->priv; + const struct ieee80211_mgmt *mgmt = (void *)skb->data; + struct element *nan_elem = (void *)mgmt->u.beacon.variable; + struct ieee80211_nan_anchor_master_info *ami = NULL; + const struct ieee80211_nan_attr *nan_attr; + struct ieee80211_rx_status rx_status; + bool joined_cluster = false; + bool adopt_tsf = false; + bool is_sync_beacon; + bool is_same_cluster; + u64 master_rank = 0; + ssize_t data_len; + u8 slot; + + /* Need a NAN vendor element at the start */ + if (skb->len < (offsetofend(struct ieee80211_mgmt, u.beacon) + 6) || + !ieee80211_is_beacon(mgmt->frame_control)) + return; + + data_len = skb->len - offsetofend(struct ieee80211_mgmt, u.beacon); + + /* Copy the RX status to add a MAC timestamp if needed */ + memcpy(&rx_status, IEEE80211_SKB_RXCB(skb), + sizeof(struct ieee80211_rx_status)); + + /* And deal with the lack of mac time stamp */ + if ((rx_status.flag & RX_FLAG_MACTIME) != RX_FLAG_MACTIME_START) { + u64 tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);; + + /* In that case there should be no timestamp */ + WARN_ON_ONCE(rx_status.flag & RX_FLAG_MACTIME); + + /* No mac timestamp, set current TSF for the frame end */ + rx_status.flag |= RX_FLAG_MACTIME_END; + rx_status.mactime = tsf; + + /* And translate to the start for the rest of the code */ + rx_status.mactime = + ieee80211_calculate_rx_timestamp(hw, &rx_status, + skb->len, 0); + rx_status.flag &= ~RX_FLAG_MACTIME; + rx_status.flag |= RX_FLAG_MACTIME_START; + + /* Match mac80211_hwsim_nan_receive, see comment there */ + slot = hwsim_nan_slot_from_tsf(tsf + 128); + } else { + slot = hwsim_nan_slot_from_tsf(rx_status.mactime); + } + + /* + * (overly) simplify things, only track 2.4 GHz here. Also, ignore + * frames outside of the 2.4 GHz DW slot, unless in the initial SCAN + * phase. + */ + if ((slot != SLOT_24GHZ_DW && + data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN) || + rx_status.freq != 2437) + return; + + /* Just ignore low RSSI beacons that we cannot sync to */ + if (rx_status.signal < NAN_RSSI_MIDDLE) + return; + + /* Needs to be a valid NAN cluster ID in A3 */ + if (get_unaligned_be32(mgmt->bssid) != ((WLAN_OUI_WFA << 8) | 0x01)) + return; + + /* We are only interested in NAN beacons */ + if (nan_elem->id != WLAN_EID_VENDOR_SPECIFIC || + nan_elem->datalen < 4 || + get_unaligned_be32(nan_elem->data) != + (WLAN_OUI_WFA << 8 | WLAN_OUI_TYPE_WFA_NAN)) + return; + + u8 *nan_defragmented __free(kfree) = kzalloc(data_len, GFP_ATOMIC); + data_len = cfg80211_defragment_element(nan_elem, + mgmt->u.beacon.variable, + data_len, + nan_defragmented, data_len, + WLAN_EID_FRAGMENT); + + if (data_len < 0) + return; + + /* Assume it is a synchronization beacon if beacon_int is 512 TUs */ + is_sync_beacon = le16_to_cpu(mgmt->u.beacon.beacon_int) == DWST_TU; + is_same_cluster = ether_addr_equal(mgmt->bssid, data->nan.cluster_id); + + for_each_nan_attr(nan_attr, nan_defragmented + 4, data_len - 4) { + if (nan_attr->attr == NAN_ATTR_MASTER_INDICATION && + le16_to_cpu(nan_attr->length) >= + sizeof(struct ieee80211_nan_master_indication)) { + struct ieee80211_nan_master_indication *mi = + (void *)nan_attr->data; + + master_rank = + hwsim_nan_encode_master_rank(mi->master_pref, + mi->random_factor, + mgmt->sa); + } + + if (nan_attr->attr == NAN_ATTR_CLUSTER_INFO && + le16_to_cpu(nan_attr->length) >= + sizeof(struct ieee80211_nan_anchor_master_info)) { + ami = (void *)nan_attr->data; + + /* + * The AMBTT should be set to the beacon timestamp when + * the sender is the anchor master. We can simply + * modify the structure because we created a copy when + * defragmenting the NAN element. + */ + if (ami->hop_count == 0) + ami->ambtt = cpu_to_le32( + le64_to_cpu(mgmt->u.beacon.timestamp)); + } + } + + /* Do the rest of the processing under lock */ + spin_lock_bh(&data->nan.state_lock); + + /* + * sync beacon should be discarded if the master rank is the same + * and the AMBTT is older than 16 * 512 TUs compared to our own TSF. + * + * Subtract the AMBTT from the lowered TSF. If the AMBTT is older + * (smaller) then the calculation will not underflow. + */ + if (is_sync_beacon && ami && + ami->master_rank == data->nan.current_ami.master_rank && + (((u32)rx_status.mactime - + ieee80211_tu_to_usec(16 * 512)) - + le32_to_cpu(ami->ambtt)) < 0x8000000) { + wiphy_dbg(hw->wiphy, + "NAN: ignoring sync beacon with old AMBTT\n"); + is_sync_beacon = false; + } + + if (is_same_cluster && is_sync_beacon && + master_rank > hwsim_nan_get_master_rank(data)) { + if (rx_status.signal > NAN_RSSI_CLOSE) + data->nan.master_transition_score += 3; + else + data->nan.master_transition_score += 1; + } + + if (is_same_cluster && is_sync_beacon && ami && + ((ami->master_rank == data->nan.current_ami.master_rank && + ami->hop_count < data->nan.current_ami.hop_count) || + (master_rank > hwsim_nan_get_master_rank(data) && + ami->hop_count == data->nan.current_ami.hop_count))) { + if (rx_status.signal > NAN_RSSI_CLOSE) + data->nan.sync_transition_score += 3; + else + data->nan.sync_transition_score += 1; + } + + /* + * Decide on TSF adjustments before updating any other state + */ + if (is_same_cluster && is_sync_beacon && ami && + data->nan.current_ami.hop_count != 0) { + if (le64_to_cpu(ami->master_rank) > + le64_to_cpu(data->nan.current_ami.master_rank) && + ami->master_rank != data->nan.last_ami.master_rank) + adopt_tsf = true; + + if (le64_to_cpu(ami->master_rank) > + le64_to_cpu(data->nan.current_ami.master_rank) && + ami->master_rank == data->nan.last_ami.master_rank && + le32_to_cpu(ami->ambtt) > + le32_to_cpu(data->nan.last_ami.ambtt)) + adopt_tsf = true; + + if (le64_to_cpu(ami->master_rank) < + le64_to_cpu(data->nan.current_ami.master_rank) && + le64_to_cpu(ami->master_rank) > + hwsim_nan_get_master_rank(data) && + ether_addr_equal(ami->master_addr, + data->nan.current_ami.master_addr)) + adopt_tsf = true; + + if (ami->master_rank == data->nan.current_ami.master_rank && + le32_to_cpu(ami->ambtt) > + le32_to_cpu(data->nan.current_ami.ambtt)) + adopt_tsf = true; + + /* Anchor Master case is handled below */ + } + + /* + * NAN Cluster merging + */ + if (!is_same_cluster && ami) { + u64 curr_amr; + u64 own_cg; + u64 frame_amr; + u64 cg; + + /* Shifted down by 19 bits compared to spec */ + frame_amr = le64_to_cpu(ami->master_rank); + cg = (u64)ami->master_pref << (64 - 19); + cg += le64_to_cpu(mgmt->u.beacon.timestamp) >> 19; + + curr_amr = le64_to_cpu(data->nan.current_ami.master_rank); + own_cg = (u64)data->nan.current_ami.master_pref << (64 - 19); + own_cg += rx_status.mactime >> 19; + + /* + * Check if the cluster shall be joined + * + * When in the "scan" phase, just join immediately. + */ + if (cg > own_cg || + (cg == own_cg && frame_amr > curr_amr) || + data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) { + /* Avoid a state transition */ + data->nan.master_transition_score = 0; + data->nan.sync_transition_score = 0; + + /* + * NOTE: The spec says we should TX sync beacons on the + * old schedule after joining. We do not implement this. + */ + + wiphy_dbg(hw->wiphy, "NAN: joining cluster %pM\n", + mgmt->bssid); + + joined_cluster = true; + adopt_tsf = true; + + memcpy(&data->nan.last_ami, &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + + memcpy(data->nan.cluster_id, mgmt->bssid, ETH_ALEN); + + /* + * Assume we are UP if we joined a cluster. + * + * If the other anchor master is still in the warmup + * phase, then we may temporarily become the anchor + * master until it sets its own master preference to + * be non-zero. + */ + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP; + data->nan.random_factor_valid_dwst = 0; + } + } + + /* + * Anchor master selection + */ + /* We are not anchor master */ + if (is_same_cluster && is_sync_beacon && ami && + data->nan.current_ami.hop_count != 0) { + if (le64_to_cpu(data->nan.current_ami.master_rank) < + le64_to_cpu(ami->master_rank)) { + if (ami->master_rank == data->nan.last_ami.master_rank && + le32_to_cpu(ami->ambtt) <= + le32_to_cpu(data->nan.last_ami.ambtt)) { + /* disregard frame */ + } else { + memcpy(&data->nan.last_ami, + &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + + if (le64_to_cpu(data->nan.current_ami.master_rank) > + le64_to_cpu(ami->master_rank)) { + if (!ether_addr_equal(data->nan.current_ami.master_addr, + ami->master_addr)) { + /* disregard frame */ + } else { + u64 amr = hwsim_nan_get_master_rank(data); + + if (amr > le64_to_cpu(ami->master_rank)) { + /* assume ourselves as anchor master */ + wiphy_dbg(hw->wiphy, + "NAN: assume anchor master role\n"); + data->nan.current_ami.master_rank = + cpu_to_le64(amr); + data->nan.current_ami.hop_count = 0; + memset(&data->nan.last_ami, 0, + sizeof(data->nan.last_ami)); + data->nan.last_ami.ambtt = + data->nan.current_ami.ambtt; + data->nan.current_ami.ambtt = 0; + } else { + memcpy(&data->nan.last_ami, + &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + } + + if (data->nan.current_ami.master_rank == ami->master_rank) { + if (le32_to_cpu(data->nan.current_ami.ambtt) < + le32_to_cpu(ami->ambtt)) { + data->nan.current_ami.ambtt = ami->ambtt; + } + + if (data->nan.current_ami.hop_count > + ami->hop_count + 1) { + data->nan.current_ami.hop_count = + ami->hop_count + 1; + } + } + } + + /* We are anchor master */ + if (is_same_cluster && is_sync_beacon && + data->nan.current_ami.hop_count == 0) { + WARN_ON_ONCE(!ether_addr_equal(data->nan.current_ami.master_addr, + data->nan.device_vif->addr)); + + if (le64_to_cpu(ami->master_rank) < + le64_to_cpu(data->nan.current_ami.master_rank) || + ether_addr_equal(ami->master_addr, + data->nan.current_ami.master_addr)) { + /* disregard */ + } else { + wiphy_dbg(hw->wiphy, "NAN: lost anchor master role\n"); + adopt_tsf = true; + memcpy(&data->nan.last_ami, &data->nan.current_ami, + sizeof(data->nan.last_ami)); + memcpy(&data->nan.current_ami, ami, + sizeof(data->nan.last_ami)); + data->nan.current_ami.hop_count += 1; + } + } + + if (adopt_tsf && !data->nan.tsf_adjusted) { + int threshold = 5; + s64 adjustment; + + /* Timestamp is likely inaccurate (and late) in this case */ + if (!(IEEE80211_SKB_RXCB(skb)->flag & RX_FLAG_MACTIME)) + threshold = 128; + + adjustment = + le64_to_cpu(mgmt->u.beacon.timestamp) - + ieee80211_calculate_rx_timestamp(hw, &rx_status, + skb->len, 24); + + scoped_guard(spinlock_bh, &data->tsf_offset_lock) { + if (adjustment < -threshold || adjustment > threshold) { + if (adjustment < -(s64)ieee80211_tu_to_usec(4) || + adjustment > (s64)ieee80211_tu_to_usec(4)) + data->nan.tsf_discontinuity = true; + + wiphy_debug(hw->wiphy, + "NAN: Adjusting TSF by +/- %d us or more: %lld us (discontinuity: %d, from: %pM, old offset: %lld)\n", + threshold, adjustment, + data->nan.tsf_discontinuity, mgmt->sa, + data->tsf_offset); + } else { + /* smooth things out a little bit */ + adjustment /= 2; + } + + /* + * Do the TSF adjustment + * The flag prevents further adjustments until the next + * 2.4 GHz DW starts to avoid race conditions for + * in-flight packets. + */ + data->nan.tsf_adjusted = true; + data->tsf_offset += adjustment; + } + } + + spin_unlock_bh(&data->nan.state_lock); + + if (joined_cluster) { + struct wireless_dev *wdev = + ieee80211_vif_to_wdev(data->nan.device_vif); + + cfg80211_nan_cluster_joined(wdev, data->nan.cluster_id, false, + GFP_ATOMIC); + } +} + static void mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data) { + bool notify_join = false; + /* * Handle NAN role and state transitions at the end of the DW period * in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e. * end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW. - * - * TODO: Implement */ + + spin_lock(&data->nan.state_lock); + + /* Handle role transitions, Wi-Fi Aware version 4.0 section 3.3.6 */ + if (data->nan.master_transition_score < 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER && + data->nan.master_transition_score >= 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_SYNC && + data->nan.sync_transition_score >= 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_NON_SYNC; + else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_NON_SYNC && + data->nan.sync_transition_score < 3) + data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC; + + /* + * The discovery beacon timer will stop automatically. Make sure it is + * running if we are master. Do not bother with a proper alignment it + * will sync itself to the TSF after the first TX. + */ + if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER && + !hrtimer_active(&data->nan.discovery_beacon_timer)) + hrtimer_start(&data->nan.discovery_beacon_timer, + ns_to_ktime(10 * NSEC_PER_USEC), + HRTIMER_MODE_REL_SOFT); + + data->nan.master_transition_score = 0; + data->nan.sync_transition_score = 0; + + if (data->nan.random_factor_valid_dwst == 0) { + u64 amr; + + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) { + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_WARMUP; + data->nan.random_factor_valid_dwst = NAN_WARMUP_DWST; + + notify_join = true; + } else { + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP; + data->nan.random_factor_valid_dwst = + get_random_u32_inclusive(120, 240); + data->nan.random_factor = get_random_u8(); + } + + amr = hwsim_nan_get_master_rank(data); + + if (data->nan.current_ami.hop_count == 0) { + /* Update if we are already anchor master */ + data->nan.current_ami.master_rank = cpu_to_le64(amr); + } else if (le64_to_cpu(data->nan.current_ami.master_rank) < amr) { + /* assume role if we have a higher rank */ + wiphy_dbg(data->hw->wiphy, + "NAN: assume anchor master role\n"); + data->nan.current_ami.master_rank = cpu_to_le64(amr); + data->nan.current_ami.hop_count = 0; + memset(&data->nan.last_ami, 0, + sizeof(data->nan.last_ami)); + data->nan.last_ami.ambtt = data->nan.current_ami.ambtt; + data->nan.current_ami.ambtt = 0; + } + } else { + data->nan.random_factor_valid_dwst--; + } + + spin_unlock(&data->nan.state_lock); + + if (notify_join) { + struct wireless_dev *wdev = + ieee80211_vif_to_wdev(data->nan.device_vif); + + cfg80211_nan_cluster_joined(wdev, data->nan.cluster_id, true, + GFP_ATOMIC); + } +} + +static void +mac80211_hwsim_nan_tx_beacon(struct mac80211_hwsim_data *data, + bool is_discovery, + struct ieee80211_channel *channel) +{ + struct ieee80211_vendor_ie nan_ie = { + .element_id = WLAN_EID_VENDOR_SPECIFIC, + .len = 27 - 2, + .oui = { u32_get_bits(WLAN_OUI_WFA, 0xff0000), + u32_get_bits(WLAN_OUI_WFA, 0xff00), + u32_get_bits(WLAN_OUI_WFA, 0xff) }, + .oui_type = WLAN_OUI_TYPE_WFA_NAN, + }; + size_t alloc_size = + IEEE80211_TX_STATUS_HEADROOM + + offsetofend(struct ieee80211_mgmt, u.beacon) + + 27 /* size of NAN vendor element */; + struct ieee80211_nan_master_indication master_indication; + struct ieee80211_nan_attr nan_attr; + struct ieee80211_mgmt *mgmt; + struct sk_buff *skb; + + /* + * TODO: Should the configured vendor elements or NAN attributes be + * included in some of these beacons? + */ + + skb = alloc_skb(alloc_size, GFP_ATOMIC); + if (!skb) + return; + + spin_lock(&data->nan.state_lock); + + skb_reserve(skb, IEEE80211_TX_STATUS_HEADROOM); + mgmt = skb_put(skb, offsetofend(struct ieee80211_mgmt, u.beacon)); + + memset(mgmt, 0, offsetofend(struct ieee80211_mgmt, u.beacon)); + memcpy(mgmt->sa, data->nan.device_vif->addr, ETH_ALEN); + memset(mgmt->da, 0xff, ETH_ALEN); + memcpy(mgmt->bssid, data->nan.cluster_id, ETH_ALEN); + + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_BEACON); + mgmt->u.beacon.beacon_int = cpu_to_le16(is_discovery ? 100 : DWST_TU); + mgmt->u.beacon.capab_info = + cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME | + WLAN_CAPABILITY_SHORT_PREAMBLE); + + /* FIXME: set these to saner values? */ + mgmt->duration = 0; + mgmt->seq_ctrl = 0; + + /* Put the NAN element */ + skb_put_data(skb, &nan_ie, sizeof(nan_ie)); + + nan_attr.attr = NAN_ATTR_MASTER_INDICATION; + nan_attr.length = cpu_to_le16(sizeof(master_indication)); + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) { + master_indication.master_pref = data->nan.master_pref; + master_indication.random_factor = data->nan.random_factor; + } else { + master_indication.master_pref = 0; + master_indication.random_factor = 0; + } + + skb_put_data(skb, &nan_attr, sizeof(nan_attr)); + skb_put_data(skb, &master_indication, sizeof(master_indication)); + + nan_attr.attr = NAN_ATTR_CLUSTER_INFO; + nan_attr.length = cpu_to_le16(sizeof(data->nan.current_ami)); + skb_put_data(skb, &nan_attr, sizeof(nan_attr)); + skb_put_data(skb, &data->nan.current_ami, + sizeof(data->nan.current_ami)); + + spin_unlock(&data->nan.state_lock); + + mac80211_hwsim_tx_frame(data->hw, skb, channel); } enum hrtimer_restart @@ -80,6 +666,7 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) nan.slot_timer); struct ieee80211_hw *hw = data->hw; struct ieee80211_channel *notify_dw_chan = NULL; + struct ieee80211_channel *beacon_sync_chan = NULL; u64 tsf = hwsim_nan_get_timer_tsf(data); u8 slot = hwsim_nan_slot_from_tsf(tsf); bool dwst_of_dw0 = false; @@ -92,10 +679,24 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) dwst_of_dw0 = true; + scoped_guard(spinlock, &data->nan.state_lock) { + if (data->nan.tsf_discontinuity) { + data->nan.tsf_discontinuity = false; + + mac80211_hwsim_nan_schedule_slot(data, 32, true); + + return HRTIMER_RESTART; + } + + if (slot == SLOT_24GHZ_DW) + data->nan.tsf_adjusted = false; + } + switch (slot) { case SLOT_24GHZ_DW: wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n", dwst_of_dw0); + beacon_sync_chan = ieee80211_get_channel(hw->wiphy, 2437); break; case SLOT_24GHZ_DW + 1: @@ -110,6 +711,8 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) case SLOT_5GHZ_DW: if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) { wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n"); + beacon_sync_chan = + ieee80211_get_channel(hw->wiphy, 5745); } break; @@ -122,6 +725,12 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) break; } + /* TODO: This does not implement DW contention mitigation */ + if (beacon_sync_chan && + data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN && + data->nan.role != MAC80211_HWSIM_NAN_ROLE_NON_SYNC) + mac80211_hwsim_nan_tx_beacon(data, false, beacon_sync_chan); + if (dw_end) mac80211_hwsim_nan_exec_state_transitions(data); @@ -134,7 +743,48 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer) mac80211_hwsim_nan_resume_txqs(data); - mac80211_hwsim_nan_schedule_slot(data, slot + 1); + mac80211_hwsim_nan_schedule_slot(data, slot + 1, false); + + return HRTIMER_RESTART; +} + +enum hrtimer_restart +mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer) +{ + struct mac80211_hwsim_data *data = + container_of(timer, struct mac80211_hwsim_data, + nan.discovery_beacon_timer); + u64 tsf_now; + u64 tbtt; + + if (!data->nan.device_vif) + return HRTIMER_NORESTART; + + scoped_guard(spinlock, &data->nan.state_lock) { + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN || + data->nan.role != MAC80211_HWSIM_NAN_ROLE_MASTER) + return HRTIMER_NORESTART; + } + + mac80211_hwsim_nan_tx_beacon( + data, true, ieee80211_get_channel(data->hw->wiphy, 2437)); + + if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) + mac80211_hwsim_nan_tx_beacon( + data, true, + ieee80211_get_channel(data->hw->wiphy, 5745)); + + /* Read the TSF from the current time in case of adjustments */ + tsf_now = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif); + + /* Wrap value to be after the next TBTT */ + tbtt = tsf_now + ieee80211_tu_to_usec(100); + + /* Round TBTT down to the correct time */ + tbtt = tbtt - tbtt % ieee80211_tu_to_usec(100); + + hrtimer_set_expires(&data->nan.discovery_beacon_timer, + mac80211_hwsim_tsf_to_boottime(data, tbtt)); return HRTIMER_RESTART; } @@ -144,7 +794,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, struct cfg80211_nan_conf *conf) { struct mac80211_hwsim_data *data = hw->priv; - struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); if (vif->type != NL80211_IFTYPE_NAN) return -EINVAL; @@ -156,28 +805,33 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, data->nan.device_vif = vif; data->nan.bands = conf->bands; + /* Start in the "scan" phase and stay there for a little bit */ + data->nan.phase = MAC80211_HWSIM_NAN_PHASE_SCAN; + data->nan.random_factor_valid_dwst = 1; + data->nan.random_factor = 0; + data->nan.master_pref = conf->master_pref; + data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER; + memset(&data->nan.current_ami, 0, sizeof(data->nan.current_ami)); + memset(&data->nan.last_ami, 0, sizeof(data->nan.last_ami)); + data->nan.current_ami.master_rank = + cpu_to_le64(hwsim_nan_get_master_rank(data)); + /* Just run this "soon" and start in a random schedule position */ hrtimer_start(&data->nan.slot_timer, ns_to_ktime(10 * NSEC_PER_USEC), HRTIMER_MODE_REL_SOFT); - if (conf->cluster_id && !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(); + if (conf->cluster_id && !is_zero_ether_addr(conf->cluster_id)) { + memcpy(data->nan.cluster_id, conf->cluster_id, ETH_ALEN); + } else { + put_unaligned_be32((WLAN_OUI_WFA << 8) | 0x01, + data->nan.cluster_id); + data->nan.cluster_id[4] = get_random_u8(); + data->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; } @@ -185,8 +839,6 @@ 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) @@ -194,20 +846,9 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw, hrtimer_cancel(&data->nan.slot_timer); hrtimer_cancel(&data->nan.resume_txqs_timer); + hrtimer_cancel(&data->nan.discovery_beacon_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; } @@ -233,6 +874,11 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw, if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG) data->nan.notify_dw = conf->enable_dw_notification; + if (changes & CFG80211_NAN_CONF_CHANGED_PREF) { + scoped_guard(spinlock_bh, &data->nan.state_lock) + data->nan.master_pref = conf->master_pref; + } + return 0; } @@ -335,7 +981,10 @@ bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, if (WARN_ON_ONCE(!data->nan.device_vif)) return false; - if (rx_status->rx_flags & RX_FLAG_MACTIME) { + if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) + return channel->center_freq == 2437; + + if (rx_status->flag & RX_FLAG_MACTIME) { slot = hwsim_nan_slot_from_tsf(rx_status->mactime); } else { u64 tsf; diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h index af8dd7ff00cc..3199e5c5376b 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h +++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h @@ -7,6 +7,18 @@ #ifndef __MAC80211_HWSIM_NAN_H #define __MAC80211_HWSIM_NAN_H +enum mac80211_hwsim_nan_phase { + MAC80211_HWSIM_NAN_PHASE_SCAN, + MAC80211_HWSIM_NAN_PHASE_WARMUP, + MAC80211_HWSIM_NAN_PHASE_UP, +}; + +enum mac80211_hwsim_nan_role { + MAC80211_HWSIM_NAN_ROLE_MASTER, + MAC80211_HWSIM_NAN_ROLE_SYNC, + MAC80211_HWSIM_NAN_ROLE_NON_SYNC, +}; + struct mac80211_hwsim_nan_data { struct ieee80211_vif *device_vif; u8 bands; @@ -14,12 +26,40 @@ struct mac80211_hwsim_nan_data { struct hrtimer slot_timer; struct hrtimer resume_txqs_timer; bool notify_dw; + + struct hrtimer discovery_beacon_timer; + + /* Later members are protected by this lock */ + spinlock_t state_lock; + + u8 master_pref; + u8 random_factor; + + u8 random_factor_valid_dwst; + + enum mac80211_hwsim_nan_phase phase; + enum mac80211_hwsim_nan_role role; + + u8 cluster_id[ETH_ALEN]; + + struct ieee80211_nan_anchor_master_info current_ami; + struct ieee80211_nan_anchor_master_info last_ami; + + /* Wi-Fi Aware version 4.0, section 3.3.6.1 and 3.3.6.2 */ + int master_transition_score; + /* Wi-Fi Aware version 4.0, section 3.3.6.3 and 3.3.6.4 */ + int sync_transition_score; + + bool tsf_adjusted; + bool tsf_discontinuity; }; enum hrtimer_restart mac80211_hwsim_nan_slot_timer(struct hrtimer *timer); enum hrtimer_restart mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer); +enum hrtimer_restart +mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer); int mac80211_hwsim_nan_start(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -43,4 +83,7 @@ bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw, struct ieee80211_channel *channel, struct ieee80211_rx_status *rx_status); +void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw, + struct sk_buff *skb); + #endif /* __MAC80211_HWSIM_NAN_H */ -- 2.51.1