18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file is part of wl12xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments. All rights reserved. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/ieee80211.h> 98c2ecf20Sopenharmony_ci#include "scan.h" 108c2ecf20Sopenharmony_ci#include "../wlcore/debug.h" 118c2ecf20Sopenharmony_ci#include "../wlcore/tx.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic int wl1271_get_scan_channels(struct wl1271 *wl, 148c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req, 158c2ecf20Sopenharmony_ci struct basic_scan_channel_params *channels, 168c2ecf20Sopenharmony_ci enum nl80211_band band, bool passive) 178c2ecf20Sopenharmony_ci{ 188c2ecf20Sopenharmony_ci struct conf_scan_settings *c = &wl->conf.scan; 198c2ecf20Sopenharmony_ci int i, j; 208c2ecf20Sopenharmony_ci u32 flags; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci for (i = 0, j = 0; 238c2ecf20Sopenharmony_ci i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; 248c2ecf20Sopenharmony_ci i++) { 258c2ecf20Sopenharmony_ci flags = req->channels[i]->flags; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci if (!test_bit(i, wl->scan.scanned_ch) && 288c2ecf20Sopenharmony_ci !(flags & IEEE80211_CHAN_DISABLED) && 298c2ecf20Sopenharmony_ci (req->channels[i]->band == band) && 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * In passive scans, we scan all remaining 328c2ecf20Sopenharmony_ci * channels, even if not marked as such. 338c2ecf20Sopenharmony_ci * In active scans, we only scan channels not 348c2ecf20Sopenharmony_ci * marked as passive. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci (passive || !(flags & IEEE80211_CHAN_NO_IR))) { 378c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", 388c2ecf20Sopenharmony_ci req->channels[i]->band, 398c2ecf20Sopenharmony_ci req->channels[i]->center_freq); 408c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", 418c2ecf20Sopenharmony_ci req->channels[i]->hw_value, 428c2ecf20Sopenharmony_ci req->channels[i]->flags); 438c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_SCAN, 448c2ecf20Sopenharmony_ci "max_antenna_gain %d, max_power %d", 458c2ecf20Sopenharmony_ci req->channels[i]->max_antenna_gain, 468c2ecf20Sopenharmony_ci req->channels[i]->max_power); 478c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "beacon_found %d", 488c2ecf20Sopenharmony_ci req->channels[i]->beacon_found); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (!passive) { 518c2ecf20Sopenharmony_ci channels[j].min_duration = 528c2ecf20Sopenharmony_ci cpu_to_le32(c->min_dwell_time_active); 538c2ecf20Sopenharmony_ci channels[j].max_duration = 548c2ecf20Sopenharmony_ci cpu_to_le32(c->max_dwell_time_active); 558c2ecf20Sopenharmony_ci } else { 568c2ecf20Sopenharmony_ci channels[j].min_duration = 578c2ecf20Sopenharmony_ci cpu_to_le32(c->dwell_time_passive); 588c2ecf20Sopenharmony_ci channels[j].max_duration = 598c2ecf20Sopenharmony_ci cpu_to_le32(c->dwell_time_passive); 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci channels[j].early_termination = 0; 628c2ecf20Sopenharmony_ci channels[j].tx_power_att = req->channels[i]->max_power; 638c2ecf20Sopenharmony_ci channels[j].channel = req->channels[i]->hw_value; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci memset(&channels[j].bssid_lsb, 0xff, 4); 668c2ecf20Sopenharmony_ci memset(&channels[j].bssid_msb, 0xff, 2); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Mark the channels we already used */ 698c2ecf20Sopenharmony_ci set_bit(i, wl->scan.scanned_ch); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci j++; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return j; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define WL1271_NOTHING_TO_SCAN 1 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, 818c2ecf20Sopenharmony_ci enum nl80211_band band, 828c2ecf20Sopenharmony_ci bool passive, u32 basic_rate) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); 858c2ecf20Sopenharmony_ci struct wl1271_cmd_scan *cmd; 868c2ecf20Sopenharmony_ci struct wl1271_cmd_trigger_scan_to *trigger; 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci u16 scan_options = 0; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* skip active scans if we don't have SSIDs */ 918c2ecf20Sopenharmony_ci if (!passive && wl->scan.req->n_ssids == 0) 928c2ecf20Sopenharmony_ci return WL1271_NOTHING_TO_SCAN; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 958c2ecf20Sopenharmony_ci trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (!cmd || !trigger) { 978c2ecf20Sopenharmony_ci ret = -ENOMEM; 988c2ecf20Sopenharmony_ci goto out; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (wl->conf.scan.split_scan_timeout) 1028c2ecf20Sopenharmony_ci scan_options |= WL1271_SCAN_OPT_SPLIT_SCAN; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (passive) 1058c2ecf20Sopenharmony_ci scan_options |= WL1271_SCAN_OPT_PASSIVE; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* scan on the dev role if the regular one is not started */ 1088c2ecf20Sopenharmony_ci if (wlcore_is_p2p_mgmt(wlvif)) 1098c2ecf20Sopenharmony_ci cmd->params.role_id = wlvif->dev_role_id; 1108c2ecf20Sopenharmony_ci else 1118c2ecf20Sopenharmony_ci cmd->params.role_id = wlvif->role_id; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (WARN_ON(cmd->params.role_id == WL12XX_INVALID_ROLE_ID)) { 1148c2ecf20Sopenharmony_ci ret = -EINVAL; 1158c2ecf20Sopenharmony_ci goto out; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci cmd->params.scan_options = cpu_to_le16(scan_options); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, 1218c2ecf20Sopenharmony_ci cmd->channels, 1228c2ecf20Sopenharmony_ci band, passive); 1238c2ecf20Sopenharmony_ci if (cmd->params.n_ch == 0) { 1248c2ecf20Sopenharmony_ci ret = WL1271_NOTHING_TO_SCAN; 1258c2ecf20Sopenharmony_ci goto out; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci cmd->params.tx_rate = cpu_to_le32(basic_rate); 1298c2ecf20Sopenharmony_ci cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; 1308c2ecf20Sopenharmony_ci cmd->params.tid_trigger = CONF_TX_AC_ANY_TID; 1318c2ecf20Sopenharmony_ci cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (band == NL80211_BAND_2GHZ) 1348c2ecf20Sopenharmony_ci cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; 1358c2ecf20Sopenharmony_ci else 1368c2ecf20Sopenharmony_ci cmd->params.band = WL1271_SCAN_BAND_5_GHZ; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (wl->scan.ssid_len) { 1398c2ecf20Sopenharmony_ci cmd->params.ssid_len = wl->scan.ssid_len; 1408c2ecf20Sopenharmony_ci memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci memcpy(cmd->addr, vif->addr, ETH_ALEN); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 1468c2ecf20Sopenharmony_ci cmd->params.role_id, band, 1478c2ecf20Sopenharmony_ci wl->scan.ssid, wl->scan.ssid_len, 1488c2ecf20Sopenharmony_ci wl->scan.req->ie, 1498c2ecf20Sopenharmony_ci wl->scan.req->ie_len, NULL, 0, false); 1508c2ecf20Sopenharmony_ci if (ret < 0) { 1518c2ecf20Sopenharmony_ci wl1271_error("PROBE request template failed"); 1528c2ecf20Sopenharmony_ci goto out; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci trigger->timeout = cpu_to_le32(wl->conf.scan.split_scan_timeout); 1568c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, 1578c2ecf20Sopenharmony_ci sizeof(*trigger), 0); 1588c2ecf20Sopenharmony_ci if (ret < 0) { 1598c2ecf20Sopenharmony_ci wl1271_error("trigger scan to failed for hw scan"); 1608c2ecf20Sopenharmony_ci goto out; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 1668c2ecf20Sopenharmony_ci if (ret < 0) { 1678c2ecf20Sopenharmony_ci wl1271_error("SCAN failed"); 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ciout: 1728c2ecf20Sopenharmony_ci kfree(cmd); 1738c2ecf20Sopenharmony_ci kfree(trigger); 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ciint wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci struct wl1271_cmd_header *cmd = NULL; 1808c2ecf20Sopenharmony_ci int ret = 0; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd scan stop"); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 1888c2ecf20Sopenharmony_ci if (!cmd) { 1898c2ecf20Sopenharmony_ci ret = -ENOMEM; 1908c2ecf20Sopenharmony_ci goto out; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, 1948c2ecf20Sopenharmony_ci sizeof(*cmd), 0); 1958c2ecf20Sopenharmony_ci if (ret < 0) { 1968c2ecf20Sopenharmony_ci wl1271_error("cmd stop_scan failed"); 1978c2ecf20Sopenharmony_ci goto out; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ciout: 2008c2ecf20Sopenharmony_ci kfree(cmd); 2018c2ecf20Sopenharmony_ci return ret; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_civoid wl1271_scan_stm(struct wl1271 *wl, struct wl12xx_vif *wlvif) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int ret = 0; 2078c2ecf20Sopenharmony_ci enum nl80211_band band; 2088c2ecf20Sopenharmony_ci u32 rate, mask; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci switch (wl->scan.state) { 2118c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_IDLE: 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_2GHZ_ACTIVE: 2158c2ecf20Sopenharmony_ci band = NL80211_BAND_2GHZ; 2168c2ecf20Sopenharmony_ci mask = wlvif->bitrate_masks[band]; 2178c2ecf20Sopenharmony_ci if (wl->scan.req->no_cck) { 2188c2ecf20Sopenharmony_ci mask &= ~CONF_TX_CCK_RATES; 2198c2ecf20Sopenharmony_ci if (!mask) 2208c2ecf20Sopenharmony_ci mask = CONF_TX_RATE_MASK_BASIC_P2P; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, mask); 2238c2ecf20Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, false, rate); 2248c2ecf20Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 2258c2ecf20Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; 2268c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_2GHZ_PASSIVE: 2328c2ecf20Sopenharmony_ci band = NL80211_BAND_2GHZ; 2338c2ecf20Sopenharmony_ci mask = wlvif->bitrate_masks[band]; 2348c2ecf20Sopenharmony_ci if (wl->scan.req->no_cck) { 2358c2ecf20Sopenharmony_ci mask &= ~CONF_TX_CCK_RATES; 2368c2ecf20Sopenharmony_ci if (!mask) 2378c2ecf20Sopenharmony_ci mask = CONF_TX_RATE_MASK_BASIC_P2P; 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, mask); 2408c2ecf20Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, true, rate); 2418c2ecf20Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 2428c2ecf20Sopenharmony_ci if (wl->enable_11a) 2438c2ecf20Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; 2448c2ecf20Sopenharmony_ci else 2458c2ecf20Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_DONE; 2468c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_5GHZ_ACTIVE: 2528c2ecf20Sopenharmony_ci band = NL80211_BAND_5GHZ; 2538c2ecf20Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); 2548c2ecf20Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, false, rate); 2558c2ecf20Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 2568c2ecf20Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; 2578c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_5GHZ_PASSIVE: 2638c2ecf20Sopenharmony_ci band = NL80211_BAND_5GHZ; 2648c2ecf20Sopenharmony_ci rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[band]); 2658c2ecf20Sopenharmony_ci ret = wl1271_scan_send(wl, wlvif, band, true, rate); 2668c2ecf20Sopenharmony_ci if (ret == WL1271_NOTHING_TO_SCAN) { 2678c2ecf20Sopenharmony_ci wl->scan.state = WL1271_SCAN_STATE_DONE; 2688c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci case WL1271_SCAN_STATE_DONE: 2748c2ecf20Sopenharmony_ci wl->scan.failed = false; 2758c2ecf20Sopenharmony_ci cancel_delayed_work(&wl->scan_complete_work); 2768c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 2778c2ecf20Sopenharmony_ci msecs_to_jiffies(0)); 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci default: 2818c2ecf20Sopenharmony_ci wl1271_error("invalid scan state"); 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (ret < 0) { 2868c2ecf20Sopenharmony_ci cancel_delayed_work(&wl->scan_complete_work); 2878c2ecf20Sopenharmony_ci ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 2888c2ecf20Sopenharmony_ci msecs_to_jiffies(0)); 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd, 2938c2ecf20Sopenharmony_ci struct wlcore_scan_channels *cmd_channels) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); 2968c2ecf20Sopenharmony_ci memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); 2978c2ecf20Sopenharmony_ci cmd->dfs = cmd_channels->dfs; 2988c2ecf20Sopenharmony_ci cmd->n_pactive_ch = cmd_channels->passive_active; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci memcpy(cmd->channels_2, cmd_channels->channels_2, 3018c2ecf20Sopenharmony_ci sizeof(cmd->channels_2)); 3028c2ecf20Sopenharmony_ci memcpy(cmd->channels_5, cmd_channels->channels_5, 3038c2ecf20Sopenharmony_ci sizeof(cmd->channels_5)); 3048c2ecf20Sopenharmony_ci /* channels_4 are not supported, so no need to copy them */ 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciint wl1271_scan_sched_scan_config(struct wl1271 *wl, 3088c2ecf20Sopenharmony_ci struct wl12xx_vif *wlvif, 3098c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *req, 3108c2ecf20Sopenharmony_ci struct ieee80211_scan_ies *ies) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct wl1271_cmd_sched_scan_config *cfg = NULL; 3138c2ecf20Sopenharmony_ci struct wlcore_scan_channels *cfg_channels = NULL; 3148c2ecf20Sopenharmony_ci struct conf_sched_scan_settings *c = &wl->conf.sched_scan; 3158c2ecf20Sopenharmony_ci int i, ret; 3168c2ecf20Sopenharmony_ci bool force_passive = !req->n_ssids; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 3218c2ecf20Sopenharmony_ci if (!cfg) 3228c2ecf20Sopenharmony_ci return -ENOMEM; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci cfg->role_id = wlvif->role_id; 3258c2ecf20Sopenharmony_ci cfg->rssi_threshold = c->rssi_threshold; 3268c2ecf20Sopenharmony_ci cfg->snr_threshold = c->snr_threshold; 3278c2ecf20Sopenharmony_ci cfg->n_probe_reqs = c->num_probe_reqs; 3288c2ecf20Sopenharmony_ci /* cycles set to 0 it means infinite (until manually stopped) */ 3298c2ecf20Sopenharmony_ci cfg->cycles = 0; 3308c2ecf20Sopenharmony_ci /* report APs when at least 1 is found */ 3318c2ecf20Sopenharmony_ci cfg->report_after = 1; 3328c2ecf20Sopenharmony_ci /* don't stop scanning automatically when something is found */ 3338c2ecf20Sopenharmony_ci cfg->terminate = 0; 3348c2ecf20Sopenharmony_ci cfg->tag = WL1271_SCAN_DEFAULT_TAG; 3358c2ecf20Sopenharmony_ci /* don't filter on BSS type */ 3368c2ecf20Sopenharmony_ci cfg->bss_type = SCAN_BSS_TYPE_ANY; 3378c2ecf20Sopenharmony_ci /* currently NL80211 supports only a single interval */ 3388c2ecf20Sopenharmony_ci for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) 3398c2ecf20Sopenharmony_ci cfg->intervals[i] = cpu_to_le32(req->scan_plans[0].interval * 3408c2ecf20Sopenharmony_ci MSEC_PER_SEC); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci cfg->ssid_len = 0; 3438c2ecf20Sopenharmony_ci ret = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); 3448c2ecf20Sopenharmony_ci if (ret < 0) 3458c2ecf20Sopenharmony_ci goto out; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci cfg->filter_type = ret; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci cfg_channels = kzalloc(sizeof(*cfg_channels), GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!cfg_channels) { 3538c2ecf20Sopenharmony_ci ret = -ENOMEM; 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (!wlcore_set_scan_chan_params(wl, cfg_channels, req->channels, 3588c2ecf20Sopenharmony_ci req->n_channels, req->n_ssids, 3598c2ecf20Sopenharmony_ci SCAN_TYPE_PERIODIC)) { 3608c2ecf20Sopenharmony_ci wl1271_error("scan channel list is empty"); 3618c2ecf20Sopenharmony_ci ret = -EINVAL; 3628c2ecf20Sopenharmony_ci goto out; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci wl12xx_adjust_channels(cfg, cfg_channels); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!force_passive && cfg->active[0]) { 3678c2ecf20Sopenharmony_ci u8 band = NL80211_BAND_2GHZ; 3688c2ecf20Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 3698c2ecf20Sopenharmony_ci wlvif->role_id, band, 3708c2ecf20Sopenharmony_ci req->ssids[0].ssid, 3718c2ecf20Sopenharmony_ci req->ssids[0].ssid_len, 3728c2ecf20Sopenharmony_ci ies->ies[band], 3738c2ecf20Sopenharmony_ci ies->len[band], 3748c2ecf20Sopenharmony_ci ies->common_ies, 3758c2ecf20Sopenharmony_ci ies->common_ie_len, 3768c2ecf20Sopenharmony_ci true); 3778c2ecf20Sopenharmony_ci if (ret < 0) { 3788c2ecf20Sopenharmony_ci wl1271_error("2.4GHz PROBE request template failed"); 3798c2ecf20Sopenharmony_ci goto out; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (!force_passive && cfg->active[1]) { 3848c2ecf20Sopenharmony_ci u8 band = NL80211_BAND_5GHZ; 3858c2ecf20Sopenharmony_ci ret = wl12xx_cmd_build_probe_req(wl, wlvif, 3868c2ecf20Sopenharmony_ci wlvif->role_id, band, 3878c2ecf20Sopenharmony_ci req->ssids[0].ssid, 3888c2ecf20Sopenharmony_ci req->ssids[0].ssid_len, 3898c2ecf20Sopenharmony_ci ies->ies[band], 3908c2ecf20Sopenharmony_ci ies->len[band], 3918c2ecf20Sopenharmony_ci ies->common_ies, 3928c2ecf20Sopenharmony_ci ies->common_ie_len, 3938c2ecf20Sopenharmony_ci true); 3948c2ecf20Sopenharmony_ci if (ret < 0) { 3958c2ecf20Sopenharmony_ci wl1271_error("5GHz PROBE request template failed"); 3968c2ecf20Sopenharmony_ci goto out; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, 4038c2ecf20Sopenharmony_ci sizeof(*cfg), 0); 4048c2ecf20Sopenharmony_ci if (ret < 0) { 4058c2ecf20Sopenharmony_ci wl1271_error("SCAN configuration failed"); 4068c2ecf20Sopenharmony_ci goto out; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ciout: 4098c2ecf20Sopenharmony_ci kfree(cfg_channels); 4108c2ecf20Sopenharmony_ci kfree(cfg); 4118c2ecf20Sopenharmony_ci return ret; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ciint wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct wl1271_cmd_sched_scan_start *start; 4178c2ecf20Sopenharmony_ci int ret = 0; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (wlvif->bss_type != BSS_TYPE_STA_BSS) 4228c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if ((wl->quirks & WLCORE_QUIRK_NO_SCHED_SCAN_WHILE_CONN) && 4258c2ecf20Sopenharmony_ci test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) 4268c2ecf20Sopenharmony_ci return -EBUSY; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci start = kzalloc(sizeof(*start), GFP_KERNEL); 4298c2ecf20Sopenharmony_ci if (!start) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci start->role_id = wlvif->role_id; 4338c2ecf20Sopenharmony_ci start->tag = WL1271_SCAN_DEFAULT_TAG; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, 4368c2ecf20Sopenharmony_ci sizeof(*start), 0); 4378c2ecf20Sopenharmony_ci if (ret < 0) { 4388c2ecf20Sopenharmony_ci wl1271_error("failed to send scan start command"); 4398c2ecf20Sopenharmony_ci goto out_free; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ciout_free: 4438c2ecf20Sopenharmony_ci kfree(start); 4448c2ecf20Sopenharmony_ci return ret; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ciint wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 4488c2ecf20Sopenharmony_ci struct cfg80211_sched_scan_request *req, 4498c2ecf20Sopenharmony_ci struct ieee80211_scan_ies *ies) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ret = wl1271_scan_sched_scan_config(wl, wlvif, req, ies); 4548c2ecf20Sopenharmony_ci if (ret < 0) 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return wl1271_scan_sched_scan_start(wl, wlvif); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_civoid wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct wl1271_cmd_sched_scan_stop *stop; 4638c2ecf20Sopenharmony_ci int ret = 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* FIXME: what to do if alloc'ing to stop fails? */ 4688c2ecf20Sopenharmony_ci stop = kzalloc(sizeof(*stop), GFP_KERNEL); 4698c2ecf20Sopenharmony_ci if (!stop) { 4708c2ecf20Sopenharmony_ci wl1271_error("failed to alloc memory to send sched scan stop"); 4718c2ecf20Sopenharmony_ci return; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci stop->role_id = wlvif->role_id; 4758c2ecf20Sopenharmony_ci stop->tag = WL1271_SCAN_DEFAULT_TAG; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, 4788c2ecf20Sopenharmony_ci sizeof(*stop), 0); 4798c2ecf20Sopenharmony_ci if (ret < 0) { 4808c2ecf20Sopenharmony_ci wl1271_error("failed to send sched scan stop command"); 4818c2ecf20Sopenharmony_ci goto out_free; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ciout_free: 4858c2ecf20Sopenharmony_ci kfree(stop); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciint wl12xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 4898c2ecf20Sopenharmony_ci struct cfg80211_scan_request *req) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_civoid wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci wl1271_scan_stm(wl, wlvif); 4988c2ecf20Sopenharmony_ci} 499