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