From 1c055e55a242a33e574e48be530e06770a210dcd Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 19 Feb 2024 03:26:26 +0000 Subject: [PATCH] add r8169 read mac form eeprom --- kernel/drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 1110 +++++++++++++++++++++++++++++++++++++++++++++++----------- 1 files changed, 894 insertions(+), 216 deletions(-) diff --git a/kernel/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/kernel/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 16b614c..17b9925 100644 --- a/kernel/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/kernel/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -8,7 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -18,9 +18,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program * * The full GNU General Public License is included in this distribution * in the file called COPYING. @@ -34,7 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -80,16 +77,28 @@ #define IWL_SCAN_DWELL_FRAGMENTED 44 #define IWL_SCAN_DWELL_EXTENDED 90 #define IWL_SCAN_NUM_OF_FRAGS 3 - +#define IWL_SCAN_LAST_2_4_CHN 14 /* adaptive dwell max budget time [TU] for full scan */ #define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 /* adaptive dwell max budget time [TU] for directed scan */ #define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100 -/* adaptive dwell default APs number */ -#define IWL_SCAN_ADWELL_DEFAULT_N_APS 2 +/* adaptive dwell default high band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8 +/* adaptive dwell default low band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2 /* adaptive dwell default APs number in social channels (1, 6, 11) */ #define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 +/* number of scan channels */ +#define IWL_SCAN_NUM_CHANNELS 112 +/* adaptive dwell number of APs override mask for p2p friendly GO */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20) +/* adaptive dwell number of APs override mask for social channels */ +#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21) +/* adaptive dwell number of APs override for p2p friendly GO channels */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10 +/* adaptive dwell number of APs override for social channels */ +#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2 struct iwl_mvm_scan_timing_params { u32 suspend_time; @@ -113,6 +122,10 @@ .suspend_time = 95, .max_out_time = 44, }, + [IWL_SCAN_TYPE_FAST_BALANCE] = { + .suspend_time = 30, + .max_out_time = 37, + }, }; struct iwl_mvm_scan_params { @@ -134,7 +147,7 @@ struct cfg80211_match_set *match_sets; int n_scan_plans; struct cfg80211_sched_scan_plan *scan_plans; - u32 measurement_dwell; + bool iter_notif; }; static inline void *iwl_mvm_get_scan_req_umac_data(struct iwl_mvm *mvm) @@ -190,23 +203,13 @@ return cpu_to_le16(rx_chain); } -static __le32 iwl_mvm_scan_rxon_flags(enum nl80211_band band) -{ - if (band == NL80211_BAND_2GHZ) - return cpu_to_le32(PHY_BAND_24); - else - return cpu_to_le32(PHY_BAND_5); -} - static inline __le32 iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum nl80211_band band, bool no_cck) { u32 tx_ant; - mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->scan_last_antenna_idx); + iwl_mvm_toggle_tx_ant(mvm, &mvm->scan_last_antenna_idx); tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; if (band == NL80211_BAND_2GHZ && !no_cck) @@ -238,8 +241,32 @@ return mvm->tcm.result.band_load[band]; } +struct iwl_is_dcm_with_go_iterator_data { + struct ieee80211_vif *current_vif; + bool is_dcm_with_p2p_go; +}; + +static void iwl_mvm_is_dcm_with_go_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_is_dcm_with_go_iterator_data *data = _data; + struct iwl_mvm_vif *other_mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif *curr_mvmvif = + iwl_mvm_vif_from_mac80211(data->current_vif); + + /* exclude the given vif */ + if (vif == data->current_vif) + return; + + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && + other_mvmvif->phy_ctxt && curr_mvmvif->phy_ctxt && + other_mvmvif->phy_ctxt->id != curr_mvmvif->phy_ctxt->id) + data->is_dcm_with_p2p_go = true; +} + static enum -iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device, +iwl_mvm_scan_type _iwl_mvm_get_scan_type(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, enum iwl_mvm_traffic_load load, bool low_latency) { @@ -252,9 +279,30 @@ if (!global_cnt) return IWL_SCAN_TYPE_UNASSOC; - if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && !p2p_device && - fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) - return IWL_SCAN_TYPE_FRAGMENTED; + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_FRAGMENTED_SCAN)) { + if ((load == IWL_MVM_TRAFFIC_HIGH || low_latency) && + (!vif || vif->type != NL80211_IFTYPE_P2P_DEVICE)) + return IWL_SCAN_TYPE_FRAGMENTED; + + /* in case of DCM with GO where BSS DTIM interval < 220msec + * set all scan requests as fast-balance scan + * */ + if (vif && vif->type == NL80211_IFTYPE_STATION && + vif->bss_conf.dtim_period < 220) { + struct iwl_is_dcm_with_go_iterator_data data = { + .current_vif = vif, + .is_dcm_with_p2p_go = false, + }; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_is_dcm_with_go_iterator, + &data); + if (data.is_dcm_with_p2p_go) + return IWL_SCAN_TYPE_FAST_BALANCE; + } + } if (load >= IWL_MVM_TRAFFIC_MEDIUM || low_latency) return IWL_SCAN_TYPE_MILD; @@ -263,7 +311,8 @@ } static enum -iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, bool p2p_device) +iwl_mvm_scan_type iwl_mvm_get_scan_type(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { enum iwl_mvm_traffic_load load; bool low_latency; @@ -271,12 +320,12 @@ load = iwl_mvm_get_traffic_load(mvm); low_latency = iwl_mvm_low_latency(mvm); - return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency); + return _iwl_mvm_get_scan_type(mvm, vif, load, low_latency); } static enum iwl_mvm_scan_type iwl_mvm_get_scan_type_band(struct iwl_mvm *mvm, - bool p2p_device, + struct ieee80211_vif *vif, enum nl80211_band band) { enum iwl_mvm_traffic_load load; @@ -285,34 +334,7 @@ load = iwl_mvm_get_traffic_load_band(mvm, band); low_latency = iwl_mvm_low_latency_band(mvm, band); - return _iwl_mvm_get_scan_type(mvm, p2p_device, load, low_latency); -} - -static int -iwl_mvm_get_measurement_dwell(struct iwl_mvm *mvm, - struct cfg80211_scan_request *req, - struct iwl_mvm_scan_params *params) -{ - u32 duration = scan_timing[params->type].max_out_time; - - if (!req->duration) - return 0; - - if (iwl_mvm_is_cdb_supported(mvm)) { - u32 hb_time = scan_timing[params->hb_type].max_out_time; - - duration = min_t(u32, duration, hb_time); - } - - if (req->duration_mandatory && req->duration > duration) { - IWL_DEBUG_SCAN(mvm, - "Measurement scan - too long dwell %hu (max out time %u)\n", - req->duration, - duration); - return -EOPNOTSUPP; - } - - return min_t(u32, (u32)req->duration, duration); + return _iwl_mvm_get_scan_type(mvm, vif, load, low_latency); } static inline bool iwl_mvm_rrm_scan_needed(struct iwl_mvm *mvm) @@ -465,7 +487,6 @@ mvm->scan_status &= ~IWL_MVM_SCAN_REGULAR; ieee80211_scan_completed(mvm->hw, &info); - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); cancel_delayed_work(&mvm->scan_timeout_dwork); iwl_mvm_resume_tcm(mvm); } else { @@ -501,6 +522,7 @@ { int i, j; int index; + u32 tmp_bitmap = 0; /* * copy SSIDs from match list. @@ -520,7 +542,6 @@ } /* add SSIDs from scan SSID list */ - *ssid_bitmap = 0; for (j = params->n_ssids - 1; j >= 0 && i < PROBE_OPTION_MAX; i++, j--) { @@ -532,11 +553,13 @@ ssids[i].len = params->ssids[j].ssid_len; memcpy(ssids[i].ssid, params->ssids[j].ssid, ssids[i].len); - *ssid_bitmap |= BIT(i); + tmp_bitmap |= BIT(i); } else { - *ssid_bitmap |= BIT(index); + tmp_bitmap |= BIT(index); } } + if (ssid_bitmap) + *ssid_bitmap = tmp_bitmap; } static int @@ -544,51 +567,65 @@ struct cfg80211_sched_scan_request *req) { struct iwl_scan_offload_profile *profile; - struct iwl_scan_offload_profile_cfg *profile_cfg; - struct iwl_scan_offload_blacklist *blacklist; + struct iwl_scan_offload_profile_cfg_v1 *profile_cfg_v1; + struct iwl_scan_offload_blocklist *blocklist; + struct iwl_scan_offload_profile_cfg_data *data; + int max_profiles = iwl_umac_scan_get_max_profiles(mvm->fw); + int profile_cfg_size = sizeof(*data) + + sizeof(*profile) * max_profiles; struct iwl_host_cmd cmd = { .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, - .len[1] = sizeof(*profile_cfg), + .len[1] = profile_cfg_size, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .dataflags[1] = IWL_HCMD_DFL_NOCOPY, }; - int blacklist_len; + int blocklist_len; int i; int ret; - if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) + if (WARN_ON(req->n_match_sets > max_profiles)) return -EIO; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) - blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; + blocklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; else - blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; + blocklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; - blacklist = kcalloc(blacklist_len, sizeof(*blacklist), GFP_KERNEL); - if (!blacklist) + blocklist = kcalloc(blocklist_len, sizeof(*blocklist), GFP_KERNEL); + if (!blocklist) return -ENOMEM; - profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); - if (!profile_cfg) { + profile_cfg_v1 = kzalloc(profile_cfg_size, GFP_KERNEL); + if (!profile_cfg_v1) { ret = -ENOMEM; - goto free_blacklist; + goto free_blocklist; } - cmd.data[0] = blacklist; - cmd.len[0] = sizeof(*blacklist) * blacklist_len; - cmd.data[1] = profile_cfg; + cmd.data[0] = blocklist; + cmd.len[0] = sizeof(*blocklist) * blocklist_len; + cmd.data[1] = profile_cfg_v1; - /* No blacklist configuration */ + /* if max_profile is MAX_PROFILES_V2, we have the new API */ + if (max_profiles == IWL_SCAN_MAX_PROFILES_V2) { + struct iwl_scan_offload_profile_cfg *profile_cfg = + (struct iwl_scan_offload_profile_cfg *)profile_cfg_v1; - profile_cfg->num_profiles = req->n_match_sets; - profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; - profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; + data = &profile_cfg->data; + } else { + data = &profile_cfg_v1->data; + } + + /* No blocklist configuration */ + data->num_profiles = req->n_match_sets; + data->active_clients = SCAN_CLIENT_SCHED_SCAN; + data->pass_match = SCAN_CLIENT_SCHED_SCAN; + data->match_notify = SCAN_CLIENT_SCHED_SCAN; + if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) - profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; + data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; for (i = 0; i < req->n_match_sets; i++) { - profile = &profile_cfg->profiles[i]; + profile = &profile_cfg_v1->profiles[i]; profile->ssid_index = i; /* Support any cipher and auth algorithm */ profile->unicast_cipher = 0xff; @@ -601,9 +638,9 @@ IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); ret = iwl_mvm_send_cmd(mvm, &cmd); - kfree(profile_cfg); -free_blacklist: - kfree(blacklist); + kfree(profile_cfg_v1); +free_blocklist: + kfree(blocklist); return ret; } @@ -661,14 +698,28 @@ tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, NL80211_BAND_2GHZ, no_cck); - tx_cmd[0].sta_id = mvm->aux_sta.sta_id; + + if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, + 0) < 12) { + tx_cmd[0].sta_id = mvm->aux_sta.sta_id; + tx_cmd[1].sta_id = mvm->aux_sta.sta_id; + + /* + * Fw doesn't use this sta anymore, pending deprecation via HOST API + * change + */ + } else { + tx_cmd[0].sta_id = 0xff; + tx_cmd[1].sta_id = 0xff; + } tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_BT_DIS); + tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, NL80211_BAND_5GHZ, no_cck); - tx_cmd[1].sta_id = mvm->aux_sta.sta_id; } static void @@ -836,22 +887,37 @@ struct ieee80211_vif *vif) { const struct iwl_ucode_capabilities *capa = &mvm->fw->ucode_capa; + bool low_latency; + + if (iwl_mvm_is_cdb_supported(mvm)) + low_latency = iwl_mvm_low_latency_band(mvm, NL80211_BAND_5GHZ); + else + low_latency = iwl_mvm_low_latency(mvm); /* We can only use EBS if: * 1. the feature is supported; * 2. the last EBS was successful; * 3. if only single scan, the single scan EBS API is supported; * 4. it's not a p2p find operation. + * 5. we are not in low latency mode, + * or if fragmented ebs is supported by the FW */ return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && mvm->last_ebs_successful && IWL_MVM_ENABLE_EBS && - vif->type != NL80211_IFTYPE_P2P_DEVICE); + vif->type != NL80211_IFTYPE_P2P_DEVICE && + (!low_latency || iwl_mvm_is_frag_ebs_supported(mvm))); } static inline bool iwl_mvm_is_regular_scan(struct iwl_mvm_scan_params *params) { return params->n_scan_plans == 1 && params->scan_plans[0].iterations == 1; +} + +static bool iwl_mvm_is_scan_fragmented(enum iwl_mvm_scan_type type) +{ + return (type == IWL_SCAN_TYPE_FRAGMENTED || + type == IWL_SCAN_TYPE_FAST_BALANCE); } static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, @@ -866,7 +932,7 @@ if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) + if (iwl_mvm_is_scan_fragmented(params->type)) flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED; if (iwl_mvm_rrm_scan_needed(mvm) && @@ -889,25 +955,35 @@ if (iwl_mvm_is_regular_scan(params) && vif->type != NL80211_IFTYPE_P2P_DEVICE && - params->type != IWL_SCAN_TYPE_FRAGMENTED) + !iwl_mvm_is_scan_fragmented(params->type)) flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL; return flags; +} + +static void +iwl_mvm_scan_set_legacy_probe_req(struct iwl_scan_probe_req_v1 *p_req, + struct iwl_scan_probe_req *src_p_req) +{ + int i; + + p_req->mac_header = src_p_req->mac_header; + for (i = 0; i < SCAN_NUM_BAND_PROBE_DATA_V_1; i++) + p_req->band_data[i] = src_p_req->band_data[i]; + p_req->common_data = src_p_req->common_data; + memcpy(p_req->buf, src_p_req->buf, sizeof(p_req->buf)); } static int iwl_mvm_scan_lmac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_scan_params *params) { struct iwl_scan_req_lmac *cmd = mvm->scan_cmd; - struct iwl_scan_probe_req *preq = + struct iwl_scan_probe_req_v1 *preq = (void *)(cmd->data + sizeof(struct iwl_scan_channel_cfg_lmac) * mvm->fw->ucode_capa.n_scan_channels); u32 ssid_bitmap = 0; int i; - - lockdep_assert_held(&mvm->mutex); - - memset(cmd, 0, ksize(cmd)); + u8 band; if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) return -EINVAL; @@ -923,7 +999,8 @@ cmd->scan_flags = cpu_to_le32(iwl_mvm_scan_lmac_flags(mvm, params, vif)); - cmd->flags = iwl_mvm_scan_rxon_flags(params->channels[0]->band); + band = iwl_mvm_phy_band_from_nl80211(params->channels[0]->band); + cmd->flags = cpu_to_le32(band); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP | MAC_FILTER_IN_BEACON); iwl_mvm_scan_fill_tx_cmd(mvm, cmd->tx_cmd, params->no_cck); @@ -969,7 +1046,7 @@ iwl_mvm_lmac_scan_cfg_channels(mvm, params->channels, params->n_channels, ssid_bitmap, cmd); - *preq = params->preq; + iwl_mvm_scan_set_legacy_probe_req(preq, ¶ms->preq); return 0; } @@ -1022,23 +1099,25 @@ dwell->extended = IWL_SCAN_DWELL_EXTENDED; } -static void iwl_mvm_fill_channels(struct iwl_mvm *mvm, u8 *channels) +static void iwl_mvm_fill_channels(struct iwl_mvm *mvm, u8 *channels, + u32 max_channels) { struct ieee80211_supported_band *band; int i, j = 0; band = &mvm->nvm_data->bands[NL80211_BAND_2GHZ]; - for (i = 0; i < band->n_channels; i++, j++) + for (i = 0; i < band->n_channels && j < max_channels; i++, j++) channels[j] = band->channels[i].hw_value; band = &mvm->nvm_data->bands[NL80211_BAND_5GHZ]; - for (i = 0; i < band->n_channels; i++, j++) + for (i = 0; i < band->n_channels && j < max_channels; i++, j++) channels[j] = band->channels[i].hw_value; } static void iwl_mvm_fill_scan_config_v1(struct iwl_mvm *mvm, void *config, - u32 flags, u8 channel_flags) + u32 flags, u8 channel_flags, + u32 max_channels) { - enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, false); + enum iwl_mvm_scan_type type = iwl_mvm_get_scan_type(mvm, NULL); struct iwl_scan_config_v1 *cfg = config; cfg->flags = cpu_to_le32(flags); @@ -1052,16 +1131,21 @@ memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); + /* This function should not be called when using ADD_STA ver >=12 */ + WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, 0) >= 12); + cfg->bcast_sta_id = mvm->aux_sta.sta_id; cfg->channel_flags = channel_flags; - iwl_mvm_fill_channels(mvm, cfg->channel_array); + iwl_mvm_fill_channels(mvm, cfg->channel_array, max_channels); } -static void iwl_mvm_fill_scan_config(struct iwl_mvm *mvm, void *config, - u32 flags, u8 channel_flags) +static void iwl_mvm_fill_scan_config_v2(struct iwl_mvm *mvm, void *config, + u32 flags, u8 channel_flags, + u32 max_channels) { - struct iwl_scan_config *cfg = config; + struct iwl_scan_config_v2 *cfg = config; cfg->flags = cpu_to_le32(flags); cfg->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); @@ -1071,9 +1155,9 @@ if (iwl_mvm_is_cdb_supported(mvm)) { enum iwl_mvm_scan_type lb_type, hb_type; - lb_type = iwl_mvm_get_scan_type_band(mvm, false, + lb_type = iwl_mvm_get_scan_type_band(mvm, NULL, NL80211_BAND_2GHZ); - hb_type = iwl_mvm_get_scan_type_band(mvm, false, + hb_type = iwl_mvm_get_scan_type_band(mvm, NULL, NL80211_BAND_5GHZ); cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] = @@ -1087,7 +1171,7 @@ cpu_to_le32(scan_timing[hb_type].suspend_time); } else { enum iwl_mvm_scan_type type = - iwl_mvm_get_scan_type(mvm, false); + iwl_mvm_get_scan_type(mvm, NULL); cfg->out_of_channel_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(scan_timing[type].max_out_time); @@ -1099,13 +1183,17 @@ memcpy(&cfg->mac_addr, &mvm->addresses[0].addr, ETH_ALEN); + /* This function should not be called when using ADD_STA ver >=12 */ + WARN_ON_ONCE(iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, 0) >= 12); + cfg->bcast_sta_id = mvm->aux_sta.sta_id; cfg->channel_flags = channel_flags; - iwl_mvm_fill_channels(mvm, cfg->channel_array); + iwl_mvm_fill_channels(mvm, cfg->channel_array, max_channels); } -int iwl_mvm_config_scan(struct iwl_mvm *mvm) +static int iwl_mvm_legacy_config_scan(struct iwl_mvm *mvm) { void *cfg; int ret, cmd_size; @@ -1121,23 +1209,23 @@ u8 channel_flags; if (WARN_ON(num_channels > mvm->fw->ucode_capa.n_scan_channels)) - return -ENOBUFS; + num_channels = mvm->fw->ucode_capa.n_scan_channels; if (iwl_mvm_is_cdb_supported(mvm)) { - type = iwl_mvm_get_scan_type_band(mvm, false, + type = iwl_mvm_get_scan_type_band(mvm, NULL, NL80211_BAND_2GHZ); - hb_type = iwl_mvm_get_scan_type_band(mvm, false, + hb_type = iwl_mvm_get_scan_type_band(mvm, NULL, NL80211_BAND_5GHZ); if (type == mvm->scan_type && hb_type == mvm->hb_scan_type) return 0; } else { - type = iwl_mvm_get_scan_type(mvm, false); + type = iwl_mvm_get_scan_type(mvm, NULL); if (type == mvm->scan_type) return 0; } if (iwl_mvm_cdb_scan_api(mvm)) - cmd_size = sizeof(struct iwl_scan_config); + cmd_size = sizeof(struct iwl_scan_config_v2); else cmd_size = sizeof(struct iwl_scan_config_v1); cmd_size += mvm->fw->ucode_capa.n_scan_channels; @@ -1156,7 +1244,7 @@ SCAN_CONFIG_FLAG_SET_MAC_ADDR | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS | SCAN_CONFIG_N_CHANNELS(num_channels) | - (type == IWL_SCAN_TYPE_FRAGMENTED ? + (iwl_mvm_is_scan_fragmented(type) ? SCAN_CONFIG_FLAG_SET_FRAGMENTED : SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED); @@ -1171,12 +1259,14 @@ */ if (iwl_mvm_cdb_scan_api(mvm)) { if (iwl_mvm_is_cdb_supported(mvm)) - flags |= (hb_type == IWL_SCAN_TYPE_FRAGMENTED) ? + flags |= (iwl_mvm_is_scan_fragmented(hb_type)) ? SCAN_CONFIG_FLAG_SET_LMAC2_FRAGMENTED : SCAN_CONFIG_FLAG_CLEAR_LMAC2_FRAGMENTED; - iwl_mvm_fill_scan_config(mvm, cfg, flags, channel_flags); + iwl_mvm_fill_scan_config_v2(mvm, cfg, flags, channel_flags, + num_channels); } else { - iwl_mvm_fill_scan_config_v1(mvm, cfg, flags, channel_flags); + iwl_mvm_fill_scan_config_v1(mvm, cfg, flags, channel_flags, + num_channels); } cmd.data[0] = cfg; @@ -1193,6 +1283,39 @@ kfree(cfg); return ret; +} + +int iwl_mvm_config_scan(struct iwl_mvm *mvm) +{ + struct iwl_scan_config cfg; + struct iwl_host_cmd cmd = { + .id = iwl_cmd_id(SCAN_CFG_CMD, IWL_ALWAYS_LONG_GROUP, 0), + .len[0] = sizeof(cfg), + .data[0] = &cfg, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!iwl_mvm_is_reduced_config_scan_supported(mvm)) + return iwl_mvm_legacy_config_scan(mvm); + + memset(&cfg, 0, sizeof(cfg)); + + if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, 0) < 12) + cfg.bcast_sta_id = mvm->aux_sta.sta_id; + /* + * Fw doesn't use this sta anymore, pending deprecation via HOST API + * change. + */ + else + cfg.bcast_sta_id = 0xff; + + cfg.tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); + cfg.rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); + + IWL_DEBUG_SCAN(mvm, "Sending UMAC scan config\n"); + + return iwl_mvm_send_cmd(mvm, &cmd); } static int iwl_mvm_scan_uid_by_status(struct iwl_mvm *mvm, int status) @@ -1214,16 +1337,18 @@ u8 active_dwell, passive_dwell; timing = &scan_timing[params->type]; - active_dwell = params->measurement_dwell ? - params->measurement_dwell : IWL_SCAN_DWELL_ACTIVE; - passive_dwell = params->measurement_dwell ? - params->measurement_dwell : IWL_SCAN_DWELL_PASSIVE; + active_dwell = IWL_SCAN_DWELL_ACTIVE; + passive_dwell = IWL_SCAN_DWELL_PASSIVE; if (iwl_mvm_is_adaptive_dwell_supported(mvm)) { cmd->v7.adwell_default_n_aps_social = IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; cmd->v7.adwell_default_n_aps = - IWL_SCAN_ADWELL_DEFAULT_N_APS; + IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; + + if (iwl_mvm_is_adwell_hb_ap_num_supported(mvm)) + cmd->v9.adwell_default_hb_n_aps = + IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; /* if custom max budget was configured with debugfs */ if (IWL_MVM_ADWELL_MAX_BUDGET) @@ -1266,8 +1391,7 @@ } } } else { - cmd->v1.extended_dwell = params->measurement_dwell ? - params->measurement_dwell : IWL_SCAN_DWELL_EXTENDED; + cmd->v1.extended_dwell = IWL_SCAN_DWELL_EXTENDED; cmd->v1.active_dwell = active_dwell; cmd->v1.passive_dwell = passive_dwell; cmd->v1.fragmented_dwell = IWL_SCAN_DWELL_FRAGMENTED; @@ -1304,20 +1428,328 @@ cmd->ooc_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_2); } +static u32 iwl_mvm_scan_umac_ooc_priority(struct iwl_mvm_scan_params *params) +{ + return iwl_mvm_is_regular_scan(params) ? + IWL_SCAN_PRIORITY_EXT_6 : + IWL_SCAN_PRIORITY_EXT_2; +} + +static void +iwl_mvm_scan_umac_dwell_v10(struct iwl_mvm *mvm, + struct iwl_scan_general_params_v10 *general_params, + struct iwl_mvm_scan_params *params) +{ + struct iwl_mvm_scan_timing_params *timing, *hb_timing; + u8 active_dwell, passive_dwell; + + timing = &scan_timing[params->type]; + active_dwell = IWL_SCAN_DWELL_ACTIVE; + passive_dwell = IWL_SCAN_DWELL_PASSIVE; + + general_params->adwell_default_social_chn = + IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; + general_params->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; + general_params->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; + + /* if custom max budget was configured with debugfs */ + if (IWL_MVM_ADWELL_MAX_BUDGET) + general_params->adwell_max_budget = + cpu_to_le16(IWL_MVM_ADWELL_MAX_BUDGET); + else if (params->ssids && params->ssids[0].ssid_len) + general_params->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); + else + general_params->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); + + general_params->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + general_params->max_out_of_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->max_out_time); + general_params->suspend_time[SCAN_LB_LMAC_IDX] = + cpu_to_le32(timing->suspend_time); + + hb_timing = &scan_timing[params->hb_type]; + + general_params->max_out_of_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(hb_timing->max_out_time); + general_params->suspend_time[SCAN_HB_LMAC_IDX] = + cpu_to_le32(hb_timing->suspend_time); + + general_params->active_dwell[SCAN_LB_LMAC_IDX] = active_dwell; + general_params->passive_dwell[SCAN_LB_LMAC_IDX] = passive_dwell; + general_params->active_dwell[SCAN_HB_LMAC_IDX] = active_dwell; + general_params->passive_dwell[SCAN_HB_LMAC_IDX] = passive_dwell; +} + +struct iwl_mvm_scan_channel_segment { + u8 start_idx; + u8 end_idx; + u8 first_channel_id; + u8 last_channel_id; + u8 channel_spacing_shift; + u8 band; +}; + +static const struct iwl_mvm_scan_channel_segment scan_channel_segments[] = { + { + .start_idx = 0, + .end_idx = 13, + .first_channel_id = 1, + .last_channel_id = 14, + .channel_spacing_shift = 0, + .band = PHY_BAND_24 + }, + { + .start_idx = 14, + .end_idx = 41, + .first_channel_id = 36, + .last_channel_id = 144, + .channel_spacing_shift = 2, + .band = PHY_BAND_5 + }, + { + .start_idx = 42, + .end_idx = 50, + .first_channel_id = 149, + .last_channel_id = 181, + .channel_spacing_shift = 2, + .band = PHY_BAND_5 + }, +}; + +static int iwl_mvm_scan_ch_and_band_to_idx(u8 channel_id, u8 band) +{ + int i, index; + + if (!channel_id) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(scan_channel_segments); i++) { + const struct iwl_mvm_scan_channel_segment *ch_segment = + &scan_channel_segments[i]; + u32 ch_offset; + + if (ch_segment->band != band || + ch_segment->first_channel_id > channel_id || + ch_segment->last_channel_id < channel_id) + continue; + + ch_offset = (channel_id - ch_segment->first_channel_id) >> + ch_segment->channel_spacing_shift; + + index = scan_channel_segments[i].start_idx + ch_offset; + if (index < IWL_SCAN_NUM_CHANNELS) + return index; + + break; + } + + return -EINVAL; +} + +static const u8 p2p_go_friendly_chs[] = { + 36, 40, 44, 48, 149, 153, 157, 161, 165, +}; + +static const u8 social_chs[] = { + 1, 6, 11 +}; + +static void iwl_mvm_scan_ch_add_n_aps_override(enum nl80211_iftype vif_type, + u8 ch_id, u8 band, u8 *ch_bitmap, + size_t bitmap_n_entries) +{ + int i; + + if (vif_type != NL80211_IFTYPE_P2P_DEVICE) + return; + + for (i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { + if (p2p_go_friendly_chs[i] == ch_id) { + int ch_idx, bitmap_idx; + + ch_idx = iwl_mvm_scan_ch_and_band_to_idx(ch_id, band); + if (ch_idx < 0) + return; + + bitmap_idx = ch_idx / 8; + if (bitmap_idx >= bitmap_n_entries) + return; + + ch_idx = ch_idx % 8; + ch_bitmap[bitmap_idx] |= BIT(ch_idx); + + return; + } + } +} + +static u32 iwl_mvm_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id) +{ + int i; + u32 flags = 0; + + if (vif_type != NL80211_IFTYPE_P2P_DEVICE) + goto out; + + for (i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { + if (p2p_go_friendly_chs[i] == ch_id) { + flags |= IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; + break; + } + } + + if (flags) + goto out; + + for (i = 0; i < ARRAY_SIZE(social_chs); i++) { + if (social_chs[i] == ch_id) { + flags |= IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT; + break; + } + } + +out: + return flags; +} + static void iwl_mvm_umac_scan_cfg_channels(struct iwl_mvm *mvm, struct ieee80211_channel **channels, - int n_channels, u32 ssid_bitmap, + int n_channels, u32 flags, struct iwl_scan_channel_cfg_umac *channel_cfg) { int i; for (i = 0; i < n_channels; i++) { - channel_cfg[i].flags = cpu_to_le32(ssid_bitmap); - channel_cfg[i].channel_num = channels[i]->hw_value; - channel_cfg[i].iter_count = 1; - channel_cfg[i].iter_interval = 0; + channel_cfg[i].flags = cpu_to_le32(flags); + channel_cfg[i].v1.channel_num = channels[i]->hw_value; + if (iwl_mvm_is_scan_ext_chan_supported(mvm)) { + enum nl80211_band band = channels[i]->band; + + channel_cfg[i].v2.band = + iwl_mvm_phy_band_from_nl80211(band); + channel_cfg[i].v2.iter_count = 1; + channel_cfg[i].v2.iter_interval = 0; + } else { + channel_cfg[i].v1.iter_count = 1; + channel_cfg[i].v1.iter_interval = 0; + } } +} + +static void +iwl_mvm_umac_scan_cfg_channels_v4(struct iwl_mvm *mvm, + struct ieee80211_channel **channels, + struct iwl_scan_channel_params_v4 *cp, + int n_channels, u32 flags, + enum nl80211_iftype vif_type) +{ + u8 *bitmap = cp->adwell_ch_override_bitmap; + size_t bitmap_n_entries = ARRAY_SIZE(cp->adwell_ch_override_bitmap); + int i; + + for (i = 0; i < n_channels; i++) { + enum nl80211_band band = channels[i]->band; + struct iwl_scan_channel_cfg_umac *cfg = + &cp->channel_config[i]; + + cfg->flags = cpu_to_le32(flags); + cfg->v2.channel_num = channels[i]->hw_value; + cfg->v2.band = iwl_mvm_phy_band_from_nl80211(band); + cfg->v2.iter_count = 1; + cfg->v2.iter_interval = 0; + + iwl_mvm_scan_ch_add_n_aps_override(vif_type, + cfg->v2.channel_num, + cfg->v2.band, bitmap, + bitmap_n_entries); + } +} + +static void +iwl_mvm_umac_scan_cfg_channels_v6(struct iwl_mvm *mvm, + struct ieee80211_channel **channels, + struct iwl_scan_channel_params_v6 *cp, + int n_channels, u32 flags, + enum nl80211_iftype vif_type) +{ + int i; + + for (i = 0; i < n_channels; i++) { + enum nl80211_band band = channels[i]->band; + struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i]; + u32 n_aps_flag = + iwl_mvm_scan_ch_n_aps_flag(vif_type, + channels[i]->hw_value); + + cfg->flags = cpu_to_le32(flags | n_aps_flag); + cfg->v2.channel_num = channels[i]->hw_value; + cfg->v2.band = iwl_mvm_phy_band_from_nl80211(band); + cfg->v2.iter_count = 1; + cfg->v2.iter_interval = 0; + } +} + +static u8 iwl_mvm_scan_umac_chan_flags_v2(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif) +{ + u8 flags = 0; + + flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; + + if (iwl_mvm_scan_use_ebs(mvm, vif)) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + + /* set fragmented ebs for fragmented scan on HB channels */ + if ((!iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->type)) || + (iwl_mvm_is_cdb_supported(mvm) && + iwl_mvm_is_scan_fragmented(params->hb_type))) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + + return flags; +} + +static u16 iwl_mvm_scan_umac_flags_v2(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif, + int type) +{ + u16 flags = 0; + + if (params->n_ssids == 0) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE; + + if (iwl_mvm_is_scan_fragmented(params->type)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1; + + if (iwl_mvm_is_scan_fragmented(params->hb_type)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2; + + if (params->pass_all) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL; + else + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH; + + if (!iwl_mvm_is_regular_scan(params)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC; + + if (params->iter_notif || + mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE; + + if (IWL_MVM_ADWELL_ENABLE) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL; + + if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE; + + return flags; } static u16 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, @@ -1332,11 +1764,11 @@ if (params->n_ssids == 1 && params->ssids[0].ssid_len != 0) flags |= IWL_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT; - if (params->type == IWL_SCAN_TYPE_FRAGMENTED) + if (iwl_mvm_is_scan_fragmented(params->type)) flags |= IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED; if (iwl_mvm_is_cdb_supported(mvm) && - params->hb_type == IWL_SCAN_TYPE_FRAGMENTED) + iwl_mvm_is_scan_fragmented(params->hb_type)) flags |= IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED; if (iwl_mvm_rrm_scan_needed(mvm) && @@ -1352,7 +1784,7 @@ if (!iwl_mvm_is_regular_scan(params)) flags |= IWL_UMAC_SCAN_GEN_FLAGS_PERIODIC; - if (params->measurement_dwell) + if (params->iter_notif) flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1363,8 +1795,7 @@ if (mvm->sched_scan_pass_all == SCHED_SCAN_PASS_ALL_ENABLED) flags |= IWL_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE; - if (iwl_mvm_is_adaptive_dwell_supported(mvm) && IWL_MVM_ADWELL_ENABLE && - vif->type != NL80211_IFTYPE_P2P_DEVICE) + if (iwl_mvm_is_adaptive_dwell_supported(mvm) && IWL_MVM_ADWELL_ENABLE) flags |= IWL_UMAC_SCAN_GEN_FLAGS_ADAPTIVE_DWELL; /* @@ -1374,7 +1805,7 @@ */ if (iwl_mvm_is_regular_scan(params) && vif->type != NL80211_IFTYPE_P2P_DEVICE && - params->type != IWL_SCAN_TYPE_FRAGMENTED && + !iwl_mvm_is_scan_fragmented(params->type) && !iwl_mvm_is_adaptive_dwell_supported(mvm) && !iwl_mvm_is_oce_supported(mvm)) flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL; @@ -1399,34 +1830,59 @@ return flags; } +static int +iwl_mvm_fill_scan_sched_params(struct iwl_mvm_scan_params *params, + struct iwl_scan_umac_schedule *schedule, + __le16 *delay) +{ + int i; + if (WARN_ON(!params->n_scan_plans || + params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) + return -EINVAL; + + for (i = 0; i < params->n_scan_plans; i++) { + struct cfg80211_sched_scan_plan *scan_plan = + ¶ms->scan_plans[i]; + + schedule[i].iter_count = scan_plan->iterations; + schedule[i].interval = + cpu_to_le16(scan_plan->interval); + } + + /* + * If the number of iterations of the last scan plan is set to + * zero, it should run infinitely. However, this is not always the case. + * For example, when regular scan is requested the driver sets one scan + * plan with one iteration. + */ + if (!schedule[params->n_scan_plans - 1].iter_count) + schedule[params->n_scan_plans - 1].iter_count = 0xff; + + *delay = cpu_to_le16(params->delay); + + return 0; +} + static int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_scan_params *params, - int type) + int type, int uid) { struct iwl_scan_req_umac *cmd = mvm->scan_cmd; struct iwl_scan_umac_chan_param *chan_param; void *cmd_data = iwl_mvm_get_scan_req_umac_data(mvm); - struct iwl_scan_req_umac_tail *sec_part = cmd_data + - sizeof(struct iwl_scan_channel_cfg_umac) * - mvm->fw->ucode_capa.n_scan_channels; - int uid, i; + void *sec_part = cmd_data + sizeof(struct iwl_scan_channel_cfg_umac) * + mvm->fw->ucode_capa.n_scan_channels; + struct iwl_scan_req_umac_tail_v2 *tail_v2 = + (struct iwl_scan_req_umac_tail_v2 *)sec_part; + struct iwl_scan_req_umac_tail_v1 *tail_v1; + struct iwl_ssid_ie *direct_scan; + int ret = 0; u32 ssid_bitmap = 0; u8 channel_flags = 0; u16 gen_flags; struct iwl_mvm_vif *scan_vif = iwl_mvm_vif_from_mac80211(vif); chan_param = iwl_mvm_get_scan_req_umac_channel(mvm); - - lockdep_assert_held(&mvm->mutex); - - if (WARN_ON(params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) - return -EINVAL; - - uid = iwl_mvm_scan_uid_by_status(mvm, 0); - if (uid < 0) - return uid; - - memset(cmd, 0, ksize(cmd)); iwl_mvm_scan_umac_dwell(mvm, cmd, params); @@ -1442,6 +1898,9 @@ if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED) cmd->v8.num_of_fragments[SCAN_HB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + + cmd->v8.general_flags2 = + IWL_UMAC_SCAN_GEN_FLAGS2_ALLOW_CHNL_REORDER; } cmd->scan_start_mac_id = scan_vif->id; @@ -1449,40 +1908,181 @@ if (type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) cmd->flags = cpu_to_le32(IWL_UMAC_SCAN_FLAG_PREEMPTIVE); - if (iwl_mvm_scan_use_ebs(mvm, vif)) + if (iwl_mvm_scan_use_ebs(mvm, vif)) { channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + /* set fragmented ebs for fragmented scan on HB channels */ + if (iwl_mvm_is_frag_ebs_supported(mvm)) { + if (gen_flags & + IWL_UMAC_SCAN_GEN_FLAGS_LMAC2_FRAGMENTED || + (!iwl_mvm_is_cdb_supported(mvm) && + gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_FRAGMENTED)) + channel_flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + } + } + chan_param->flags = channel_flags; chan_param->count = params->n_channels; - iwl_scan_build_ssids(params, sec_part->direct_scan, &ssid_bitmap); + ret = iwl_mvm_fill_scan_sched_params(params, tail_v2->schedule, + &tail_v2->delay); + if (ret) + return ret; + if (iwl_mvm_is_scan_ext_chan_supported(mvm)) { + tail_v2->preq = params->preq; + direct_scan = tail_v2->direct_scan; + } else { + tail_v1 = (struct iwl_scan_req_umac_tail_v1 *)sec_part; + iwl_mvm_scan_set_legacy_probe_req(&tail_v1->preq, + ¶ms->preq); + direct_scan = tail_v1->direct_scan; + } + iwl_scan_build_ssids(params, direct_scan, &ssid_bitmap); iwl_mvm_umac_scan_cfg_channels(mvm, params->channels, params->n_channels, ssid_bitmap, cmd_data); + return 0; +} - for (i = 0; i < params->n_scan_plans; i++) { - struct cfg80211_sched_scan_plan *scan_plan = - ¶ms->scan_plans[i]; +static void +iwl_mvm_scan_umac_fill_general_p_v10(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_general_params_v10 *gp, + u16 gen_flags) +{ + struct iwl_mvm_vif *scan_vif = iwl_mvm_vif_from_mac80211(vif); - sec_part->schedule[i].iter_count = scan_plan->iterations; - sec_part->schedule[i].interval = - cpu_to_le16(scan_plan->interval); - } + iwl_mvm_scan_umac_dwell_v10(mvm, gp, params); - /* - * If the number of iterations of the last scan plan is set to - * zero, it should run infinitely. However, this is not always the case. - * For example, when regular scan is requested the driver sets one scan - * plan with one iteration. - */ - if (!sec_part->schedule[i - 1].iter_count) - sec_part->schedule[i - 1].iter_count = 0xff; + gp->flags = cpu_to_le16(gen_flags); - sec_part->delay = cpu_to_le16(params->delay); - sec_part->preq = params->preq; + if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) + gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC2) + gp->num_of_fragments[SCAN_HB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + + gp->scan_start_mac_id = scan_vif->id; +} + +static void +iwl_mvm_scan_umac_fill_probe_p_v3(struct iwl_mvm_scan_params *params, + struct iwl_scan_probe_params_v3 *pp) +{ + pp->preq = params->preq; + pp->ssid_num = params->n_ssids; + iwl_scan_build_ssids(params, pp->direct_scan, NULL); +} + +static void +iwl_mvm_scan_umac_fill_probe_p_v4(struct iwl_mvm_scan_params *params, + struct iwl_scan_probe_params_v4 *pp, + u32 *bitmap_ssid) +{ + pp->preq = params->preq; + iwl_scan_build_ssids(params, pp->direct_scan, bitmap_ssid); +} + +static void +iwl_mvm_scan_umac_fill_ch_p_v4(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_channel_params_v4 *cp, + u32 channel_cfg_flags) +{ + cp->flags = iwl_mvm_scan_umac_chan_flags_v2(mvm, params, vif); + cp->count = params->n_channels; + cp->num_of_aps_override = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; + + iwl_mvm_umac_scan_cfg_channels_v4(mvm, params->channels, cp, + params->n_channels, + channel_cfg_flags, + vif->type); +} + +static void +iwl_mvm_scan_umac_fill_ch_p_v6(struct iwl_mvm *mvm, + struct iwl_mvm_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_channel_params_v6 *cp, + u32 channel_cfg_flags) +{ + cp->flags = iwl_mvm_scan_umac_chan_flags_v2(mvm, params, vif); + cp->count = params->n_channels; + cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; + cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS; + + iwl_mvm_umac_scan_cfg_channels_v6(mvm, params->channels, cp, + params->n_channels, + channel_cfg_flags, + vif->type); +} + +static int iwl_mvm_scan_umac_v12(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params, int type, + int uid) +{ + struct iwl_scan_req_umac_v12 *cmd = mvm->scan_cmd; + struct iwl_scan_req_params_v12 *scan_p = &cmd->scan_params; + int ret; + u16 gen_flags; + + mvm->scan_uid_status[uid] = type; + + cmd->ooc_priority = cpu_to_le32(iwl_mvm_scan_umac_ooc_priority(params)); + cmd->uid = cpu_to_le32(uid); + + gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); + iwl_mvm_scan_umac_fill_general_p_v10(mvm, params, vif, + &scan_p->general_params, + gen_flags); + + ret = iwl_mvm_fill_scan_sched_params(params, + scan_p->periodic_params.schedule, + &scan_p->periodic_params.delay); + if (ret) + return ret; + + iwl_mvm_scan_umac_fill_probe_p_v3(params, &scan_p->probe_params); + iwl_mvm_scan_umac_fill_ch_p_v4(mvm, params, vif, + &scan_p->channel_params, 0); + + return 0; +} + +static int iwl_mvm_scan_umac_v14(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params, int type, + int uid) +{ + struct iwl_scan_req_umac_v14 *cmd = mvm->scan_cmd; + struct iwl_scan_req_params_v14 *scan_p = &cmd->scan_params; + int ret; + u16 gen_flags; + u32 bitmap_ssid = 0; + + mvm->scan_uid_status[uid] = type; + + cmd->ooc_priority = cpu_to_le32(iwl_mvm_scan_umac_ooc_priority(params)); + cmd->uid = cpu_to_le32(uid); + + gen_flags = iwl_mvm_scan_umac_flags_v2(mvm, params, vif, type); + iwl_mvm_scan_umac_fill_general_p_v10(mvm, params, vif, + &scan_p->general_params, + gen_flags); + + ret = iwl_mvm_fill_scan_sched_params(params, + scan_p->periodic_params.schedule, + &scan_p->periodic_params.delay); + if (ret) + return ret; + + iwl_mvm_scan_umac_fill_probe_p_v4(params, &scan_p->probe_params, + &bitmap_ssid); + iwl_mvm_scan_umac_fill_ch_p_v6(mvm, params, vif, + &scan_p->channel_params, bitmap_ssid); return 0; } @@ -1506,6 +2106,11 @@ * compatibility with old wpa_supplicant that wouldn't stop a * scheduled scan before starting a normal scan. */ + + /* FW supports only a single periodic scan */ + if ((type == IWL_MVM_SCAN_SCHED || type == IWL_MVM_SCAN_NETDETECT) && + mvm->scan_status & (IWL_MVM_SCAN_SCHED | IWL_MVM_SCAN_NETDETECT)) + return -EBUSY; if (iwl_mvm_num_scans(mvm) < mvm->max_scans) return 0; @@ -1543,10 +2148,10 @@ if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - - /* fall through, something is wrong if no scan was - * running but we ran out of scans. + /* Something is wrong if no scan was running but we + * ran out of scans. */ + /* fall through */ default: WARN_ON(1); break; @@ -1570,19 +2175,78 @@ static void iwl_mvm_fill_scan_type(struct iwl_mvm *mvm, struct iwl_mvm_scan_params *params, - bool p2p) + struct ieee80211_vif *vif) { if (iwl_mvm_is_cdb_supported(mvm)) { params->type = - iwl_mvm_get_scan_type_band(mvm, p2p, + iwl_mvm_get_scan_type_band(mvm, vif, NL80211_BAND_2GHZ); params->hb_type = - iwl_mvm_get_scan_type_band(mvm, p2p, + iwl_mvm_get_scan_type_band(mvm, vif, NL80211_BAND_5GHZ); } else { - params->type = iwl_mvm_get_scan_type(mvm, p2p); + params->type = iwl_mvm_get_scan_type(mvm, vif); } } + +struct iwl_scan_umac_handler { + u8 version; + int (*handler)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct iwl_mvm_scan_params *params, int type, int uid); +}; + +#define IWL_SCAN_UMAC_HANDLER(_ver) { \ + .version = _ver, \ + .handler = iwl_mvm_scan_umac_v##_ver, \ +} + +static const struct iwl_scan_umac_handler iwl_scan_umac_handlers[] = { + /* set the newest version first to shorten the list traverse time */ + IWL_SCAN_UMAC_HANDLER(14), + IWL_SCAN_UMAC_HANDLER(12), +}; + +static int iwl_mvm_build_scan_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_host_cmd *hcmd, + struct iwl_mvm_scan_params *params, + int type) +{ + int uid, i; + u8 scan_ver; + + lockdep_assert_held(&mvm->mutex); + memset(mvm->scan_cmd, 0, ksize(mvm->scan_cmd)); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + hcmd->id = SCAN_OFFLOAD_REQUEST_CMD; + + return iwl_mvm_scan_lmac(mvm, vif, params); + } + + uid = iwl_mvm_scan_uid_by_status(mvm, 0); + if (uid < 0) + return uid; + + hcmd->id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); + + scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, + SCAN_REQ_UMAC, + IWL_FW_CMD_VER_UNKNOWN); + + for (i = 0; i < ARRAY_SIZE(iwl_scan_umac_handlers); i++) { + const struct iwl_scan_umac_handler *ver_handler = + &iwl_scan_umac_handlers[i]; + + if (ver_handler->version != scan_ver) + continue; + + return ver_handler->handler(mvm, vif, params, type, uid); + } + + return iwl_mvm_scan_umac(mvm, vif, params, type, uid); +} + int iwl_mvm_reg_scan_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_scan_request *req, struct ieee80211_scan_ies *ies) @@ -1630,25 +2294,15 @@ params.scan_plans = &scan_plan; params.n_scan_plans = 1; - iwl_mvm_fill_scan_type(mvm, ¶ms, - vif->type == NL80211_IFTYPE_P2P_DEVICE); + iwl_mvm_fill_scan_type(mvm, ¶ms, vif); - ret = iwl_mvm_get_measurement_dwell(mvm, req, ¶ms); - if (ret < 0) - return ret; - - params.measurement_dwell = ret; + if (req->duration) + params.iter_notif = true; iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, - IWL_MVM_SCAN_REGULAR); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } + ret = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, + IWL_MVM_SCAN_REGULAR); if (ret) return ret; @@ -1669,7 +2323,6 @@ IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n"); mvm->scan_status |= IWL_MVM_SCAN_REGULAR; mvm->scan_vif = iwl_mvm_vif_from_mac80211(vif); - iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); schedule_delayed_work(&mvm->scan_timeout_dwork, msecs_to_jiffies(SCAN_TIMEOUT)); @@ -1726,8 +2379,7 @@ params.n_scan_plans = req->n_scan_plans; params.scan_plans = req->scan_plans; - iwl_mvm_fill_scan_type(mvm, ¶ms, - vif->type == NL80211_IFTYPE_P2P_DEVICE); + iwl_mvm_fill_scan_type(mvm, ¶ms, vif); /* In theory, LMAC scans can handle a 32-bit delay, but since * waiting for over 18 hours to start the scan is a bit silly @@ -1748,13 +2400,7 @@ iwl_mvm_build_scan_probe(mvm, vif, ies, ¶ms); - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { - hcmd.id = iwl_cmd_id(SCAN_REQ_UMAC, IWL_ALWAYS_LONG_GROUP, 0); - ret = iwl_mvm_scan_umac(mvm, vif, ¶ms, type); - } else { - hcmd.id = SCAN_OFFLOAD_REQUEST_CMD; - ret = iwl_mvm_scan_lmac(mvm, vif, ¶ms); - } + ret = iwl_mvm_build_scan_cmd(mvm, vif, &hcmd, ¶ms, type); if (ret) return ret; @@ -1796,7 +2442,6 @@ memcpy(info.tsf_bssid, mvm->scan_vif->bssid, ETH_ALEN); ieee80211_scan_completed(mvm->hw, &info); mvm->scan_vif = NULL; - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); cancel_delayed_work(&mvm->scan_timeout_dwork); iwl_mvm_resume_tcm(mvm); } else if (mvm->scan_uid_status[uid] == IWL_MVM_SCAN_SCHED) { @@ -1902,14 +2547,35 @@ return ret; } - ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); + return iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, + 1 * HZ); +} - return ret; +#define IWL_SCAN_REQ_UMAC_HANDLE_SIZE(_ver) { \ + case (_ver): return sizeof(struct iwl_scan_req_umac_v##_ver); \ +} + +static int iwl_scan_req_umac_get_size(u8 scan_ver) +{ + switch (scan_ver) { + IWL_SCAN_REQ_UMAC_HANDLE_SIZE(14); + IWL_SCAN_REQ_UMAC_HANDLE_SIZE(12); + } + + return 0; } int iwl_mvm_scan_size(struct iwl_mvm *mvm) { - int base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; + int base_size, tail_size; + u8 scan_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, + SCAN_REQ_UMAC, + IWL_FW_CMD_VER_UNKNOWN); + + base_size = iwl_scan_req_umac_get_size(scan_ver); + if (base_size) + return base_size; + if (iwl_mvm_is_adaptive_dwell_v2_supported(mvm)) base_size = IWL_SCAN_REQ_UMAC_SIZE_V8; @@ -1917,17 +2583,24 @@ base_size = IWL_SCAN_REQ_UMAC_SIZE_V7; else if (iwl_mvm_cdb_scan_api(mvm)) base_size = IWL_SCAN_REQ_UMAC_SIZE_V6; + else + base_size = IWL_SCAN_REQ_UMAC_SIZE_V1; - if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + if (iwl_mvm_is_scan_ext_chan_supported(mvm)) + tail_size = sizeof(struct iwl_scan_req_umac_tail_v2); + else + tail_size = sizeof(struct iwl_scan_req_umac_tail_v1); + return base_size + sizeof(struct iwl_scan_channel_cfg_umac) * mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_req_umac_tail); - + tail_size; + } return sizeof(struct iwl_scan_req_lmac) + sizeof(struct iwl_scan_channel_cfg_lmac) * mvm->fw->ucode_capa.n_scan_channels + - sizeof(struct iwl_scan_probe_req); + sizeof(struct iwl_scan_probe_req_v1); } /* @@ -1954,6 +2627,15 @@ mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_DISABLED; mvm->scan_uid_status[uid] = 0; } + uid = iwl_mvm_scan_uid_by_status(mvm, + IWL_MVM_SCAN_STOPPING_REGULAR); + if (uid >= 0) + mvm->scan_uid_status[uid] = 0; + + uid = iwl_mvm_scan_uid_by_status(mvm, + IWL_MVM_SCAN_STOPPING_SCHED); + if (uid >= 0) + mvm->scan_uid_status[uid] = 0; /* We shouldn't have any UIDs still set. Loop over all the * UIDs to make sure there's nothing left there and warn if @@ -2010,10 +2692,6 @@ mvm->scan_status &= ~type; if (type == IWL_MVM_SCAN_REGULAR) { - /* Since the rx handler won't do anything now, we have - * to release the scan reference here. - */ - iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); cancel_delayed_work(&mvm->scan_timeout_dwork); if (notify) { struct cfg80211_scan_info info = { -- Gitblit v1.6.2