xref: /linux/drivers/net/wireless/virtual/mac80211_hwsim_nan.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1340f2ab8SBenjamin Berg // SPDX-License-Identifier: GPL-2.0-only
2340f2ab8SBenjamin Berg /*
3340f2ab8SBenjamin Berg  * mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
442d3002bSBenjamin Berg  * Copyright (C) 2025-2026 Intel Corporation
5340f2ab8SBenjamin Berg  */
6340f2ab8SBenjamin Berg 
742d3002bSBenjamin Berg #include <net/cfg80211.h>
8340f2ab8SBenjamin Berg #include "mac80211_hwsim_i.h"
9340f2ab8SBenjamin Berg 
10df0cb6e2SBenjamin Berg /* Defined as the lower 23 bits being zero */
11df0cb6e2SBenjamin Berg #define DW0_TSF_MASK		GENMASK(22, 0)
12df0cb6e2SBenjamin Berg 
13df0cb6e2SBenjamin Berg /* DWs are repeated every 512 TUs */
14df0cb6e2SBenjamin Berg #define DWST_TU			512
15df0cb6e2SBenjamin Berg #define DWST_TSF_MASK		(ieee80211_tu_to_usec(DWST_TU) - 1)
16df0cb6e2SBenjamin Berg 
17df0cb6e2SBenjamin Berg #define SLOT_TU			16
18df0cb6e2SBenjamin Berg #define SLOT_TSF_MASK		(ieee80211_tu_to_usec(DWST_TU) - 1)
19df0cb6e2SBenjamin Berg 
20df0cb6e2SBenjamin Berg /* The 2.4 GHz DW is at the start, the 5 GHz is in slot 8 (after 128 TUs) */
21df0cb6e2SBenjamin Berg #define DW_5G_OFFSET_TU		128
22df0cb6e2SBenjamin Berg 
23df0cb6e2SBenjamin Berg #define SLOT_24GHZ_DW		0
24df0cb6e2SBenjamin Berg #define SLOT_5GHZ_DW		(DW_5G_OFFSET_TU / SLOT_TU)
25df0cb6e2SBenjamin Berg 
26df0cb6e2SBenjamin Berg /* The special DW0 happens every 16 DWSTs (8192 TUs) */
27df0cb6e2SBenjamin Berg static_assert(16 * DWST_TU * 1024 == 8192 * 1024);
28df0cb6e2SBenjamin Berg static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
29df0cb6e2SBenjamin Berg 
3042d3002bSBenjamin Berg /* warmup phase should be 120 seconds, which is approximately 225 DWSTs */
3142d3002bSBenjamin Berg #define NAN_WARMUP_DWST		225
3242d3002bSBenjamin Berg 
3342d3002bSBenjamin Berg #define NAN_RSSI_CLOSE (-60)
3442d3002bSBenjamin Berg #define NAN_RSSI_MIDDLE (-75)
3542d3002bSBenjamin Berg 
36f9aeb3a2SBenjamin Berg /* Quiet time at the end of each slot where TX is suppressed */
37f9aeb3a2SBenjamin Berg #define NAN_CHAN_SWITCH_TIME_US		256
38f9aeb3a2SBenjamin Berg 
392c8ec5a9SDaniel Gabay struct hwsim_nan_sta_iter_ctx {
402c8ec5a9SDaniel Gabay 	struct ieee80211_hw *hw;
412c8ec5a9SDaniel Gabay 	bool can_tx;
422c8ec5a9SDaniel Gabay };
432c8ec5a9SDaniel Gabay 
44*55eaca33SIlan Peer struct hwsim_nan_mcast_data_iter_ctx {
45*55eaca33SIlan Peer 	struct ieee80211_hw *hw;
46*55eaca33SIlan Peer 	struct ieee80211_vif *vif;
47*55eaca33SIlan Peer 	size_t n_vif_sta;
48*55eaca33SIlan Peer 	size_t n_sta_can_tx;
49*55eaca33SIlan Peer };
50*55eaca33SIlan Peer 
51f9aeb3a2SBenjamin Berg static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data);
52f9aeb3a2SBenjamin Berg 
53df0cb6e2SBenjamin Berg static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
54df0cb6e2SBenjamin Berg {
55df0cb6e2SBenjamin Berg 	ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer);
56df0cb6e2SBenjamin Berg 
57df0cb6e2SBenjamin Berg 	return mac80211_hwsim_boottime_to_tsf(data, expires);
58df0cb6e2SBenjamin Berg }
59df0cb6e2SBenjamin Berg 
60df0cb6e2SBenjamin Berg static u8 hwsim_nan_slot_from_tsf(u64 tsf)
61df0cb6e2SBenjamin Berg {
62df0cb6e2SBenjamin Berg 	return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU);
63df0cb6e2SBenjamin Berg }
64df0cb6e2SBenjamin Berg 
6542d3002bSBenjamin Berg static u64 hwsim_nan_encode_master_rank(u8 master_pref, u8 random_factor,
6642d3002bSBenjamin Berg 					const u8 *addr)
67df0cb6e2SBenjamin Berg {
6842d3002bSBenjamin Berg 	return ((u64)master_pref << 56) +
6942d3002bSBenjamin Berg 		((u64)random_factor << 48) +
7042d3002bSBenjamin Berg 		((u64)addr[5] << 40) +
7142d3002bSBenjamin Berg 		((u64)addr[4] << 32) +
7242d3002bSBenjamin Berg 		((u64)addr[3] << 24) +
7342d3002bSBenjamin Berg 		((u64)addr[2] << 16) +
7442d3002bSBenjamin Berg 		((u64)addr[1] << 8) +
7542d3002bSBenjamin Berg 		((u64)addr[0] << 0);
7642d3002bSBenjamin Berg }
7742d3002bSBenjamin Berg 
7842d3002bSBenjamin Berg static u64 hwsim_nan_get_master_rank(struct mac80211_hwsim_data *data)
7942d3002bSBenjamin Berg {
8042d3002bSBenjamin Berg 	u8 master_pref = 0;
8142d3002bSBenjamin Berg 	u8 random_factor = 0;
8242d3002bSBenjamin Berg 
8342d3002bSBenjamin Berg 	if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) {
8442d3002bSBenjamin Berg 		master_pref = data->nan.master_pref;
8542d3002bSBenjamin Berg 		random_factor = data->nan.random_factor;
8642d3002bSBenjamin Berg 	}
8742d3002bSBenjamin Berg 
8842d3002bSBenjamin Berg 	return hwsim_nan_encode_master_rank(master_pref, random_factor,
8942d3002bSBenjamin Berg 					    data->nan.device_vif->addr);
9042d3002bSBenjamin Berg }
9142d3002bSBenjamin Berg 
922c8ec5a9SDaniel Gabay static bool mac80211_hwsim_nan_is_dw_slot(struct mac80211_hwsim_data *data,
932c8ec5a9SDaniel Gabay 					  u8 slot)
942c8ec5a9SDaniel Gabay {
952c8ec5a9SDaniel Gabay 	return slot == SLOT_24GHZ_DW ||
962c8ec5a9SDaniel Gabay 		(slot == SLOT_5GHZ_DW &&
972c8ec5a9SDaniel Gabay 		 (data->nan.bands & BIT(NL80211_BAND_5GHZ)));
982c8ec5a9SDaniel Gabay }
992c8ec5a9SDaniel Gabay 
1002c8ec5a9SDaniel Gabay static bool
1012c8ec5a9SDaniel Gabay hwsim_nan_rx_chandef_compatible(struct mac80211_hwsim_data *data, u8 slot,
1022c8ec5a9SDaniel Gabay 				struct ieee80211_channel *rx_chan, u8 rx_bw)
1032c8ec5a9SDaniel Gabay {
1042c8ec5a9SDaniel Gabay 	static const int bw_to_mhz[] = {
1052c8ec5a9SDaniel Gabay 		[RATE_INFO_BW_20] = 20, [RATE_INFO_BW_40] = 40,
1062c8ec5a9SDaniel Gabay 		[RATE_INFO_BW_80] = 80, [RATE_INFO_BW_160] = 160,
1072c8ec5a9SDaniel Gabay 	};
1082c8ec5a9SDaniel Gabay 	struct cfg80211_chan_def sched_chandef;
1092c8ec5a9SDaniel Gabay 	int rx_mhz, sched_mhz;
1102c8ec5a9SDaniel Gabay 
1112c8ec5a9SDaniel Gabay 	scoped_guard(spinlock_bh, &data->nan.state_lock)
1122c8ec5a9SDaniel Gabay 		sched_chandef = data->nan.local_sched[slot];
1132c8ec5a9SDaniel Gabay 
1142c8ec5a9SDaniel Gabay 	if (!sched_chandef.chan ||
1152c8ec5a9SDaniel Gabay 	    sched_chandef.chan->center_freq != rx_chan->center_freq)
1162c8ec5a9SDaniel Gabay 		return false;
1172c8ec5a9SDaniel Gabay 
1182c8ec5a9SDaniel Gabay 	if (rx_bw >= ARRAY_SIZE(bw_to_mhz) || !bw_to_mhz[rx_bw])
1192c8ec5a9SDaniel Gabay 		return false;
1202c8ec5a9SDaniel Gabay 
1212c8ec5a9SDaniel Gabay 	rx_mhz = bw_to_mhz[rx_bw];
1222c8ec5a9SDaniel Gabay 	sched_mhz = cfg80211_chandef_get_width(&sched_chandef);
1232c8ec5a9SDaniel Gabay 
1242c8ec5a9SDaniel Gabay 	/* Accept RX at narrower or equal bandwidth */
1252c8ec5a9SDaniel Gabay 	return rx_mhz <= sched_mhz;
1262c8ec5a9SDaniel Gabay }
1272c8ec5a9SDaniel Gabay 
1282c8ec5a9SDaniel Gabay static bool hwsim_nan_peer_present_in_dw(struct hwsim_sta_priv *sp, u64 tsf)
1292c8ec5a9SDaniel Gabay {
1302c8ec5a9SDaniel Gabay 	u8 slot = hwsim_nan_slot_from_tsf(tsf);
1312c8ec5a9SDaniel Gabay 	u8 cdw = 0;
1322c8ec5a9SDaniel Gabay 	u8 dw_index, wake_interval;
1332c8ec5a9SDaniel Gabay 	u16 committed_dw;
1342c8ec5a9SDaniel Gabay 
1352c8ec5a9SDaniel Gabay 	scoped_guard(spinlock_bh, &sp->nan_sched.lock)
1362c8ec5a9SDaniel Gabay 		committed_dw = sp->nan_sched.committed_dw;
1372c8ec5a9SDaniel Gabay 
1382c8ec5a9SDaniel Gabay 	/* If peer doesn't advertise committed DW, assume presence in
1392c8ec5a9SDaniel Gabay 	 * all 2.4 GHz DW slots
1402c8ec5a9SDaniel Gabay 	 */
1412c8ec5a9SDaniel Gabay 	if (!committed_dw)
1422c8ec5a9SDaniel Gabay 		return slot == SLOT_24GHZ_DW;
1432c8ec5a9SDaniel Gabay 
1442c8ec5a9SDaniel Gabay 	/* Get DW index (0-15) within the 16-DWST DW0 cycle */
1452c8ec5a9SDaniel Gabay 	dw_index = (tsf / ieee80211_tu_to_usec(DWST_TU)) & 0xf;
1462c8ec5a9SDaniel Gabay 
1472c8ec5a9SDaniel Gabay 	/* Extract CDW for the appropriate band (spec Table 80) */
1482c8ec5a9SDaniel Gabay 	if (slot == SLOT_24GHZ_DW)
1492c8ec5a9SDaniel Gabay 		cdw = committed_dw & 0x7;
1502c8ec5a9SDaniel Gabay 	else if (slot == SLOT_5GHZ_DW)
1512c8ec5a9SDaniel Gabay 		cdw = (committed_dw >> 3) & 0x7;
1522c8ec5a9SDaniel Gabay 
1532c8ec5a9SDaniel Gabay 	if (cdw == 0)
1542c8ec5a9SDaniel Gabay 		return false;
1552c8ec5a9SDaniel Gabay 
1562c8ec5a9SDaniel Gabay 	/* Peer wakes every 2^(cdw-1) DWs: 1, 2, 4, 8, or 16 */
1572c8ec5a9SDaniel Gabay 	wake_interval = 1 << (cdw - 1);
1582c8ec5a9SDaniel Gabay 
1592c8ec5a9SDaniel Gabay 	return (dw_index % wake_interval) == 0;
1602c8ec5a9SDaniel Gabay }
1612c8ec5a9SDaniel Gabay 
1622c8ec5a9SDaniel Gabay static bool
1632c8ec5a9SDaniel Gabay hwsim_nan_peer_present_in_faw(struct hwsim_sta_priv *sp,
1642c8ec5a9SDaniel Gabay 			      struct mac80211_hwsim_data *data, u8 slot)
1652c8ec5a9SDaniel Gabay {
1662c8ec5a9SDaniel Gabay 	struct cfg80211_chan_def local_chandef;
1672c8ec5a9SDaniel Gabay 
1682c8ec5a9SDaniel Gabay 	scoped_guard(spinlock_bh, &data->nan.state_lock)
1692c8ec5a9SDaniel Gabay 		local_chandef = data->nan.local_sched[slot];
1702c8ec5a9SDaniel Gabay 
1712c8ec5a9SDaniel Gabay 	if (!local_chandef.chan)
1722c8ec5a9SDaniel Gabay 		return false;
1732c8ec5a9SDaniel Gabay 
1742c8ec5a9SDaniel Gabay 	scoped_guard(spinlock_bh, &sp->nan_sched.lock) {
1752c8ec5a9SDaniel Gabay 		for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) {
1762c8ec5a9SDaniel Gabay 			struct cfg80211_chan_def *peer_chandef;
1772c8ec5a9SDaniel Gabay 
1782c8ec5a9SDaniel Gabay 			if (sp->nan_sched.maps[i].map_id ==
1792c8ec5a9SDaniel Gabay 			    CFG80211_NAN_INVALID_MAP_ID)
1802c8ec5a9SDaniel Gabay 				continue;
1812c8ec5a9SDaniel Gabay 
1822c8ec5a9SDaniel Gabay 			peer_chandef = &sp->nan_sched.maps[i].chans[slot];
1832c8ec5a9SDaniel Gabay 			if (!peer_chandef->chan)
1842c8ec5a9SDaniel Gabay 				continue;
1852c8ec5a9SDaniel Gabay 
1862c8ec5a9SDaniel Gabay 			if (cfg80211_chandef_compatible(&local_chandef,
1872c8ec5a9SDaniel Gabay 							peer_chandef))
1882c8ec5a9SDaniel Gabay 				return true;
1892c8ec5a9SDaniel Gabay 		}
1902c8ec5a9SDaniel Gabay 	}
1912c8ec5a9SDaniel Gabay 
1922c8ec5a9SDaniel Gabay 	return false;
1932c8ec5a9SDaniel Gabay }
1942c8ec5a9SDaniel Gabay 
19542d3002bSBenjamin Berg static void
19642d3002bSBenjamin Berg mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot,
19742d3002bSBenjamin Berg 				 bool discontinuity)
19842d3002bSBenjamin Berg {
19942d3002bSBenjamin Berg 	u64 tsf;
20042d3002bSBenjamin Berg 
20142d3002bSBenjamin Berg 	if (!discontinuity)
20242d3002bSBenjamin Berg 		tsf = hwsim_nan_get_timer_tsf(data);
20342d3002bSBenjamin Berg 	else
20442d3002bSBenjamin Berg 		tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
205df0cb6e2SBenjamin Berg 
206df0cb6e2SBenjamin Berg 	/* Only called by mac80211_hwsim_nan_dw_timer from softirq context */
207df0cb6e2SBenjamin Berg 	lockdep_assert_in_softirq();
208df0cb6e2SBenjamin Berg 
209df0cb6e2SBenjamin Berg 	tsf &= ~DWST_TSF_MASK;
210df0cb6e2SBenjamin Berg 	tsf += ieee80211_tu_to_usec(slot * SLOT_TU);
211df0cb6e2SBenjamin Berg 
212df0cb6e2SBenjamin Berg 	hrtimer_set_expires(&data->nan.slot_timer,
213df0cb6e2SBenjamin Berg 			    mac80211_hwsim_tsf_to_boottime(data, tsf));
214df0cb6e2SBenjamin Berg }
215df0cb6e2SBenjamin Berg 
21642d3002bSBenjamin Berg void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,
21742d3002bSBenjamin Berg 			   struct sk_buff *skb)
21842d3002bSBenjamin Berg {
21942d3002bSBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
22042d3002bSBenjamin Berg 	const struct ieee80211_mgmt *mgmt = (void *)skb->data;
22142d3002bSBenjamin Berg 	struct element *nan_elem = (void *)mgmt->u.beacon.variable;
22242d3002bSBenjamin Berg 	struct ieee80211_nan_anchor_master_info *ami = NULL;
22342d3002bSBenjamin Berg 	const struct ieee80211_nan_attr *nan_attr;
22442d3002bSBenjamin Berg 	struct ieee80211_rx_status rx_status;
22542d3002bSBenjamin Berg 	bool joined_cluster = false;
22642d3002bSBenjamin Berg 	bool adopt_tsf = false;
22742d3002bSBenjamin Berg 	bool is_sync_beacon;
22842d3002bSBenjamin Berg 	bool is_same_cluster;
22942d3002bSBenjamin Berg 	u64 master_rank = 0;
23042d3002bSBenjamin Berg 	ssize_t data_len;
23142d3002bSBenjamin Berg 	u8 slot;
23242d3002bSBenjamin Berg 
23342d3002bSBenjamin Berg 	/* Need a NAN vendor element at the start */
23442d3002bSBenjamin Berg 	if (skb->len < (offsetofend(struct ieee80211_mgmt, u.beacon) + 6) ||
23542d3002bSBenjamin Berg 	    !ieee80211_is_beacon(mgmt->frame_control))
23642d3002bSBenjamin Berg 		return;
23742d3002bSBenjamin Berg 
23842d3002bSBenjamin Berg 	data_len = skb->len - offsetofend(struct ieee80211_mgmt, u.beacon);
23942d3002bSBenjamin Berg 
24042d3002bSBenjamin Berg 	/* Copy the RX status to add a MAC timestamp if needed */
24142d3002bSBenjamin Berg 	memcpy(&rx_status, IEEE80211_SKB_RXCB(skb),
24242d3002bSBenjamin Berg 	       sizeof(struct ieee80211_rx_status));
24342d3002bSBenjamin Berg 
24442d3002bSBenjamin Berg 	/* And deal with the lack of mac time stamp */
24542d3002bSBenjamin Berg 	if ((rx_status.flag & RX_FLAG_MACTIME) != RX_FLAG_MACTIME_START) {
24642d3002bSBenjamin Berg 		u64 tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);;
24742d3002bSBenjamin Berg 
24842d3002bSBenjamin Berg 		/* In that case there should be no timestamp */
24942d3002bSBenjamin Berg 		WARN_ON_ONCE(rx_status.flag & RX_FLAG_MACTIME);
25042d3002bSBenjamin Berg 
25142d3002bSBenjamin Berg 		/* No mac timestamp, set current TSF for the frame end */
25242d3002bSBenjamin Berg 		rx_status.flag |= RX_FLAG_MACTIME_END;
25342d3002bSBenjamin Berg 		rx_status.mactime = tsf;
25442d3002bSBenjamin Berg 
25542d3002bSBenjamin Berg 		/* And translate to the start for the rest of the code */
25642d3002bSBenjamin Berg 		rx_status.mactime =
25742d3002bSBenjamin Berg 			ieee80211_calculate_rx_timestamp(hw, &rx_status,
25842d3002bSBenjamin Berg 							 skb->len, 0);
25942d3002bSBenjamin Berg 		rx_status.flag &= ~RX_FLAG_MACTIME;
26042d3002bSBenjamin Berg 		rx_status.flag |= RX_FLAG_MACTIME_START;
26142d3002bSBenjamin Berg 
26242d3002bSBenjamin Berg 		/* Match mac80211_hwsim_nan_receive, see comment there */
26342d3002bSBenjamin Berg 		slot = hwsim_nan_slot_from_tsf(tsf + 128);
26442d3002bSBenjamin Berg 	} else {
26542d3002bSBenjamin Berg 		slot = hwsim_nan_slot_from_tsf(rx_status.mactime);
26642d3002bSBenjamin Berg 	}
26742d3002bSBenjamin Berg 
26842d3002bSBenjamin Berg 	/*
26942d3002bSBenjamin Berg 	 * (overly) simplify things, only track 2.4 GHz here. Also, ignore
27042d3002bSBenjamin Berg 	 * frames outside of the 2.4 GHz DW slot, unless in the initial SCAN
27142d3002bSBenjamin Berg 	 * phase.
27242d3002bSBenjamin Berg 	 */
27342d3002bSBenjamin Berg 	if ((slot != SLOT_24GHZ_DW &&
27442d3002bSBenjamin Berg 	     data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN) ||
27542d3002bSBenjamin Berg 	    rx_status.freq != 2437)
27642d3002bSBenjamin Berg 		return;
27742d3002bSBenjamin Berg 
27842d3002bSBenjamin Berg 	/* Just ignore low RSSI beacons that we cannot sync to */
27942d3002bSBenjamin Berg 	if (rx_status.signal < NAN_RSSI_MIDDLE)
28042d3002bSBenjamin Berg 		return;
28142d3002bSBenjamin Berg 
28242d3002bSBenjamin Berg 	/* Needs to be a valid NAN cluster ID in A3 */
28342d3002bSBenjamin Berg 	if (get_unaligned_be32(mgmt->bssid) != ((WLAN_OUI_WFA << 8) | 0x01))
28442d3002bSBenjamin Berg 		return;
28542d3002bSBenjamin Berg 
28642d3002bSBenjamin Berg 	/* We are only interested in NAN beacons */
28742d3002bSBenjamin Berg 	if (nan_elem->id != WLAN_EID_VENDOR_SPECIFIC ||
28842d3002bSBenjamin Berg 	    nan_elem->datalen < 4 ||
28942d3002bSBenjamin Berg 	    get_unaligned_be32(nan_elem->data) !=
29042d3002bSBenjamin Berg 	    (WLAN_OUI_WFA << 8 | WLAN_OUI_TYPE_WFA_NAN))
29142d3002bSBenjamin Berg 		return;
29242d3002bSBenjamin Berg 
29342d3002bSBenjamin Berg 	u8 *nan_defragmented __free(kfree) = kzalloc(data_len, GFP_ATOMIC);
29442d3002bSBenjamin Berg 	if (!nan_defragmented)
29542d3002bSBenjamin Berg 		return;
29642d3002bSBenjamin Berg 
29742d3002bSBenjamin Berg 	data_len = cfg80211_defragment_element(nan_elem,
29842d3002bSBenjamin Berg 					       mgmt->u.beacon.variable,
29942d3002bSBenjamin Berg 					       data_len,
30042d3002bSBenjamin Berg 					       nan_defragmented, data_len,
30142d3002bSBenjamin Berg 					       WLAN_EID_FRAGMENT);
30242d3002bSBenjamin Berg 
30342d3002bSBenjamin Berg 	if (data_len < 0)
30442d3002bSBenjamin Berg 		return;
30542d3002bSBenjamin Berg 
30642d3002bSBenjamin Berg 	/* Assume it is a synchronization beacon if beacon_int is 512 TUs */
30742d3002bSBenjamin Berg 	is_sync_beacon = le16_to_cpu(mgmt->u.beacon.beacon_int) == DWST_TU;
30842d3002bSBenjamin Berg 	is_same_cluster = ether_addr_equal(mgmt->bssid, data->nan.cluster_id);
30942d3002bSBenjamin Berg 
31042d3002bSBenjamin Berg 	for_each_nan_attr(nan_attr, nan_defragmented + 4, data_len - 4) {
31142d3002bSBenjamin Berg 		if (nan_attr->attr == NAN_ATTR_MASTER_INDICATION &&
31242d3002bSBenjamin Berg 		    le16_to_cpu(nan_attr->length) >=
31342d3002bSBenjamin Berg 		    sizeof(struct ieee80211_nan_master_indication)) {
31442d3002bSBenjamin Berg 			struct ieee80211_nan_master_indication *mi =
31542d3002bSBenjamin Berg 				(void *)nan_attr->data;
31642d3002bSBenjamin Berg 
31742d3002bSBenjamin Berg 			master_rank =
31842d3002bSBenjamin Berg 				hwsim_nan_encode_master_rank(mi->master_pref,
31942d3002bSBenjamin Berg 							     mi->random_factor,
32042d3002bSBenjamin Berg 							     mgmt->sa);
32142d3002bSBenjamin Berg 		}
32242d3002bSBenjamin Berg 
32342d3002bSBenjamin Berg 		if (nan_attr->attr == NAN_ATTR_CLUSTER_INFO &&
32442d3002bSBenjamin Berg 		    le16_to_cpu(nan_attr->length) >=
32542d3002bSBenjamin Berg 		    sizeof(struct ieee80211_nan_anchor_master_info)) {
32642d3002bSBenjamin Berg 			ami = (void *)nan_attr->data;
32742d3002bSBenjamin Berg 
32842d3002bSBenjamin Berg 			/*
32942d3002bSBenjamin Berg 			 * The AMBTT should be set to the beacon timestamp when
33042d3002bSBenjamin Berg 			 * the sender is the anchor master. We can simply
33142d3002bSBenjamin Berg 			 * modify the structure because we created a copy when
33242d3002bSBenjamin Berg 			 * defragmenting the NAN element.
33342d3002bSBenjamin Berg 			 */
33442d3002bSBenjamin Berg 			if (ami->hop_count == 0)
33542d3002bSBenjamin Berg 				ami->ambtt = cpu_to_le32(
33642d3002bSBenjamin Berg 					le64_to_cpu(mgmt->u.beacon.timestamp));
33742d3002bSBenjamin Berg 		}
33842d3002bSBenjamin Berg 	}
33942d3002bSBenjamin Berg 
34042d3002bSBenjamin Berg 	/* Do the rest of the processing under lock */
34142d3002bSBenjamin Berg 	spin_lock_bh(&data->nan.state_lock);
34242d3002bSBenjamin Berg 
34342d3002bSBenjamin Berg 	/*
34442d3002bSBenjamin Berg 	 * sync beacon should be discarded if the master rank is the same
34542d3002bSBenjamin Berg 	 * and the AMBTT is older than 16 * 512 TUs compared to our own TSF.
34642d3002bSBenjamin Berg 	 *
34742d3002bSBenjamin Berg 	 * Subtract the AMBTT from the lowered TSF. If the AMBTT is older
34842d3002bSBenjamin Berg 	 * (smaller) then the calculation will not underflow.
34942d3002bSBenjamin Berg 	 */
35042d3002bSBenjamin Berg 	if (is_sync_beacon && ami &&
35142d3002bSBenjamin Berg 	    ami->master_rank == data->nan.current_ami.master_rank &&
35242d3002bSBenjamin Berg 	    (((u32)rx_status.mactime -
35342d3002bSBenjamin Berg 	      ieee80211_tu_to_usec(16 * 512)) -
35442d3002bSBenjamin Berg 	     le32_to_cpu(ami->ambtt)) < 0x8000000) {
35542d3002bSBenjamin Berg 		wiphy_dbg(hw->wiphy,
35642d3002bSBenjamin Berg 			  "NAN: ignoring sync beacon with old AMBTT\n");
35742d3002bSBenjamin Berg 		is_sync_beacon = false;
35842d3002bSBenjamin Berg 	}
35942d3002bSBenjamin Berg 
36042d3002bSBenjamin Berg 	if (is_same_cluster && is_sync_beacon &&
36142d3002bSBenjamin Berg 	    master_rank > hwsim_nan_get_master_rank(data)) {
36242d3002bSBenjamin Berg 		if (rx_status.signal > NAN_RSSI_CLOSE)
36342d3002bSBenjamin Berg 			data->nan.master_transition_score += 3;
36442d3002bSBenjamin Berg 		else
36542d3002bSBenjamin Berg 			data->nan.master_transition_score += 1;
36642d3002bSBenjamin Berg 	}
36742d3002bSBenjamin Berg 
36842d3002bSBenjamin Berg 	if (is_same_cluster && is_sync_beacon && ami &&
36942d3002bSBenjamin Berg 	    ((ami->master_rank == data->nan.current_ami.master_rank &&
37042d3002bSBenjamin Berg 	      ami->hop_count < data->nan.current_ami.hop_count) ||
37142d3002bSBenjamin Berg 	     (master_rank > hwsim_nan_get_master_rank(data) &&
37242d3002bSBenjamin Berg 	      ami->hop_count == data->nan.current_ami.hop_count))) {
37342d3002bSBenjamin Berg 		if (rx_status.signal > NAN_RSSI_CLOSE)
37442d3002bSBenjamin Berg 			data->nan.sync_transition_score += 3;
37542d3002bSBenjamin Berg 		else
37642d3002bSBenjamin Berg 			data->nan.sync_transition_score += 1;
37742d3002bSBenjamin Berg 	}
37842d3002bSBenjamin Berg 
37942d3002bSBenjamin Berg 	/*
38042d3002bSBenjamin Berg 	 * Decide on TSF adjustments before updating any other state
38142d3002bSBenjamin Berg 	 */
38242d3002bSBenjamin Berg 	if (is_same_cluster && is_sync_beacon && ami &&
38342d3002bSBenjamin Berg 	    data->nan.current_ami.hop_count != 0) {
38442d3002bSBenjamin Berg 		if (le64_to_cpu(ami->master_rank) >
38542d3002bSBenjamin Berg 		    le64_to_cpu(data->nan.current_ami.master_rank) &&
38642d3002bSBenjamin Berg 		    ami->master_rank != data->nan.last_ami.master_rank)
38742d3002bSBenjamin Berg 			adopt_tsf = true;
38842d3002bSBenjamin Berg 
38942d3002bSBenjamin Berg 		if (le64_to_cpu(ami->master_rank) >
39042d3002bSBenjamin Berg 		    le64_to_cpu(data->nan.current_ami.master_rank) &&
39142d3002bSBenjamin Berg 		    ami->master_rank == data->nan.last_ami.master_rank &&
39242d3002bSBenjamin Berg 		    le32_to_cpu(ami->ambtt) >
39342d3002bSBenjamin Berg 		    le32_to_cpu(data->nan.last_ami.ambtt))
39442d3002bSBenjamin Berg 			adopt_tsf = true;
39542d3002bSBenjamin Berg 
39642d3002bSBenjamin Berg 		if (le64_to_cpu(ami->master_rank) <
39742d3002bSBenjamin Berg 		    le64_to_cpu(data->nan.current_ami.master_rank) &&
39842d3002bSBenjamin Berg 		    le64_to_cpu(ami->master_rank) >
39942d3002bSBenjamin Berg 		    hwsim_nan_get_master_rank(data) &&
40042d3002bSBenjamin Berg 		    ether_addr_equal(ami->master_addr,
40142d3002bSBenjamin Berg 				     data->nan.current_ami.master_addr))
40242d3002bSBenjamin Berg 			adopt_tsf = true;
40342d3002bSBenjamin Berg 
40442d3002bSBenjamin Berg 		if (ami->master_rank == data->nan.current_ami.master_rank &&
40542d3002bSBenjamin Berg 		    le32_to_cpu(ami->ambtt) >
40642d3002bSBenjamin Berg 		    le32_to_cpu(data->nan.current_ami.ambtt))
40742d3002bSBenjamin Berg 			adopt_tsf = true;
40842d3002bSBenjamin Berg 
40942d3002bSBenjamin Berg 		/* Anchor Master case is handled below */
41042d3002bSBenjamin Berg 	}
41142d3002bSBenjamin Berg 
41242d3002bSBenjamin Berg 	/*
41342d3002bSBenjamin Berg 	 * NAN Cluster merging
41442d3002bSBenjamin Berg 	 */
41542d3002bSBenjamin Berg 	if (!is_same_cluster && ami) {
41642d3002bSBenjamin Berg 		u64 curr_amr;
41742d3002bSBenjamin Berg 		u64 own_cg;
41842d3002bSBenjamin Berg 		u64 frame_amr;
41942d3002bSBenjamin Berg 		u64 cg;
42042d3002bSBenjamin Berg 
42142d3002bSBenjamin Berg 		/* Shifted down by 19 bits compared to spec */
42242d3002bSBenjamin Berg 		frame_amr = le64_to_cpu(ami->master_rank);
42342d3002bSBenjamin Berg 		cg = (u64)ami->master_pref << (64 - 19);
42442d3002bSBenjamin Berg 		cg += le64_to_cpu(mgmt->u.beacon.timestamp) >> 19;
42542d3002bSBenjamin Berg 
42642d3002bSBenjamin Berg 		curr_amr = le64_to_cpu(data->nan.current_ami.master_rank);
42742d3002bSBenjamin Berg 		own_cg = (u64)data->nan.current_ami.master_pref << (64 - 19);
42842d3002bSBenjamin Berg 		own_cg += rx_status.mactime >> 19;
42942d3002bSBenjamin Berg 
43042d3002bSBenjamin Berg 		/*
43142d3002bSBenjamin Berg 		 * Check if the cluster shall be joined
43242d3002bSBenjamin Berg 		 *
43342d3002bSBenjamin Berg 		 * When in the "scan" phase, just join immediately.
43442d3002bSBenjamin Berg 		 */
43542d3002bSBenjamin Berg 		if (cg > own_cg ||
43642d3002bSBenjamin Berg 		    (cg == own_cg && frame_amr > curr_amr) ||
43742d3002bSBenjamin Berg 		    data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) {
43842d3002bSBenjamin Berg 			/* Avoid a state transition */
43942d3002bSBenjamin Berg 			data->nan.master_transition_score = 0;
44042d3002bSBenjamin Berg 			data->nan.sync_transition_score = 0;
44142d3002bSBenjamin Berg 
44242d3002bSBenjamin Berg 			/*
44342d3002bSBenjamin Berg 			 * NOTE: The spec says we should TX sync beacons on the
44442d3002bSBenjamin Berg 			 * old schedule after joining. We do not implement this.
44542d3002bSBenjamin Berg 			 */
44642d3002bSBenjamin Berg 
44742d3002bSBenjamin Berg 			wiphy_dbg(hw->wiphy, "NAN: joining cluster %pM\n",
44842d3002bSBenjamin Berg 				  mgmt->bssid);
44942d3002bSBenjamin Berg 
45042d3002bSBenjamin Berg 			joined_cluster = true;
45142d3002bSBenjamin Berg 			adopt_tsf = true;
45242d3002bSBenjamin Berg 
45342d3002bSBenjamin Berg 			memcpy(&data->nan.last_ami, &data->nan.current_ami,
45442d3002bSBenjamin Berg 			       sizeof(data->nan.last_ami));
45542d3002bSBenjamin Berg 			memcpy(&data->nan.current_ami, ami,
45642d3002bSBenjamin Berg 			       sizeof(data->nan.last_ami));
45742d3002bSBenjamin Berg 			data->nan.current_ami.hop_count += 1;
45842d3002bSBenjamin Berg 
45942d3002bSBenjamin Berg 			memcpy(data->nan.cluster_id, mgmt->bssid, ETH_ALEN);
46042d3002bSBenjamin Berg 
46142d3002bSBenjamin Berg 			/*
46242d3002bSBenjamin Berg 			 * Assume we are UP if we joined a cluster.
46342d3002bSBenjamin Berg 			 *
46442d3002bSBenjamin Berg 			 * If the other anchor master is still in the warmup
46542d3002bSBenjamin Berg 			 * phase, then we may temporarily become the anchor
46642d3002bSBenjamin Berg 			 * master until it sets its own master preference to
46742d3002bSBenjamin Berg 			 * be non-zero.
46842d3002bSBenjamin Berg 			 */
46942d3002bSBenjamin Berg 			data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP;
47042d3002bSBenjamin Berg 			data->nan.random_factor_valid_dwst = 0;
47142d3002bSBenjamin Berg 		}
47242d3002bSBenjamin Berg 	}
47342d3002bSBenjamin Berg 
47442d3002bSBenjamin Berg 	/*
47542d3002bSBenjamin Berg 	 * Anchor master selection
47642d3002bSBenjamin Berg 	 */
47742d3002bSBenjamin Berg 	/* We are not anchor master */
47842d3002bSBenjamin Berg 	if (is_same_cluster && is_sync_beacon && ami &&
47942d3002bSBenjamin Berg 	    data->nan.current_ami.hop_count != 0) {
48042d3002bSBenjamin Berg 		if (le64_to_cpu(data->nan.current_ami.master_rank) <
48142d3002bSBenjamin Berg 		    le64_to_cpu(ami->master_rank)) {
48242d3002bSBenjamin Berg 			if (ami->master_rank == data->nan.last_ami.master_rank &&
48342d3002bSBenjamin Berg 			    le32_to_cpu(ami->ambtt) <=
48442d3002bSBenjamin Berg 			    le32_to_cpu(data->nan.last_ami.ambtt)) {
48542d3002bSBenjamin Berg 				/* disregard frame */
48642d3002bSBenjamin Berg 			} else {
48742d3002bSBenjamin Berg 				memcpy(&data->nan.last_ami,
48842d3002bSBenjamin Berg 				       &data->nan.current_ami,
48942d3002bSBenjamin Berg 				       sizeof(data->nan.last_ami));
49042d3002bSBenjamin Berg 				memcpy(&data->nan.current_ami, ami,
49142d3002bSBenjamin Berg 				       sizeof(data->nan.last_ami));
49242d3002bSBenjamin Berg 				data->nan.current_ami.hop_count += 1;
49342d3002bSBenjamin Berg 			}
49442d3002bSBenjamin Berg 		}
49542d3002bSBenjamin Berg 
49642d3002bSBenjamin Berg 		if (le64_to_cpu(data->nan.current_ami.master_rank) >
49742d3002bSBenjamin Berg 		    le64_to_cpu(ami->master_rank)) {
49842d3002bSBenjamin Berg 			if (!ether_addr_equal(data->nan.current_ami.master_addr,
49942d3002bSBenjamin Berg 					      ami->master_addr)) {
50042d3002bSBenjamin Berg 				/* disregard frame */
50142d3002bSBenjamin Berg 			} else {
50242d3002bSBenjamin Berg 				u64 amr = hwsim_nan_get_master_rank(data);
50342d3002bSBenjamin Berg 
50442d3002bSBenjamin Berg 				if (amr > le64_to_cpu(ami->master_rank)) {
50542d3002bSBenjamin Berg 					/* assume ourselves as anchor master */
50642d3002bSBenjamin Berg 					wiphy_dbg(hw->wiphy,
50742d3002bSBenjamin Berg 						  "NAN: assume anchor master role\n");
50842d3002bSBenjamin Berg 					data->nan.current_ami.master_rank =
50942d3002bSBenjamin Berg 						cpu_to_le64(amr);
51042d3002bSBenjamin Berg 					data->nan.current_ami.hop_count = 0;
51142d3002bSBenjamin Berg 					memset(&data->nan.last_ami, 0,
51242d3002bSBenjamin Berg 					       sizeof(data->nan.last_ami));
51342d3002bSBenjamin Berg 					data->nan.last_ami.ambtt =
51442d3002bSBenjamin Berg 						data->nan.current_ami.ambtt;
51542d3002bSBenjamin Berg 					data->nan.current_ami.ambtt = 0;
51642d3002bSBenjamin Berg 				} else {
51742d3002bSBenjamin Berg 					memcpy(&data->nan.last_ami,
51842d3002bSBenjamin Berg 					       &data->nan.current_ami,
51942d3002bSBenjamin Berg 					       sizeof(data->nan.last_ami));
52042d3002bSBenjamin Berg 					memcpy(&data->nan.current_ami, ami,
52142d3002bSBenjamin Berg 					       sizeof(data->nan.last_ami));
52242d3002bSBenjamin Berg 					data->nan.current_ami.hop_count += 1;
52342d3002bSBenjamin Berg 				}
52442d3002bSBenjamin Berg 			}
52542d3002bSBenjamin Berg 		}
52642d3002bSBenjamin Berg 
52742d3002bSBenjamin Berg 		if (data->nan.current_ami.master_rank == ami->master_rank) {
52842d3002bSBenjamin Berg 			if (le32_to_cpu(data->nan.current_ami.ambtt) <
52942d3002bSBenjamin Berg 			    le32_to_cpu(ami->ambtt)) {
53042d3002bSBenjamin Berg 				data->nan.current_ami.ambtt = ami->ambtt;
53142d3002bSBenjamin Berg 			}
53242d3002bSBenjamin Berg 
53342d3002bSBenjamin Berg 			if (data->nan.current_ami.hop_count >
53442d3002bSBenjamin Berg 			    ami->hop_count + 1) {
53542d3002bSBenjamin Berg 				data->nan.current_ami.hop_count =
53642d3002bSBenjamin Berg 					ami->hop_count + 1;
53742d3002bSBenjamin Berg 			}
53842d3002bSBenjamin Berg 		}
53942d3002bSBenjamin Berg 	}
54042d3002bSBenjamin Berg 
54142d3002bSBenjamin Berg 	/* We are anchor master */
54242d3002bSBenjamin Berg 	if (is_same_cluster && is_sync_beacon && ami &&
54342d3002bSBenjamin Berg 	    data->nan.current_ami.hop_count == 0) {
54442d3002bSBenjamin Berg 		WARN_ON_ONCE(!ether_addr_equal(data->nan.current_ami.master_addr,
54542d3002bSBenjamin Berg 					       data->nan.device_vif->addr));
54642d3002bSBenjamin Berg 
54742d3002bSBenjamin Berg 		if (le64_to_cpu(ami->master_rank) <
54842d3002bSBenjamin Berg 		    le64_to_cpu(data->nan.current_ami.master_rank) ||
54942d3002bSBenjamin Berg 		    ether_addr_equal(ami->master_addr,
55042d3002bSBenjamin Berg 				     data->nan.current_ami.master_addr)) {
55142d3002bSBenjamin Berg 			/* disregard */
55242d3002bSBenjamin Berg 		} else {
55342d3002bSBenjamin Berg 			wiphy_dbg(hw->wiphy, "NAN: lost anchor master role\n");
55442d3002bSBenjamin Berg 			adopt_tsf = true;
55542d3002bSBenjamin Berg 			memcpy(&data->nan.last_ami, &data->nan.current_ami,
55642d3002bSBenjamin Berg 			       sizeof(data->nan.last_ami));
55742d3002bSBenjamin Berg 			memcpy(&data->nan.current_ami, ami,
55842d3002bSBenjamin Berg 			       sizeof(data->nan.last_ami));
55942d3002bSBenjamin Berg 			data->nan.current_ami.hop_count += 1;
56042d3002bSBenjamin Berg 		}
56142d3002bSBenjamin Berg 	}
56242d3002bSBenjamin Berg 
56342d3002bSBenjamin Berg 	if (adopt_tsf && !data->nan.tsf_adjusted) {
56442d3002bSBenjamin Berg 		int threshold = 5;
56542d3002bSBenjamin Berg 		s64 adjustment;
56642d3002bSBenjamin Berg 
56742d3002bSBenjamin Berg 		/* Timestamp is likely inaccurate (and late) in this case */
56842d3002bSBenjamin Berg 		if (!(IEEE80211_SKB_RXCB(skb)->flag & RX_FLAG_MACTIME))
56942d3002bSBenjamin Berg 			threshold = 128;
57042d3002bSBenjamin Berg 
57142d3002bSBenjamin Berg 		adjustment =
57242d3002bSBenjamin Berg 			le64_to_cpu(mgmt->u.beacon.timestamp) -
57342d3002bSBenjamin Berg 			ieee80211_calculate_rx_timestamp(hw, &rx_status,
57442d3002bSBenjamin Berg 							 skb->len, 24);
57542d3002bSBenjamin Berg 
57642d3002bSBenjamin Berg 		scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
57742d3002bSBenjamin Berg 			if (adjustment < -threshold || adjustment > threshold) {
57842d3002bSBenjamin Berg 				if (adjustment < -(s64)ieee80211_tu_to_usec(4) ||
57942d3002bSBenjamin Berg 				    adjustment > (s64)ieee80211_tu_to_usec(4))
58042d3002bSBenjamin Berg 					data->nan.tsf_discontinuity = true;
58142d3002bSBenjamin Berg 
58242d3002bSBenjamin Berg 				wiphy_debug(hw->wiphy,
58342d3002bSBenjamin Berg 					    "NAN: Adjusting TSF by +/- %d us or more: %lld us (discontinuity: %d, from: %pM, old offset: %lld)\n",
58442d3002bSBenjamin Berg 					    threshold, adjustment,
58542d3002bSBenjamin Berg 					    data->nan.tsf_discontinuity, mgmt->sa,
58642d3002bSBenjamin Berg 					    data->tsf_offset);
58742d3002bSBenjamin Berg 			} else {
58842d3002bSBenjamin Berg 				/* smooth things out a little bit */
58942d3002bSBenjamin Berg 				adjustment /= 2;
59042d3002bSBenjamin Berg 			}
59142d3002bSBenjamin Berg 
59242d3002bSBenjamin Berg 			/*
59342d3002bSBenjamin Berg 			 * Do the TSF adjustment
59442d3002bSBenjamin Berg 			 * The flag prevents further adjustments until the next
59542d3002bSBenjamin Berg 			 * 2.4 GHz DW starts to avoid race conditions for
59642d3002bSBenjamin Berg 			 * in-flight packets.
59742d3002bSBenjamin Berg 			 */
59842d3002bSBenjamin Berg 			data->nan.tsf_adjusted = true;
59942d3002bSBenjamin Berg 			data->tsf_offset += adjustment;
60042d3002bSBenjamin Berg 		}
60142d3002bSBenjamin Berg 	}
60242d3002bSBenjamin Berg 
60342d3002bSBenjamin Berg 	spin_unlock_bh(&data->nan.state_lock);
60442d3002bSBenjamin Berg 
60542d3002bSBenjamin Berg 	if (joined_cluster)
60642d3002bSBenjamin Berg 		ieee80211_nan_cluster_joined(data->nan.device_vif,
60742d3002bSBenjamin Berg 					     data->nan.cluster_id, false,
60842d3002bSBenjamin Berg 					     GFP_ATOMIC);
60942d3002bSBenjamin Berg }
61042d3002bSBenjamin Berg 
611df0cb6e2SBenjamin Berg static void
612df0cb6e2SBenjamin Berg mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data)
613df0cb6e2SBenjamin Berg {
61442d3002bSBenjamin Berg 	bool notify_join = false;
61542d3002bSBenjamin Berg 
616df0cb6e2SBenjamin Berg 	/*
617df0cb6e2SBenjamin Berg 	 * Handle NAN role and state transitions at the end of the DW period
618df0cb6e2SBenjamin Berg 	 * in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e.
619df0cb6e2SBenjamin Berg 	 * end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW.
620df0cb6e2SBenjamin Berg 	 */
62142d3002bSBenjamin Berg 
62242d3002bSBenjamin Berg 	spin_lock(&data->nan.state_lock);
62342d3002bSBenjamin Berg 
62442d3002bSBenjamin Berg 	/* Handle role transitions, Wi-Fi Aware version 4.0 section 3.3.6  */
62542d3002bSBenjamin Berg 	if (data->nan.master_transition_score < 3)
62642d3002bSBenjamin Berg 		data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER;
62742d3002bSBenjamin Berg 	else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER &&
62842d3002bSBenjamin Berg 		 data->nan.master_transition_score >= 3)
62942d3002bSBenjamin Berg 		data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC;
63042d3002bSBenjamin Berg 	else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_SYNC &&
63142d3002bSBenjamin Berg 		 data->nan.sync_transition_score >= 3)
63242d3002bSBenjamin Berg 		data->nan.role = MAC80211_HWSIM_NAN_ROLE_NON_SYNC;
63342d3002bSBenjamin Berg 	else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_NON_SYNC &&
63442d3002bSBenjamin Berg 		 data->nan.sync_transition_score < 3)
63542d3002bSBenjamin Berg 		data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC;
63642d3002bSBenjamin Berg 
63742d3002bSBenjamin Berg 	/*
63842d3002bSBenjamin Berg 	 * The discovery beacon timer will stop automatically. Make sure it is
63942d3002bSBenjamin Berg 	 * running if we are master. Do not bother with a proper alignment it
64042d3002bSBenjamin Berg 	 * will sync itself to the TSF after the first TX.
64142d3002bSBenjamin Berg 	 */
64242d3002bSBenjamin Berg 	if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER &&
64342d3002bSBenjamin Berg 	    !hrtimer_active(&data->nan.discovery_beacon_timer))
64442d3002bSBenjamin Berg 		hrtimer_start(&data->nan.discovery_beacon_timer,
64542d3002bSBenjamin Berg 			      ns_to_ktime(10 * NSEC_PER_USEC),
64642d3002bSBenjamin Berg 			      HRTIMER_MODE_REL_SOFT);
64742d3002bSBenjamin Berg 
64842d3002bSBenjamin Berg 	data->nan.master_transition_score = 0;
64942d3002bSBenjamin Berg 	data->nan.sync_transition_score = 0;
65042d3002bSBenjamin Berg 
65142d3002bSBenjamin Berg 	if (data->nan.random_factor_valid_dwst == 0) {
65242d3002bSBenjamin Berg 		u64 amr;
65342d3002bSBenjamin Berg 
65442d3002bSBenjamin Berg 		if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) {
65542d3002bSBenjamin Berg 			data->nan.phase = MAC80211_HWSIM_NAN_PHASE_WARMUP;
65642d3002bSBenjamin Berg 			data->nan.random_factor_valid_dwst = NAN_WARMUP_DWST;
65742d3002bSBenjamin Berg 
65842d3002bSBenjamin Berg 			notify_join = true;
65942d3002bSBenjamin Berg 		} else {
66042d3002bSBenjamin Berg 			data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP;
66142d3002bSBenjamin Berg 			data->nan.random_factor_valid_dwst =
66242d3002bSBenjamin Berg 				get_random_u32_inclusive(120, 240);
66342d3002bSBenjamin Berg 			data->nan.random_factor = get_random_u8();
66442d3002bSBenjamin Berg 		}
66542d3002bSBenjamin Berg 
66642d3002bSBenjamin Berg 		amr = hwsim_nan_get_master_rank(data);
66742d3002bSBenjamin Berg 
66842d3002bSBenjamin Berg 		if (data->nan.current_ami.hop_count == 0) {
66942d3002bSBenjamin Berg 			/* Update if we are already anchor master */
67042d3002bSBenjamin Berg 			data->nan.current_ami.master_rank = cpu_to_le64(amr);
67142d3002bSBenjamin Berg 		} else if (le64_to_cpu(data->nan.current_ami.master_rank) < amr) {
67242d3002bSBenjamin Berg 			/* assume role if we have a higher rank */
67342d3002bSBenjamin Berg 			wiphy_dbg(data->hw->wiphy,
67442d3002bSBenjamin Berg 				  "NAN: assume anchor master role\n");
67542d3002bSBenjamin Berg 			data->nan.current_ami.master_rank = cpu_to_le64(amr);
67642d3002bSBenjamin Berg 			data->nan.current_ami.hop_count = 0;
67742d3002bSBenjamin Berg 			memset(&data->nan.last_ami, 0,
67842d3002bSBenjamin Berg 			       sizeof(data->nan.last_ami));
67942d3002bSBenjamin Berg 			data->nan.last_ami.ambtt = data->nan.current_ami.ambtt;
68042d3002bSBenjamin Berg 			data->nan.current_ami.ambtt = 0;
68142d3002bSBenjamin Berg 		}
68242d3002bSBenjamin Berg 	} else {
68342d3002bSBenjamin Berg 		data->nan.random_factor_valid_dwst--;
68442d3002bSBenjamin Berg 	}
68542d3002bSBenjamin Berg 
68642d3002bSBenjamin Berg 	spin_unlock(&data->nan.state_lock);
68742d3002bSBenjamin Berg 
68842d3002bSBenjamin Berg 	if (notify_join)
68942d3002bSBenjamin Berg 		ieee80211_nan_cluster_joined(data->nan.device_vif,
69042d3002bSBenjamin Berg 					     data->nan.cluster_id, true,
69142d3002bSBenjamin Berg 					     GFP_ATOMIC);
69242d3002bSBenjamin Berg }
69342d3002bSBenjamin Berg 
69442d3002bSBenjamin Berg static void
69542d3002bSBenjamin Berg mac80211_hwsim_nan_tx_beacon(struct mac80211_hwsim_data *data,
69642d3002bSBenjamin Berg 			     bool is_discovery,
69742d3002bSBenjamin Berg 			     struct ieee80211_channel *channel)
69842d3002bSBenjamin Berg {
69942d3002bSBenjamin Berg 	struct ieee80211_vendor_ie nan_ie = {
70042d3002bSBenjamin Berg 		.element_id = WLAN_EID_VENDOR_SPECIFIC,
70142d3002bSBenjamin Berg 		.len = 27 - 2,
70242d3002bSBenjamin Berg 		.oui = { u32_get_bits(WLAN_OUI_WFA, 0xff0000),
70342d3002bSBenjamin Berg 			 u32_get_bits(WLAN_OUI_WFA, 0xff00),
70442d3002bSBenjamin Berg 			 u32_get_bits(WLAN_OUI_WFA, 0xff) },
70542d3002bSBenjamin Berg 		.oui_type = WLAN_OUI_TYPE_WFA_NAN,
70642d3002bSBenjamin Berg 	};
70742d3002bSBenjamin Berg 	size_t alloc_size =
70842d3002bSBenjamin Berg 		IEEE80211_TX_STATUS_HEADROOM +
70942d3002bSBenjamin Berg 		offsetofend(struct ieee80211_mgmt, u.beacon) +
71042d3002bSBenjamin Berg 		27 /* size of NAN vendor element */;
71142d3002bSBenjamin Berg 	struct ieee80211_nan_master_indication master_indication;
71242d3002bSBenjamin Berg 	struct ieee80211_nan_attr nan_attr;
71342d3002bSBenjamin Berg 	struct ieee80211_mgmt *mgmt;
71442d3002bSBenjamin Berg 	struct sk_buff *skb;
71542d3002bSBenjamin Berg 
71642d3002bSBenjamin Berg 	/*
71742d3002bSBenjamin Berg 	 * TODO: Should the configured vendor elements or NAN attributes be
71842d3002bSBenjamin Berg 	 * included in some of these beacons?
71942d3002bSBenjamin Berg 	 */
72042d3002bSBenjamin Berg 
72142d3002bSBenjamin Berg 	skb = alloc_skb(alloc_size, GFP_ATOMIC);
72242d3002bSBenjamin Berg 	if (!skb)
72342d3002bSBenjamin Berg 		return;
72442d3002bSBenjamin Berg 
72542d3002bSBenjamin Berg 	spin_lock(&data->nan.state_lock);
72642d3002bSBenjamin Berg 
72742d3002bSBenjamin Berg 	skb_reserve(skb, IEEE80211_TX_STATUS_HEADROOM);
72842d3002bSBenjamin Berg 	mgmt = skb_put(skb, offsetofend(struct ieee80211_mgmt, u.beacon));
72942d3002bSBenjamin Berg 
73042d3002bSBenjamin Berg 	memset(mgmt, 0, offsetofend(struct ieee80211_mgmt, u.beacon));
73142d3002bSBenjamin Berg 	memcpy(mgmt->sa, data->nan.device_vif->addr, ETH_ALEN);
73242d3002bSBenjamin Berg 	memset(mgmt->da, 0xff, ETH_ALEN);
73342d3002bSBenjamin Berg 	memcpy(mgmt->bssid, data->nan.cluster_id, ETH_ALEN);
73442d3002bSBenjamin Berg 
73542d3002bSBenjamin Berg 	mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
73642d3002bSBenjamin Berg 					  IEEE80211_STYPE_BEACON);
73742d3002bSBenjamin Berg 	mgmt->u.beacon.beacon_int = cpu_to_le16(is_discovery ? 100 : DWST_TU);
73842d3002bSBenjamin Berg 	mgmt->u.beacon.capab_info =
73942d3002bSBenjamin Berg 		cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME |
74042d3002bSBenjamin Berg 			    WLAN_CAPABILITY_SHORT_PREAMBLE);
74142d3002bSBenjamin Berg 
74242d3002bSBenjamin Berg 	/* FIXME: set these to saner values? */
74342d3002bSBenjamin Berg 	mgmt->duration = 0;
74442d3002bSBenjamin Berg 	mgmt->seq_ctrl = 0;
74542d3002bSBenjamin Berg 
74642d3002bSBenjamin Berg 	/* Put the NAN element */
74742d3002bSBenjamin Berg 	skb_put_data(skb, &nan_ie, sizeof(nan_ie));
74842d3002bSBenjamin Berg 
74942d3002bSBenjamin Berg 	nan_attr.attr = NAN_ATTR_MASTER_INDICATION;
75042d3002bSBenjamin Berg 	nan_attr.length = cpu_to_le16(sizeof(master_indication));
75142d3002bSBenjamin Berg 	if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) {
75242d3002bSBenjamin Berg 		master_indication.master_pref = data->nan.master_pref;
75342d3002bSBenjamin Berg 		master_indication.random_factor = data->nan.random_factor;
75442d3002bSBenjamin Berg 	} else {
75542d3002bSBenjamin Berg 		master_indication.master_pref = 0;
75642d3002bSBenjamin Berg 		master_indication.random_factor = 0;
75742d3002bSBenjamin Berg 	}
75842d3002bSBenjamin Berg 
75942d3002bSBenjamin Berg 	skb_put_data(skb, &nan_attr, sizeof(nan_attr));
76042d3002bSBenjamin Berg 	skb_put_data(skb, &master_indication, sizeof(master_indication));
76142d3002bSBenjamin Berg 
76242d3002bSBenjamin Berg 	nan_attr.attr = NAN_ATTR_CLUSTER_INFO;
76342d3002bSBenjamin Berg 	nan_attr.length = cpu_to_le16(sizeof(data->nan.current_ami));
76442d3002bSBenjamin Berg 	skb_put_data(skb, &nan_attr, sizeof(nan_attr));
76542d3002bSBenjamin Berg 	skb_put_data(skb, &data->nan.current_ami,
76642d3002bSBenjamin Berg 		     sizeof(data->nan.current_ami));
76742d3002bSBenjamin Berg 
76842d3002bSBenjamin Berg 	spin_unlock(&data->nan.state_lock);
76942d3002bSBenjamin Berg 
77042d3002bSBenjamin Berg 	mac80211_hwsim_tx_frame(data->hw, skb, channel);
771df0cb6e2SBenjamin Berg }
772df0cb6e2SBenjamin Berg 
773340f2ab8SBenjamin Berg enum hrtimer_restart
774df0cb6e2SBenjamin Berg mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
775340f2ab8SBenjamin Berg {
776340f2ab8SBenjamin Berg 	struct mac80211_hwsim_data *data =
777340f2ab8SBenjamin Berg 		container_of(timer, struct mac80211_hwsim_data,
778df0cb6e2SBenjamin Berg 			     nan.slot_timer);
779340f2ab8SBenjamin Berg 	struct ieee80211_hw *hw = data->hw;
780df0cb6e2SBenjamin Berg 	struct ieee80211_channel *notify_dw_chan = NULL;
78142d3002bSBenjamin Berg 	struct ieee80211_channel *beacon_sync_chan = NULL;
782df0cb6e2SBenjamin Berg 	u64 tsf = hwsim_nan_get_timer_tsf(data);
783df0cb6e2SBenjamin Berg 	u8 slot = hwsim_nan_slot_from_tsf(tsf);
784df0cb6e2SBenjamin Berg 	bool dwst_of_dw0 = false;
785df0cb6e2SBenjamin Berg 	bool dw_end = false;
78642d3002bSBenjamin Berg 	bool tx_sync_beacon;
787340f2ab8SBenjamin Berg 
788340f2ab8SBenjamin Berg 	if (!data->nan.device_vif)
789340f2ab8SBenjamin Berg 		return HRTIMER_NORESTART;
790340f2ab8SBenjamin Berg 
791df0cb6e2SBenjamin Berg 	if ((tsf & DW0_TSF_MASK & ~DWST_TSF_MASK) == 0)
792df0cb6e2SBenjamin Berg 		dwst_of_dw0 = true;
793df0cb6e2SBenjamin Berg 
794df0cb6e2SBenjamin Berg 
79542d3002bSBenjamin Berg 	scoped_guard(spinlock, &data->nan.state_lock) {
79642d3002bSBenjamin Berg 		if (data->nan.tsf_discontinuity) {
79742d3002bSBenjamin Berg 			data->nan.tsf_discontinuity = false;
79842d3002bSBenjamin Berg 
79942d3002bSBenjamin Berg 			mac80211_hwsim_nan_schedule_slot(data, 32, true);
80042d3002bSBenjamin Berg 
80142d3002bSBenjamin Berg 			return HRTIMER_RESTART;
80242d3002bSBenjamin Berg 		}
80342d3002bSBenjamin Berg 
80442d3002bSBenjamin Berg 		if (slot == SLOT_24GHZ_DW)
80542d3002bSBenjamin Berg 			data->nan.tsf_adjusted = false;
80642d3002bSBenjamin Berg 
80742d3002bSBenjamin Berg 		tx_sync_beacon =
80842d3002bSBenjamin Berg 			data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN &&
80942d3002bSBenjamin Berg 			data->nan.role != MAC80211_HWSIM_NAN_ROLE_NON_SYNC;
81042d3002bSBenjamin Berg 	}
81142d3002bSBenjamin Berg 
812df0cb6e2SBenjamin Berg 	switch (slot) {
813df0cb6e2SBenjamin Berg 	case SLOT_24GHZ_DW:
814df0cb6e2SBenjamin Berg 		wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n",
815df0cb6e2SBenjamin Berg 			  dwst_of_dw0);
81642d3002bSBenjamin Berg 		beacon_sync_chan = ieee80211_get_channel(hw->wiphy, 2437);
817df0cb6e2SBenjamin Berg 		break;
818df0cb6e2SBenjamin Berg 
819df0cb6e2SBenjamin Berg 	case SLOT_24GHZ_DW + 1:
820df0cb6e2SBenjamin Berg 		if (!(data->nan.bands & BIT(NL80211_BAND_5GHZ))) {
821df0cb6e2SBenjamin Berg 			notify_dw_chan = ieee80211_get_channel(hw->wiphy, 2437);
822df0cb6e2SBenjamin Berg 			dw_end = true;
823df0cb6e2SBenjamin Berg 		} else {
824df0cb6e2SBenjamin Berg 			notify_dw_chan = ieee80211_get_channel(hw->wiphy, 5745);
825df0cb6e2SBenjamin Berg 		}
826df0cb6e2SBenjamin Berg 		break;
827df0cb6e2SBenjamin Berg 
828df0cb6e2SBenjamin Berg 	case SLOT_5GHZ_DW:
829340f2ab8SBenjamin Berg 		if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
830df0cb6e2SBenjamin Berg 			wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n");
83142d3002bSBenjamin Berg 			beacon_sync_chan =
83242d3002bSBenjamin Berg 				ieee80211_get_channel(hw->wiphy, 5745);
833340f2ab8SBenjamin Berg 		}
834df0cb6e2SBenjamin Berg 		break;
835df0cb6e2SBenjamin Berg 
836df0cb6e2SBenjamin Berg 	case SLOT_5GHZ_DW + 1:
837df0cb6e2SBenjamin Berg 		if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
838df0cb6e2SBenjamin Berg 			notify_dw_chan =
839df0cb6e2SBenjamin Berg 				ieee80211_get_channel(hw->wiphy, 2437);
840df0cb6e2SBenjamin Berg 			dw_end = true;
841df0cb6e2SBenjamin Berg 		}
842df0cb6e2SBenjamin Berg 		break;
843340f2ab8SBenjamin Berg 	}
844340f2ab8SBenjamin Berg 
84542d3002bSBenjamin Berg 	/* TODO: This does not implement DW contention mitigation */
84642d3002bSBenjamin Berg 	if (beacon_sync_chan && tx_sync_beacon)
84742d3002bSBenjamin Berg 		mac80211_hwsim_nan_tx_beacon(data, false, beacon_sync_chan);
84842d3002bSBenjamin Berg 
849df0cb6e2SBenjamin Berg 	if (dw_end)
850df0cb6e2SBenjamin Berg 		mac80211_hwsim_nan_exec_state_transitions(data);
851340f2ab8SBenjamin Berg 
852df0cb6e2SBenjamin Berg 	if (data->nan.notify_dw && notify_dw_chan) {
853340f2ab8SBenjamin Berg 		struct wireless_dev *wdev =
854340f2ab8SBenjamin Berg 			ieee80211_vif_to_wdev(data->nan.device_vif);
855340f2ab8SBenjamin Berg 
856df0cb6e2SBenjamin Berg 		cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC);
857340f2ab8SBenjamin Berg 	}
858340f2ab8SBenjamin Berg 
859f9aeb3a2SBenjamin Berg 	mac80211_hwsim_nan_resume_txqs(data);
860f9aeb3a2SBenjamin Berg 
86142d3002bSBenjamin Berg 	mac80211_hwsim_nan_schedule_slot(data, slot + 1, false);
86242d3002bSBenjamin Berg 
86342d3002bSBenjamin Berg 	return HRTIMER_RESTART;
86442d3002bSBenjamin Berg }
86542d3002bSBenjamin Berg 
86642d3002bSBenjamin Berg enum hrtimer_restart
86742d3002bSBenjamin Berg mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer)
86842d3002bSBenjamin Berg {
86942d3002bSBenjamin Berg 	struct mac80211_hwsim_data *data =
87042d3002bSBenjamin Berg 		container_of(timer, struct mac80211_hwsim_data,
87142d3002bSBenjamin Berg 			     nan.discovery_beacon_timer);
87242d3002bSBenjamin Berg 	u32 remainder;
87342d3002bSBenjamin Berg 	u64 tsf_now;
87442d3002bSBenjamin Berg 	u64 tbtt;
87542d3002bSBenjamin Berg 
87642d3002bSBenjamin Berg 	if (!data->nan.device_vif)
87742d3002bSBenjamin Berg 		return HRTIMER_NORESTART;
87842d3002bSBenjamin Berg 
87942d3002bSBenjamin Berg 	scoped_guard(spinlock, &data->nan.state_lock) {
88042d3002bSBenjamin Berg 		if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN ||
88142d3002bSBenjamin Berg 		    data->nan.role != MAC80211_HWSIM_NAN_ROLE_MASTER)
88242d3002bSBenjamin Berg 			return HRTIMER_NORESTART;
88342d3002bSBenjamin Berg 	}
88442d3002bSBenjamin Berg 
88542d3002bSBenjamin Berg 	mac80211_hwsim_nan_tx_beacon(
88642d3002bSBenjamin Berg 		data, true, ieee80211_get_channel(data->hw->wiphy, 2437));
88742d3002bSBenjamin Berg 
88842d3002bSBenjamin Berg 	if (data->nan.bands & BIT(NL80211_BAND_5GHZ))
88942d3002bSBenjamin Berg 		mac80211_hwsim_nan_tx_beacon(
89042d3002bSBenjamin Berg 			data, true,
89142d3002bSBenjamin Berg 			ieee80211_get_channel(data->hw->wiphy, 5745));
89242d3002bSBenjamin Berg 
89342d3002bSBenjamin Berg 	/* Read the TSF from the current time in case of adjustments */
89442d3002bSBenjamin Berg 	tsf_now = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
89542d3002bSBenjamin Berg 
89642d3002bSBenjamin Berg 	/* Wrap value to be after the next TBTT */
89742d3002bSBenjamin Berg 	tbtt = tsf_now + ieee80211_tu_to_usec(100);
89842d3002bSBenjamin Berg 
89942d3002bSBenjamin Berg 	/* Round TBTT down to the correct time */
90042d3002bSBenjamin Berg 	div_u64_rem(tbtt, ieee80211_tu_to_usec(100), &remainder);
90142d3002bSBenjamin Berg 	tbtt = tbtt - remainder;
90242d3002bSBenjamin Berg 
90342d3002bSBenjamin Berg 	hrtimer_set_expires(&data->nan.discovery_beacon_timer,
90442d3002bSBenjamin Berg 			    mac80211_hwsim_tsf_to_boottime(data, tbtt));
905df0cb6e2SBenjamin Berg 
906340f2ab8SBenjamin Berg 	return HRTIMER_RESTART;
907340f2ab8SBenjamin Berg }
908340f2ab8SBenjamin Berg 
909340f2ab8SBenjamin Berg int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
910340f2ab8SBenjamin Berg 			     struct ieee80211_vif *vif,
911340f2ab8SBenjamin Berg 			     struct cfg80211_nan_conf *conf)
912340f2ab8SBenjamin Berg {
913340f2ab8SBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
914340f2ab8SBenjamin Berg 
915340f2ab8SBenjamin Berg 	if (vif->type != NL80211_IFTYPE_NAN)
916340f2ab8SBenjamin Berg 		return -EINVAL;
917340f2ab8SBenjamin Berg 
918340f2ab8SBenjamin Berg 	if (data->nan.device_vif)
919340f2ab8SBenjamin Berg 		return -EALREADY;
920340f2ab8SBenjamin Berg 
921340f2ab8SBenjamin Berg 	/* set this before starting the timer, as preemption might occur */
922340f2ab8SBenjamin Berg 	data->nan.device_vif = vif;
923340f2ab8SBenjamin Berg 	data->nan.bands = conf->bands;
924340f2ab8SBenjamin Berg 
92542d3002bSBenjamin Berg 	scoped_guard(spinlock_bh, &data->nan.state_lock) {
92642d3002bSBenjamin Berg 		/* Start in the "scan" phase and stay there for a little bit */
92742d3002bSBenjamin Berg 		data->nan.phase = MAC80211_HWSIM_NAN_PHASE_SCAN;
92842d3002bSBenjamin Berg 		data->nan.random_factor_valid_dwst = 1;
92942d3002bSBenjamin Berg 		data->nan.random_factor = 0;
93042d3002bSBenjamin Berg 		data->nan.master_pref = conf->master_pref;
93142d3002bSBenjamin Berg 		data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER;
93242d3002bSBenjamin Berg 		memset(&data->nan.current_ami, 0,
93342d3002bSBenjamin Berg 		       sizeof(data->nan.current_ami));
93442d3002bSBenjamin Berg 		memset(&data->nan.last_ami, 0, sizeof(data->nan.last_ami));
93542d3002bSBenjamin Berg 		data->nan.current_ami.master_rank =
93642d3002bSBenjamin Berg 			cpu_to_le64(hwsim_nan_get_master_rank(data));
93742d3002bSBenjamin Berg 	}
93842d3002bSBenjamin Berg 
939df0cb6e2SBenjamin Berg 	/* Just run this "soon" and start in a random schedule position */
940df0cb6e2SBenjamin Berg 	hrtimer_start(&data->nan.slot_timer,
941df0cb6e2SBenjamin Berg 		      ns_to_ktime(10 * NSEC_PER_USEC),
942340f2ab8SBenjamin Berg 		      HRTIMER_MODE_REL_SOFT);
943340f2ab8SBenjamin Berg 
94442d3002bSBenjamin Berg 	ether_addr_copy(data->nan.cluster_id, conf->cluster_id);
945340f2ab8SBenjamin Berg 
946340f2ab8SBenjamin Berg 	data->nan.notify_dw = conf->enable_dw_notification;
947340f2ab8SBenjamin Berg 
948340f2ab8SBenjamin Berg 	return 0;
949340f2ab8SBenjamin Berg }
950340f2ab8SBenjamin Berg 
951340f2ab8SBenjamin Berg int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
952340f2ab8SBenjamin Berg 			    struct ieee80211_vif *vif)
953340f2ab8SBenjamin Berg {
954340f2ab8SBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
955340f2ab8SBenjamin Berg 
956340f2ab8SBenjamin Berg 	if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
957340f2ab8SBenjamin Berg 	    data->nan.device_vif != vif)
958340f2ab8SBenjamin Berg 		return -EINVAL;
959340f2ab8SBenjamin Berg 
960df0cb6e2SBenjamin Berg 	hrtimer_cancel(&data->nan.slot_timer);
961f9aeb3a2SBenjamin Berg 	hrtimer_cancel(&data->nan.resume_txqs_timer);
96242d3002bSBenjamin Berg 	hrtimer_cancel(&data->nan.discovery_beacon_timer);
963340f2ab8SBenjamin Berg 	data->nan.device_vif = NULL;
964340f2ab8SBenjamin Berg 
965340f2ab8SBenjamin Berg 	return 0;
966340f2ab8SBenjamin Berg }
967340f2ab8SBenjamin Berg 
968340f2ab8SBenjamin Berg int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
969340f2ab8SBenjamin Berg 				     struct ieee80211_vif *vif,
970340f2ab8SBenjamin Berg 				     struct cfg80211_nan_conf *conf,
971340f2ab8SBenjamin Berg 				     u32 changes)
972340f2ab8SBenjamin Berg {
973340f2ab8SBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
974340f2ab8SBenjamin Berg 
975340f2ab8SBenjamin Berg 	if (vif->type != NL80211_IFTYPE_NAN)
976340f2ab8SBenjamin Berg 		return -EINVAL;
977340f2ab8SBenjamin Berg 
978340f2ab8SBenjamin Berg 	if (!data->nan.device_vif)
979340f2ab8SBenjamin Berg 		return -EINVAL;
980340f2ab8SBenjamin Berg 
981340f2ab8SBenjamin Berg 	wiphy_debug(hw->wiphy, "nan_config_changed: changes=0x%x\n", changes);
982340f2ab8SBenjamin Berg 
983340f2ab8SBenjamin Berg 	/* Handle only the changes we care about for simulation purposes */
984df0cb6e2SBenjamin Berg 	if (changes & CFG80211_NAN_CONF_CHANGED_BANDS)
985340f2ab8SBenjamin Berg 		data->nan.bands = conf->bands;
986340f2ab8SBenjamin Berg 
987340f2ab8SBenjamin Berg 	if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
988340f2ab8SBenjamin Berg 		data->nan.notify_dw = conf->enable_dw_notification;
989340f2ab8SBenjamin Berg 
99042d3002bSBenjamin Berg 	if (changes & CFG80211_NAN_CONF_CHANGED_PREF) {
99142d3002bSBenjamin Berg 		scoped_guard(spinlock_bh, &data->nan.state_lock)
99242d3002bSBenjamin Berg 			data->nan.master_pref = conf->master_pref;
99342d3002bSBenjamin Berg 	}
99442d3002bSBenjamin Berg 
995340f2ab8SBenjamin Berg 	return 0;
996340f2ab8SBenjamin Berg }
997f9aeb3a2SBenjamin Berg 
9982c8ec5a9SDaniel Gabay static void hwsim_nan_can_sta_transmit(void *_ctx, struct ieee80211_sta *sta)
999f9aeb3a2SBenjamin Berg {
10002c8ec5a9SDaniel Gabay 	struct hwsim_nan_sta_iter_ctx *ctx = _ctx;
1001f9aeb3a2SBenjamin Berg 
10022c8ec5a9SDaniel Gabay 	if (ctx->can_tx)
1003f9aeb3a2SBenjamin Berg 		return;
1004f9aeb3a2SBenjamin Berg 
10052c8ec5a9SDaniel Gabay 	for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
10062c8ec5a9SDaniel Gabay 		struct ieee80211_txq *txq = sta->txq[i];
10072c8ec5a9SDaniel Gabay 
10082c8ec5a9SDaniel Gabay 		if (!txq)
10092c8ec5a9SDaniel Gabay 			continue;
10102c8ec5a9SDaniel Gabay 
10112c8ec5a9SDaniel Gabay 		if (txq->vif->type != NL80211_IFTYPE_NAN &&
10122c8ec5a9SDaniel Gabay 		    txq->vif->type != NL80211_IFTYPE_NAN_DATA)
10132c8ec5a9SDaniel Gabay 			return;
10142c8ec5a9SDaniel Gabay 
10152c8ec5a9SDaniel Gabay 		if (mac80211_hwsim_nan_txq_transmitting(ctx->hw, txq)) {
10162c8ec5a9SDaniel Gabay 			ctx->can_tx = true;
10172c8ec5a9SDaniel Gabay 			return;
10182c8ec5a9SDaniel Gabay 		}
10192c8ec5a9SDaniel Gabay 	}
10202c8ec5a9SDaniel Gabay }
10212c8ec5a9SDaniel Gabay 
10222c8ec5a9SDaniel Gabay static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
10232c8ec5a9SDaniel Gabay {
10242c8ec5a9SDaniel Gabay 	u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
10252c8ec5a9SDaniel Gabay 	u8 slot = hwsim_nan_slot_from_tsf(tsf);
10262c8ec5a9SDaniel Gabay 	bool is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot);
10272c8ec5a9SDaniel Gabay 	struct hwsim_nan_sta_iter_ctx ctx = {
10282c8ec5a9SDaniel Gabay 		.hw = data->hw,
10292c8ec5a9SDaniel Gabay 		.can_tx = false,
10302c8ec5a9SDaniel Gabay 	};
10312c8ec5a9SDaniel Gabay 	u32 timeout_ns;
10322c8ec5a9SDaniel Gabay 
10332c8ec5a9SDaniel Gabay 	/* Outside DW, require local FAW schedule to proceed */
10342c8ec5a9SDaniel Gabay 	if (!is_dw_slot) {
10352c8ec5a9SDaniel Gabay 		scoped_guard(spinlock_bh, &data->nan.state_lock) {
10362c8ec5a9SDaniel Gabay 			if (!data->nan.local_sched[slot].chan)
10372c8ec5a9SDaniel Gabay 				return;
10382c8ec5a9SDaniel Gabay 		}
10392c8ec5a9SDaniel Gabay 	}
10402c8ec5a9SDaniel Gabay 
10412c8ec5a9SDaniel Gabay 	guard(rcu)();
10422c8ec5a9SDaniel Gabay 
10432c8ec5a9SDaniel Gabay 	/* Check if management queue can transmit */
10442c8ec5a9SDaniel Gabay 	if (mac80211_hwsim_nan_txq_transmitting(data->hw,
10452c8ec5a9SDaniel Gabay 						data->nan.device_vif->txq_mgmt))
10462c8ec5a9SDaniel Gabay 		goto resume_txqs_timer;
10472c8ec5a9SDaniel Gabay 
10482c8ec5a9SDaniel Gabay 	/* Check if any STA queue can transmit */
10492c8ec5a9SDaniel Gabay 	ieee80211_iterate_stations_atomic(data->hw,
10502c8ec5a9SDaniel Gabay 					  hwsim_nan_can_sta_transmit,
10512c8ec5a9SDaniel Gabay 					  &ctx);
10522c8ec5a9SDaniel Gabay 
10532c8ec5a9SDaniel Gabay 	if (!ctx.can_tx)
10542c8ec5a9SDaniel Gabay 		return;
10552c8ec5a9SDaniel Gabay 
10562c8ec5a9SDaniel Gabay resume_txqs_timer:
1057f9aeb3a2SBenjamin Berg 	/*
1058f9aeb3a2SBenjamin Berg 	 * Wait a bit and also randomize things so that not everyone is TXing
1059f9aeb3a2SBenjamin Berg 	 * at the same time. Each slot is 16 TU long, this waits between 100 us
1060f9aeb3a2SBenjamin Berg 	 * and 5 ms before starting to TX (unless a new frame arrives).
1061f9aeb3a2SBenjamin Berg 	 */
1062f9aeb3a2SBenjamin Berg 	timeout_ns = get_random_u32_inclusive(100 * NSEC_PER_USEC,
1063f9aeb3a2SBenjamin Berg 					      5 * NSEC_PER_MSEC);
1064f9aeb3a2SBenjamin Berg 
1065f9aeb3a2SBenjamin Berg 	hrtimer_start(&data->nan.resume_txqs_timer,
1066f9aeb3a2SBenjamin Berg 		      ns_to_ktime(timeout_ns),
1067f9aeb3a2SBenjamin Berg 		      HRTIMER_MODE_REL_SOFT);
1068f9aeb3a2SBenjamin Berg }
1069f9aeb3a2SBenjamin Berg 
10702c8ec5a9SDaniel Gabay static void hwsim_nan_wake_sta_iter(void *_data, struct ieee80211_sta *sta)
10712c8ec5a9SDaniel Gabay {
10722c8ec5a9SDaniel Gabay 	struct ieee80211_hw *hw = _data;
10732c8ec5a9SDaniel Gabay 
10742c8ec5a9SDaniel Gabay 	for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) {
10752c8ec5a9SDaniel Gabay 		struct ieee80211_txq *txq = sta->txq[i];
10762c8ec5a9SDaniel Gabay 
10772c8ec5a9SDaniel Gabay 		if (!txq)
10782c8ec5a9SDaniel Gabay 			continue;
10792c8ec5a9SDaniel Gabay 
10802c8ec5a9SDaniel Gabay 		/* exit early if non-NAN */
10812c8ec5a9SDaniel Gabay 		if (txq->vif->type != NL80211_IFTYPE_NAN &&
10822c8ec5a9SDaniel Gabay 		    txq->vif->type != NL80211_IFTYPE_NAN_DATA)
10832c8ec5a9SDaniel Gabay 			return;
10842c8ec5a9SDaniel Gabay 
10852c8ec5a9SDaniel Gabay 		if (mac80211_hwsim_nan_txq_transmitting(hw, txq))
10862c8ec5a9SDaniel Gabay 			ieee80211_hwsim_wake_tx_queue(hw, txq);
10872c8ec5a9SDaniel Gabay 	}
10882c8ec5a9SDaniel Gabay }
10892c8ec5a9SDaniel Gabay 
1090f9aeb3a2SBenjamin Berg enum hrtimer_restart
1091f9aeb3a2SBenjamin Berg mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer)
1092f9aeb3a2SBenjamin Berg {
1093f9aeb3a2SBenjamin Berg 	struct mac80211_hwsim_data *data =
1094f9aeb3a2SBenjamin Berg 		container_of(timer, struct mac80211_hwsim_data,
1095f9aeb3a2SBenjamin Berg 			     nan.resume_txqs_timer);
1096f9aeb3a2SBenjamin Berg 
1097f9aeb3a2SBenjamin Berg 	guard(rcu)();
1098f9aeb3a2SBenjamin Berg 
1099f9aeb3a2SBenjamin Berg 	/* Wake TX queue for management frames on the NAN device interface */
1100f9aeb3a2SBenjamin Berg 	if (mac80211_hwsim_nan_txq_transmitting(data->hw,
1101f9aeb3a2SBenjamin Berg 						data->nan.device_vif->txq_mgmt))
1102f9aeb3a2SBenjamin Berg 		ieee80211_hwsim_wake_tx_queue(data->hw,
1103f9aeb3a2SBenjamin Berg 					      data->nan.device_vif->txq_mgmt);
1104f9aeb3a2SBenjamin Berg 
11052c8ec5a9SDaniel Gabay 	/* Wake TX queues for all stations */
11062c8ec5a9SDaniel Gabay 	ieee80211_iterate_stations_atomic(data->hw,
11072c8ec5a9SDaniel Gabay 					  hwsim_nan_wake_sta_iter,
11082c8ec5a9SDaniel Gabay 					  data->hw);
11092c8ec5a9SDaniel Gabay 
1110f9aeb3a2SBenjamin Berg 	return HRTIMER_NORESTART;
1111f9aeb3a2SBenjamin Berg }
1112f9aeb3a2SBenjamin Berg 
1113*55eaca33SIlan Peer static void
1114*55eaca33SIlan Peer hwsim_nan_can_mcast_sta_transmit(void *_ctx, struct ieee80211_sta *sta)
1115*55eaca33SIlan Peer {
1116*55eaca33SIlan Peer 	struct hwsim_nan_mcast_data_iter_ctx *ctx = _ctx;
1117*55eaca33SIlan Peer 	struct ieee80211_txq *txq = sta->txq[0];
1118*55eaca33SIlan Peer 
1119*55eaca33SIlan Peer 	if (!txq || txq->vif != ctx->vif)
1120*55eaca33SIlan Peer 		return;
1121*55eaca33SIlan Peer 
1122*55eaca33SIlan Peer 	ctx->n_vif_sta++;
1123*55eaca33SIlan Peer 	if (mac80211_hwsim_nan_txq_transmitting(ctx->hw, txq))
1124*55eaca33SIlan Peer 		ctx->n_sta_can_tx++;
1125*55eaca33SIlan Peer }
1126*55eaca33SIlan Peer 
1127*55eaca33SIlan Peer static bool
1128*55eaca33SIlan Peer mac80211_hwsim_nan_mcast_data_transmitting(struct ieee80211_hw *hw,
1129*55eaca33SIlan Peer 					   struct ieee80211_txq *txq)
1130*55eaca33SIlan Peer {
1131*55eaca33SIlan Peer 	struct mac80211_hwsim_data *data = hw->priv;
1132*55eaca33SIlan Peer 	struct hwsim_nan_mcast_data_iter_ctx ctx = {
1133*55eaca33SIlan Peer 		.hw = hw,
1134*55eaca33SIlan Peer 		.vif = txq->vif,
1135*55eaca33SIlan Peer 		.n_sta_can_tx = 0,
1136*55eaca33SIlan Peer 		.n_vif_sta = 0,
1137*55eaca33SIlan Peer 	};
1138*55eaca33SIlan Peer 
1139*55eaca33SIlan Peer 	/* Check if all the stations associated with the current
1140*55eaca33SIlan Peer 	 * interface are available.
1141*55eaca33SIlan Peer 	 */
1142*55eaca33SIlan Peer 	ieee80211_iterate_stations_atomic(data->hw,
1143*55eaca33SIlan Peer 					  hwsim_nan_can_mcast_sta_transmit,
1144*55eaca33SIlan Peer 					  &ctx);
1145*55eaca33SIlan Peer 
1146*55eaca33SIlan Peer 	return ctx.n_vif_sta && ctx.n_sta_can_tx == ctx.n_vif_sta;
1147*55eaca33SIlan Peer }
1148*55eaca33SIlan Peer 
1149f9aeb3a2SBenjamin Berg bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
1150f9aeb3a2SBenjamin Berg 					 struct ieee80211_txq *txq)
1151f9aeb3a2SBenjamin Berg {
1152f9aeb3a2SBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
11532c8ec5a9SDaniel Gabay 	struct ieee80211_sta *nmi_sta;
11542c8ec5a9SDaniel Gabay 	struct hwsim_sta_priv *sp;
11552c8ec5a9SDaniel Gabay 	bool is_dw_slot;
1156f9aeb3a2SBenjamin Berg 	u64 tsf;
1157f9aeb3a2SBenjamin Berg 	u8 slot;
1158f9aeb3a2SBenjamin Berg 
1159f9aeb3a2SBenjamin Berg 	if (WARN_ON_ONCE(!data->nan.device_vif))
1160f9aeb3a2SBenjamin Berg 		return true;
1161f9aeb3a2SBenjamin Berg 
1162f9aeb3a2SBenjamin Berg 	tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);
1163f9aeb3a2SBenjamin Berg 	slot = hwsim_nan_slot_from_tsf(tsf);
1164f9aeb3a2SBenjamin Berg 
1165f9aeb3a2SBenjamin Berg 	/* Enforce a maximum channel switch time and guard against TX delays */
1166f9aeb3a2SBenjamin Berg 	if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US))
1167f9aeb3a2SBenjamin Berg 		return false;
1168f9aeb3a2SBenjamin Berg 
11692c8ec5a9SDaniel Gabay 	is_dw_slot = mac80211_hwsim_nan_is_dw_slot(data, slot);
1170f9aeb3a2SBenjamin Berg 
1171*55eaca33SIlan Peer 	if (!txq->sta) {
11722c8ec5a9SDaniel Gabay 		/* Non-STA TXQ: allow management frames during DW */
1173*55eaca33SIlan Peer 		if (txq->vif->type == NL80211_IFTYPE_NAN)
11742c8ec5a9SDaniel Gabay 			return is_dw_slot;
11752c8ec5a9SDaniel Gabay 
1176*55eaca33SIlan Peer 		/* Allow multicast data when all the peers are available
1177*55eaca33SIlan Peer 		 * on this slot
1178*55eaca33SIlan Peer 		 */
1179*55eaca33SIlan Peer 		return mac80211_hwsim_nan_mcast_data_transmitting(hw, txq);
1180*55eaca33SIlan Peer 	}
1181*55eaca33SIlan Peer 
11822c8ec5a9SDaniel Gabay 	/* STA TXQ: need peer schedule for availability check */
11832c8ec5a9SDaniel Gabay 	nmi_sta = rcu_dereference(txq->sta->nmi) ?: txq->sta;
11842c8ec5a9SDaniel Gabay 	sp = (void *)nmi_sta->drv_priv;
11852c8ec5a9SDaniel Gabay 
11862c8ec5a9SDaniel Gabay 	/* DW slot: NDI can TX only mgmt but not worth checking,
11872c8ec5a9SDaniel Gabay 	 * NMI checks peer's committed DW
11882c8ec5a9SDaniel Gabay 	 */
11892c8ec5a9SDaniel Gabay 	if (is_dw_slot) {
11902c8ec5a9SDaniel Gabay 		if (txq->vif->type == NL80211_IFTYPE_NAN_DATA)
1191f9aeb3a2SBenjamin Berg 			return false;
11922c8ec5a9SDaniel Gabay 		return hwsim_nan_peer_present_in_dw(sp, tsf);
1193f9aeb3a2SBenjamin Berg 	}
1194f9aeb3a2SBenjamin Berg 
11952c8ec5a9SDaniel Gabay 	/* FAW slot: verify local schedule and peer availability */
11962c8ec5a9SDaniel Gabay 	return hwsim_nan_peer_present_in_faw(sp, data, slot);
1197f9aeb3a2SBenjamin Berg }
119861de252fSBenjamin Berg 
11992c8ec5a9SDaniel Gabay void mac80211_hwsim_nan_get_tx_chandef(struct ieee80211_hw *hw,
12002c8ec5a9SDaniel Gabay 				       struct cfg80211_chan_def *chandef)
120161de252fSBenjamin Berg {
120261de252fSBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
120361de252fSBenjamin Berg 	u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
120461de252fSBenjamin Berg 	u8 slot = hwsim_nan_slot_from_tsf(tsf);
120561de252fSBenjamin Berg 
12062c8ec5a9SDaniel Gabay 	/* DW slots are always 20 MHz */
12072c8ec5a9SDaniel Gabay 	if (slot == SLOT_24GHZ_DW) {
12082c8ec5a9SDaniel Gabay 		cfg80211_chandef_create(chandef,
12092c8ec5a9SDaniel Gabay 					ieee80211_get_channel(hw->wiphy, 2437),
12102c8ec5a9SDaniel Gabay 					NL80211_CHAN_NO_HT);
12112c8ec5a9SDaniel Gabay 		return;
12122c8ec5a9SDaniel Gabay 	}
121361de252fSBenjamin Berg 
12142c8ec5a9SDaniel Gabay 	if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
12152c8ec5a9SDaniel Gabay 		cfg80211_chandef_create(chandef,
12162c8ec5a9SDaniel Gabay 					ieee80211_get_channel(hw->wiphy, 5745),
12172c8ec5a9SDaniel Gabay 					NL80211_CHAN_NO_HT);
12182c8ec5a9SDaniel Gabay 		return;
12192c8ec5a9SDaniel Gabay 	}
122061de252fSBenjamin Berg 
12212c8ec5a9SDaniel Gabay 	/* FAW slot: copy local schedule for this slot */
12222c8ec5a9SDaniel Gabay 	scoped_guard(spinlock_bh, &data->nan.state_lock)
12232c8ec5a9SDaniel Gabay 		*chandef = data->nan.local_sched[slot];
122461de252fSBenjamin Berg }
12254f4b0f4eSBenjamin Berg 
12264f4b0f4eSBenjamin Berg bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
12274f4b0f4eSBenjamin Berg 				struct ieee80211_channel *channel,
12284f4b0f4eSBenjamin Berg 				struct ieee80211_rx_status *rx_status)
12294f4b0f4eSBenjamin Berg {
12304f4b0f4eSBenjamin Berg 	struct mac80211_hwsim_data *data = hw->priv;
12314f4b0f4eSBenjamin Berg 	u8 slot;
12324f4b0f4eSBenjamin Berg 
12334f4b0f4eSBenjamin Berg 	if (WARN_ON_ONCE(!data->nan.device_vif))
12344f4b0f4eSBenjamin Berg 		return false;
12354f4b0f4eSBenjamin Berg 
123642d3002bSBenjamin Berg 	if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN)
123742d3002bSBenjamin Berg 		return channel->center_freq == 2437;
123842d3002bSBenjamin Berg 
123942d3002bSBenjamin Berg 	if (rx_status->flag & RX_FLAG_MACTIME) {
12404f4b0f4eSBenjamin Berg 		slot = hwsim_nan_slot_from_tsf(rx_status->mactime);
12414f4b0f4eSBenjamin Berg 	} else {
12424f4b0f4eSBenjamin Berg 		u64 tsf;
12434f4b0f4eSBenjamin Berg 
12444f4b0f4eSBenjamin Berg 		/*
12454f4b0f4eSBenjamin Berg 		 * This is not perfect, but that should be fine.
12464f4b0f4eSBenjamin Berg 		 *
12474f4b0f4eSBenjamin Berg 		 * Assume the frame might be a bit early in relation to our
12484f4b0f4eSBenjamin Berg 		 * own TSF. This is largely because the TSF sync is going to be
12494f4b0f4eSBenjamin Berg 		 * pretty bad when the frame was RXed via NL and the beacon as
12504f4b0f4eSBenjamin Berg 		 * well as RX timestamps are not accurate.
12514f4b0f4eSBenjamin Berg 		 */
12524f4b0f4eSBenjamin Berg 		tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
12534f4b0f4eSBenjamin Berg 		slot = hwsim_nan_slot_from_tsf(tsf + 128);
12544f4b0f4eSBenjamin Berg 	}
12554f4b0f4eSBenjamin Berg 
12564f4b0f4eSBenjamin Berg 	if (slot == SLOT_24GHZ_DW && channel->center_freq == 2437)
12574f4b0f4eSBenjamin Berg 		return true;
12584f4b0f4eSBenjamin Berg 
12594f4b0f4eSBenjamin Berg 	if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ) &&
12604f4b0f4eSBenjamin Berg 	    channel->center_freq == 5745)
12614f4b0f4eSBenjamin Berg 		return true;
12624f4b0f4eSBenjamin Berg 
12632c8ec5a9SDaniel Gabay 	/* Accept frames during FAW slots if chandef is compatible */
12642c8ec5a9SDaniel Gabay 	return hwsim_nan_rx_chandef_compatible(data, slot, channel,
12652c8ec5a9SDaniel Gabay 					       rx_status->bw);
12664f4b0f4eSBenjamin Berg }
1267a6ea8948SDaniel Gabay 
1268a6ea8948SDaniel Gabay void mac80211_hwsim_nan_local_sched_changed(struct ieee80211_hw *hw,
1269a6ea8948SDaniel Gabay 					    struct ieee80211_vif *vif)
1270a6ea8948SDaniel Gabay {
1271a6ea8948SDaniel Gabay 	struct mac80211_hwsim_data *data = hw->priv;
1272a6ea8948SDaniel Gabay 	struct ieee80211_nan_channel **slots = vif->cfg.nan_sched.schedule;
1273a6ea8948SDaniel Gabay 
1274a6ea8948SDaniel Gabay 	if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
1275a6ea8948SDaniel Gabay 		return;
1276a6ea8948SDaniel Gabay 
1277a6ea8948SDaniel Gabay 	spin_lock_bh(&data->nan.state_lock);
1278a6ea8948SDaniel Gabay 
1279a6ea8948SDaniel Gabay 	for (int i = 0; i < ARRAY_SIZE(data->nan.local_sched); i++) {
1280a6ea8948SDaniel Gabay 		struct ieee80211_chanctx_conf *chanctx;
1281a6ea8948SDaniel Gabay 
1282a6ea8948SDaniel Gabay 		if (!slots[i] || IS_ERR(slots[i])) {
1283a6ea8948SDaniel Gabay 			memset(&data->nan.local_sched[i], 0,
1284a6ea8948SDaniel Gabay 			       sizeof(data->nan.local_sched[i]));
1285a6ea8948SDaniel Gabay 			continue;
1286a6ea8948SDaniel Gabay 		}
1287a6ea8948SDaniel Gabay 
1288a6ea8948SDaniel Gabay 		chanctx = slots[i]->chanctx_conf;
1289a6ea8948SDaniel Gabay 		if (!chanctx) {
1290a6ea8948SDaniel Gabay 			memset(&data->nan.local_sched[i], 0,
1291a6ea8948SDaniel Gabay 			       sizeof(data->nan.local_sched[i]));
1292a6ea8948SDaniel Gabay 			continue;
1293a6ea8948SDaniel Gabay 		}
1294a6ea8948SDaniel Gabay 
1295a6ea8948SDaniel Gabay 		data->nan.local_sched[i] = chanctx->def;
1296a6ea8948SDaniel Gabay 	}
1297a6ea8948SDaniel Gabay 
1298a6ea8948SDaniel Gabay 	spin_unlock_bh(&data->nan.state_lock);
1299a6ea8948SDaniel Gabay }
1300a6ea8948SDaniel Gabay 
1301a6ea8948SDaniel Gabay int mac80211_hwsim_nan_peer_sched_changed(struct ieee80211_hw *hw,
1302a6ea8948SDaniel Gabay 					  struct ieee80211_sta *sta)
1303a6ea8948SDaniel Gabay {
1304a6ea8948SDaniel Gabay 	struct hwsim_sta_priv *sp = (void *)sta->drv_priv;
1305a6ea8948SDaniel Gabay 	struct ieee80211_nan_peer_sched *sched = sta->nan_sched;
1306a6ea8948SDaniel Gabay 
1307a6ea8948SDaniel Gabay 	spin_lock_bh(&sp->nan_sched.lock);
1308a6ea8948SDaniel Gabay 
1309a6ea8948SDaniel Gabay 	/* Clear existing schedule */
1310a6ea8948SDaniel Gabay 	sp->nan_sched.committed_dw = 0;
1311a6ea8948SDaniel Gabay 	for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) {
1312a6ea8948SDaniel Gabay 		sp->nan_sched.maps[i].map_id = CFG80211_NAN_INVALID_MAP_ID;
1313a6ea8948SDaniel Gabay 		memset(sp->nan_sched.maps[i].chans, 0,
1314a6ea8948SDaniel Gabay 		       sizeof(sp->nan_sched.maps[i].chans));
1315a6ea8948SDaniel Gabay 	}
1316a6ea8948SDaniel Gabay 
1317a6ea8948SDaniel Gabay 	if (!sched)
1318a6ea8948SDaniel Gabay 		goto out;
1319a6ea8948SDaniel Gabay 
1320a6ea8948SDaniel Gabay 	sp->nan_sched.committed_dw = sched->committed_dw;
1321a6ea8948SDaniel Gabay 
1322a6ea8948SDaniel Gabay 	for (int i = 0; i < CFG80211_NAN_MAX_PEER_MAPS; i++) {
1323a6ea8948SDaniel Gabay 		struct ieee80211_nan_peer_map *map = &sched->maps[i];
1324a6ea8948SDaniel Gabay 
1325a6ea8948SDaniel Gabay 		if (map->map_id == CFG80211_NAN_INVALID_MAP_ID)
1326a6ea8948SDaniel Gabay 			continue;
1327a6ea8948SDaniel Gabay 
1328a6ea8948SDaniel Gabay 		sp->nan_sched.maps[i].map_id = map->map_id;
1329a6ea8948SDaniel Gabay 
1330a6ea8948SDaniel Gabay 		for (int j = 0; j < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; j++) {
1331a6ea8948SDaniel Gabay 			struct ieee80211_nan_channel *peer_chan =
1332a6ea8948SDaniel Gabay 				map->slots[j];
1333a6ea8948SDaniel Gabay 
1334a6ea8948SDaniel Gabay 			if (peer_chan && peer_chan->chanreq.oper.chan)
1335a6ea8948SDaniel Gabay 				sp->nan_sched.maps[i].chans[j] =
1336a6ea8948SDaniel Gabay 					peer_chan->chanreq.oper;
1337a6ea8948SDaniel Gabay 			else
1338a6ea8948SDaniel Gabay 				memset(&sp->nan_sched.maps[i].chans[j], 0,
1339a6ea8948SDaniel Gabay 				       sizeof(sp->nan_sched.maps[i].chans[j]));
1340a6ea8948SDaniel Gabay 		}
1341a6ea8948SDaniel Gabay 	}
1342a6ea8948SDaniel Gabay 
1343a6ea8948SDaniel Gabay out:
1344a6ea8948SDaniel Gabay 	spin_unlock_bh(&sp->nan_sched.lock);
1345a6ea8948SDaniel Gabay 	return 0;
1346a6ea8948SDaniel Gabay }
1347