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, 37 sizeof(cmd->channels_5)); 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 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 false); 117 if (ret < 0) { 118 wl1271_error("2.4GHz PROBE request template failed"); 119 goto out; 120 } 121 } 122 123 if (cmd->active[1] || cmd->dfs) { 124 u8 band = IEEE80211_BAND_5GHZ; 125 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 126 cmd->role_id, band, 127 req->ssids ? req->ssids[0].ssid : NULL, 128 req->ssids ? req->ssids[0].ssid_len : 0, 129 req->ie, 130 req->ie_len, 131 false); 132 if (ret < 0) { 133 wl1271_error("5GHz PROBE request template failed"); 134 goto out; 135 } 136 } 137 138 wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 139 140 ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 141 if (ret < 0) { 142 wl1271_error("SCAN failed"); 143 goto out; 144 } 145 146 out: 147 kfree(cmd_channels); 148 kfree(cmd); 149 return ret; 150 } 151 152 void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) 153 { 154 wl->scan.failed = false; 155 cancel_delayed_work(&wl->scan_complete_work); 156 ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, 157 msecs_to_jiffies(0)); 158 } 159 160 static 161 int wl18xx_scan_sched_scan_config(struct wl1271 *wl, 162 struct wl12xx_vif *wlvif, 163 struct cfg80211_sched_scan_request *req, 164 struct ieee80211_sched_scan_ies *ies) 165 { 166 struct wl18xx_cmd_scan_params *cmd; 167 struct wlcore_scan_channels *cmd_channels = NULL; 168 struct conf_sched_scan_settings *c = &wl->conf.sched_scan; 169 int ret; 170 int filter_type; 171 172 wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); 173 174 filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); 175 if (filter_type < 0) 176 return filter_type; 177 178 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 179 if (!cmd) { 180 ret = -ENOMEM; 181 goto out; 182 } 183 184 cmd->role_id = wlvif->role_id; 185 186 if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { 187 ret = -EINVAL; 188 goto out; 189 } 190 191 cmd->scan_type = SCAN_TYPE_PERIODIC; 192 cmd->rssi_threshold = c->rssi_threshold; 193 cmd->snr_threshold = c->snr_threshold; 194 195 /* don't filter on BSS type */ 196 cmd->bss_type = SCAN_BSS_TYPE_ANY; 197 198 cmd->ssid_from_list = 1; 199 if (filter_type == SCAN_SSID_FILTER_LIST) 200 cmd->filter = 1; 201 cmd->add_broadcast = 0; 202 203 cmd->urgency = 0; 204 cmd->protect = 0; 205 206 cmd->n_probe_reqs = c->num_probe_reqs; 207 /* don't stop scanning automatically when something is found */ 208 cmd->terminate_after = 0; 209 210 cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); 211 if (!cmd_channels) { 212 ret = -ENOMEM; 213 goto out; 214 } 215 216 /* configure channels */ 217 wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, 218 req->n_channels, req->n_ssids, 219 SCAN_TYPE_PERIODIC); 220 wl18xx_adjust_channels(cmd, cmd_channels); 221 222 cmd->short_cycles_sec = 0; 223 cmd->long_cycles_sec = cpu_to_le16(req->interval); 224 cmd->short_cycles_count = 0; 225 226 cmd->total_cycles = 0; 227 228 cmd->tag = WL1271_SCAN_DEFAULT_TAG; 229 230 /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ 231 cmd->report_threshold = 1; 232 cmd->terminate_on_report = 0; 233 234 if (cmd->active[0]) { 235 u8 band = IEEE80211_BAND_2GHZ; 236 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 237 cmd->role_id, band, 238 req->ssids ? req->ssids[0].ssid : NULL, 239 req->ssids ? req->ssids[0].ssid_len : 0, 240 ies->ie[band], 241 ies->len[band], 242 true); 243 if (ret < 0) { 244 wl1271_error("2.4GHz PROBE request template failed"); 245 goto out; 246 } 247 } 248 249 if (cmd->active[1] || cmd->dfs) { 250 u8 band = IEEE80211_BAND_5GHZ; 251 ret = wl12xx_cmd_build_probe_req(wl, wlvif, 252 cmd->role_id, band, 253 req->ssids ? req->ssids[0].ssid : NULL, 254 req->ssids ? req->ssids[0].ssid_len : 0, 255 ies->ie[band], 256 ies->len[band], 257 true); 258 if (ret < 0) { 259 wl1271_error("5GHz PROBE request template failed"); 260 goto out; 261 } 262 } 263 264 wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); 265 266 ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); 267 if (ret < 0) { 268 wl1271_error("SCAN failed"); 269 goto out; 270 } 271 272 out: 273 kfree(cmd_channels); 274 kfree(cmd); 275 return ret; 276 } 277 278 int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 279 struct cfg80211_sched_scan_request *req, 280 struct ieee80211_sched_scan_ies *ies) 281 { 282 return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); 283 } 284 285 static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif, 286 u8 scan_type) 287 { 288 struct wl18xx_cmd_scan_stop *stop; 289 int ret; 290 291 wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); 292 293 stop = kzalloc(sizeof(*stop), GFP_KERNEL); 294 if (!stop) { 295 wl1271_error("failed to alloc memory to send sched scan stop"); 296 return -ENOMEM; 297 } 298 299 stop->role_id = wlvif->role_id; 300 stop->scan_type = scan_type; 301 302 ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0); 303 if (ret < 0) { 304 wl1271_error("failed to send sched scan stop command"); 305 goto out_free; 306 } 307 308 out_free: 309 kfree(stop); 310 return ret; 311 } 312 313 void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 314 { 315 __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC); 316 } 317 int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, 318 struct cfg80211_scan_request *req) 319 { 320 return wl18xx_scan_send(wl, wlvif, req); 321 } 322 323 int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) 324 { 325 return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH); 326 } 327