1d1e879ecSMiri Korenblit // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2d1e879ecSMiri Korenblit /* 3d1e879ecSMiri Korenblit * Copyright (C) 2024-2025 Intel Corporation 4d1e879ecSMiri Korenblit */ 5d1e879ecSMiri Korenblit #include <linux/crc32.h> 6d1e879ecSMiri Korenblit 7d1e879ecSMiri Korenblit #include "mld.h" 8d1e879ecSMiri Korenblit #include "scan.h" 9d1e879ecSMiri Korenblit #include "hcmd.h" 10d1e879ecSMiri Korenblit #include "iface.h" 11d1e879ecSMiri Korenblit #include "phy.h" 12d1e879ecSMiri Korenblit #include "mlo.h" 13d1e879ecSMiri Korenblit 14d1e879ecSMiri Korenblit #include "fw/api/scan.h" 15d1e879ecSMiri Korenblit #include "fw/dbg.h" 16d1e879ecSMiri Korenblit 17d1e879ecSMiri Korenblit #define IWL_SCAN_DWELL_ACTIVE 10 18d1e879ecSMiri Korenblit #define IWL_SCAN_DWELL_PASSIVE 110 19d1e879ecSMiri Korenblit #define IWL_SCAN_NUM_OF_FRAGS 3 20d1e879ecSMiri Korenblit 21d1e879ecSMiri Korenblit /* adaptive dwell max budget time [TU] for full scan */ 22d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 23d1e879ecSMiri Korenblit 24d1e879ecSMiri Korenblit /* adaptive dwell max budget time [TU] for directed scan */ 25d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100 26d1e879ecSMiri Korenblit 27d1e879ecSMiri Korenblit /* adaptive dwell default high band APs number */ 28d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8 29d1e879ecSMiri Korenblit 30d1e879ecSMiri Korenblit /* adaptive dwell default low band APs number */ 31d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2 32d1e879ecSMiri Korenblit 33d1e879ecSMiri Korenblit /* adaptive dwell default APs number for P2P social channels (1, 6, 11) */ 34d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 35d1e879ecSMiri Korenblit 36d1e879ecSMiri Korenblit /* adaptive dwell number of APs override for P2P friendly GO channels */ 37d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10 38d1e879ecSMiri Korenblit 39d1e879ecSMiri Korenblit /* adaptive dwell number of APs override for P2P social channels */ 40d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2 41d1e879ecSMiri Korenblit 42d1e879ecSMiri Korenblit /* adaptive dwell number of APs override mask for p2p friendly GO */ 43d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20) 44d1e879ecSMiri Korenblit 45d1e879ecSMiri Korenblit /* adaptive dwell number of APs override mask for social channels */ 46d1e879ecSMiri Korenblit #define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21) 47d1e879ecSMiri Korenblit 48d1e879ecSMiri Korenblit #define SCAN_TIMEOUT_MSEC (30000 * HZ) 49d1e879ecSMiri Korenblit 50d1e879ecSMiri Korenblit /* minimal number of 2GHz and 5GHz channels in the regular scan request */ 51d1e879ecSMiri Korenblit #define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4 52d1e879ecSMiri Korenblit 53d1e879ecSMiri Korenblit enum iwl_mld_scan_type { 54d1e879ecSMiri Korenblit IWL_SCAN_TYPE_NOT_SET, 55d1e879ecSMiri Korenblit IWL_SCAN_TYPE_UNASSOC, 56d1e879ecSMiri Korenblit IWL_SCAN_TYPE_WILD, 57d1e879ecSMiri Korenblit IWL_SCAN_TYPE_MILD, 58d1e879ecSMiri Korenblit IWL_SCAN_TYPE_FRAGMENTED, 59d1e879ecSMiri Korenblit IWL_SCAN_TYPE_FAST_BALANCE, 60d1e879ecSMiri Korenblit }; 61d1e879ecSMiri Korenblit 62d1e879ecSMiri Korenblit struct iwl_mld_scan_timing_params { 63d1e879ecSMiri Korenblit u32 suspend_time; 64d1e879ecSMiri Korenblit u32 max_out_time; 65d1e879ecSMiri Korenblit }; 66d1e879ecSMiri Korenblit 67d1e879ecSMiri Korenblit static const struct iwl_mld_scan_timing_params scan_timing[] = { 68d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_UNASSOC] = { 69d1e879ecSMiri Korenblit .suspend_time = 0, 70d1e879ecSMiri Korenblit .max_out_time = 0, 71d1e879ecSMiri Korenblit }, 72d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_WILD] = { 73d1e879ecSMiri Korenblit .suspend_time = 30, 74d1e879ecSMiri Korenblit .max_out_time = 120, 75d1e879ecSMiri Korenblit }, 76d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_MILD] = { 77d1e879ecSMiri Korenblit .suspend_time = 120, 78d1e879ecSMiri Korenblit .max_out_time = 120, 79d1e879ecSMiri Korenblit }, 80d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_FRAGMENTED] = { 81d1e879ecSMiri Korenblit .suspend_time = 95, 82d1e879ecSMiri Korenblit .max_out_time = 44, 83d1e879ecSMiri Korenblit }, 84d1e879ecSMiri Korenblit [IWL_SCAN_TYPE_FAST_BALANCE] = { 85d1e879ecSMiri Korenblit .suspend_time = 30, 86d1e879ecSMiri Korenblit .max_out_time = 37, 87d1e879ecSMiri Korenblit }, 88d1e879ecSMiri Korenblit }; 89d1e879ecSMiri Korenblit 90d1e879ecSMiri Korenblit struct iwl_mld_scan_params { 91d1e879ecSMiri Korenblit enum iwl_mld_scan_type type; 92d1e879ecSMiri Korenblit u32 n_channels; 93d1e879ecSMiri Korenblit u16 delay; 94d1e879ecSMiri Korenblit int n_ssids; 95d1e879ecSMiri Korenblit struct cfg80211_ssid *ssids; 96d1e879ecSMiri Korenblit struct ieee80211_channel **channels; 97d1e879ecSMiri Korenblit u32 flags; 98d1e879ecSMiri Korenblit u8 *mac_addr; 99d1e879ecSMiri Korenblit u8 *mac_addr_mask; 100d1e879ecSMiri Korenblit bool no_cck; 101d1e879ecSMiri Korenblit bool pass_all; 102d1e879ecSMiri Korenblit int n_match_sets; 103d1e879ecSMiri Korenblit struct iwl_scan_probe_req preq; 104d1e879ecSMiri Korenblit struct cfg80211_match_set *match_sets; 105d1e879ecSMiri Korenblit int n_scan_plans; 106d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan *scan_plans; 107d1e879ecSMiri Korenblit bool iter_notif; 108d1e879ecSMiri Korenblit bool respect_p2p_go; 109d1e879ecSMiri Korenblit u8 fw_link_id; 110d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params; 111d1e879ecSMiri Korenblit u32 n_6ghz_params; 112d1e879ecSMiri Korenblit bool scan_6ghz; 113d1e879ecSMiri Korenblit bool enable_6ghz_passive; 114d1e879ecSMiri Korenblit u8 bssid[ETH_ALEN] __aligned(2); 115d1e879ecSMiri Korenblit }; 116d1e879ecSMiri Korenblit 117d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data { 118d1e879ecSMiri Korenblit struct ieee80211_vif *current_vif; 119d1e879ecSMiri Korenblit bool p2p_go; 120d1e879ecSMiri Korenblit }; 121d1e879ecSMiri Korenblit 122d1e879ecSMiri Korenblit static void iwl_mld_scan_respect_p2p_go_iter(void *_data, u8 *mac, 123d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 124d1e879ecSMiri Korenblit { 125d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data *data = _data; 126d1e879ecSMiri Korenblit 127d1e879ecSMiri Korenblit /* exclude the given vif */ 128d1e879ecSMiri Korenblit if (vif == data->current_vif) 129d1e879ecSMiri Korenblit return; 130d1e879ecSMiri Korenblit 131d1e879ecSMiri Korenblit /* TODO: CDB check the band of the GO */ 132d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && 133d1e879ecSMiri Korenblit iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) 134d1e879ecSMiri Korenblit data->p2p_go = true; 135d1e879ecSMiri Korenblit } 136d1e879ecSMiri Korenblit 137d1e879ecSMiri Korenblit static bool iwl_mld_get_respect_p2p_go(struct iwl_mld *mld, 138d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 139d1e879ecSMiri Korenblit bool low_latency) 140d1e879ecSMiri Korenblit { 141d1e879ecSMiri Korenblit struct iwl_mld_scan_respect_p2p_go_iter_data data = { 142d1e879ecSMiri Korenblit .current_vif = vif, 143d1e879ecSMiri Korenblit .p2p_go = false, 144d1e879ecSMiri Korenblit }; 145d1e879ecSMiri Korenblit 146d1e879ecSMiri Korenblit if (!low_latency) 147d1e879ecSMiri Korenblit return false; 148d1e879ecSMiri Korenblit 149d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw, 150d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL, 151d1e879ecSMiri Korenblit iwl_mld_scan_respect_p2p_go_iter, 152d1e879ecSMiri Korenblit &data); 153d1e879ecSMiri Korenblit 154d1e879ecSMiri Korenblit return data.p2p_go; 155d1e879ecSMiri Korenblit } 156d1e879ecSMiri Korenblit 157d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data { 158d1e879ecSMiri Korenblit struct ieee80211_vif *current_vif; 159d1e879ecSMiri Korenblit bool active_vif; 160d1e879ecSMiri Korenblit bool is_dcm_with_p2p_go; 161d1e879ecSMiri Korenblit bool global_low_latency; 162d1e879ecSMiri Korenblit }; 163d1e879ecSMiri Korenblit 164d1e879ecSMiri Korenblit static void iwl_mld_scan_iterator(void *_data, u8 *mac, 165d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 166d1e879ecSMiri Korenblit { 167d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data *data = _data; 168d1e879ecSMiri Korenblit struct ieee80211_vif *curr_vif = data->current_vif; 169d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 170d1e879ecSMiri Korenblit struct iwl_mld_vif *curr_mld_vif; 171d1e879ecSMiri Korenblit unsigned long curr_vif_active_links; 172d1e879ecSMiri Korenblit u16 link_id; 173d1e879ecSMiri Korenblit 174d1e879ecSMiri Korenblit data->global_low_latency |= iwl_mld_vif_low_latency(mld_vif); 175d1e879ecSMiri Korenblit 176d1e879ecSMiri Korenblit if ((ieee80211_vif_is_mld(vif) && vif->active_links) || 177d1e879ecSMiri Korenblit (vif->type != NL80211_IFTYPE_P2P_DEVICE && 178d1e879ecSMiri Korenblit mld_vif->deflink.active)) 179d1e879ecSMiri Korenblit data->active_vif = true; 180d1e879ecSMiri Korenblit 181d1e879ecSMiri Korenblit if (vif == curr_vif) 182d1e879ecSMiri Korenblit return; 183d1e879ecSMiri Korenblit 184d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_GO) 185d1e879ecSMiri Korenblit return; 186d1e879ecSMiri Korenblit 187d1e879ecSMiri Korenblit /* Currently P2P GO can't be AP MLD so the logic below assumes that */ 188d1e879ecSMiri Korenblit WARN_ON_ONCE(ieee80211_vif_is_mld(vif)); 189d1e879ecSMiri Korenblit 190d1e879ecSMiri Korenblit curr_vif_active_links = 191d1e879ecSMiri Korenblit ieee80211_vif_is_mld(curr_vif) ? curr_vif->active_links : 1; 192d1e879ecSMiri Korenblit 193d1e879ecSMiri Korenblit curr_mld_vif = iwl_mld_vif_from_mac80211(curr_vif); 194d1e879ecSMiri Korenblit 195d1e879ecSMiri Korenblit for_each_set_bit(link_id, &curr_vif_active_links, 196d1e879ecSMiri Korenblit IEEE80211_MLD_MAX_NUM_LINKS) { 197d1e879ecSMiri Korenblit struct iwl_mld_link *curr_mld_link = 198d1e879ecSMiri Korenblit iwl_mld_link_dereference_check(curr_mld_vif, link_id); 199d1e879ecSMiri Korenblit 200d1e879ecSMiri Korenblit if (WARN_ON(!curr_mld_link)) 201d1e879ecSMiri Korenblit return; 202d1e879ecSMiri Korenblit 203d1e879ecSMiri Korenblit if (rcu_access_pointer(curr_mld_link->chan_ctx) && 204d1e879ecSMiri Korenblit rcu_access_pointer(mld_vif->deflink.chan_ctx) != 205d1e879ecSMiri Korenblit rcu_access_pointer(curr_mld_link->chan_ctx)) { 206d1e879ecSMiri Korenblit data->is_dcm_with_p2p_go = true; 207d1e879ecSMiri Korenblit return; 208d1e879ecSMiri Korenblit } 209d1e879ecSMiri Korenblit } 210d1e879ecSMiri Korenblit } 211d1e879ecSMiri Korenblit 212d1e879ecSMiri Korenblit static enum 213d1e879ecSMiri Korenblit iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld, 214d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 215d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data *data) 216d1e879ecSMiri Korenblit { 217d1e879ecSMiri Korenblit enum iwl_mld_traffic_load load = mld->scan.traffic_load.status; 218d1e879ecSMiri Korenblit 219d1e879ecSMiri Korenblit /* A scanning AP interface probably wants to generate a survey to do 220d1e879ecSMiri Korenblit * ACS (automatic channel selection). 221d1e879ecSMiri Korenblit * Force a non-fragmented scan in that case. 222d1e879ecSMiri Korenblit */ 223d1e879ecSMiri Korenblit if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) 224d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_WILD; 225d1e879ecSMiri Korenblit 226d1e879ecSMiri Korenblit if (!data->active_vif) 227d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_UNASSOC; 228d1e879ecSMiri Korenblit 229d1e879ecSMiri Korenblit if ((load == IWL_MLD_TRAFFIC_HIGH || data->global_low_latency) && 230d1e879ecSMiri Korenblit vif->type != NL80211_IFTYPE_P2P_DEVICE) 231d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_FRAGMENTED; 232d1e879ecSMiri Korenblit 233d1e879ecSMiri Korenblit /* In case of DCM with P2P GO set all scan requests as 234d1e879ecSMiri Korenblit * fast-balance scan 235d1e879ecSMiri Korenblit */ 236d1e879ecSMiri Korenblit if (vif->type == NL80211_IFTYPE_STATION && 237d1e879ecSMiri Korenblit data->is_dcm_with_p2p_go) 238d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_FAST_BALANCE; 239d1e879ecSMiri Korenblit 240d1e879ecSMiri Korenblit if (load >= IWL_MLD_TRAFFIC_MEDIUM || data->global_low_latency) 241d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_MILD; 242d1e879ecSMiri Korenblit 243d1e879ecSMiri Korenblit return IWL_SCAN_TYPE_WILD; 244d1e879ecSMiri Korenblit } 245d1e879ecSMiri Korenblit 246d1e879ecSMiri Korenblit static u8 * 247d1e879ecSMiri Korenblit iwl_mld_scan_add_2ghz_elems(struct iwl_mld *mld, const u8 *ies, 248d1e879ecSMiri Korenblit size_t len, u8 *const pos) 249d1e879ecSMiri Korenblit { 250d1e879ecSMiri Korenblit static const u8 before_ds_params[] = { 251d1e879ecSMiri Korenblit WLAN_EID_SSID, 252d1e879ecSMiri Korenblit WLAN_EID_SUPP_RATES, 253d1e879ecSMiri Korenblit WLAN_EID_REQUEST, 254d1e879ecSMiri Korenblit WLAN_EID_EXT_SUPP_RATES, 255d1e879ecSMiri Korenblit }; 256d1e879ecSMiri Korenblit size_t offs; 257d1e879ecSMiri Korenblit u8 *newpos = pos; 258d1e879ecSMiri Korenblit 259d1e879ecSMiri Korenblit offs = ieee80211_ie_split(ies, len, 260d1e879ecSMiri Korenblit before_ds_params, 261d1e879ecSMiri Korenblit ARRAY_SIZE(before_ds_params), 262d1e879ecSMiri Korenblit 0); 263d1e879ecSMiri Korenblit 264d1e879ecSMiri Korenblit memcpy(newpos, ies, offs); 265d1e879ecSMiri Korenblit newpos += offs; 266d1e879ecSMiri Korenblit 267d1e879ecSMiri Korenblit /* Add a placeholder for DS Parameter Set element */ 268d1e879ecSMiri Korenblit *newpos++ = WLAN_EID_DS_PARAMS; 269d1e879ecSMiri Korenblit *newpos++ = 1; 270d1e879ecSMiri Korenblit *newpos++ = 0; 271d1e879ecSMiri Korenblit 272d1e879ecSMiri Korenblit memcpy(newpos, ies + offs, len - offs); 273d1e879ecSMiri Korenblit newpos += len - offs; 274d1e879ecSMiri Korenblit 275d1e879ecSMiri Korenblit return newpos; 276d1e879ecSMiri Korenblit } 277d1e879ecSMiri Korenblit 278d1e879ecSMiri Korenblit static void 279d1e879ecSMiri Korenblit iwl_mld_scan_add_tpc_report_elem(u8 *pos) 280d1e879ecSMiri Korenblit { 281d1e879ecSMiri Korenblit pos[0] = WLAN_EID_VENDOR_SPECIFIC; 282d1e879ecSMiri Korenblit pos[1] = WFA_TPC_IE_LEN - 2; 283d1e879ecSMiri Korenblit pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff; 284d1e879ecSMiri Korenblit pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff; 285d1e879ecSMiri Korenblit pos[4] = WLAN_OUI_MICROSOFT & 0xff; 286d1e879ecSMiri Korenblit pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC; 287d1e879ecSMiri Korenblit pos[6] = 0; 288d1e879ecSMiri Korenblit /* pos[7] - tx power will be inserted by the FW */ 289d1e879ecSMiri Korenblit pos[7] = 0; 290d1e879ecSMiri Korenblit pos[8] = 0; 291d1e879ecSMiri Korenblit } 292d1e879ecSMiri Korenblit 293d1e879ecSMiri Korenblit static u32 294d1e879ecSMiri Korenblit iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status) 295d1e879ecSMiri Korenblit { 296d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_REGULAR) 297d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_6; 298d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_INT_MLO) 299d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_4; 300d1e879ecSMiri Korenblit 301d1e879ecSMiri Korenblit return IWL_SCAN_PRIORITY_EXT_2; 302d1e879ecSMiri Korenblit } 303d1e879ecSMiri Korenblit 304d1e879ecSMiri Korenblit static bool 305d1e879ecSMiri Korenblit iwl_mld_scan_is_regular(struct iwl_mld_scan_params *params) 306d1e879ecSMiri Korenblit { 307d1e879ecSMiri Korenblit return params->n_scan_plans == 1 && 308d1e879ecSMiri Korenblit params->scan_plans[0].iterations == 1; 309d1e879ecSMiri Korenblit } 310d1e879ecSMiri Korenblit 311d1e879ecSMiri Korenblit static bool 312d1e879ecSMiri Korenblit iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type) 313d1e879ecSMiri Korenblit { 314d1e879ecSMiri Korenblit return (type == IWL_SCAN_TYPE_FRAGMENTED || 315d1e879ecSMiri Korenblit type == IWL_SCAN_TYPE_FAST_BALANCE); 316d1e879ecSMiri Korenblit } 317d1e879ecSMiri Korenblit 318d1e879ecSMiri Korenblit static int 319d1e879ecSMiri Korenblit iwl_mld_scan_uid_by_status(struct iwl_mld *mld, int status) 320d1e879ecSMiri Korenblit { 321d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++) 322d1e879ecSMiri Korenblit if (mld->scan.uid_status[i] == status) 323d1e879ecSMiri Korenblit return i; 324d1e879ecSMiri Korenblit 325d1e879ecSMiri Korenblit return -ENOENT; 326d1e879ecSMiri Korenblit } 327d1e879ecSMiri Korenblit 328d1e879ecSMiri Korenblit static const char * 329d1e879ecSMiri Korenblit iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status) 330d1e879ecSMiri Korenblit { 331d1e879ecSMiri Korenblit switch (status) { 332d1e879ecSMiri Korenblit case IWL_SCAN_EBS_SUCCESS: 333d1e879ecSMiri Korenblit return "successful"; 334d1e879ecSMiri Korenblit case IWL_SCAN_EBS_INACTIVE: 335d1e879ecSMiri Korenblit return "inactive"; 336d1e879ecSMiri Korenblit case IWL_SCAN_EBS_FAILED: 337d1e879ecSMiri Korenblit case IWL_SCAN_EBS_CHAN_NOT_FOUND: 338d1e879ecSMiri Korenblit default: 339d1e879ecSMiri Korenblit return "failed"; 340d1e879ecSMiri Korenblit } 341d1e879ecSMiri Korenblit } 342d1e879ecSMiri Korenblit 343d1e879ecSMiri Korenblit static int 344d1e879ecSMiri Korenblit iwl_mld_scan_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) 345d1e879ecSMiri Korenblit { 346d1e879ecSMiri Korenblit for (int i = 0; i < PROBE_OPTION_MAX; i++) { 347d1e879ecSMiri Korenblit if (!ssid_list[i].len) 348d1e879ecSMiri Korenblit return -1; 349d1e879ecSMiri Korenblit if (ssid_list[i].len == ssid_len && 350d1e879ecSMiri Korenblit !memcmp(ssid_list[i].ssid, ssid, ssid_len)) 351d1e879ecSMiri Korenblit return i; 352d1e879ecSMiri Korenblit } 353d1e879ecSMiri Korenblit 354d1e879ecSMiri Korenblit return -1; 355d1e879ecSMiri Korenblit } 356d1e879ecSMiri Korenblit 357d1e879ecSMiri Korenblit static bool 358d1e879ecSMiri Korenblit iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids, 359d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies, int n_channels) 360d1e879ecSMiri Korenblit { 361d1e879ecSMiri Korenblit return ((n_ssids <= PROBE_OPTION_MAX) && 362d1e879ecSMiri Korenblit (n_channels <= mld->fw->ucode_capa.n_scan_channels) & 363d1e879ecSMiri Korenblit (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] + 364d1e879ecSMiri Korenblit ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <= 365d1e879ecSMiri Korenblit iwl_mld_scan_max_template_size())); 366d1e879ecSMiri Korenblit } 367d1e879ecSMiri Korenblit 368d1e879ecSMiri Korenblit static void 369d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(struct iwl_mld *mld, struct ieee80211_vif *vif, 370d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies, 371d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params) 372d1e879ecSMiri Korenblit { 373d1e879ecSMiri Korenblit struct ieee80211_mgmt *frame = (void *)params->preq.buf; 374d1e879ecSMiri Korenblit u8 *pos, *newpos; 375d1e879ecSMiri Korenblit const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? 376d1e879ecSMiri Korenblit params->mac_addr : NULL; 377d1e879ecSMiri Korenblit 378d1e879ecSMiri Korenblit if (mac_addr) 379d1e879ecSMiri Korenblit get_random_mask_addr(frame->sa, mac_addr, 380d1e879ecSMiri Korenblit params->mac_addr_mask); 381d1e879ecSMiri Korenblit else 382d1e879ecSMiri Korenblit memcpy(frame->sa, vif->addr, ETH_ALEN); 383d1e879ecSMiri Korenblit 384d1e879ecSMiri Korenblit frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); 385d1e879ecSMiri Korenblit eth_broadcast_addr(frame->da); 386d1e879ecSMiri Korenblit ether_addr_copy(frame->bssid, params->bssid); 387d1e879ecSMiri Korenblit frame->seq_ctrl = 0; 388d1e879ecSMiri Korenblit 389d1e879ecSMiri Korenblit pos = frame->u.probe_req.variable; 390d1e879ecSMiri Korenblit *pos++ = WLAN_EID_SSID; 391d1e879ecSMiri Korenblit *pos++ = 0; 392d1e879ecSMiri Korenblit 393d1e879ecSMiri Korenblit params->preq.mac_header.offset = 0; 394d1e879ecSMiri Korenblit params->preq.mac_header.len = cpu_to_le16(24 + 2); 395d1e879ecSMiri Korenblit 396d1e879ecSMiri Korenblit /* Insert DS parameter set element on 2.4 GHz band */ 397d1e879ecSMiri Korenblit newpos = iwl_mld_scan_add_2ghz_elems(mld, 398d1e879ecSMiri Korenblit ies->ies[NL80211_BAND_2GHZ], 399d1e879ecSMiri Korenblit ies->len[NL80211_BAND_2GHZ], 400d1e879ecSMiri Korenblit pos); 401d1e879ecSMiri Korenblit params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); 402d1e879ecSMiri Korenblit params->preq.band_data[0].len = cpu_to_le16(newpos - pos); 403d1e879ecSMiri Korenblit pos = newpos; 404d1e879ecSMiri Korenblit 405d1e879ecSMiri Korenblit memcpy(pos, ies->ies[NL80211_BAND_5GHZ], 406d1e879ecSMiri Korenblit ies->len[NL80211_BAND_5GHZ]); 407d1e879ecSMiri Korenblit params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); 408d1e879ecSMiri Korenblit params->preq.band_data[1].len = 409d1e879ecSMiri Korenblit cpu_to_le16(ies->len[NL80211_BAND_5GHZ]); 410d1e879ecSMiri Korenblit pos += ies->len[NL80211_BAND_5GHZ]; 411d1e879ecSMiri Korenblit 412d1e879ecSMiri Korenblit memcpy(pos, ies->ies[NL80211_BAND_6GHZ], 413d1e879ecSMiri Korenblit ies->len[NL80211_BAND_6GHZ]); 414d1e879ecSMiri Korenblit params->preq.band_data[2].offset = cpu_to_le16(pos - params->preq.buf); 415d1e879ecSMiri Korenblit params->preq.band_data[2].len = 416d1e879ecSMiri Korenblit cpu_to_le16(ies->len[NL80211_BAND_6GHZ]); 417d1e879ecSMiri Korenblit pos += ies->len[NL80211_BAND_6GHZ]; 418d1e879ecSMiri Korenblit 419d1e879ecSMiri Korenblit memcpy(pos, ies->common_ies, ies->common_ie_len); 420d1e879ecSMiri Korenblit params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); 421d1e879ecSMiri Korenblit 422d1e879ecSMiri Korenblit iwl_mld_scan_add_tpc_report_elem(pos + ies->common_ie_len); 423d1e879ecSMiri Korenblit params->preq.common_data.len = cpu_to_le16(ies->common_ie_len + 424d1e879ecSMiri Korenblit WFA_TPC_IE_LEN); 425d1e879ecSMiri Korenblit } 426d1e879ecSMiri Korenblit 427d1e879ecSMiri Korenblit static u16 428d1e879ecSMiri Korenblit iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, 429d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 430d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 431d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status) 432d1e879ecSMiri Korenblit { 433d1e879ecSMiri Korenblit u16 flags = 0; 434d1e879ecSMiri Korenblit 435d1e879ecSMiri Korenblit /* If no direct SSIDs are provided perform a passive scan. Otherwise, 436d1e879ecSMiri Korenblit * if there is a single SSID which is not the broadcast SSID, assume 437d1e879ecSMiri Korenblit * that the scan is intended for roaming purposes and thus enable Rx on 438d1e879ecSMiri Korenblit * all chains to improve chances of hearing the beacons/probe responses. 439d1e879ecSMiri Korenblit */ 440d1e879ecSMiri Korenblit if (params->n_ssids == 0) 441d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE; 442d1e879ecSMiri Korenblit else if (params->n_ssids == 1 && params->ssids[0].ssid_len) 443d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_USE_ALL_RX_CHAINS; 444d1e879ecSMiri Korenblit 445d1e879ecSMiri Korenblit if (params->pass_all) 446d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL; 447d1e879ecSMiri Korenblit else 448d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH; 449d1e879ecSMiri Korenblit 450d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type)) 451d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1; 452d1e879ecSMiri Korenblit 453d1e879ecSMiri Korenblit if (!iwl_mld_scan_is_regular(params)) 454d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC; 455d1e879ecSMiri Korenblit 456d1e879ecSMiri Korenblit if (params->iter_notif || 457d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) 458d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE; 459d1e879ecSMiri Korenblit 460d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_SCHED || 461d1e879ecSMiri Korenblit scan_status == IWL_MLD_SCAN_NETDETECT) 462d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE; 463d1e879ecSMiri Korenblit 464d1e879ecSMiri Korenblit if (params->flags & (NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP | 465d1e879ecSMiri Korenblit NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE | 466d1e879ecSMiri Korenblit NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME)) 467d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_OCE; 468d1e879ecSMiri Korenblit 469d1e879ecSMiri Korenblit if ((scan_status == IWL_MLD_SCAN_SCHED || 470d1e879ecSMiri Korenblit scan_status == IWL_MLD_SCAN_NETDETECT) && 471d1e879ecSMiri Korenblit params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) 472d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN; 473d1e879ecSMiri Korenblit 474d1e879ecSMiri Korenblit if (params->enable_6ghz_passive) 475d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN; 476d1e879ecSMiri Korenblit 477d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL; 478d1e879ecSMiri Korenblit 479d1e879ecSMiri Korenblit return flags; 480d1e879ecSMiri Korenblit } 481d1e879ecSMiri Korenblit 482d1e879ecSMiri Korenblit static u8 483d1e879ecSMiri Korenblit iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, 484d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 485d1e879ecSMiri Korenblit struct ieee80211_vif *vif, u16 gen_flags) 486d1e879ecSMiri Korenblit { 487d1e879ecSMiri Korenblit u8 flags = 0; 488d1e879ecSMiri Korenblit 489d1e879ecSMiri Korenblit /* TODO: CDB */ 490d1e879ecSMiri Korenblit if (params->respect_p2p_go) 491d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB | 492d1e879ecSMiri Korenblit IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB; 493d1e879ecSMiri Korenblit 494d1e879ecSMiri Korenblit if (params->scan_6ghz) 495d1e879ecSMiri Korenblit flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT; 496d1e879ecSMiri Korenblit 497d1e879ecSMiri Korenblit return flags; 498d1e879ecSMiri Korenblit } 499d1e879ecSMiri Korenblit 500d1e879ecSMiri Korenblit static void 501d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld, 502d1e879ecSMiri Korenblit struct iwl_scan_general_params_v11 *gp, 503d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params) 504d1e879ecSMiri Korenblit { 505d1e879ecSMiri Korenblit const struct iwl_mld_scan_timing_params *timing = 506d1e879ecSMiri Korenblit &scan_timing[params->type]; 507d1e879ecSMiri Korenblit 508d1e879ecSMiri Korenblit gp->adwell_default_social_chn = 509d1e879ecSMiri Korenblit IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; 510d1e879ecSMiri Korenblit gp->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; 511d1e879ecSMiri Korenblit gp->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; 512d1e879ecSMiri Korenblit 513d1e879ecSMiri Korenblit if (params->n_ssids && params->ssids[0].ssid_len) 514d1e879ecSMiri Korenblit gp->adwell_max_budget = 515d1e879ecSMiri Korenblit cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); 516d1e879ecSMiri Korenblit else 517d1e879ecSMiri Korenblit gp->adwell_max_budget = 518d1e879ecSMiri Korenblit cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); 519d1e879ecSMiri Korenblit 520d1e879ecSMiri Korenblit gp->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); 521d1e879ecSMiri Korenblit 522d1e879ecSMiri Korenblit gp->max_out_of_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->max_out_time); 523d1e879ecSMiri Korenblit gp->suspend_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->suspend_time); 524d1e879ecSMiri Korenblit 525d1e879ecSMiri Korenblit gp->active_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; 526d1e879ecSMiri Korenblit gp->passive_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; 527d1e879ecSMiri Korenblit gp->active_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; 528d1e879ecSMiri Korenblit gp->passive_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; 529d1e879ecSMiri Korenblit 530d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 531d1e879ecSMiri Korenblit "Scan: adwell_max_budget=%d max_out_of_time=%d suspend_time=%d\n", 532d1e879ecSMiri Korenblit gp->adwell_max_budget, 533d1e879ecSMiri Korenblit gp->max_out_of_time[SCAN_LB_LMAC_IDX], 534d1e879ecSMiri Korenblit gp->suspend_time[SCAN_LB_LMAC_IDX]); 535d1e879ecSMiri Korenblit } 536d1e879ecSMiri Korenblit 537d1e879ecSMiri Korenblit static void 538d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, 539d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 540d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 541d1e879ecSMiri Korenblit struct iwl_scan_general_params_v11 *gp, 542d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status) 543d1e879ecSMiri Korenblit { 544d1e879ecSMiri Korenblit u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, 545d1e879ecSMiri Korenblit scan_status); 546d1e879ecSMiri Korenblit u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif, 547d1e879ecSMiri Korenblit gen_flags); 548d1e879ecSMiri Korenblit 549d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n", 550d1e879ecSMiri Korenblit gen_flags, gen_flags2); 551d1e879ecSMiri Korenblit 552d1e879ecSMiri Korenblit gp->flags = cpu_to_le16(gen_flags); 553d1e879ecSMiri Korenblit gp->flags2 = gen_flags2; 554d1e879ecSMiri Korenblit 555d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_dwell(mld, gp, params); 556d1e879ecSMiri Korenblit 557d1e879ecSMiri Korenblit if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) 558d1e879ecSMiri Korenblit gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; 559d1e879ecSMiri Korenblit 560d1e879ecSMiri Korenblit if (params->fw_link_id != IWL_MLD_INVALID_FW_ID) 561d1e879ecSMiri Korenblit gp->scan_start_mac_or_link_id = params->fw_link_id; 562d1e879ecSMiri Korenblit } 563d1e879ecSMiri Korenblit 564d1e879ecSMiri Korenblit static int 565d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params, 566d1e879ecSMiri Korenblit struct iwl_scan_umac_schedule *schedule, 567d1e879ecSMiri Korenblit __le16 *delay) 568d1e879ecSMiri Korenblit { 569d1e879ecSMiri Korenblit if (WARN_ON(!params->n_scan_plans || 570d1e879ecSMiri Korenblit params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) 571d1e879ecSMiri Korenblit return -EINVAL; 572d1e879ecSMiri Korenblit 573d1e879ecSMiri Korenblit for (int i = 0; i < params->n_scan_plans; i++) { 574d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan *scan_plan = 575d1e879ecSMiri Korenblit ¶ms->scan_plans[i]; 576d1e879ecSMiri Korenblit 577d1e879ecSMiri Korenblit schedule[i].iter_count = scan_plan->iterations; 578d1e879ecSMiri Korenblit schedule[i].interval = 579d1e879ecSMiri Korenblit cpu_to_le16(scan_plan->interval); 580d1e879ecSMiri Korenblit } 581d1e879ecSMiri Korenblit 582d1e879ecSMiri Korenblit /* If the number of iterations of the last scan plan is set to zero, 583d1e879ecSMiri Korenblit * it should run infinitely. However, this is not always the case. 584d1e879ecSMiri Korenblit * For example, when regular scan is requested the driver sets one scan 585d1e879ecSMiri Korenblit * plan with one iteration. 586d1e879ecSMiri Korenblit */ 587d1e879ecSMiri Korenblit if (!schedule[params->n_scan_plans - 1].iter_count) 588d1e879ecSMiri Korenblit schedule[params->n_scan_plans - 1].iter_count = 0xff; 589d1e879ecSMiri Korenblit 590d1e879ecSMiri Korenblit *delay = cpu_to_le16(params->delay); 591d1e879ecSMiri Korenblit 592d1e879ecSMiri Korenblit return 0; 593d1e879ecSMiri Korenblit } 594d1e879ecSMiri Korenblit 595d1e879ecSMiri Korenblit /* We insert the SSIDs in an inverted order, because the FW will 596d1e879ecSMiri Korenblit * invert it back. 597d1e879ecSMiri Korenblit */ 598d1e879ecSMiri Korenblit static void 599d1e879ecSMiri Korenblit iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params, 600d1e879ecSMiri Korenblit struct iwl_ssid_ie *ssids, u32 *ssid_bitmap) 601d1e879ecSMiri Korenblit { 602d1e879ecSMiri Korenblit int i, j; 603d1e879ecSMiri Korenblit int index; 604d1e879ecSMiri Korenblit u32 tmp_bitmap = 0; 605d1e879ecSMiri Korenblit 606d1e879ecSMiri Korenblit /* copy SSIDs from match list. iwl_config_sched_scan_profiles() 607d1e879ecSMiri Korenblit * uses the order of these ssids to config match list. 608d1e879ecSMiri Korenblit */ 609d1e879ecSMiri Korenblit for (i = 0, j = params->n_match_sets - 1; 610d1e879ecSMiri Korenblit j >= 0 && i < PROBE_OPTION_MAX; 611d1e879ecSMiri Korenblit i++, j--) { 612d1e879ecSMiri Korenblit /* skip empty SSID match_sets */ 613d1e879ecSMiri Korenblit if (!params->match_sets[j].ssid.ssid_len) 614d1e879ecSMiri Korenblit continue; 615d1e879ecSMiri Korenblit 616d1e879ecSMiri Korenblit ssids[i].id = WLAN_EID_SSID; 617d1e879ecSMiri Korenblit ssids[i].len = params->match_sets[j].ssid.ssid_len; 618d1e879ecSMiri Korenblit memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, 619d1e879ecSMiri Korenblit ssids[i].len); 620d1e879ecSMiri Korenblit } 621d1e879ecSMiri Korenblit 622d1e879ecSMiri Korenblit /* add SSIDs from scan SSID list */ 623d1e879ecSMiri Korenblit for (j = params->n_ssids - 1; 624d1e879ecSMiri Korenblit j >= 0 && i < PROBE_OPTION_MAX; 625d1e879ecSMiri Korenblit i++, j--) { 626d1e879ecSMiri Korenblit index = iwl_mld_scan_ssid_exist(params->ssids[j].ssid, 627d1e879ecSMiri Korenblit params->ssids[j].ssid_len, 628d1e879ecSMiri Korenblit ssids); 629d1e879ecSMiri Korenblit if (index < 0) { 630d1e879ecSMiri Korenblit ssids[i].id = WLAN_EID_SSID; 631d1e879ecSMiri Korenblit ssids[i].len = params->ssids[j].ssid_len; 632d1e879ecSMiri Korenblit memcpy(ssids[i].ssid, params->ssids[j].ssid, 633d1e879ecSMiri Korenblit ssids[i].len); 634d1e879ecSMiri Korenblit tmp_bitmap |= BIT(i); 635d1e879ecSMiri Korenblit } else { 636d1e879ecSMiri Korenblit tmp_bitmap |= BIT(index); 637d1e879ecSMiri Korenblit } 638d1e879ecSMiri Korenblit } 639d1e879ecSMiri Korenblit 640d1e879ecSMiri Korenblit if (ssid_bitmap) 641d1e879ecSMiri Korenblit *ssid_bitmap = tmp_bitmap; 642d1e879ecSMiri Korenblit } 643d1e879ecSMiri Korenblit 644d1e879ecSMiri Korenblit static void 645d1e879ecSMiri Korenblit iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params, 646d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp) 647d1e879ecSMiri Korenblit { 648d1e879ecSMiri Korenblit int j, idex_s = 0, idex_b = 0; 649d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params = 650d1e879ecSMiri Korenblit params->scan_6ghz_params; 651d1e879ecSMiri Korenblit 652d1e879ecSMiri Korenblit for (j = 0; 653d1e879ecSMiri Korenblit j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE; 654d1e879ecSMiri Korenblit j++) { 655d1e879ecSMiri Korenblit if (!params->ssids[j].ssid_len) 656d1e879ecSMiri Korenblit continue; 657d1e879ecSMiri Korenblit 658d1e879ecSMiri Korenblit pp->short_ssid[idex_s] = 659d1e879ecSMiri Korenblit cpu_to_le32(~crc32_le(~0, params->ssids[j].ssid, 660d1e879ecSMiri Korenblit params->ssids[j].ssid_len)); 661d1e879ecSMiri Korenblit 662d1e879ecSMiri Korenblit /* hidden 6ghz scan */ 663d1e879ecSMiri Korenblit pp->direct_scan[idex_s].id = WLAN_EID_SSID; 664d1e879ecSMiri Korenblit pp->direct_scan[idex_s].len = params->ssids[j].ssid_len; 665d1e879ecSMiri Korenblit memcpy(pp->direct_scan[idex_s].ssid, params->ssids[j].ssid, 666d1e879ecSMiri Korenblit params->ssids[j].ssid_len); 667d1e879ecSMiri Korenblit idex_s++; 668d1e879ecSMiri Korenblit } 669d1e879ecSMiri Korenblit 670d1e879ecSMiri Korenblit /* Populate the arrays of the short SSIDs and the BSSIDs using the 6GHz 671d1e879ecSMiri Korenblit * collocated parameters. This might not be optimal, as this processing 672d1e879ecSMiri Korenblit * does not (yet) correspond to the actual channels, so it is possible 673d1e879ecSMiri Korenblit * that some entries would be left out. 674d1e879ecSMiri Korenblit */ 675d1e879ecSMiri Korenblit for (j = 0; j < params->n_6ghz_params; j++) { 676d1e879ecSMiri Korenblit int k; 677d1e879ecSMiri Korenblit 678d1e879ecSMiri Korenblit /* First, try to place the short SSID */ 679d1e879ecSMiri Korenblit if (scan_6ghz_params[j].short_ssid_valid) { 680d1e879ecSMiri Korenblit for (k = 0; k < idex_s; k++) { 681d1e879ecSMiri Korenblit if (pp->short_ssid[k] == 682d1e879ecSMiri Korenblit cpu_to_le32(scan_6ghz_params[j].short_ssid)) 683d1e879ecSMiri Korenblit break; 684d1e879ecSMiri Korenblit } 685d1e879ecSMiri Korenblit 686d1e879ecSMiri Korenblit if (k == idex_s && idex_s < SCAN_SHORT_SSID_MAX_SIZE) { 687d1e879ecSMiri Korenblit pp->short_ssid[idex_s++] = 688d1e879ecSMiri Korenblit cpu_to_le32(scan_6ghz_params[j].short_ssid); 689d1e879ecSMiri Korenblit } 690d1e879ecSMiri Korenblit } 691d1e879ecSMiri Korenblit 692d1e879ecSMiri Korenblit /* try to place BSSID for the same entry */ 693d1e879ecSMiri Korenblit for (k = 0; k < idex_b; k++) { 694d1e879ecSMiri Korenblit if (!memcmp(&pp->bssid_array[k], 695d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid, ETH_ALEN)) 696d1e879ecSMiri Korenblit break; 697d1e879ecSMiri Korenblit } 698d1e879ecSMiri Korenblit 699d1e879ecSMiri Korenblit if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE && 700d1e879ecSMiri Korenblit !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid), 701d1e879ecSMiri Korenblit "scan: invalid BSSID at index %u, index_b=%u\n", 702d1e879ecSMiri Korenblit j, idex_b)) { 703d1e879ecSMiri Korenblit memcpy(&pp->bssid_array[idex_b++], 704d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid, ETH_ALEN); 705d1e879ecSMiri Korenblit } 706d1e879ecSMiri Korenblit } 707d1e879ecSMiri Korenblit 708d1e879ecSMiri Korenblit pp->short_ssid_num = idex_s; 709d1e879ecSMiri Korenblit pp->bssid_num = idex_b; 710d1e879ecSMiri Korenblit } 711d1e879ecSMiri Korenblit 712d1e879ecSMiri Korenblit static void 713d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params, 714d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp, 715d1e879ecSMiri Korenblit u32 *bitmap_ssid) 716d1e879ecSMiri Korenblit { 717d1e879ecSMiri Korenblit pp->preq = params->preq; 718d1e879ecSMiri Korenblit 719d1e879ecSMiri Korenblit if (params->scan_6ghz) { 720d1e879ecSMiri Korenblit iwl_mld_scan_fill_6g_chan_list(params, pp); 721d1e879ecSMiri Korenblit return; 722d1e879ecSMiri Korenblit } 723d1e879ecSMiri Korenblit 724d1e879ecSMiri Korenblit /* relevant only for 2.4 GHz /5 GHz scan */ 725d1e879ecSMiri Korenblit iwl_mld_scan_cmd_build_ssids(params, pp->direct_scan, bitmap_ssid); 726d1e879ecSMiri Korenblit } 727d1e879ecSMiri Korenblit 728d1e879ecSMiri Korenblit static bool 729d1e879ecSMiri Korenblit iwl_mld_scan_use_ebs(struct iwl_mld *mld, struct ieee80211_vif *vif, 730d1e879ecSMiri Korenblit bool low_latency) 731d1e879ecSMiri Korenblit { 732d1e879ecSMiri Korenblit const struct iwl_ucode_capabilities *capa = &mld->fw->ucode_capa; 733d1e879ecSMiri Korenblit 734d1e879ecSMiri Korenblit /* We can only use EBS if: 735d1e879ecSMiri Korenblit * 1. the feature is supported. 736d1e879ecSMiri Korenblit * 2. the last EBS was successful. 737d1e879ecSMiri Korenblit * 3. it's not a p2p find operation. 738d1e879ecSMiri Korenblit * 4. we are not in low latency mode, 739d1e879ecSMiri Korenblit * or if fragmented ebs is supported by the FW 740d1e879ecSMiri Korenblit * 5. the VIF is not an AP interface (scan wants survey results) 741d1e879ecSMiri Korenblit */ 742d1e879ecSMiri Korenblit return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && 743d1e879ecSMiri Korenblit !mld->scan.last_ebs_failed && 744d1e879ecSMiri Korenblit vif->type != NL80211_IFTYPE_P2P_DEVICE && 745d1e879ecSMiri Korenblit (!low_latency || fw_has_api(capa, IWL_UCODE_TLV_API_FRAG_EBS)) && 746d1e879ecSMiri Korenblit ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_AP); 747d1e879ecSMiri Korenblit } 748d1e879ecSMiri Korenblit 749d1e879ecSMiri Korenblit static u8 750d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld *mld, 751d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 752d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 753d1e879ecSMiri Korenblit bool low_latency) 754d1e879ecSMiri Korenblit { 755d1e879ecSMiri Korenblit u8 flags = 0; 756d1e879ecSMiri Korenblit 757d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; 758d1e879ecSMiri Korenblit 759d1e879ecSMiri Korenblit if (iwl_mld_scan_use_ebs(mld, vif, low_latency)) 760d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_EBS | 761d1e879ecSMiri Korenblit IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | 762d1e879ecSMiri Korenblit IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; 763d1e879ecSMiri Korenblit 764d1e879ecSMiri Korenblit /* set fragmented ebs for fragmented scan */ 765d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type)) 766d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; 767d1e879ecSMiri Korenblit 768d1e879ecSMiri Korenblit /* Force EBS in case the scan is a fragmented and there is a need 769d1e879ecSMiri Korenblit * to take P2P GO operation into consideration during scan operation. 770d1e879ecSMiri Korenblit */ 771d1e879ecSMiri Korenblit /* TODO: CDB */ 772d1e879ecSMiri Korenblit if (iwl_mld_scan_is_fragmented(params->type) && 773d1e879ecSMiri Korenblit params->respect_p2p_go) { 774d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Respect P2P GO. Force EBS\n"); 775d1e879ecSMiri Korenblit flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS; 776d1e879ecSMiri Korenblit } 777d1e879ecSMiri Korenblit 778d1e879ecSMiri Korenblit return flags; 779d1e879ecSMiri Korenblit } 780d1e879ecSMiri Korenblit 781d1e879ecSMiri Korenblit static const u8 p2p_go_friendly_chs[] = { 782d1e879ecSMiri Korenblit 36, 40, 44, 48, 149, 153, 157, 161, 165, 783d1e879ecSMiri Korenblit }; 784d1e879ecSMiri Korenblit 785d1e879ecSMiri Korenblit static const u8 social_chs[] = { 786d1e879ecSMiri Korenblit 1, 6, 11 787d1e879ecSMiri Korenblit }; 788d1e879ecSMiri Korenblit 789d1e879ecSMiri Korenblit static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id) 790d1e879ecSMiri Korenblit { 791d1e879ecSMiri Korenblit if (vif_type != NL80211_IFTYPE_P2P_DEVICE) 792d1e879ecSMiri Korenblit return 0; 793d1e879ecSMiri Korenblit 794d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { 795d1e879ecSMiri Korenblit if (ch_id == p2p_go_friendly_chs[i]) 796d1e879ecSMiri Korenblit return IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; 797d1e879ecSMiri Korenblit } 798d1e879ecSMiri Korenblit 799d1e879ecSMiri Korenblit for (int i = 0; i < ARRAY_SIZE(social_chs); i++) { 800d1e879ecSMiri Korenblit if (ch_id == social_chs[i]) 801d1e879ecSMiri Korenblit return IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT; 802d1e879ecSMiri Korenblit } 803d1e879ecSMiri Korenblit 804d1e879ecSMiri Korenblit return 0; 805d1e879ecSMiri Korenblit } 806d1e879ecSMiri Korenblit 807d1e879ecSMiri Korenblit static void 808d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld, 809d1e879ecSMiri Korenblit struct ieee80211_channel **channels, 810d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp, 811d1e879ecSMiri Korenblit int n_channels, u32 flags, 812d1e879ecSMiri Korenblit enum nl80211_iftype vif_type) 813d1e879ecSMiri Korenblit { 814d1e879ecSMiri Korenblit for (int i = 0; i < n_channels; i++) { 815d1e879ecSMiri Korenblit enum nl80211_band band = channels[i]->band; 816d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i]; 817d1e879ecSMiri Korenblit u8 iwl_band = iwl_mld_nl80211_band_to_fw(band); 818d1e879ecSMiri Korenblit u32 n_aps_flag = 819d1e879ecSMiri Korenblit iwl_mld_scan_ch_n_aps_flag(vif_type, 820d1e879ecSMiri Korenblit channels[i]->hw_value); 821d1e879ecSMiri Korenblit 822d1e879ecSMiri Korenblit if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) 823d1e879ecSMiri Korenblit n_aps_flag = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; 824d1e879ecSMiri Korenblit 825d1e879ecSMiri Korenblit cfg->flags = cpu_to_le32(flags | n_aps_flag); 826d1e879ecSMiri Korenblit cfg->channel_num = channels[i]->hw_value; 827d1e879ecSMiri Korenblit if (cfg80211_channel_is_psc(channels[i])) 828d1e879ecSMiri Korenblit cfg->flags = 0; 829d1e879ecSMiri Korenblit 830d1e879ecSMiri Korenblit if (band == NL80211_BAND_6GHZ) { 831d1e879ecSMiri Korenblit /* 6 GHz channels should only appear in a scan request 832d1e879ecSMiri Korenblit * that has scan_6ghz set. The only exception is MLO 833d1e879ecSMiri Korenblit * scan, which has to be passive. 834d1e879ecSMiri Korenblit */ 835d1e879ecSMiri Korenblit WARN_ON_ONCE(cfg->flags != 0); 836d1e879ecSMiri Korenblit cfg->flags = 837d1e879ecSMiri Korenblit cpu_to_le32(IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE); 838d1e879ecSMiri Korenblit } 839d1e879ecSMiri Korenblit 840d1e879ecSMiri Korenblit cfg->v2.iter_count = 1; 841d1e879ecSMiri Korenblit cfg->v2.iter_interval = 0; 842d1e879ecSMiri Korenblit cfg->flags |= cpu_to_le32(iwl_band << 843d1e879ecSMiri Korenblit IWL_CHAN_CFG_FLAGS_BAND_POS); 844d1e879ecSMiri Korenblit } 845d1e879ecSMiri Korenblit } 846d1e879ecSMiri Korenblit 847d1e879ecSMiri Korenblit static u8 848d1e879ecSMiri Korenblit iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld, 849d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 850d1e879ecSMiri Korenblit u32 n_channels, 851d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *pp, 852d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp, 853d1e879ecSMiri Korenblit enum nl80211_iftype vif_type) 854d1e879ecSMiri Korenblit { 855d1e879ecSMiri Korenblit struct cfg80211_scan_6ghz_params *scan_6ghz_params = 856d1e879ecSMiri Korenblit params->scan_6ghz_params; 857d1e879ecSMiri Korenblit u32 i; 858d1e879ecSMiri Korenblit u8 ch_cnt; 859d1e879ecSMiri Korenblit 860d1e879ecSMiri Korenblit for (i = 0, ch_cnt = 0; i < params->n_channels; i++) { 861d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg = 862d1e879ecSMiri Korenblit &cp->channel_config[ch_cnt]; 863d1e879ecSMiri Korenblit 864d1e879ecSMiri Korenblit u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; 865d1e879ecSMiri Korenblit u8 k, n_s_ssids = 0, n_bssids = 0; 866d1e879ecSMiri Korenblit u8 max_s_ssids, max_bssids; 867d1e879ecSMiri Korenblit bool force_passive = false, found = false, allow_passive = true, 868d1e879ecSMiri Korenblit unsolicited_probe_on_chan = false, psc_no_listen = false; 869d1e879ecSMiri Korenblit s8 psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; 870d1e879ecSMiri Korenblit 871d1e879ecSMiri Korenblit /* Avoid performing passive scan on non PSC channels unless the 872d1e879ecSMiri Korenblit * scan is specifically a passive scan, i.e., no SSIDs 873d1e879ecSMiri Korenblit * configured in the scan command. 874d1e879ecSMiri Korenblit */ 875d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(params->channels[i]) && 876d1e879ecSMiri Korenblit !params->n_6ghz_params && params->n_ssids) 877d1e879ecSMiri Korenblit continue; 878d1e879ecSMiri Korenblit 879d1e879ecSMiri Korenblit cfg->channel_num = params->channels[i]->hw_value; 880d1e879ecSMiri Korenblit cfg->flags |= 881d1e879ecSMiri Korenblit cpu_to_le32(PHY_BAND_6 << IWL_CHAN_CFG_FLAGS_BAND_POS); 882d1e879ecSMiri Korenblit 883d1e879ecSMiri Korenblit cfg->v5.iter_count = 1; 884d1e879ecSMiri Korenblit cfg->v5.iter_interval = 0; 885d1e879ecSMiri Korenblit 886d1e879ecSMiri Korenblit for (u32 j = 0; j < params->n_6ghz_params; j++) { 887d1e879ecSMiri Korenblit s8 tmp_psd_20; 888d1e879ecSMiri Korenblit 889d1e879ecSMiri Korenblit if (!(scan_6ghz_params[j].channel_idx == i)) 890d1e879ecSMiri Korenblit continue; 891d1e879ecSMiri Korenblit 892d1e879ecSMiri Korenblit unsolicited_probe_on_chan |= 893d1e879ecSMiri Korenblit scan_6ghz_params[j].unsolicited_probe; 894d1e879ecSMiri Korenblit 895d1e879ecSMiri Korenblit /* Use the highest PSD value allowed as advertised by 896d1e879ecSMiri Korenblit * APs for this channel 897d1e879ecSMiri Korenblit */ 898d1e879ecSMiri Korenblit tmp_psd_20 = scan_6ghz_params[j].psd_20; 899d1e879ecSMiri Korenblit if (tmp_psd_20 != 900d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED && 901d1e879ecSMiri Korenblit (psd_20 == 902d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED || 903d1e879ecSMiri Korenblit psd_20 < tmp_psd_20)) 904d1e879ecSMiri Korenblit psd_20 = tmp_psd_20; 905d1e879ecSMiri Korenblit 906d1e879ecSMiri Korenblit psc_no_listen |= scan_6ghz_params[j].psc_no_listen; 907d1e879ecSMiri Korenblit } 908d1e879ecSMiri Korenblit 909d1e879ecSMiri Korenblit /* In the following cases apply passive scan: 910d1e879ecSMiri Korenblit * 1. Non fragmented scan: 911d1e879ecSMiri Korenblit * - PSC channel with NO_LISTEN_FLAG on should be treated 912d1e879ecSMiri Korenblit * like non PSC channel 913d1e879ecSMiri Korenblit * - Non PSC channel with more than 3 short SSIDs or more 914d1e879ecSMiri Korenblit * than 9 BSSIDs. 915d1e879ecSMiri Korenblit * - Non PSC Channel with unsolicited probe response and 916d1e879ecSMiri Korenblit * more than 2 short SSIDs or more than 6 BSSIDs. 917d1e879ecSMiri Korenblit * - PSC channel with more than 2 short SSIDs or more than 918d1e879ecSMiri Korenblit * 6 BSSIDs. 919d1e879ecSMiri Korenblit * 2. Fragmented scan: 920d1e879ecSMiri Korenblit * - PSC channel with more than 1 SSID or 3 BSSIDs. 921d1e879ecSMiri Korenblit * - Non PSC channel with more than 2 SSIDs or 6 BSSIDs. 922d1e879ecSMiri Korenblit * - Non PSC channel with unsolicited probe response and 923d1e879ecSMiri Korenblit * more than 1 SSID or more than 3 BSSIDs. 924d1e879ecSMiri Korenblit */ 925d1e879ecSMiri Korenblit if (!iwl_mld_scan_is_fragmented(params->type)) { 926d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(params->channels[i]) || 927d1e879ecSMiri Korenblit psc_no_listen) { 928d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan) { 929d1e879ecSMiri Korenblit max_s_ssids = 2; 930d1e879ecSMiri Korenblit max_bssids = 6; 931d1e879ecSMiri Korenblit } else { 932d1e879ecSMiri Korenblit max_s_ssids = 3; 933d1e879ecSMiri Korenblit max_bssids = 9; 934d1e879ecSMiri Korenblit } 935d1e879ecSMiri Korenblit } else { 936d1e879ecSMiri Korenblit max_s_ssids = 2; 937d1e879ecSMiri Korenblit max_bssids = 6; 938d1e879ecSMiri Korenblit } 939d1e879ecSMiri Korenblit } else if (cfg80211_channel_is_psc(params->channels[i])) { 940d1e879ecSMiri Korenblit max_s_ssids = 1; 941d1e879ecSMiri Korenblit max_bssids = 3; 942d1e879ecSMiri Korenblit } else { 943d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan) { 944d1e879ecSMiri Korenblit max_s_ssids = 1; 945d1e879ecSMiri Korenblit max_bssids = 3; 946d1e879ecSMiri Korenblit } else { 947d1e879ecSMiri Korenblit max_s_ssids = 2; 948d1e879ecSMiri Korenblit max_bssids = 6; 949d1e879ecSMiri Korenblit } 950d1e879ecSMiri Korenblit } 951d1e879ecSMiri Korenblit 952d1e879ecSMiri Korenblit /* To optimize the scan time, i.e., reduce the scan dwell time 953d1e879ecSMiri Korenblit * on each channel, the below logic tries to set 3 direct BSSID 954d1e879ecSMiri Korenblit * probe requests for each broadcast probe request with a short 955d1e879ecSMiri Korenblit * SSID. 956d1e879ecSMiri Korenblit */ 957d1e879ecSMiri Korenblit for (u32 j = 0; j < params->n_6ghz_params; j++) { 958d1e879ecSMiri Korenblit if (!(scan_6ghz_params[j].channel_idx == i)) 959d1e879ecSMiri Korenblit continue; 960d1e879ecSMiri Korenblit 961d1e879ecSMiri Korenblit found = false; 962d1e879ecSMiri Korenblit 963d1e879ecSMiri Korenblit for (k = 0; 964d1e879ecSMiri Korenblit k < pp->short_ssid_num && n_s_ssids < max_s_ssids; 965d1e879ecSMiri Korenblit k++) { 966d1e879ecSMiri Korenblit if (!scan_6ghz_params[j].unsolicited_probe && 967d1e879ecSMiri Korenblit le32_to_cpu(pp->short_ssid[k]) == 968d1e879ecSMiri Korenblit scan_6ghz_params[j].short_ssid) { 969d1e879ecSMiri Korenblit /* Relevant short SSID bit set */ 970d1e879ecSMiri Korenblit if (s_ssid_bitmap & BIT(k)) { 971d1e879ecSMiri Korenblit found = true; 972d1e879ecSMiri Korenblit break; 973d1e879ecSMiri Korenblit } 974d1e879ecSMiri Korenblit 975d1e879ecSMiri Korenblit /* Prefer creating BSSID entries unless 976d1e879ecSMiri Korenblit * the short SSID probe can be done in 977d1e879ecSMiri Korenblit * the same channel dwell iteration. 978d1e879ecSMiri Korenblit * 979d1e879ecSMiri Korenblit * We also need to create a short SSID 980d1e879ecSMiri Korenblit * entry for any hidden AP. 981d1e879ecSMiri Korenblit */ 982d1e879ecSMiri Korenblit if (3 * n_s_ssids > n_bssids && 983d1e879ecSMiri Korenblit !pp->direct_scan[k].len) 984d1e879ecSMiri Korenblit break; 985d1e879ecSMiri Korenblit 986d1e879ecSMiri Korenblit /* Hidden AP, cannot do passive scan */ 987d1e879ecSMiri Korenblit if (pp->direct_scan[k].len) 988d1e879ecSMiri Korenblit allow_passive = false; 989d1e879ecSMiri Korenblit 990d1e879ecSMiri Korenblit s_ssid_bitmap |= BIT(k); 991d1e879ecSMiri Korenblit n_s_ssids++; 992d1e879ecSMiri Korenblit found = true; 993d1e879ecSMiri Korenblit break; 994d1e879ecSMiri Korenblit } 995d1e879ecSMiri Korenblit } 996d1e879ecSMiri Korenblit 997d1e879ecSMiri Korenblit if (found) 998d1e879ecSMiri Korenblit continue; 999d1e879ecSMiri Korenblit 1000d1e879ecSMiri Korenblit for (k = 0; k < pp->bssid_num; k++) { 1001d1e879ecSMiri Korenblit if (memcmp(&pp->bssid_array[k], 1002d1e879ecSMiri Korenblit scan_6ghz_params[j].bssid, 1003d1e879ecSMiri Korenblit ETH_ALEN)) 1004d1e879ecSMiri Korenblit continue; 1005d1e879ecSMiri Korenblit 1006d1e879ecSMiri Korenblit if (bssid_bitmap & BIT(k)) 1007d1e879ecSMiri Korenblit break; 1008d1e879ecSMiri Korenblit 1009d1e879ecSMiri Korenblit if (n_bssids < max_bssids) { 1010d1e879ecSMiri Korenblit bssid_bitmap |= BIT(k); 1011d1e879ecSMiri Korenblit n_bssids++; 1012d1e879ecSMiri Korenblit } else { 1013d1e879ecSMiri Korenblit force_passive = TRUE; 1014d1e879ecSMiri Korenblit } 1015d1e879ecSMiri Korenblit 1016d1e879ecSMiri Korenblit break; 1017d1e879ecSMiri Korenblit } 1018d1e879ecSMiri Korenblit } 1019d1e879ecSMiri Korenblit 1020d1e879ecSMiri Korenblit if (cfg80211_channel_is_psc(params->channels[i]) && 1021d1e879ecSMiri Korenblit psc_no_listen) 1022d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_PSC_CHAN_NO_LISTEN; 1023d1e879ecSMiri Korenblit 1024d1e879ecSMiri Korenblit if (unsolicited_probe_on_chan) 1025d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_UNSOLICITED_PROBE_RES; 1026d1e879ecSMiri Korenblit 1027d1e879ecSMiri Korenblit if ((allow_passive && force_passive) || 1028d1e879ecSMiri Korenblit (!(bssid_bitmap | s_ssid_bitmap) && 1029d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(params->channels[i]))) 1030d1e879ecSMiri Korenblit flags |= IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE; 1031d1e879ecSMiri Korenblit else 1032d1e879ecSMiri Korenblit flags |= bssid_bitmap | (s_ssid_bitmap << 16); 1033d1e879ecSMiri Korenblit 1034d1e879ecSMiri Korenblit cfg->flags |= cpu_to_le32(flags); 1035d1e879ecSMiri Korenblit cfg->v5.psd_20 = psd_20; 1036d1e879ecSMiri Korenblit 1037d1e879ecSMiri Korenblit ch_cnt++; 1038d1e879ecSMiri Korenblit } 1039d1e879ecSMiri Korenblit 1040d1e879ecSMiri Korenblit if (params->n_channels > ch_cnt) 1041d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1042d1e879ecSMiri Korenblit "6GHz: reducing number channels: (%u->%u)\n", 1043d1e879ecSMiri Korenblit params->n_channels, ch_cnt); 1044d1e879ecSMiri Korenblit 1045d1e879ecSMiri Korenblit return ch_cnt; 1046d1e879ecSMiri Korenblit } 1047d1e879ecSMiri Korenblit 1048d1e879ecSMiri Korenblit static int 1049d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld, 1050d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 1051d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1052d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p, 1053d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status) 1054d1e879ecSMiri Korenblit { 1055d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params; 1056d1e879ecSMiri Korenblit struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params; 1057d1e879ecSMiri Korenblit 1058d1e879ecSMiri Korenblit chan_p->flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, 1059d1e879ecSMiri Korenblit scan_status); 1060d1e879ecSMiri Korenblit chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params, 1061d1e879ecSMiri Korenblit params->n_channels, 1062d1e879ecSMiri Korenblit probe_p, chan_p, 1063d1e879ecSMiri Korenblit vif->type); 1064d1e879ecSMiri Korenblit if (!chan_p->count) 1065d1e879ecSMiri Korenblit return -EINVAL; 1066d1e879ecSMiri Korenblit 1067d1e879ecSMiri Korenblit if (!params->n_ssids || 1068d1e879ecSMiri Korenblit (params->n_ssids == 1 && !params->ssids[0].ssid_len)) 1069d1e879ecSMiri Korenblit chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER; 1070d1e879ecSMiri Korenblit 1071d1e879ecSMiri Korenblit return 0; 1072d1e879ecSMiri Korenblit } 1073d1e879ecSMiri Korenblit 1074d1e879ecSMiri Korenblit static int 1075d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld, 1076d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 1077d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1078d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p, 1079d1e879ecSMiri Korenblit bool low_latency, 1080d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status, 1081d1e879ecSMiri Korenblit u32 channel_cfg_flags) 1082d1e879ecSMiri Korenblit { 1083d1e879ecSMiri Korenblit struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params; 1084d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband = 1085d1e879ecSMiri Korenblit &mld->nvm_data->bands[NL80211_BAND_6GHZ]; 1086d1e879ecSMiri Korenblit 1087d1e879ecSMiri Korenblit cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; 1088d1e879ecSMiri Korenblit cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS; 1089d1e879ecSMiri Korenblit 1090d1e879ecSMiri Korenblit if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) 1091d1e879ecSMiri Korenblit cp->n_aps_override[0] = IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE; 1092d1e879ecSMiri Korenblit 1093d1e879ecSMiri Korenblit if (params->scan_6ghz) 1094d1e879ecSMiri Korenblit return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params, 1095d1e879ecSMiri Korenblit vif, scan_p, 1096d1e879ecSMiri Korenblit scan_status); 1097d1e879ecSMiri Korenblit 1098d1e879ecSMiri Korenblit /* relevant only for 2.4 GHz/5 GHz scan */ 1099d1e879ecSMiri Korenblit cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif, 1100d1e879ecSMiri Korenblit low_latency); 1101d1e879ecSMiri Korenblit cp->count = params->n_channels; 1102d1e879ecSMiri Korenblit 1103d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_channels(mld, params->channels, cp, 1104d1e879ecSMiri Korenblit params->n_channels, channel_cfg_flags, 1105d1e879ecSMiri Korenblit vif->type); 1106d1e879ecSMiri Korenblit 1107d1e879ecSMiri Korenblit if (!params->enable_6ghz_passive) 1108d1e879ecSMiri Korenblit return 0; 1109d1e879ecSMiri Korenblit 1110d1e879ecSMiri Korenblit /* fill 6 GHz passive scan cfg */ 1111d1e879ecSMiri Korenblit for (int i = 0; i < sband->n_channels; i++) { 1112d1e879ecSMiri Korenblit struct ieee80211_channel *channel = 1113d1e879ecSMiri Korenblit &sband->channels[i]; 1114d1e879ecSMiri Korenblit struct iwl_scan_channel_cfg_umac *cfg = 1115d1e879ecSMiri Korenblit &cp->channel_config[cp->count]; 1116d1e879ecSMiri Korenblit 1117d1e879ecSMiri Korenblit if (!cfg80211_channel_is_psc(channel)) 1118d1e879ecSMiri Korenblit continue; 1119d1e879ecSMiri Korenblit 1120d1e879ecSMiri Korenblit cfg->channel_num = channel->hw_value; 1121d1e879ecSMiri Korenblit cfg->v5.iter_count = 1; 1122d1e879ecSMiri Korenblit cfg->v5.iter_interval = 0; 1123d1e879ecSMiri Korenblit cfg->v5.psd_20 = 1124d1e879ecSMiri Korenblit IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; 1125d1e879ecSMiri Korenblit cfg->flags = cpu_to_le32(PHY_BAND_6 << 1126d1e879ecSMiri Korenblit IWL_CHAN_CFG_FLAGS_BAND_POS); 1127d1e879ecSMiri Korenblit cp->count++; 1128d1e879ecSMiri Korenblit } 1129d1e879ecSMiri Korenblit 1130d1e879ecSMiri Korenblit return 0; 1131d1e879ecSMiri Korenblit } 1132d1e879ecSMiri Korenblit 1133d1e879ecSMiri Korenblit static int 1134d1e879ecSMiri Korenblit iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif, 1135d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 1136d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status, 1137d1e879ecSMiri Korenblit bool low_latency) 1138d1e879ecSMiri Korenblit { 1139d1e879ecSMiri Korenblit struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd; 1140d1e879ecSMiri Korenblit struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params; 1141d1e879ecSMiri Korenblit u32 bitmap_ssid = 0; 1142d1e879ecSMiri Korenblit int uid, ret; 1143d1e879ecSMiri Korenblit 1144d1e879ecSMiri Korenblit memset(mld->scan.cmd, 0, mld->scan.cmd_size); 1145d1e879ecSMiri Korenblit 1146d1e879ecSMiri Korenblit /* find a free UID entry */ 1147d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE); 1148d1e879ecSMiri Korenblit if (uid < 0) 1149d1e879ecSMiri Korenblit return uid; 1150d1e879ecSMiri Korenblit 1151d1e879ecSMiri Korenblit cmd->uid = cpu_to_le32(uid); 1152d1e879ecSMiri Korenblit cmd->ooc_priority = 1153d1e879ecSMiri Korenblit cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status)); 1154d1e879ecSMiri Korenblit 1155d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_gen_params(mld, params, vif, 1156d1e879ecSMiri Korenblit &scan_p->general_params, scan_status); 1157d1e879ecSMiri Korenblit 1158d1e879ecSMiri Korenblit ret = iwl_mld_scan_cmd_set_sched_params(params, 1159d1e879ecSMiri Korenblit scan_p->periodic_params.schedule, 1160d1e879ecSMiri Korenblit &scan_p->periodic_params.delay); 1161d1e879ecSMiri Korenblit if (ret) 1162d1e879ecSMiri Korenblit return ret; 1163d1e879ecSMiri Korenblit 1164d1e879ecSMiri Korenblit iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params, 1165d1e879ecSMiri Korenblit &bitmap_ssid); 1166d1e879ecSMiri Korenblit 1167d1e879ecSMiri Korenblit ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p, 1168d1e879ecSMiri Korenblit low_latency, scan_status, 1169d1e879ecSMiri Korenblit bitmap_ssid); 1170d1e879ecSMiri Korenblit if (ret) 1171d1e879ecSMiri Korenblit return ret; 1172d1e879ecSMiri Korenblit 1173d1e879ecSMiri Korenblit return uid; 1174d1e879ecSMiri Korenblit } 1175d1e879ecSMiri Korenblit 1176d1e879ecSMiri Korenblit static bool 1177d1e879ecSMiri Korenblit iwl_mld_scan_pass_all(struct iwl_mld *mld, 1178d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req) 1179d1e879ecSMiri Korenblit { 1180d1e879ecSMiri Korenblit if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { 1181d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1182d1e879ecSMiri Korenblit "Sending scheduled scan with filtering, n_match_sets %d\n", 1183d1e879ecSMiri Korenblit req->n_match_sets); 1184d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; 1185d1e879ecSMiri Korenblit return false; 1186d1e879ecSMiri Korenblit } 1187d1e879ecSMiri Korenblit 1188d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Sending Scheduled scan without filtering\n"); 1189d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; 1190d1e879ecSMiri Korenblit 1191d1e879ecSMiri Korenblit return true; 1192d1e879ecSMiri Korenblit } 1193d1e879ecSMiri Korenblit 1194d1e879ecSMiri Korenblit static int 1195d1e879ecSMiri Korenblit iwl_mld_config_sched_scan_profiles(struct iwl_mld *mld, 1196d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req) 1197d1e879ecSMiri Korenblit { 1198d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = { 1199d1e879ecSMiri Korenblit .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, 1200d1e879ecSMiri Korenblit .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 1201d1e879ecSMiri Korenblit }; 1202d1e879ecSMiri Korenblit struct iwl_scan_offload_profile *profile; 1203d1e879ecSMiri Korenblit struct iwl_scan_offload_profile_cfg_data *cfg_data; 1204d1e879ecSMiri Korenblit struct iwl_scan_offload_profile_cfg *profile_cfg; 1205d1e879ecSMiri Korenblit struct iwl_scan_offload_blocklist *blocklist; 1206d1e879ecSMiri Korenblit u32 blocklist_size = IWL_SCAN_MAX_BLACKLIST_LEN * sizeof(*blocklist); 1207d1e879ecSMiri Korenblit u32 cmd_size = blocklist_size + sizeof(*profile_cfg); 1208d1e879ecSMiri Korenblit u8 *cmd; 1209d1e879ecSMiri Korenblit int ret; 1210d1e879ecSMiri Korenblit 1211d1e879ecSMiri Korenblit if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES_V2)) 1212d1e879ecSMiri Korenblit return -EIO; 1213d1e879ecSMiri Korenblit 1214d1e879ecSMiri Korenblit cmd = kzalloc(cmd_size, GFP_KERNEL); 1215d1e879ecSMiri Korenblit if (!cmd) 1216d1e879ecSMiri Korenblit return -ENOMEM; 1217d1e879ecSMiri Korenblit 1218d1e879ecSMiri Korenblit hcmd.data[0] = cmd; 1219d1e879ecSMiri Korenblit hcmd.len[0] = cmd_size; 1220d1e879ecSMiri Korenblit 1221d1e879ecSMiri Korenblit blocklist = (struct iwl_scan_offload_blocklist *)cmd; 1222d1e879ecSMiri Korenblit profile_cfg = (struct iwl_scan_offload_profile_cfg *)(cmd + blocklist_size); 1223d1e879ecSMiri Korenblit 1224d1e879ecSMiri Korenblit /* No blocklist configuration */ 1225d1e879ecSMiri Korenblit cfg_data = &profile_cfg->data; 1226d1e879ecSMiri Korenblit cfg_data->num_profiles = req->n_match_sets; 1227d1e879ecSMiri Korenblit cfg_data->active_clients = SCAN_CLIENT_SCHED_SCAN; 1228d1e879ecSMiri Korenblit cfg_data->pass_match = SCAN_CLIENT_SCHED_SCAN; 1229d1e879ecSMiri Korenblit cfg_data->match_notify = SCAN_CLIENT_SCHED_SCAN; 1230d1e879ecSMiri Korenblit 1231d1e879ecSMiri Korenblit if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) 1232d1e879ecSMiri Korenblit cfg_data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; 1233d1e879ecSMiri Korenblit 1234d1e879ecSMiri Korenblit for (int i = 0; i < req->n_match_sets; i++) { 1235d1e879ecSMiri Korenblit profile = &profile_cfg->profiles[i]; 1236d1e879ecSMiri Korenblit 1237d1e879ecSMiri Korenblit /* Support any cipher and auth algorithm */ 1238d1e879ecSMiri Korenblit profile->unicast_cipher = 0xff; 1239d1e879ecSMiri Korenblit profile->auth_alg = IWL_AUTH_ALGO_UNSUPPORTED | 1240d1e879ecSMiri Korenblit IWL_AUTH_ALGO_NONE | IWL_AUTH_ALGO_PSK | 1241d1e879ecSMiri Korenblit IWL_AUTH_ALGO_8021X | IWL_AUTH_ALGO_SAE | 1242d1e879ecSMiri Korenblit IWL_AUTH_ALGO_8021X_SHA384 | IWL_AUTH_ALGO_OWE; 1243d1e879ecSMiri Korenblit profile->network_type = IWL_NETWORK_TYPE_ANY; 1244d1e879ecSMiri Korenblit profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; 1245d1e879ecSMiri Korenblit profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; 1246d1e879ecSMiri Korenblit profile->ssid_index = i; 1247d1e879ecSMiri Korenblit } 1248d1e879ecSMiri Korenblit 1249d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1250d1e879ecSMiri Korenblit "Sending scheduled scan profile config (n_match_sets=%u)\n", 1251d1e879ecSMiri Korenblit req->n_match_sets); 1252d1e879ecSMiri Korenblit 1253d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd); 1254d1e879ecSMiri Korenblit 1255d1e879ecSMiri Korenblit kfree(cmd); 1256d1e879ecSMiri Korenblit 1257d1e879ecSMiri Korenblit return ret; 1258d1e879ecSMiri Korenblit } 1259d1e879ecSMiri Korenblit 1260d1e879ecSMiri Korenblit static int 1261d1e879ecSMiri Korenblit iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params *params, 1262d1e879ecSMiri Korenblit bool *non_psc_included) 1263d1e879ecSMiri Korenblit { 1264d1e879ecSMiri Korenblit int i, j; 1265d1e879ecSMiri Korenblit 1266d1e879ecSMiri Korenblit *non_psc_included = false; 1267d1e879ecSMiri Korenblit /* for 6 GHZ band only PSC channels need to be added */ 1268d1e879ecSMiri Korenblit for (i = 0; i < params->n_channels; i++) { 1269d1e879ecSMiri Korenblit struct ieee80211_channel *channel = params->channels[i]; 1270d1e879ecSMiri Korenblit 1271d1e879ecSMiri Korenblit if (channel->band == NL80211_BAND_6GHZ && 1272d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(channel)) { 1273d1e879ecSMiri Korenblit *non_psc_included = true; 1274d1e879ecSMiri Korenblit break; 1275d1e879ecSMiri Korenblit } 1276d1e879ecSMiri Korenblit } 1277d1e879ecSMiri Korenblit 1278d1e879ecSMiri Korenblit if (!*non_psc_included) 1279d1e879ecSMiri Korenblit return 0; 1280d1e879ecSMiri Korenblit 1281d1e879ecSMiri Korenblit params->channels = 1282d1e879ecSMiri Korenblit kmemdup(params->channels, 1283d1e879ecSMiri Korenblit sizeof(params->channels[0]) * params->n_channels, 1284d1e879ecSMiri Korenblit GFP_KERNEL); 1285d1e879ecSMiri Korenblit if (!params->channels) 1286d1e879ecSMiri Korenblit return -ENOMEM; 1287d1e879ecSMiri Korenblit 1288d1e879ecSMiri Korenblit for (i = j = 0; i < params->n_channels; i++) { 1289d1e879ecSMiri Korenblit if (params->channels[i]->band == NL80211_BAND_6GHZ && 1290d1e879ecSMiri Korenblit !cfg80211_channel_is_psc(params->channels[i])) 1291d1e879ecSMiri Korenblit continue; 1292d1e879ecSMiri Korenblit params->channels[j++] = params->channels[i]; 1293d1e879ecSMiri Korenblit } 1294d1e879ecSMiri Korenblit 1295d1e879ecSMiri Korenblit params->n_channels = j; 1296d1e879ecSMiri Korenblit 1297d1e879ecSMiri Korenblit return 0; 1298d1e879ecSMiri Korenblit } 1299d1e879ecSMiri Korenblit 1300d1e879ecSMiri Korenblit static void 1301d1e879ecSMiri Korenblit iwl_mld_scan_6ghz_passive_scan(struct iwl_mld *mld, 1302d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 1303d1e879ecSMiri Korenblit struct ieee80211_vif *vif) 1304d1e879ecSMiri Korenblit { 1305d1e879ecSMiri Korenblit struct ieee80211_supported_band *sband = 1306d1e879ecSMiri Korenblit &mld->nvm_data->bands[NL80211_BAND_6GHZ]; 1307d1e879ecSMiri Korenblit u32 n_disabled, i; 1308d1e879ecSMiri Korenblit 1309d1e879ecSMiri Korenblit params->enable_6ghz_passive = false; 1310d1e879ecSMiri Korenblit 1311d1e879ecSMiri Korenblit /* 6 GHz passive scan may be enabled in the first 2.4 GHz/5 GHz scan 1312d1e879ecSMiri Korenblit * phase to discover geo location if no AP's are found. Skip it when 1313d1e879ecSMiri Korenblit * we're in the 6 GHz scan phase. 1314d1e879ecSMiri Korenblit */ 1315d1e879ecSMiri Korenblit if (params->scan_6ghz) 1316d1e879ecSMiri Korenblit return; 1317d1e879ecSMiri Korenblit 1318d1e879ecSMiri Korenblit /* 6 GHz passive scan allowed only on station interface */ 1319d1e879ecSMiri Korenblit if (vif->type != NL80211_IFTYPE_STATION) { 1320d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1321d1e879ecSMiri Korenblit "6GHz passive scan: not station interface\n"); 1322d1e879ecSMiri Korenblit return; 1323d1e879ecSMiri Korenblit } 1324d1e879ecSMiri Korenblit 1325d1e879ecSMiri Korenblit /* 6 GHz passive scan is allowed in a defined time interval following 1326d1e879ecSMiri Korenblit * HW reset or resume flow, or while not associated and a large 1327d1e879ecSMiri Korenblit * interval has passed since the last 6 GHz passive scan. 1328d1e879ecSMiri Korenblit */ 1329d1e879ecSMiri Korenblit if ((vif->cfg.assoc || 1330d1e879ecSMiri Korenblit time_after(mld->scan.last_6ghz_passive_jiffies + 1331d1e879ecSMiri Korenblit (IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT * HZ), jiffies)) && 1332d1e879ecSMiri Korenblit (time_before(mld->scan.last_start_time_jiffies + 1333d1e879ecSMiri Korenblit (IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT * HZ), 1334d1e879ecSMiri Korenblit jiffies))) { 1335d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "6GHz passive scan: %s\n", 1336d1e879ecSMiri Korenblit vif->cfg.assoc ? "associated" : 1337d1e879ecSMiri Korenblit "timeout did not expire"); 1338d1e879ecSMiri Korenblit return; 1339d1e879ecSMiri Korenblit } 1340d1e879ecSMiri Korenblit 1341d1e879ecSMiri Korenblit /* not enough channels in the regular scan request */ 1342d1e879ecSMiri Korenblit if (params->n_channels < IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS) { 1343d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1344d1e879ecSMiri Korenblit "6GHz passive scan: not enough channels %d\n", 1345d1e879ecSMiri Korenblit params->n_channels); 1346d1e879ecSMiri Korenblit return; 1347d1e879ecSMiri Korenblit } 1348d1e879ecSMiri Korenblit 1349d1e879ecSMiri Korenblit for (i = 0; i < params->n_ssids; i++) { 1350d1e879ecSMiri Korenblit if (!params->ssids[i].ssid_len) 1351d1e879ecSMiri Korenblit break; 1352d1e879ecSMiri Korenblit } 1353d1e879ecSMiri Korenblit 1354d1e879ecSMiri Korenblit /* not a wildcard scan, so cannot enable passive 6 GHz scan */ 1355d1e879ecSMiri Korenblit if (i == params->n_ssids) { 1356d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1357d1e879ecSMiri Korenblit "6GHz passive scan: no wildcard SSID\n"); 1358d1e879ecSMiri Korenblit return; 1359d1e879ecSMiri Korenblit } 1360d1e879ecSMiri Korenblit 1361d1e879ecSMiri Korenblit if (!sband || !sband->n_channels) { 1362d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1363d1e879ecSMiri Korenblit "6GHz passive scan: no 6GHz channels\n"); 1364d1e879ecSMiri Korenblit return; 1365d1e879ecSMiri Korenblit } 1366d1e879ecSMiri Korenblit 1367d1e879ecSMiri Korenblit for (i = 0, n_disabled = 0; i < sband->n_channels; i++) { 1368d1e879ecSMiri Korenblit if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED)) 1369d1e879ecSMiri Korenblit n_disabled++; 1370d1e879ecSMiri Korenblit } 1371d1e879ecSMiri Korenblit 1372d1e879ecSMiri Korenblit /* Not all the 6 GHz channels are disabled, so no need for 6 GHz 1373d1e879ecSMiri Korenblit * passive scan 1374d1e879ecSMiri Korenblit */ 1375d1e879ecSMiri Korenblit if (n_disabled != sband->n_channels) { 1376d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1377d1e879ecSMiri Korenblit "6GHz passive scan: 6GHz channels enabled\n"); 1378d1e879ecSMiri Korenblit return; 1379d1e879ecSMiri Korenblit } 1380d1e879ecSMiri Korenblit 1381d1e879ecSMiri Korenblit /* all conditions to enable 6 GHz passive scan are satisfied */ 1382d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "6GHz passive scan: can be enabled\n"); 1383d1e879ecSMiri Korenblit params->enable_6ghz_passive = true; 1384d1e879ecSMiri Korenblit } 1385d1e879ecSMiri Korenblit 1386d1e879ecSMiri Korenblit static void 1387d1e879ecSMiri Korenblit iwl_mld_scan_set_link_id(struct iwl_mld *mld, struct ieee80211_vif *vif, 1388d1e879ecSMiri Korenblit struct iwl_mld_scan_params *params, 1389d1e879ecSMiri Korenblit s8 tsf_report_link_id, 1390d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status) 1391d1e879ecSMiri Korenblit { 1392d1e879ecSMiri Korenblit struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); 1393d1e879ecSMiri Korenblit struct iwl_mld_link *link; 1394d1e879ecSMiri Korenblit 1395d1e879ecSMiri Korenblit if (tsf_report_link_id < 0) { 1396d1e879ecSMiri Korenblit if (vif->active_links) 1397d1e879ecSMiri Korenblit tsf_report_link_id = __ffs(vif->active_links); 1398d1e879ecSMiri Korenblit else 1399d1e879ecSMiri Korenblit tsf_report_link_id = 0; 1400d1e879ecSMiri Korenblit } 1401d1e879ecSMiri Korenblit 1402d1e879ecSMiri Korenblit link = iwl_mld_link_dereference_check(mld_vif, tsf_report_link_id); 1403d1e879ecSMiri Korenblit if (!WARN_ON(!link)) { 1404d1e879ecSMiri Korenblit params->fw_link_id = link->fw_id; 1405d1e879ecSMiri Korenblit /* we to store fw_link_id only for regular scan, 1406d1e879ecSMiri Korenblit * and use it in scan complete notif 1407d1e879ecSMiri Korenblit */ 1408d1e879ecSMiri Korenblit if (scan_status == IWL_MLD_SCAN_REGULAR) 1409d1e879ecSMiri Korenblit mld->scan.fw_link_id = link->fw_id; 1410d1e879ecSMiri Korenblit } else { 1411d1e879ecSMiri Korenblit mld->scan.fw_link_id = IWL_MLD_INVALID_FW_ID; 1412d1e879ecSMiri Korenblit params->fw_link_id = IWL_MLD_INVALID_FW_ID; 1413d1e879ecSMiri Korenblit } 1414d1e879ecSMiri Korenblit } 1415d1e879ecSMiri Korenblit 1416d1e879ecSMiri Korenblit static int 1417d1e879ecSMiri Korenblit _iwl_mld_single_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, 1418d1e879ecSMiri Korenblit struct cfg80211_scan_request *req, 1419d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies, 1420d1e879ecSMiri Korenblit enum iwl_mld_scan_status scan_status) 1421d1e879ecSMiri Korenblit { 1422d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = { 1423d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), 1424d1e879ecSMiri Korenblit .len = { mld->scan.cmd_size, }, 1425d1e879ecSMiri Korenblit .data = { mld->scan.cmd, }, 1426d1e879ecSMiri Korenblit .dataflags = { IWL_HCMD_DFL_NOCOPY, }, 1427d1e879ecSMiri Korenblit }; 1428d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data scan_iter_data = { 1429d1e879ecSMiri Korenblit .current_vif = vif, 1430d1e879ecSMiri Korenblit }; 1431d1e879ecSMiri Korenblit struct cfg80211_sched_scan_plan scan_plan = {.iterations = 1}; 1432d1e879ecSMiri Korenblit struct iwl_mld_scan_params params = {}; 1433d1e879ecSMiri Korenblit int ret, uid; 1434d1e879ecSMiri Korenblit 1435d1e879ecSMiri Korenblit /* we should have failed registration if scan_cmd was NULL */ 1436d1e879ecSMiri Korenblit if (WARN_ON(!mld->scan.cmd)) 1437d1e879ecSMiri Korenblit return -ENOMEM; 1438d1e879ecSMiri Korenblit 1439d1e879ecSMiri Korenblit if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, req->n_channels)) 1440d1e879ecSMiri Korenblit return -ENOBUFS; 1441d1e879ecSMiri Korenblit 1442d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw, 1443d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL, 1444d1e879ecSMiri Korenblit iwl_mld_scan_iterator, 1445d1e879ecSMiri Korenblit &scan_iter_data); 1446d1e879ecSMiri Korenblit 1447d1e879ecSMiri Korenblit params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); 1448d1e879ecSMiri Korenblit params.n_ssids = req->n_ssids; 1449d1e879ecSMiri Korenblit params.flags = req->flags; 1450d1e879ecSMiri Korenblit params.n_channels = req->n_channels; 1451d1e879ecSMiri Korenblit params.delay = 0; 1452d1e879ecSMiri Korenblit params.ssids = req->ssids; 1453d1e879ecSMiri Korenblit params.channels = req->channels; 1454d1e879ecSMiri Korenblit params.mac_addr = req->mac_addr; 1455d1e879ecSMiri Korenblit params.mac_addr_mask = req->mac_addr_mask; 1456d1e879ecSMiri Korenblit params.no_cck = req->no_cck; 1457d1e879ecSMiri Korenblit params.pass_all = true; 1458d1e879ecSMiri Korenblit params.n_match_sets = 0; 1459d1e879ecSMiri Korenblit params.match_sets = NULL; 1460d1e879ecSMiri Korenblit params.scan_plans = &scan_plan; 1461d1e879ecSMiri Korenblit params.n_scan_plans = 1; 1462d1e879ecSMiri Korenblit 1463d1e879ecSMiri Korenblit params.n_6ghz_params = req->n_6ghz_params; 1464d1e879ecSMiri Korenblit params.scan_6ghz_params = req->scan_6ghz_params; 1465d1e879ecSMiri Korenblit params.scan_6ghz = req->scan_6ghz; 1466d1e879ecSMiri Korenblit 1467d1e879ecSMiri Korenblit ether_addr_copy(params.bssid, req->bssid); 1468d1e879ecSMiri Korenblit /* TODO: CDB - per-band flag */ 1469d1e879ecSMiri Korenblit params.respect_p2p_go = 1470d1e879ecSMiri Korenblit iwl_mld_get_respect_p2p_go(mld, vif, 1471d1e879ecSMiri Korenblit scan_iter_data.global_low_latency); 1472d1e879ecSMiri Korenblit 1473d1e879ecSMiri Korenblit if (req->duration) 1474d1e879ecSMiri Korenblit params.iter_notif = true; 1475d1e879ecSMiri Korenblit 1476d1e879ecSMiri Korenblit iwl_mld_scan_set_link_id(mld, vif, ¶ms, req->tsf_report_link_id, 1477d1e879ecSMiri Korenblit scan_status); 1478d1e879ecSMiri Korenblit 1479d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); 1480d1e879ecSMiri Korenblit 1481d1e879ecSMiri Korenblit iwl_mld_scan_6ghz_passive_scan(mld, ¶ms, vif); 1482d1e879ecSMiri Korenblit 1483d1e879ecSMiri Korenblit uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, scan_status, 1484d1e879ecSMiri Korenblit scan_iter_data.global_low_latency); 1485d1e879ecSMiri Korenblit if (uid < 0) 1486d1e879ecSMiri Korenblit return uid; 1487d1e879ecSMiri Korenblit 1488d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd); 1489d1e879ecSMiri Korenblit if (ret) { 1490d1e879ecSMiri Korenblit IWL_ERR(mld, "Scan failed! ret %d\n", ret); 1491d1e879ecSMiri Korenblit return ret; 1492d1e879ecSMiri Korenblit } 1493d1e879ecSMiri Korenblit 1494d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan request send success: status=%u, uid=%u\n", 1495d1e879ecSMiri Korenblit scan_status, uid); 1496d1e879ecSMiri Korenblit 1497d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = scan_status; 1498d1e879ecSMiri Korenblit mld->scan.status |= scan_status; 1499d1e879ecSMiri Korenblit 1500d1e879ecSMiri Korenblit if (params.enable_6ghz_passive) 1501d1e879ecSMiri Korenblit mld->scan.last_6ghz_passive_jiffies = jiffies; 1502d1e879ecSMiri Korenblit 1503d1e879ecSMiri Korenblit return 0; 1504d1e879ecSMiri Korenblit } 1505d1e879ecSMiri Korenblit 1506d1e879ecSMiri Korenblit static int 1507d1e879ecSMiri Korenblit iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status) 1508d1e879ecSMiri Korenblit { 1509d1e879ecSMiri Korenblit struct iwl_umac_scan_abort abort_cmd = { 1510d1e879ecSMiri Korenblit .uid = cpu_to_le32(uid), 1511d1e879ecSMiri Korenblit }; 1512d1e879ecSMiri Korenblit struct iwl_host_cmd cmd = { 1513d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_ABORT_UMAC), 1514d1e879ecSMiri Korenblit .flags = CMD_WANT_SKB, 1515d1e879ecSMiri Korenblit .data = { &abort_cmd }, 1516d1e879ecSMiri Korenblit .len[0] = sizeof(abort_cmd), 1517d1e879ecSMiri Korenblit }; 1518d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt; 1519d1e879ecSMiri Korenblit struct iwl_cmd_response *resp; 1520d1e879ecSMiri Korenblit u32 resp_len; 1521d1e879ecSMiri Korenblit int ret; 1522d1e879ecSMiri Korenblit 1523d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &cmd); 1524d1e879ecSMiri Korenblit if (ret) 1525d1e879ecSMiri Korenblit return ret; 1526d1e879ecSMiri Korenblit 1527d1e879ecSMiri Korenblit pkt = cmd.resp_pkt; 1528d1e879ecSMiri Korenblit 1529d1e879ecSMiri Korenblit resp_len = iwl_rx_packet_payload_len(pkt); 1530d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp), 1531d1e879ecSMiri Korenblit "Scan Abort: unexpected response length %d\n", 1532d1e879ecSMiri Korenblit resp_len)) { 1533d1e879ecSMiri Korenblit ret = -EIO; 1534d1e879ecSMiri Korenblit goto out; 1535d1e879ecSMiri Korenblit } 1536d1e879ecSMiri Korenblit 1537d1e879ecSMiri Korenblit resp = (void *)pkt->data; 1538d1e879ecSMiri Korenblit *status = le32_to_cpu(resp->status); 1539d1e879ecSMiri Korenblit 1540d1e879ecSMiri Korenblit out: 1541d1e879ecSMiri Korenblit iwl_free_resp(&cmd); 1542d1e879ecSMiri Korenblit return ret; 1543d1e879ecSMiri Korenblit } 1544d1e879ecSMiri Korenblit 1545d1e879ecSMiri Korenblit static int 1546d1e879ecSMiri Korenblit iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait) 1547d1e879ecSMiri Korenblit { 1548d1e879ecSMiri Korenblit enum iwl_umac_scan_abort_status status; 1549d1e879ecSMiri Korenblit int ret; 1550d1e879ecSMiri Korenblit 1551d1e879ecSMiri Korenblit *wait = true; 1552d1e879ecSMiri Korenblit 1553d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Sending scan abort, uid %u\n", uid); 1554d1e879ecSMiri Korenblit 1555d1e879ecSMiri Korenblit ret = iwl_mld_scan_send_abort_cmd_status(mld, uid, &status); 1556d1e879ecSMiri Korenblit 1557d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan abort: ret=%d status=%u\n", ret, status); 1558d1e879ecSMiri Korenblit 1559d1e879ecSMiri Korenblit /* We don't need to wait to scan complete in the following cases: 1560d1e879ecSMiri Korenblit * 1. Driver failed to send the scan abort cmd. 1561d1e879ecSMiri Korenblit * 2. The FW is no longer familiar with the scan that needs to be 1562d1e879ecSMiri Korenblit * stopped. It is expected that the scan complete notification was 1563d1e879ecSMiri Korenblit * already received but not yet processed. 1564d1e879ecSMiri Korenblit * 1565d1e879ecSMiri Korenblit * In both cases the flow should continue similar to the case that the 1566d1e879ecSMiri Korenblit * scan was really aborted. 1567d1e879ecSMiri Korenblit */ 1568d1e879ecSMiri Korenblit if (ret || status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND) 1569d1e879ecSMiri Korenblit *wait = false; 1570d1e879ecSMiri Korenblit 1571d1e879ecSMiri Korenblit return ret; 1572d1e879ecSMiri Korenblit } 1573d1e879ecSMiri Korenblit 1574d1e879ecSMiri Korenblit static int 1575d1e879ecSMiri Korenblit iwl_mld_scan_stop_wait(struct iwl_mld *mld, int type, int uid) 1576d1e879ecSMiri Korenblit { 1577d1e879ecSMiri Korenblit struct iwl_notification_wait wait_scan_done; 1578d1e879ecSMiri Korenblit static const u16 scan_comp_notif[] = { SCAN_COMPLETE_UMAC }; 1579d1e879ecSMiri Korenblit bool wait = true; 1580d1e879ecSMiri Korenblit int ret; 1581d1e879ecSMiri Korenblit 1582d1e879ecSMiri Korenblit iwl_init_notification_wait(&mld->notif_wait, &wait_scan_done, 1583d1e879ecSMiri Korenblit scan_comp_notif, 1584d1e879ecSMiri Korenblit ARRAY_SIZE(scan_comp_notif), 1585d1e879ecSMiri Korenblit NULL, NULL); 1586d1e879ecSMiri Korenblit 1587d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Preparing to stop scan, type=%x\n", type); 1588d1e879ecSMiri Korenblit 1589d1e879ecSMiri Korenblit ret = iwl_mld_scan_abort(mld, type, uid, &wait); 1590d1e879ecSMiri Korenblit if (ret) { 1591d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "couldn't stop scan type=%d\n", type); 1592d1e879ecSMiri Korenblit goto return_no_wait; 1593d1e879ecSMiri Korenblit } 1594d1e879ecSMiri Korenblit 1595d1e879ecSMiri Korenblit if (!wait) { 1596d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "no need to wait for scan type=%d\n", type); 1597d1e879ecSMiri Korenblit goto return_no_wait; 1598d1e879ecSMiri Korenblit } 1599d1e879ecSMiri Korenblit 1600d1e879ecSMiri Korenblit return iwl_wait_notification(&mld->notif_wait, &wait_scan_done, HZ); 1601d1e879ecSMiri Korenblit 1602d1e879ecSMiri Korenblit return_no_wait: 1603d1e879ecSMiri Korenblit iwl_remove_notification(&mld->notif_wait, &wait_scan_done); 1604d1e879ecSMiri Korenblit return ret; 1605d1e879ecSMiri Korenblit } 1606d1e879ecSMiri Korenblit 1607d1e879ecSMiri Korenblit int iwl_mld_sched_scan_start(struct iwl_mld *mld, 1608d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1609d1e879ecSMiri Korenblit struct cfg80211_sched_scan_request *req, 1610d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies, 1611d1e879ecSMiri Korenblit int type) 1612d1e879ecSMiri Korenblit { 1613d1e879ecSMiri Korenblit struct iwl_host_cmd hcmd = { 1614d1e879ecSMiri Korenblit .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), 1615d1e879ecSMiri Korenblit .len = { mld->scan.cmd_size, }, 1616d1e879ecSMiri Korenblit .data = { mld->scan.cmd, }, 1617d1e879ecSMiri Korenblit .dataflags = { IWL_HCMD_DFL_NOCOPY, }, 1618d1e879ecSMiri Korenblit }; 1619d1e879ecSMiri Korenblit struct iwl_mld_scan_params params = {}; 1620d1e879ecSMiri Korenblit struct iwl_mld_scan_iter_data scan_iter_data = { 1621d1e879ecSMiri Korenblit .current_vif = vif, 1622d1e879ecSMiri Korenblit }; 1623d1e879ecSMiri Korenblit bool non_psc_included = false; 1624d1e879ecSMiri Korenblit int ret, uid; 1625d1e879ecSMiri Korenblit 1626d1e879ecSMiri Korenblit /* we should have failed registration if scan_cmd was NULL */ 1627d1e879ecSMiri Korenblit if (WARN_ON(!mld->scan.cmd)) 1628d1e879ecSMiri Korenblit return -ENOMEM; 1629d1e879ecSMiri Korenblit 1630d1e879ecSMiri Korenblit /* FW supports only a single periodic scan */ 1631d1e879ecSMiri Korenblit if (mld->scan.status & (IWL_MLD_SCAN_SCHED | IWL_MLD_SCAN_NETDETECT)) 1632d1e879ecSMiri Korenblit return -EBUSY; 1633d1e879ecSMiri Korenblit 1634d1e879ecSMiri Korenblit ieee80211_iterate_active_interfaces_mtx(mld->hw, 1635d1e879ecSMiri Korenblit IEEE80211_IFACE_ITER_NORMAL, 1636d1e879ecSMiri Korenblit iwl_mld_scan_iterator, 1637d1e879ecSMiri Korenblit &scan_iter_data); 1638d1e879ecSMiri Korenblit 1639d1e879ecSMiri Korenblit params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); 1640d1e879ecSMiri Korenblit params.flags = req->flags; 1641d1e879ecSMiri Korenblit params.n_ssids = req->n_ssids; 1642d1e879ecSMiri Korenblit params.ssids = req->ssids; 1643d1e879ecSMiri Korenblit params.n_channels = req->n_channels; 1644d1e879ecSMiri Korenblit params.channels = req->channels; 1645d1e879ecSMiri Korenblit params.mac_addr = req->mac_addr; 1646d1e879ecSMiri Korenblit params.mac_addr_mask = req->mac_addr_mask; 1647d1e879ecSMiri Korenblit params.no_cck = false; 1648d1e879ecSMiri Korenblit params.pass_all = iwl_mld_scan_pass_all(mld, req); 1649d1e879ecSMiri Korenblit params.n_match_sets = req->n_match_sets; 1650d1e879ecSMiri Korenblit params.match_sets = req->match_sets; 1651d1e879ecSMiri Korenblit params.n_scan_plans = req->n_scan_plans; 1652d1e879ecSMiri Korenblit params.scan_plans = req->scan_plans; 1653d1e879ecSMiri Korenblit /* TODO: CDB - per-band flag */ 1654d1e879ecSMiri Korenblit params.respect_p2p_go = 1655d1e879ecSMiri Korenblit iwl_mld_get_respect_p2p_go(mld, vif, 1656d1e879ecSMiri Korenblit scan_iter_data.global_low_latency); 1657d1e879ecSMiri Korenblit 1658d1e879ecSMiri Korenblit /* UMAC scan supports up to 16-bit delays, trim it down to 16-bits */ 1659d1e879ecSMiri Korenblit params.delay = req->delay > U16_MAX ? U16_MAX : req->delay; 1660d1e879ecSMiri Korenblit 1661d1e879ecSMiri Korenblit eth_broadcast_addr(params.bssid); 1662d1e879ecSMiri Korenblit 1663d1e879ecSMiri Korenblit ret = iwl_mld_config_sched_scan_profiles(mld, req); 1664d1e879ecSMiri Korenblit if (ret) 1665d1e879ecSMiri Korenblit return ret; 1666d1e879ecSMiri Korenblit 1667d1e879ecSMiri Korenblit iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); 1668d1e879ecSMiri Korenblit 1669d1e879ecSMiri Korenblit ret = iwl_mld_sched_scan_handle_non_psc_channels(¶ms, 1670d1e879ecSMiri Korenblit &non_psc_included); 1671d1e879ecSMiri Korenblit if (ret) 1672d1e879ecSMiri Korenblit goto out; 1673d1e879ecSMiri Korenblit 1674d1e879ecSMiri Korenblit if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, params.n_channels)) { 1675d1e879ecSMiri Korenblit ret = -ENOBUFS; 1676d1e879ecSMiri Korenblit goto out; 1677d1e879ecSMiri Korenblit } 1678d1e879ecSMiri Korenblit 1679d1e879ecSMiri Korenblit uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, type, 1680d1e879ecSMiri Korenblit scan_iter_data.global_low_latency); 1681d1e879ecSMiri Korenblit if (uid < 0) { 1682d1e879ecSMiri Korenblit ret = uid; 1683d1e879ecSMiri Korenblit goto out; 1684d1e879ecSMiri Korenblit } 1685d1e879ecSMiri Korenblit 1686d1e879ecSMiri Korenblit ret = iwl_mld_send_cmd(mld, &hcmd); 1687d1e879ecSMiri Korenblit if (!ret) { 1688d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1689d1e879ecSMiri Korenblit "Sched scan request send success: type=%u, uid=%u\n", 1690d1e879ecSMiri Korenblit type, uid); 1691d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = type; 1692d1e879ecSMiri Korenblit mld->scan.status |= type; 1693d1e879ecSMiri Korenblit } else { 1694d1e879ecSMiri Korenblit IWL_ERR(mld, "Sched scan failed! ret %d\n", ret); 1695d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; 1696d1e879ecSMiri Korenblit } 1697d1e879ecSMiri Korenblit 1698d1e879ecSMiri Korenblit out: 1699d1e879ecSMiri Korenblit if (non_psc_included) 1700d1e879ecSMiri Korenblit kfree(params.channels); 1701d1e879ecSMiri Korenblit return ret; 1702d1e879ecSMiri Korenblit } 1703d1e879ecSMiri Korenblit 1704d1e879ecSMiri Korenblit int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify) 1705d1e879ecSMiri Korenblit { 1706d1e879ecSMiri Korenblit int uid, ret; 1707d1e879ecSMiri Korenblit 1708d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1709d1e879ecSMiri Korenblit "Request to stop scan: type=0x%x, status=0x%x\n", 1710d1e879ecSMiri Korenblit type, mld->scan.status); 1711d1e879ecSMiri Korenblit 1712d1e879ecSMiri Korenblit if (!(mld->scan.status & type)) 1713d1e879ecSMiri Korenblit return 0; 1714d1e879ecSMiri Korenblit 1715d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, type); 1716d1e879ecSMiri Korenblit /* must be valid, we just checked it's running */ 1717d1e879ecSMiri Korenblit if (WARN_ON_ONCE(uid < 0)) 1718d1e879ecSMiri Korenblit return uid; 1719d1e879ecSMiri Korenblit 1720d1e879ecSMiri Korenblit ret = iwl_mld_scan_stop_wait(mld, type, uid); 1721d1e879ecSMiri Korenblit if (ret) 1722d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Failed to stop scan\n"); 1723d1e879ecSMiri Korenblit 1724d1e879ecSMiri Korenblit /* Clear the scan status so the next scan requests will 1725d1e879ecSMiri Korenblit * succeed and mark the scan as stopping, so that the Rx 1726d1e879ecSMiri Korenblit * handler doesn't do anything, as the scan was stopped from 1727d1e879ecSMiri Korenblit * above. Also remove the handler to not notify mac80211 1728d1e879ecSMiri Korenblit * erroneously after a new scan starts, for example. 1729d1e879ecSMiri Korenblit */ 1730d1e879ecSMiri Korenblit mld->scan.status &= ~type; 1731d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; 1732d1e879ecSMiri Korenblit iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_SCAN, 1733d1e879ecSMiri Korenblit uid); 1734d1e879ecSMiri Korenblit 1735d1e879ecSMiri Korenblit if (type == IWL_MLD_SCAN_REGULAR) { 1736d1e879ecSMiri Korenblit if (notify) { 1737d1e879ecSMiri Korenblit struct cfg80211_scan_info info = { 1738d1e879ecSMiri Korenblit .aborted = true, 1739d1e879ecSMiri Korenblit }; 1740d1e879ecSMiri Korenblit 1741d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info); 1742d1e879ecSMiri Korenblit } 1743d1e879ecSMiri Korenblit } else if (notify) { 1744d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw); 1745d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; 1746d1e879ecSMiri Korenblit } 1747d1e879ecSMiri Korenblit 1748d1e879ecSMiri Korenblit return ret; 1749d1e879ecSMiri Korenblit } 1750d1e879ecSMiri Korenblit 1751d1e879ecSMiri Korenblit int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, 1752d1e879ecSMiri Korenblit struct cfg80211_scan_request *req, 1753d1e879ecSMiri Korenblit struct ieee80211_scan_ies *ies) 1754d1e879ecSMiri Korenblit { 1755d1e879ecSMiri Korenblit return _iwl_mld_single_scan_start(mld, vif, req, ies, 1756d1e879ecSMiri Korenblit IWL_MLD_SCAN_REGULAR); 1757d1e879ecSMiri Korenblit } 1758d1e879ecSMiri Korenblit 1759d1e879ecSMiri Korenblit static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, 1760d1e879ecSMiri Korenblit struct ieee80211_vif *vif, 1761d1e879ecSMiri Korenblit struct ieee80211_channel **channels, 1762d1e879ecSMiri Korenblit size_t n_channels) 1763d1e879ecSMiri Korenblit { 1764d1e879ecSMiri Korenblit struct cfg80211_scan_request *req __free(kfree) = NULL; 1765d1e879ecSMiri Korenblit struct ieee80211_scan_ies ies = {}; 1766d1e879ecSMiri Korenblit size_t size; 1767d1e879ecSMiri Korenblit int ret; 1768d1e879ecSMiri Korenblit 1769d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Starting Internal MLO scan: n_channels=%zu\n", 1770d1e879ecSMiri Korenblit n_channels); 1771d1e879ecSMiri Korenblit 1772d1e879ecSMiri Korenblit size = struct_size(req, channels, n_channels); 1773d1e879ecSMiri Korenblit req = kzalloc(size, GFP_KERNEL); 1774d1e879ecSMiri Korenblit if (!req) 1775d1e879ecSMiri Korenblit return; 1776d1e879ecSMiri Korenblit 1777d1e879ecSMiri Korenblit /* set the requested channels */ 1778d1e879ecSMiri Korenblit for (int i = 0; i < n_channels; i++) 1779d1e879ecSMiri Korenblit req->channels[i] = channels[i]; 1780d1e879ecSMiri Korenblit 1781d1e879ecSMiri Korenblit req->n_channels = n_channels; 1782d1e879ecSMiri Korenblit 1783d1e879ecSMiri Korenblit /* set the rates */ 1784d1e879ecSMiri Korenblit for (int i = 0; i < NUM_NL80211_BANDS; i++) 1785d1e879ecSMiri Korenblit if (mld->wiphy->bands[i]) 1786d1e879ecSMiri Korenblit req->rates[i] = 1787d1e879ecSMiri Korenblit (1 << mld->wiphy->bands[i]->n_bitrates) - 1; 1788d1e879ecSMiri Korenblit 1789d1e879ecSMiri Korenblit req->wdev = ieee80211_vif_to_wdev(vif); 1790d1e879ecSMiri Korenblit req->wiphy = mld->wiphy; 1791d1e879ecSMiri Korenblit req->scan_start = jiffies; 1792d1e879ecSMiri Korenblit req->tsf_report_link_id = -1; 1793d1e879ecSMiri Korenblit 1794d1e879ecSMiri Korenblit ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, 1795d1e879ecSMiri Korenblit IWL_MLD_SCAN_INT_MLO); 1796d1e879ecSMiri Korenblit 1797*9324731bSMiri Korenblit if (!ret) 1798*9324731bSMiri Korenblit mld->scan.last_mlo_scan_time = ktime_get_boottime_ns(); 1799*9324731bSMiri Korenblit 1800d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); 1801d1e879ecSMiri Korenblit } 1802d1e879ecSMiri Korenblit 1803d1e879ecSMiri Korenblit void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif) 1804d1e879ecSMiri Korenblit { 1805d1e879ecSMiri Korenblit struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS]; 1806d1e879ecSMiri Korenblit unsigned long usable_links = ieee80211_vif_usable_links(vif); 1807d1e879ecSMiri Korenblit size_t n_channels = 0; 1808d1e879ecSMiri Korenblit u8 link_id; 1809d1e879ecSMiri Korenblit 1810d1e879ecSMiri Korenblit lockdep_assert_wiphy(mld->wiphy); 1811d1e879ecSMiri Korenblit 1812d1e879ecSMiri Korenblit if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) || 1813d1e879ecSMiri Korenblit hweight16(vif->valid_links) == 1) 1814d1e879ecSMiri Korenblit return; 1815d1e879ecSMiri Korenblit 1816d1e879ecSMiri Korenblit if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) { 1817d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan is already running\n"); 1818d1e879ecSMiri Korenblit return; 1819d1e879ecSMiri Korenblit } 1820d1e879ecSMiri Korenblit 1821d1e879ecSMiri Korenblit for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { 1822d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf = 1823d1e879ecSMiri Korenblit link_conf_dereference_check(vif, link_id); 1824d1e879ecSMiri Korenblit 1825d1e879ecSMiri Korenblit if (WARN_ON_ONCE(!link_conf)) 1826d1e879ecSMiri Korenblit continue; 1827d1e879ecSMiri Korenblit 1828d1e879ecSMiri Korenblit channels[n_channels++] = link_conf->chanreq.oper.chan; 1829d1e879ecSMiri Korenblit } 1830d1e879ecSMiri Korenblit 1831d1e879ecSMiri Korenblit if (!n_channels) 1832d1e879ecSMiri Korenblit return; 1833d1e879ecSMiri Korenblit 1834d1e879ecSMiri Korenblit iwl_mld_int_mlo_scan_start(mld, vif, channels, n_channels); 1835d1e879ecSMiri Korenblit } 1836d1e879ecSMiri Korenblit 1837d1e879ecSMiri Korenblit void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld, 1838d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 1839d1e879ecSMiri Korenblit { 1840d1e879ecSMiri Korenblit struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; 1841d1e879ecSMiri Korenblit u32 uid = __le32_to_cpu(notif->uid); 1842d1e879ecSMiri Korenblit 1843d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), 1844d1e879ecSMiri Korenblit "FW reports out-of-range scan UID %d\n", uid)) 1845d1e879ecSMiri Korenblit return; 1846d1e879ecSMiri Korenblit 1847d1e879ecSMiri Korenblit if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) 1848d1e879ecSMiri Korenblit mld->scan.start_tsf = le64_to_cpu(notif->start_tsf); 1849d1e879ecSMiri Korenblit 1850d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1851d1e879ecSMiri Korenblit "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n", 1852d1e879ecSMiri Korenblit notif->status, notif->scanned_channels); 1853d1e879ecSMiri Korenblit 1854d1e879ecSMiri Korenblit if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_FOUND) { 1855d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Pass all scheduled scan results found\n"); 1856d1e879ecSMiri Korenblit ieee80211_sched_scan_results(mld->hw); 1857d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; 1858d1e879ecSMiri Korenblit } 1859d1e879ecSMiri Korenblit 1860d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1861d1e879ecSMiri Korenblit "UMAC Scan iteration complete: scan started at %llu (TSF)\n", 1862d1e879ecSMiri Korenblit le64_to_cpu(notif->start_tsf)); 1863d1e879ecSMiri Korenblit } 1864d1e879ecSMiri Korenblit 1865d1e879ecSMiri Korenblit void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, 1866d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 1867d1e879ecSMiri Korenblit { 1868d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scheduled scan results\n"); 1869d1e879ecSMiri Korenblit ieee80211_sched_scan_results(mld->hw); 1870d1e879ecSMiri Korenblit } 1871d1e879ecSMiri Korenblit 1872d1e879ecSMiri Korenblit void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, 1873d1e879ecSMiri Korenblit struct iwl_rx_packet *pkt) 1874d1e879ecSMiri Korenblit { 1875d1e879ecSMiri Korenblit struct iwl_umac_scan_complete *notif = (void *)pkt->data; 1876d1e879ecSMiri Korenblit bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); 1877d1e879ecSMiri Korenblit u32 uid = __le32_to_cpu(notif->uid); 1878d1e879ecSMiri Korenblit 1879d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), 1880d1e879ecSMiri Korenblit "FW reports out-of-range scan UID %d\n", uid)) 1881d1e879ecSMiri Korenblit return; 1882d1e879ecSMiri Korenblit 1883d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1884d1e879ecSMiri Korenblit "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n", 1885d1e879ecSMiri Korenblit uid, mld->scan.uid_status[uid], 1886d1e879ecSMiri Korenblit notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? 1887d1e879ecSMiri Korenblit "completed" : "aborted", 1888d1e879ecSMiri Korenblit iwl_mld_scan_ebs_status_str(notif->ebs_status)); 1889d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan completed: scan_status=0x%x\n", 1890d1e879ecSMiri Korenblit mld->scan.status); 1891d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, 1892d1e879ecSMiri Korenblit "Scan completed: line=%u, iter=%u, elapsed time=%u\n", 1893d1e879ecSMiri Korenblit notif->last_schedule, notif->last_iter, 1894d1e879ecSMiri Korenblit __le32_to_cpu(notif->time_from_last_iter)); 1895d1e879ecSMiri Korenblit 1896d1e879ecSMiri Korenblit if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status), 1897d1e879ecSMiri Korenblit "FW reports scan UID %d we didn't trigger\n", uid)) 1898d1e879ecSMiri Korenblit return; 1899d1e879ecSMiri Korenblit 1900d1e879ecSMiri Korenblit /* if the scan is already stopping, we don't need to notify mac80211 */ 1901d1e879ecSMiri Korenblit if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) { 1902d1e879ecSMiri Korenblit struct cfg80211_scan_info info = { 1903d1e879ecSMiri Korenblit .aborted = aborted, 1904d1e879ecSMiri Korenblit .scan_start_tsf = mld->scan.start_tsf, 1905d1e879ecSMiri Korenblit }; 1906d1e879ecSMiri Korenblit int fw_link_id = mld->scan.fw_link_id; 1907d1e879ecSMiri Korenblit struct ieee80211_bss_conf *link_conf = NULL; 1908d1e879ecSMiri Korenblit 1909d1e879ecSMiri Korenblit if (fw_link_id != IWL_MLD_INVALID_FW_ID) 1910d1e879ecSMiri Korenblit link_conf = 1911d1e879ecSMiri Korenblit wiphy_dereference(mld->wiphy, 1912d1e879ecSMiri Korenblit mld->fw_id_to_bss_conf[fw_link_id]); 1913d1e879ecSMiri Korenblit 1914d1e879ecSMiri Korenblit /* It is possible that by the time the scan is complete the 1915d1e879ecSMiri Korenblit * link was already removed and is not valid. 1916d1e879ecSMiri Korenblit */ 1917d1e879ecSMiri Korenblit if (link_conf) 1918d1e879ecSMiri Korenblit ether_addr_copy(info.tsf_bssid, link_conf->bssid); 1919d1e879ecSMiri Korenblit else 1920d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n"); 1921d1e879ecSMiri Korenblit 1922d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info); 1923d1e879ecSMiri Korenblit } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) { 1924d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw); 1925d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; 1926d1e879ecSMiri Korenblit } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) { 1927d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n"); 1928d1e879ecSMiri Korenblit 1929d1e879ecSMiri Korenblit /* 1930d1e879ecSMiri Korenblit * We limit link selection to internal MLO scans as otherwise 1931d1e879ecSMiri Korenblit * we do not know whether all channels were covered. 1932d1e879ecSMiri Korenblit */ 1933d1e879ecSMiri Korenblit iwl_mld_select_links(mld); 1934d1e879ecSMiri Korenblit } 1935d1e879ecSMiri Korenblit 1936d1e879ecSMiri Korenblit mld->scan.status &= ~mld->scan.uid_status[uid]; 1937d1e879ecSMiri Korenblit 1938d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Scan completed: after update: scan_status=0x%x\n", 1939d1e879ecSMiri Korenblit mld->scan.status); 1940d1e879ecSMiri Korenblit 1941d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; 1942d1e879ecSMiri Korenblit 1943d1e879ecSMiri Korenblit if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && 1944d1e879ecSMiri Korenblit notif->ebs_status != IWL_SCAN_EBS_INACTIVE) 1945d1e879ecSMiri Korenblit mld->scan.last_ebs_failed = true; 1946d1e879ecSMiri Korenblit } 1947d1e879ecSMiri Korenblit 1948d1e879ecSMiri Korenblit /* This function is used in nic restart flow, to inform mac80211 about scans 1949d1e879ecSMiri Korenblit * that were aborted by restart flow or by an assert. 1950d1e879ecSMiri Korenblit */ 1951d1e879ecSMiri Korenblit void iwl_mld_report_scan_aborted(struct iwl_mld *mld) 1952d1e879ecSMiri Korenblit { 1953d1e879ecSMiri Korenblit int uid; 1954d1e879ecSMiri Korenblit 1955d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_REGULAR); 1956d1e879ecSMiri Korenblit if (uid >= 0) { 1957d1e879ecSMiri Korenblit struct cfg80211_scan_info info = { 1958d1e879ecSMiri Korenblit .aborted = true, 1959d1e879ecSMiri Korenblit }; 1960d1e879ecSMiri Korenblit 1961d1e879ecSMiri Korenblit ieee80211_scan_completed(mld->hw, &info); 1962d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; 1963d1e879ecSMiri Korenblit } 1964d1e879ecSMiri Korenblit 1965d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_SCHED); 1966d1e879ecSMiri Korenblit if (uid >= 0) { 1967d1e879ecSMiri Korenblit mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; 1968d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; 1969d1e879ecSMiri Korenblit 1970d1e879ecSMiri Korenblit /* sched scan will be restarted by mac80211 in reconfig. 1971d1e879ecSMiri Korenblit * report to mac80211 that sched scan stopped only if we won't 1972d1e879ecSMiri Korenblit * restart the firmware. 1973d1e879ecSMiri Korenblit */ 1974d1e879ecSMiri Korenblit if (!iwlwifi_mod_params.fw_restart) 1975d1e879ecSMiri Korenblit ieee80211_sched_scan_stopped(mld->hw); 1976d1e879ecSMiri Korenblit } 1977d1e879ecSMiri Korenblit 1978d1e879ecSMiri Korenblit uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_INT_MLO); 1979d1e879ecSMiri Korenblit if (uid >= 0) { 1980d1e879ecSMiri Korenblit IWL_DEBUG_SCAN(mld, "Internal MLO scan aborted\n"); 1981d1e879ecSMiri Korenblit mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; 1982d1e879ecSMiri Korenblit } 1983d1e879ecSMiri Korenblit 1984d1e879ecSMiri Korenblit BUILD_BUG_ON(IWL_MLD_SCAN_NONE != 0); 1985d1e879ecSMiri Korenblit memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); 1986d1e879ecSMiri Korenblit } 1987d1e879ecSMiri Korenblit 1988d1e879ecSMiri Korenblit int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) 1989d1e879ecSMiri Korenblit { 1990d1e879ecSMiri Korenblit u8 scan_cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, SCAN_REQ_UMAC, 1991d1e879ecSMiri Korenblit IWL_FW_CMD_VER_UNKNOWN); 1992d1e879ecSMiri Korenblit size_t scan_cmd_size; 1993d1e879ecSMiri Korenblit 1994d1e879ecSMiri Korenblit if (scan_cmd_ver == 17) { 1995d1e879ecSMiri Korenblit scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17); 1996d1e879ecSMiri Korenblit } else { 1997d1e879ecSMiri Korenblit IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver); 1998d1e879ecSMiri Korenblit return -EINVAL; 1999d1e879ecSMiri Korenblit } 2000d1e879ecSMiri Korenblit 2001d1e879ecSMiri Korenblit mld->scan.cmd = kmalloc(scan_cmd_size, GFP_KERNEL); 2002d1e879ecSMiri Korenblit if (!mld->scan.cmd) 2003d1e879ecSMiri Korenblit return -ENOMEM; 2004d1e879ecSMiri Korenblit 2005d1e879ecSMiri Korenblit mld->scan.cmd_size = scan_cmd_size; 2006d1e879ecSMiri Korenblit 2007d1e879ecSMiri Korenblit return 0; 2008d1e879ecSMiri Korenblit } 2009