1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * This file is part of wl18xx 4 * 5 * Copyright (C) 2012 Texas Instruments. All rights reserved. 6 */ 7 8 #include <linux/ieee80211.h> 9 #include "scan.h" 10 #include "../wlcore/debug.h" 11 12 static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd, 13 struct wlcore_scan_channels *cmd_channels) 14 { 15 memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); 16 memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); 17 cmd->dfs = cmd_channels->dfs; 18 cmd->passive_active = cmd_channels->passive_active; 19 20 memcpy(cmd->channels_2, cmd_channels->channels_2, 21 sizeof(cmd->channels_2)); 22 memcpy(cmd->channels_5, cmd_channels->channels_5, 23 sizeof(cmd->channels_5)); 24 /* channels_4 are not supported, so no need to copy them */ 25 } 26 27 static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, 28 struct cfg80211_scan_request *req) 29 { 30 struct wl18xx_cmd_scan_params *cmd; 31 struct wlcore_scan_channels *cmd_channels = NULL; 32 int ret; 33 34 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 35 if (!cmd) { 36 ret = -ENOMEM; 37 goto out; 38 } 39 40 /* scan on the dev role if the regular one is not started */ 41 if (wlcore_is_p2p_mgmt(wlvif)) 42 cmd->role_id = wlvif->dev_role_id; 43 else 44 cmd->role_id = wlvif->role_id; 45 46 if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { 47 ret = -EINVAL; 48 goto out; 49 } 50 51 cmd->scan_type = SCAN_TYPE_SEARCH; 52 cmd->rssi_threshold = -127; 53 cmd->snr_threshold = 0; 54 55 cmd->bss_type = SCAN_BSS_TYPE_ANY; 56 57 cmd->ssid_from_list = 0; 58 cmd->filter = 0; 59 cmd->add_broadcast = 0; 60 61 cmd->urgency = 0; 62 cmd->protect = 0; 63 64 cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs; 65 cmd->terminate_after = 0; 66 67 /* configure channels */ 68 WARN_ON(req->n_ssids > 1); 69 70 cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); 71 if (!cmd_channels) { 72 ret = -ENOMEM; 73 goto out; 74 } 75 76 wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, 77 req->n_channels, req->n_ssids, 78 SCAN_TYPE_SEARCH); 79 wl18xx_adjust_channels(cmd, cmd_channels); 80 81 /* 82 * all the cycles params (except total cycles) should 83 * remain 0 for normal scan 84 */ 85 cmd->total_cycles = 1; 86 87 if (req->no_cck) 88 cmd->rate = WL18XX_SCAN_RATE_6; 89 90 cmd->tag = WL1271_SCAN_DEFAULT_TAG; 91 92 if (req->n_ssids) { 93 cmd->ssid_len = req->ssids[0].ssid_len; 94 memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len); 95 } 96 97 /* TODO: per-band ies? */ 98 if (cmd->active[0]) { 99 u8 band = NL80211_BAND_2GHZ; 100 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 101 cmd->role_id, band, 102 req->ssids ? req->ssids[0].ssid : NULL, 103 req->ssids ? req->ssids[0].ssid_len : 0, 104 req->ie, 105 req->ie_len, 106 NULL, 107 0, 108 false); 109 if (ret < 0) { 110 wl1271_error("2.4GHz PROBE request template failed"); 111 goto out; 112 } 113 } 114 115 if (cmd->active[1] || cmd->dfs) { 116 u8 band = NL80211_BAND_5GHZ; 117 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 118 cmd->role_id, band, 119 req->ssids ? req->ssids[0].ssid : NULL, 120 req->ssids ? req->ssids[0].ssid_len : 0, 121 req->ie, 122 req->ie_len, 123 NULL, 124 0, 125 false); 126 if (ret < 0) { 127 wl1271_error("5GHz PROBE request template failed"); 128 goto out; 129 } 130 } 131 132 wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 133 134 ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 135 if (ret < 0) { 136 wl1271_error("SCAN failed"); 137 goto out; 138 } 139 140 out: 141 kfree(cmd_channels); 142 kfree(cmd); 143 return ret; 144 } 145 146 void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) 147 { 148 wl->scan.failed = false; 149 cancel_delayed_work(&wl->scan_complete_work); 150 ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 151 msecs_to_jiffies(0)); 152 } 153 154 static 155 int wl18xx_scan_sched_scan_config(struct wl1271 *wl, 156 struct wl12xx_vif *wlvif, 157 struct cfg80211_sched_scan_request *req, 158 struct ieee80211_scan_ies *ies) 159 { 160 struct wl18xx_cmd_scan_params *cmd; 161 struct wlcore_scan_channels *cmd_channels = NULL; 162 struct conf_sched_scan_settings *c = &wl->conf.sched_scan; 163 int ret; 164 int filter_type; 165 166 wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); 167 168 filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); 169 if (filter_type < 0) 170 return filter_type; 171 172 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 173 if (!cmd) { 174 ret = -ENOMEM; 175 goto out; 176 } 177 178 cmd->role_id = wlvif->role_id; 179 180 if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { 181 ret = -EINVAL; 182 goto out; 183 } 184 185 cmd->scan_type = SCAN_TYPE_PERIODIC; 186 cmd->rssi_threshold = c->rssi_threshold; 187 cmd->snr_threshold = c->snr_threshold; 188 189 /* don't filter on BSS type */ 190 cmd->bss_type = SCAN_BSS_TYPE_ANY; 191 192 cmd->ssid_from_list = 1; 193 if (filter_type == SCAN_SSID_FILTER_LIST) 194 cmd->filter = 1; 195 cmd->add_broadcast = 0; 196 197 cmd->urgency = 0; 198 cmd->protect = 0; 199 200 cmd->n_probe_reqs = c->num_probe_reqs; 201 /* don't stop scanning automatically when something is found */ 202 cmd->terminate_after = 0; 203 204 cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); 205 if (!cmd_channels) { 206 ret = -ENOMEM; 207 goto out; 208 } 209 210 /* configure channels */ 211 wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, 212 req->n_channels, req->n_ssids, 213 SCAN_TYPE_PERIODIC); 214 wl18xx_adjust_channels(cmd, cmd_channels); 215 216 if (c->num_short_intervals && c->long_interval && 217 c->long_interval > req->scan_plans[0].interval * MSEC_PER_SEC) { 218 cmd->short_cycles_msec = 219 cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); 220 cmd->long_cycles_msec = cpu_to_le16(c->long_interval); 221 cmd->short_cycles_count = c->num_short_intervals; 222 } else { 223 cmd->short_cycles_msec = 0; 224 cmd->long_cycles_msec = 225 cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); 226 cmd->short_cycles_count = 0; 227 } 228 wl1271_debug(DEBUG_SCAN, "short_interval: %d, long_interval: %d, num_short: %d", 229 le16_to_cpu(cmd->short_cycles_msec), 230 le16_to_cpu(cmd->long_cycles_msec), 231 cmd->short_cycles_count); 232 233 cmd->total_cycles = 0; 234 235 cmd->tag = WL1271_SCAN_DEFAULT_TAG; 236 237 /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ 238 cmd->report_threshold = 1; 239 cmd->terminate_on_report = 0; 240 241 if (cmd->active[0]) { 242 u8 band = NL80211_BAND_2GHZ; 243 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 244 cmd->role_id, band, 245 req->ssids ? req->ssids[0].ssid : NULL, 246 req->ssids ? req->ssids[0].ssid_len : 0, 247 ies->ies[band], 248 ies->len[band], 249 ies->common_ies, 250 ies->common_ie_len, 251 true); 252 if (ret < 0) { 253 wl1271_error("2.4GHz PROBE request template failed"); 254 goto out; 255 } 256 } 257 258 if (cmd->active[1] || cmd->dfs) { 259 u8 band = NL80211_BAND_5GHZ; 260 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 261 cmd->role_id, band, 262 req->ssids ? req->ssids[0].ssid : NULL, 263 req->ssids ? req->ssids[0].ssid_len : 0, 264 ies->ies[band], 265 ies->len[band], 266 ies->common_ies, 267 ies->common_ie_len, 268 true); 269 if (ret < 0) { 270 wl1271_error("5GHz PROBE request template failed"); 271 goto out; 272 } 273 } 274 275 wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 276 277 ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 278 if (ret < 0) { 279 wl1271_error("SCAN failed"); 280 goto out; 281 } 282 283 out: 284 kfree(cmd_channels); 285 kfree(cmd); 286 return ret; 287 } 288 289 int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 290 struct cfg80211_sched_scan_request *req, 291 struct ieee80211_scan_ies *ies) 292 { 293 return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); 294 } 295 296 static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif, 297 u8 scan_type) 298 { 299 struct wl18xx_cmd_scan_stop *stop; 300 int ret; 301 302 wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); 303 304 stop = kzalloc(sizeof(*stop), GFP_KERNEL); 305 if (!stop) { 306 wl1271_error("failed to alloc memory to send sched scan stop"); 307 return -ENOMEM; 308 } 309 310 stop->role_id = wlvif->role_id; 311 stop->scan_type = scan_type; 312 313 ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0); 314 if (ret < 0) { 315 wl1271_error("failed to send sched scan stop command"); 316 goto out_free; 317 } 318 319 out_free: 320 kfree(stop); 321 return ret; 322 } 323 324 void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 325 { 326 __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC); 327 } 328 int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 329 struct cfg80211_scan_request *req) 330 { 331 return wl18xx_scan_send(wl, wlvif, req); 332 } 333 334 int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 335 { 336 return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH); 337 } 338