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