162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of wl12xx 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments. All rights reserved. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/ieee80211.h> 962306a36Sopenharmony_ci#include "scan.h" 1062306a36Sopenharmony_ci#include "../wlcore/debug.h" 1162306a36Sopenharmony_ci#include "../wlcore/tx.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int wl1271_get_scan_channels(struct wl1271 *wl, 1462306a36Sopenharmony_ci struct cfg80211_scan_request *req, 1562306a36Sopenharmony_ci struct basic_scan_channel_params *channels, 1662306a36Sopenharmony_ci enum nl80211_band band, bool passive) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct conf_scan_settings *c = &wl->conf.scan; 1962306a36Sopenharmony_ci int i, j; 2062306a36Sopenharmony_ci u32 flags; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci for (i = 0, j = 0; 2362306a36Sopenharmony_ci i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; 2462306a36Sopenharmony_ci i++) { 2562306a36Sopenharmony_ci flags = req->channels[i]->flags; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (!test_bit(i, wl->scan.scanned_ch) && 2862306a36Sopenharmony_ci !(flags & IEEE80211_CHAN_DISABLED) && 2962306a36Sopenharmony_ci (req->channels[i]->band == band) && 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * In passive scans, we scan all remaining 3262306a36Sopenharmony_ci * channels, even if not marked as such. 3362306a36Sopenharmony_ci * In active scans, we only scan channels not 3462306a36Sopenharmony_ci * marked as passive. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci (passive || !(flags & IEEE80211_CHAN_NO_IR))) { 3762306a36Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", 3862306a36Sopenharmony_ci req->channels[i]->band, 3962306a36Sopenharmony_ci req->channels[i]->center_freq); 4062306a36Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", 4162306a36Sopenharmony_ci req->channels[i]->hw_value, 4262306a36Sopenharmony_ci req->channels[i]->flags); 4362306a36Sopenharmony_ci wl1271_debug(DEBUG_SCAN, 4462306a36Sopenharmony_ci "max_antenna_gain %d, max_power %d", 4562306a36Sopenharmony_ci req->channels[i]->max_antenna_gain, 4662306a36Sopenharmony_ci req->channels[i]->max_power); 4762306a36Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "beacon_found %d", 4862306a36Sopenharmony_ci req->channels[i]->beacon_found); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (!passive) { 5162306a36Sopenharmony_ci channels[j].min_duration = 5262306a36Sopenharmony_ci cpu_to_le32(c->min_dwell_time_active); 5362306a36Sopenharmony_ci channels[j].max_duration = 5462306a36Sopenharmony_ci cpu_to_le32(c->max_dwell_time_active); 5562306a36Sopenharmony_ci } else { 5662306a36Sopenharmony_ci channels[j].min_duration = 5762306a36Sopenharmony_ci cpu_to_le32(c->dwell_time_passive); 5862306a36Sopenharmony_ci channels[j].max_duration = 5962306a36Sopenharmony_ci cpu_to_le32(c->dwell_time_passive); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci channels[j].early_termination = 0; 6262306a36Sopenharmony_ci channels[j].tx_power_att = req->channels[i]->max_power; 6362306a36Sopenharmony_ci channels[j].channel = req->channels[i]->hw_value; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci memset(&channels[j].bssid_lsb, 0xff, 4); 6662306a36Sopenharmony_ci memset(&channels[j].bssid_msb, 0xff, 2); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Mark the channels we already used */ 6962306a36Sopenharmony_ci set_bit(i, wl->scan.scanned_ch); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci j++; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return j; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define WL1271_NOTHING_TO_SCAN 1 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, 8162306a36Sopenharmony_ci enum nl80211_band band, 8262306a36Sopenharmony_ci bool passive, u32 basic_rate) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); 8562306a36Sopenharmony_ci struct wl1271_cmd_scan *cmd; 8662306a36Sopenharmony_ci struct wl1271_cmd_trigger_scan_to *trigger; 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci u16 scan_options = 0; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* skip active scans if we don't have SSIDs */ 9162306a36Sopenharmony_ci if (!passive && wl->scan.req->n_ssids == 0) 9262306a36Sopenharmony_ci return WL1271_NOTHING_TO_SCAN; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 9562306a36Sopenharmony_ci trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); 9662306a36Sopenharmony_ci if (!cmd || !trigger) { 9762306a36Sopenharmony_ci ret = -ENOMEM; 9862306a36Sopenharmony_ci goto out; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (wl->conf.scan.split_scan_timeout) 10262306a36Sopenharmony_ci scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (passive) 10562306a36Sopenharmony_ci scan_options |= WL1271_SCAN_OPT_PASSIVE; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* scan on the dev role if the regular one is not started */ 10862306a36Sopenharmony_ci if (wlcore_is_p2p_mgmt(wlvif)) 10962306a36Sopenharmony_ci cmd->params.role_id = wlvif->dev_role_id; 11062306a36Sopenharmony_ci else 11162306a36Sopenharmony_ci cmd->params.role_id = wlvif->role_id; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { 11462306a36Sopenharmony_ci ret = -EINVAL; 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci cmd->params.scan_options = cpu_to_le16(scan_options); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, 12162306a36Sopenharmony_ci cmd->channels, 12262306a36Sopenharmony_ci band, passive); 12362306a36Sopenharmony_ci if (cmd->params.n_ch == 0) { 12462306a36Sopenharmony_ci ret = WL1271_NOTHING_TO_SCAN; 12562306a36Sopenharmony_ci goto out; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cmd->params.tx_rate = cpu_to_le32(basic_rate); 12962306a36Sopenharmony_ci cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; 13062306a36Sopenharmony_ci cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; 13162306a36Sopenharmony_ci cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (band == NL80211_BAND_2GHZ) 13462306a36Sopenharmony_ci cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; 13562306a36Sopenharmony_ci else 13662306a36Sopenharmony_ci cmd->params.band = WL1271_SCAN_BAND_5_GHZ; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (wl->scan.ssid_len) { 13962306a36Sopenharmony_ci cmd->params.ssid_len = wl->scan.ssid_len; 14062306a36Sopenharmony_ci memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci memcpy(cmd->addr, vif->addr, ETH_ALEN); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 14662306a36Sopenharmony_ci cmd->params.role_id, band, 14762306a36Sopenharmony_ci wl->scan.ssid, wl->scan.ssid_len, 14862306a36Sopenharmony_ci wl->scan.req->ie, 14962306a36Sopenharmony_ci wl->scan.req->ie_len, NULL, 0, false); 15062306a36Sopenharmony_ci if (ret < 0) { 15162306a36Sopenharmony_ci wl1271_error("PROBE request template failed"); 15262306a36Sopenharmony_ci goto out; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); 15662306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, 15762306a36Sopenharmony_ci sizeof(*trigger), 0); 15862306a36Sopenharmony_ci if (ret < 0) { 15962306a36Sopenharmony_ci wl1271_error("trigger scan to failed for hw scan"); 16062306a36Sopenharmony_ci goto out; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 16662306a36Sopenharmony_ci if (ret < 0) { 16762306a36Sopenharmony_ci wl1271_error("SCAN failed"); 16862306a36Sopenharmony_ci goto out; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ciout: 17262306a36Sopenharmony_ci kfree(cmd); 17362306a36Sopenharmony_ci kfree(trigger); 17462306a36Sopenharmony_ci return ret; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct wl1271_cmd_header *cmd = NULL; 18062306a36Sopenharmony_ci int ret = 0; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) 18362306a36Sopenharmony_ci return -EINVAL; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd scan stop"); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 18862306a36Sopenharmony_ci if (!cmd) { 18962306a36Sopenharmony_ci ret = -ENOMEM; 19062306a36Sopenharmony_ci goto out; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, 19462306a36Sopenharmony_ci sizeof(*cmd), 0); 19562306a36Sopenharmony_ci if (ret < 0) { 19662306a36Sopenharmony_ci wl1271_error("cmd stop_scan failed"); 19762306a36Sopenharmony_ci goto out; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ciout: 20062306a36Sopenharmony_ci kfree(cmd); 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_civoid wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int ret = 0; 20762306a36Sopenharmony_ci enum nl80211_band band; 20862306a36Sopenharmony_ci u32 rate, mask; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci switch (wl->scan.state) { 21162306a36Sopenharmony_ci case WL1271_SCAN_STATE_IDLE: 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci case WL1271_SCAN_STATE_2GHZ_ACTIVE: 21562306a36Sopenharmony_ci band = NL80211_BAND_2GHZ; 21662306a36Sopenharmony_ci mask = wlvif->bitrate_masks[band]; 21762306a36Sopenharmony_ci if (wl->scan.req->no_cck) { 21862306a36Sopenharmony_ci mask &= ~CONF_TX_CCK_RATES; 21962306a36Sopenharmony_ci if (!mask) 22062306a36Sopenharmony_ci mask = CONF_TX_RATE_MASK_BASIC_P2P; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, mask); 22362306a36Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, false, rate); 22462306a36Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 22562306a36Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; 22662306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci break; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci case WL1271_SCAN_STATE_2GHZ_PASSIVE: 23262306a36Sopenharmony_ci band = NL80211_BAND_2GHZ; 23362306a36Sopenharmony_ci mask = wlvif->bitrate_masks[band]; 23462306a36Sopenharmony_ci if (wl->scan.req->no_cck) { 23562306a36Sopenharmony_ci mask &= ~CONF_TX_CCK_RATES; 23662306a36Sopenharmony_ci if (!mask) 23762306a36Sopenharmony_ci mask = CONF_TX_RATE_MASK_BASIC_P2P; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, mask); 24062306a36Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, true, rate); 24162306a36Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 24262306a36Sopenharmony_ci if (wl->enable_11a) 24362306a36Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_DONE; 24662306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci case WL1271_SCAN_STATE_5GHZ_ACTIVE: 25262306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 25362306a36Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); 25462306a36Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, false, rate); 25562306a36Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 25662306a36Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; 25762306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci case WL1271_SCAN_STATE_5GHZ_PASSIVE: 26362306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 26462306a36Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); 26562306a36Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, true, rate); 26662306a36Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 26762306a36Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_DONE; 26862306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci case WL1271_SCAN_STATE_DONE: 27462306a36Sopenharmony_ci wl->scan.failed = false; 27562306a36Sopenharmony_ci cancel_delayed_work(&wl->scan_complete_work); 27662306a36Sopenharmony_ci ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 27762306a36Sopenharmony_ci msecs_to_jiffies(0)); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci default: 28162306a36Sopenharmony_ci wl1271_error("invalid scan state"); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (ret < 0) { 28662306a36Sopenharmony_ci cancel_delayed_work(&wl->scan_complete_work); 28762306a36Sopenharmony_ci ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 28862306a36Sopenharmony_ci msecs_to_jiffies(0)); 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, 29362306a36Sopenharmony_ci struct wlcore_scan_channels *cmd_channels) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); 29662306a36Sopenharmony_ci memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); 29762306a36Sopenharmony_ci cmd->dfs = cmd_channels->dfs; 29862306a36Sopenharmony_ci cmd->n_pactive_ch = cmd_channels->passive_active; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci memcpy(cmd->channels_2, cmd_channels->channels_2, 30162306a36Sopenharmony_ci sizeof(cmd->channels_2)); 30262306a36Sopenharmony_ci memcpy(cmd->channels_5, cmd_channels->channels_5, 30362306a36Sopenharmony_ci sizeof(cmd->channels_5)); 30462306a36Sopenharmony_ci /* channels_4 are not supported, so no need to copy them */ 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciint wl1271_scan_sched_scan_config(struct wl1271 *wl, 30862306a36Sopenharmony_ci struct wl12xx_vif *wlvif, 30962306a36Sopenharmony_ci struct cfg80211_sched_scan_request *req, 31062306a36Sopenharmony_ci struct ieee80211_scan_ies *ies) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci struct wl1271_cmd_sched_scan_config *cfg = NULL; 31362306a36Sopenharmony_ci struct wlcore_scan_channels *cfg_channels = NULL; 31462306a36Sopenharmony_ci struct conf_sched_scan_settings *c = &wl->conf.sched_scan; 31562306a36Sopenharmony_ci int i, ret; 31662306a36Sopenharmony_ci bool force_passive = !req->n_ssids; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 32162306a36Sopenharmony_ci if (!cfg) 32262306a36Sopenharmony_ci return -ENOMEM; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cfg->role_id = wlvif->role_id; 32562306a36Sopenharmony_ci cfg->rssi_threshold = c->rssi_threshold; 32662306a36Sopenharmony_ci cfg->snr_threshold = c->snr_threshold; 32762306a36Sopenharmony_ci cfg->n_probe_reqs = c->num_probe_reqs; 32862306a36Sopenharmony_ci /* cycles set to 0 it means infinite (until manually stopped) */ 32962306a36Sopenharmony_ci cfg->cycles = 0; 33062306a36Sopenharmony_ci /* report APs when at least 1 is found */ 33162306a36Sopenharmony_ci cfg->report_after = 1; 33262306a36Sopenharmony_ci /* don't stop scanning automatically when something is found */ 33362306a36Sopenharmony_ci cfg->terminate = 0; 33462306a36Sopenharmony_ci cfg->tag = WL1271_SCAN_DEFAULT_TAG; 33562306a36Sopenharmony_ci /* don't filter on BSS type */ 33662306a36Sopenharmony_ci cfg->bss_type = SCAN_BSS_TYPE_ANY; 33762306a36Sopenharmony_ci /* currently NL80211 supports only a single interval */ 33862306a36Sopenharmony_ci for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) 33962306a36Sopenharmony_ci cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval * 34062306a36Sopenharmony_ci MSEC_PER_SEC); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci cfg->ssid_len = 0; 34362306a36Sopenharmony_ci ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); 34462306a36Sopenharmony_ci if (ret < 0) 34562306a36Sopenharmony_ci goto out; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci cfg->filter_type = ret; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL); 35262306a36Sopenharmony_ci if (!cfg_channels) { 35362306a36Sopenharmony_ci ret = -ENOMEM; 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels, 35862306a36Sopenharmony_ci req->n_channels, req->n_ssids, 35962306a36Sopenharmony_ci SCAN_TYPE_PERIODIC)) { 36062306a36Sopenharmony_ci wl1271_error("scan channel list is empty"); 36162306a36Sopenharmony_ci ret = -EINVAL; 36262306a36Sopenharmony_ci goto out; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci wl12xx_adjust_channels(cfg, cfg_channels); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!force_passive && cfg->active[0]) { 36762306a36Sopenharmony_ci u8 band = NL80211_BAND_2GHZ; 36862306a36Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 36962306a36Sopenharmony_ci wlvif->role_id, band, 37062306a36Sopenharmony_ci req->ssids[0].ssid, 37162306a36Sopenharmony_ci req->ssids[0].ssid_len, 37262306a36Sopenharmony_ci ies->ies[band], 37362306a36Sopenharmony_ci ies->len[band], 37462306a36Sopenharmony_ci ies->common_ies, 37562306a36Sopenharmony_ci ies->common_ie_len, 37662306a36Sopenharmony_ci true); 37762306a36Sopenharmony_ci if (ret < 0) { 37862306a36Sopenharmony_ci wl1271_error("2.4GHz PROBE request template failed"); 37962306a36Sopenharmony_ci goto out; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (!force_passive && cfg->active[1]) { 38462306a36Sopenharmony_ci u8 band = NL80211_BAND_5GHZ; 38562306a36Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 38662306a36Sopenharmony_ci wlvif->role_id, band, 38762306a36Sopenharmony_ci req->ssids[0].ssid, 38862306a36Sopenharmony_ci req->ssids[0].ssid_len, 38962306a36Sopenharmony_ci ies->ies[band], 39062306a36Sopenharmony_ci ies->len[band], 39162306a36Sopenharmony_ci ies->common_ies, 39262306a36Sopenharmony_ci ies->common_ie_len, 39362306a36Sopenharmony_ci true); 39462306a36Sopenharmony_ci if (ret < 0) { 39562306a36Sopenharmony_ci wl1271_error("5GHz PROBE request template failed"); 39662306a36Sopenharmony_ci goto out; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, 40362306a36Sopenharmony_ci sizeof(*cfg), 0); 40462306a36Sopenharmony_ci if (ret < 0) { 40562306a36Sopenharmony_ci wl1271_error("SCAN configuration failed"); 40662306a36Sopenharmony_ci goto out; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ciout: 40962306a36Sopenharmony_ci kfree(cfg_channels); 41062306a36Sopenharmony_ci kfree(cfg); 41162306a36Sopenharmony_ci return ret; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ciint wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct wl1271_cmd_sched_scan_start *start; 41762306a36Sopenharmony_ci int ret = 0; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci if (wlvif->bss_type != BSS_TYPE_STA_BSS) 42262306a36Sopenharmony_ci return -EOPNOTSUPP; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && 42562306a36Sopenharmony_ci test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) 42662306a36Sopenharmony_ci return -EBUSY; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci start = kzalloc(sizeof(*start), GFP_KERNEL); 42962306a36Sopenharmony_ci if (!start) 43062306a36Sopenharmony_ci return -ENOMEM; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci start->role_id = wlvif->role_id; 43362306a36Sopenharmony_ci start->tag = WL1271_SCAN_DEFAULT_TAG; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, 43662306a36Sopenharmony_ci sizeof(*start), 0); 43762306a36Sopenharmony_ci if (ret < 0) { 43862306a36Sopenharmony_ci wl1271_error("failed to send scan start command"); 43962306a36Sopenharmony_ci goto out_free; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ciout_free: 44362306a36Sopenharmony_ci kfree(start); 44462306a36Sopenharmony_ci return ret; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ciint wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 44862306a36Sopenharmony_ci struct cfg80211_sched_scan_request *req, 44962306a36Sopenharmony_ci struct ieee80211_scan_ies *ies) 45062306a36Sopenharmony_ci{ 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); 45462306a36Sopenharmony_ci if (ret < 0) 45562306a36Sopenharmony_ci return ret; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return wl1271_scan_sched_scan_start(wl, wlvif); 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_civoid wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct wl1271_cmd_sched_scan_stop *stop; 46362306a36Sopenharmony_ci int ret = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* FIXME: what to do if alloc'ing to stop fails? */ 46862306a36Sopenharmony_ci stop = kzalloc(sizeof(*stop), GFP_KERNEL); 46962306a36Sopenharmony_ci if (!stop) { 47062306a36Sopenharmony_ci wl1271_error("failed to alloc memory to send sched scan stop"); 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci stop->role_id = wlvif->role_id; 47562306a36Sopenharmony_ci stop->tag = WL1271_SCAN_DEFAULT_TAG; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, 47862306a36Sopenharmony_ci sizeof(*stop), 0); 47962306a36Sopenharmony_ci if (ret < 0) { 48062306a36Sopenharmony_ci wl1271_error("failed to send sched scan stop command"); 48162306a36Sopenharmony_ci goto out_free; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciout_free: 48562306a36Sopenharmony_ci kfree(stop); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciint wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 48962306a36Sopenharmony_ci struct cfg80211_scan_request *req) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_civoid wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 49862306a36Sopenharmony_ci} 499