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