blob: 08f7e826d27ba27531ee564962355558435d0381 [file] [log] [blame]
Luciano Coelho34dd2aa2010-07-08 17:50:06 +03001/*
2 * This file is part of wl1271
3 *
4 * Copyright (C) 2009-2010 Nokia Corporation
5 *
6 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24#include <linux/ieee80211.h>
25
Shahar Levi00d20102010-11-08 11:20:10 +000026#include "wl12xx.h"
27#include "cmd.h"
28#include "scan.h"
29#include "acx.h"
Arik Nemtsov24225b32011-03-01 12:27:26 +020030#include "ps.h"
Luciano Coelho34dd2aa2010-07-08 17:50:06 +030031
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +030032void wl1271_scan_complete_work(struct work_struct *work)
33{
Juuso Oikarinen78abd322010-09-21 06:23:32 +020034 struct delayed_work *dwork;
35 struct wl1271 *wl;
Eliad Peller251c1772011-08-14 13:17:17 +030036 int ret;
Eliad Peller227e81e2011-08-14 13:17:26 +030037 bool is_sta, is_ibss;
Juuso Oikarinen78abd322010-09-21 06:23:32 +020038
39 dwork = container_of(work, struct delayed_work, work);
40 wl = container_of(dwork, struct wl1271, scan_complete_work);
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +030041
42 wl1271_debug(DEBUG_SCAN, "Scanning complete");
43
44 mutex_lock(&wl->mutex);
Juuso Oikarinen52a2a372010-09-21 06:23:30 +020045
Arik Nemtsov24225b32011-03-01 12:27:26 +020046 if (wl->state == WL1271_STATE_OFF)
47 goto out;
48
49 if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
50 goto out;
Juuso Oikarinen52a2a372010-09-21 06:23:30 +020051
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +030052 wl->scan.state = WL1271_SCAN_STATE_IDLE;
Luciano Coelho4a31c112011-03-21 23:16:14 +020053 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Juuso Oikarinenb739a422010-10-26 13:24:38 +020054 wl->scan.req = NULL;
Juuso Oikarinen78abd322010-09-21 06:23:32 +020055
Eliad Peller251c1772011-08-14 13:17:17 +030056 ret = wl1271_ps_elp_wakeup(wl);
57 if (ret < 0)
58 goto out;
59
Arik Nemtsov24225b32011-03-01 12:27:26 +020060 if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) {
Eliad Peller251c1772011-08-14 13:17:17 +030061 /* restore hardware connection monitoring template */
62 wl1271_cmd_build_ap_probe_req(wl, wl->probereq);
Eliad Peller227e81e2011-08-14 13:17:26 +030063 }
64
65 /* return to ROC if needed */
66 is_sta = (wl->bss_type == BSS_TYPE_STA_BSS);
67 is_ibss = (wl->bss_type == BSS_TYPE_IBSS);
Eliad Pellere0b38262011-08-28 15:11:55 +030068 if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) ||
69 (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) &&
70 !test_bit(wl->dev_role_id, wl->roc_map)) {
Eliad Peller251c1772011-08-14 13:17:17 +030071 /* restore remain on channel */
72 wl12xx_cmd_role_start_dev(wl);
73 wl12xx_roc(wl, wl->dev_role_id);
Arik Nemtsov24225b32011-03-01 12:27:26 +020074 }
Eliad Peller251c1772011-08-14 13:17:17 +030075 wl1271_ps_elp_sleep(wl);
Juuso Oikarinen2f6724b2010-11-24 08:16:57 +020076
Juuso Oikarinen78abd322010-09-21 06:23:32 +020077 if (wl->scan.failed) {
78 wl1271_info("Scan completed due to error.");
Ido Yarivbaacb9a2011-06-06 14:57:05 +030079 wl12xx_queue_recovery_work(wl);
Juuso Oikarinen78abd322010-09-21 06:23:32 +020080 }
Arik Nemtsov24225b32011-03-01 12:27:26 +020081
Eliad Peller251c1772011-08-14 13:17:17 +030082 ieee80211_scan_completed(wl->hw, false);
83
Arik Nemtsov24225b32011-03-01 12:27:26 +020084out:
Juuso Oikarinenb739a422010-10-26 13:24:38 +020085 mutex_unlock(&wl->mutex);
86
Juuso Oikarinenc454f1d2010-08-24 06:28:03 +030087}
88
89
Luciano Coelho08688d62010-07-08 17:50:07 +030090static int wl1271_get_scan_channels(struct wl1271 *wl,
91 struct cfg80211_scan_request *req,
92 struct basic_scan_channel_params *channels,
93 enum ieee80211_band band, bool passive)
Luciano Coelho34dd2aa2010-07-08 17:50:06 +030094{
Juuso Oikarinenbea39d62010-09-21 08:14:31 +020095 struct conf_scan_settings *c = &wl->conf.scan;
Luciano Coelho08688d62010-07-08 17:50:07 +030096 int i, j;
97 u32 flags;
Luciano Coelho34dd2aa2010-07-08 17:50:06 +030098
Luciano Coelho08688d62010-07-08 17:50:07 +030099 for (i = 0, j = 0;
100 i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
101 i++) {
Luciano Coelho08688d62010-07-08 17:50:07 +0300102 flags = req->channels[i]->flags;
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300103
Luciano Coelho4a31c112011-03-21 23:16:14 +0200104 if (!test_bit(i, wl->scan.scanned_ch) &&
Luciano Coelho08688d62010-07-08 17:50:07 +0300105 !(flags & IEEE80211_CHAN_DISABLED) &&
Luciano Coelho6cd9d212011-09-22 10:06:10 +0300106 (req->channels[i]->band == band) &&
107 /*
108 * In passive scans, we scan all remaining
109 * channels, even if not marked as such.
110 * In active scans, we only scan channels not
111 * marked as passive.
112 */
113 (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) {
Luciano Coelho08688d62010-07-08 17:50:07 +0300114 wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
115 req->channels[i]->band,
116 req->channels[i]->center_freq);
117 wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
118 req->channels[i]->hw_value,
119 req->channels[i]->flags);
120 wl1271_debug(DEBUG_SCAN,
121 "max_antenna_gain %d, max_power %d",
122 req->channels[i]->max_antenna_gain,
123 req->channels[i]->max_power);
124 wl1271_debug(DEBUG_SCAN, "beacon_found %d",
125 req->channels[i]->beacon_found);
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300126
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200127 if (!passive) {
128 channels[j].min_duration =
129 cpu_to_le32(c->min_dwell_time_active);
130 channels[j].max_duration =
131 cpu_to_le32(c->max_dwell_time_active);
132 } else {
133 channels[j].min_duration =
134 cpu_to_le32(c->min_dwell_time_passive);
135 channels[j].max_duration =
136 cpu_to_le32(c->max_dwell_time_passive);
137 }
Luciano Coelho08688d62010-07-08 17:50:07 +0300138 channels[j].early_termination = 0;
Luciano Coelho3cc7b542010-07-08 17:50:08 +0300139 channels[j].tx_power_att = req->channels[i]->max_power;
Luciano Coelho08688d62010-07-08 17:50:07 +0300140 channels[j].channel = req->channels[i]->hw_value;
141
142 memset(&channels[j].bssid_lsb, 0xff, 4);
143 memset(&channels[j].bssid_msb, 0xff, 2);
144
145 /* Mark the channels we already used */
Luciano Coelho4a31c112011-03-21 23:16:14 +0200146 set_bit(i, wl->scan.scanned_ch);
Luciano Coelho08688d62010-07-08 17:50:07 +0300147
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300148 j++;
149 }
150 }
151
Luciano Coelho08688d62010-07-08 17:50:07 +0300152 return j;
153}
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300154
Luciano Coelho08688d62010-07-08 17:50:07 +0300155#define WL1271_NOTHING_TO_SCAN 1
156
157static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
158 bool passive, u32 basic_rate)
159{
160 struct wl1271_cmd_scan *cmd;
161 struct wl1271_cmd_trigger_scan_to *trigger;
162 int ret;
163 u16 scan_options = 0;
164
Luciano Coelho6cd9d212011-09-22 10:06:10 +0300165 /* skip active scans if we don't have SSIDs */
166 if (!passive && wl->scan.req->n_ssids == 0)
167 return WL1271_NOTHING_TO_SCAN;
168
Luciano Coelho08688d62010-07-08 17:50:07 +0300169 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
170 trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
171 if (!cmd || !trigger) {
172 ret = -ENOMEM;
173 goto out;
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300174 }
175
Luciano Coelho6cd9d212011-09-22 10:06:10 +0300176 if (passive)
Luciano Coelho08688d62010-07-08 17:50:07 +0300177 scan_options |= WL1271_SCAN_OPT_PASSIVE;
Luciano Coelho4f35c022010-08-04 04:36:32 +0300178
Eliad Pellera4e02f32011-08-14 13:17:10 +0300179 if (WARN_ON(wl->role_id == WL12XX_INVALID_ROLE_ID)) {
180 ret = -EINVAL;
181 goto out;
182 }
183 cmd->params.role_id = wl->role_id;
Luciano Coelho08688d62010-07-08 17:50:07 +0300184 cmd->params.scan_options = cpu_to_le16(scan_options);
185
186 cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
187 cmd->channels,
188 band, passive);
189 if (cmd->params.n_ch == 0) {
190 ret = WL1271_NOTHING_TO_SCAN;
191 goto out;
192 }
193
194 cmd->params.tx_rate = cpu_to_le32(basic_rate);
Juuso Oikarinenbea39d62010-09-21 08:14:31 +0200195 cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs;
Luciano Coelho08688d62010-07-08 17:50:07 +0300196 cmd->params.tx_rate = cpu_to_le32(basic_rate);
197 cmd->params.tid_trigger = 0;
198 cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
199
200 if (band == IEEE80211_BAND_2GHZ)
201 cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
202 else
203 cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
204
205 if (wl->scan.ssid_len && wl->scan.ssid) {
206 cmd->params.ssid_len = wl->scan.ssid_len;
207 memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
208 }
209
Eliad Pellera4e02f32011-08-14 13:17:10 +0300210 memcpy(cmd->addr, wl->mac_addr, ETH_ALEN);
211
Luciano Coelho08688d62010-07-08 17:50:07 +0300212 ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len,
213 wl->scan.req->ie, wl->scan.req->ie_len,
214 band);
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300215 if (ret < 0) {
216 wl1271_error("PROBE request template failed");
217 goto out;
218 }
219
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300220 /* disable the timeout */
221 trigger->timeout = 0;
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300222 ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
223 sizeof(*trigger), 0);
224 if (ret < 0) {
225 wl1271_error("trigger scan to failed for hw scan");
226 goto out;
227 }
228
Luciano Coelho08688d62010-07-08 17:50:07 +0300229 wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300230
Luciano Coelho08688d62010-07-08 17:50:07 +0300231 ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300232 if (ret < 0) {
233 wl1271_error("SCAN failed");
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300234 goto out;
235 }
236
237out:
Luciano Coelho08688d62010-07-08 17:50:07 +0300238 kfree(cmd);
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300239 kfree(trigger);
240 return ret;
241}
242
Luciano Coelho08688d62010-07-08 17:50:07 +0300243void wl1271_scan_stm(struct wl1271 *wl)
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300244{
Juuso Oikarinen78abd322010-09-21 06:23:32 +0200245 int ret = 0;
Luciano Coelho08688d62010-07-08 17:50:07 +0300246
247 switch (wl->scan.state) {
248 case WL1271_SCAN_STATE_IDLE:
249 break;
250
251 case WL1271_SCAN_STATE_2GHZ_ACTIVE:
252 ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, false,
253 wl->conf.tx.basic_rate);
254 if (ret == WL1271_NOTHING_TO_SCAN) {
255 wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
256 wl1271_scan_stm(wl);
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300257 }
Luciano Coelho08688d62010-07-08 17:50:07 +0300258
259 break;
260
261 case WL1271_SCAN_STATE_2GHZ_PASSIVE:
262 ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true,
263 wl->conf.tx.basic_rate);
264 if (ret == WL1271_NOTHING_TO_SCAN) {
Juuso Oikarinen02fabb02010-08-19 04:41:15 +0200265 if (wl->enable_11a)
Luciano Coelho08688d62010-07-08 17:50:07 +0300266 wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
267 else
268 wl->scan.state = WL1271_SCAN_STATE_DONE;
269 wl1271_scan_stm(wl);
270 }
271
272 break;
273
274 case WL1271_SCAN_STATE_5GHZ_ACTIVE:
275 ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, false,
276 wl->conf.tx.basic_rate_5);
277 if (ret == WL1271_NOTHING_TO_SCAN) {
278 wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
279 wl1271_scan_stm(wl);
280 }
281
282 break;
283
284 case WL1271_SCAN_STATE_5GHZ_PASSIVE:
285 ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, true,
286 wl->conf.tx.basic_rate_5);
287 if (ret == WL1271_NOTHING_TO_SCAN) {
288 wl->scan.state = WL1271_SCAN_STATE_DONE;
289 wl1271_scan_stm(wl);
290 }
291
292 break;
293
294 case WL1271_SCAN_STATE_DONE:
Juuso Oikarinen78abd322010-09-21 06:23:32 +0200295 wl->scan.failed = false;
296 cancel_delayed_work(&wl->scan_complete_work);
297 ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
298 msecs_to_jiffies(0));
Luciano Coelho08688d62010-07-08 17:50:07 +0300299 break;
300
301 default:
302 wl1271_error("invalid scan state");
303 break;
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300304 }
Juuso Oikarinen78abd322010-09-21 06:23:32 +0200305
306 if (ret < 0) {
307 cancel_delayed_work(&wl->scan_complete_work);
308 ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
309 msecs_to_jiffies(0));
310 }
Luciano Coelho08688d62010-07-08 17:50:07 +0300311}
312
313int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
314 struct cfg80211_scan_request *req)
315{
Luciano Coelho4a31c112011-03-21 23:16:14 +0200316 /*
317 * cfg80211 should guarantee that we don't get more channels
318 * than what we have registered.
319 */
320 BUG_ON(req->n_channels > WL1271_MAX_CHANNELS);
321
Luciano Coelho08688d62010-07-08 17:50:07 +0300322 if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
323 return -EBUSY;
324
325 wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
326
327 if (ssid_len && ssid) {
328 wl->scan.ssid_len = ssid_len;
329 memcpy(wl->scan.ssid, ssid, ssid_len);
330 } else {
331 wl->scan.ssid_len = 0;
332 }
333
334 wl->scan.req = req;
Luciano Coelho4a31c112011-03-21 23:16:14 +0200335 memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch));
Luciano Coelho08688d62010-07-08 17:50:07 +0300336
Juuso Oikarinen78abd322010-09-21 06:23:32 +0200337 /* we assume failure so that timeout scenarios are handled correctly */
338 wl->scan.failed = true;
339 ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
340 msecs_to_jiffies(WL1271_SCAN_TIMEOUT));
341
Luciano Coelho08688d62010-07-08 17:50:07 +0300342 wl1271_scan_stm(wl);
343
Luciano Coelho34dd2aa2010-07-08 17:50:06 +0300344 return 0;
345}
Luciano Coelho95feadc2011-05-10 14:38:59 +0300346
Eliad Peller2aa01592011-06-27 13:06:44 +0300347int wl1271_scan_stop(struct wl1271 *wl)
348{
349 struct wl1271_cmd_header *cmd = NULL;
350 int ret = 0;
351
352 if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE))
353 return -EINVAL;
354
355 wl1271_debug(DEBUG_CMD, "cmd scan stop");
356
357 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
358 if (!cmd) {
359 ret = -ENOMEM;
360 goto out;
361 }
362
363 ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd,
364 sizeof(*cmd), 0);
365 if (ret < 0) {
366 wl1271_error("cmd stop_scan failed");
367 goto out;
368 }
369out:
370 kfree(cmd);
371 return ret;
372}
373
Luciano Coelho95feadc2011-05-10 14:38:59 +0300374static int
375wl1271_scan_get_sched_scan_channels(struct wl1271 *wl,
376 struct cfg80211_sched_scan_request *req,
377 struct conn_scan_ch_params *channels,
378 u32 band, bool radar, bool passive,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300379 int start, int max_channels)
Luciano Coelho95feadc2011-05-10 14:38:59 +0300380{
381 struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
382 int i, j;
383 u32 flags;
Luciano Coelho66870b12011-05-27 15:34:48 +0300384 bool force_passive = !req->n_ssids;
Luciano Coelho95feadc2011-05-10 14:38:59 +0300385
386 for (i = 0, j = start;
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300387 i < req->n_channels && j < max_channels;
Luciano Coelho95feadc2011-05-10 14:38:59 +0300388 i++) {
389 flags = req->channels[i]->flags;
390
Luciano Coelho66870b12011-05-27 15:34:48 +0300391 if (force_passive)
392 flags |= IEEE80211_CHAN_PASSIVE_SCAN;
393
Luciano Coelho2497a242011-05-27 15:34:46 +0300394 if ((req->channels[i]->band == band) &&
395 !(flags & IEEE80211_CHAN_DISABLED) &&
Luciano Coelhodd086822011-05-27 15:34:45 +0300396 (!!(flags & IEEE80211_CHAN_RADAR) == radar) &&
Luciano Coelho2497a242011-05-27 15:34:46 +0300397 /* if radar is set, we ignore the passive flag */
398 (radar ||
399 !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300400 wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
401 req->channels[i]->band,
402 req->channels[i]->center_freq);
403 wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
404 req->channels[i]->hw_value,
405 req->channels[i]->flags);
406 wl1271_debug(DEBUG_SCAN, "max_power %d",
407 req->channels[i]->max_power);
408
Luciano Coelho50a66d72011-05-27 15:34:47 +0300409 if (flags & IEEE80211_CHAN_RADAR) {
Luciano Coelho2497a242011-05-27 15:34:46 +0300410 channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS;
Luciano Coelho50a66d72011-05-27 15:34:47 +0300411 channels[j].passive_duration =
412 cpu_to_le16(c->dwell_time_dfs);
413 }
414 else if (flags & IEEE80211_CHAN_PASSIVE_SCAN) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300415 channels[j].passive_duration =
416 cpu_to_le16(c->dwell_time_passive);
417 } else {
418 channels[j].min_duration =
419 cpu_to_le16(c->min_dwell_time_active);
420 channels[j].max_duration =
421 cpu_to_le16(c->max_dwell_time_active);
422 }
Luciano Coelho2497a242011-05-27 15:34:46 +0300423 channels[j].tx_power_att = req->channels[i]->max_power;
Luciano Coelho95feadc2011-05-10 14:38:59 +0300424 channels[j].channel = req->channels[i]->hw_value;
425
426 j++;
427 }
428 }
429
430 return j - start;
431}
432
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300433static bool
Luciano Coelho95feadc2011-05-10 14:38:59 +0300434wl1271_scan_sched_scan_channels(struct wl1271 *wl,
435 struct cfg80211_sched_scan_request *req,
436 struct wl1271_cmd_sched_scan_config *cfg)
437{
Luciano Coelho95feadc2011-05-10 14:38:59 +0300438 cfg->passive[0] =
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300439 wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
Luciano Coelho95feadc2011-05-10 14:38:59 +0300440 IEEE80211_BAND_2GHZ,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300441 false, true, 0,
442 MAX_CHANNELS_2GHZ);
Luciano Coelho95feadc2011-05-10 14:38:59 +0300443 cfg->active[0] =
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300444 wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2,
Luciano Coelho95feadc2011-05-10 14:38:59 +0300445 IEEE80211_BAND_2GHZ,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300446 false, false,
447 cfg->passive[0],
448 MAX_CHANNELS_2GHZ);
Luciano Coelho95feadc2011-05-10 14:38:59 +0300449 cfg->passive[1] =
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300450 wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
Luciano Coelho95feadc2011-05-10 14:38:59 +0300451 IEEE80211_BAND_5GHZ,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300452 false, true, 0,
453 MAX_CHANNELS_5GHZ);
Luciano Coelho95feadc2011-05-10 14:38:59 +0300454 cfg->dfs =
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300455 wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
Luciano Coelho95feadc2011-05-10 14:38:59 +0300456 IEEE80211_BAND_5GHZ,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300457 true, true,
458 cfg->passive[1],
459 MAX_CHANNELS_5GHZ);
Luciano Coelho2497a242011-05-27 15:34:46 +0300460 cfg->active[1] =
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300461 wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5,
Luciano Coelho2497a242011-05-27 15:34:46 +0300462 IEEE80211_BAND_5GHZ,
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300463 false, false,
464 cfg->passive[1] + cfg->dfs,
465 MAX_CHANNELS_5GHZ);
466 /* 802.11j channels are not supported yet */
467 cfg->passive[2] = 0;
468 cfg->active[2] = 0;
Luciano Coelho2497a242011-05-27 15:34:46 +0300469
Luciano Coelho95feadc2011-05-10 14:38:59 +0300470 wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d",
471 cfg->active[0], cfg->passive[0]);
472 wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d",
473 cfg->active[1], cfg->passive[1]);
Luciano Coelho2497a242011-05-27 15:34:46 +0300474 wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs);
Luciano Coelho95feadc2011-05-10 14:38:59 +0300475
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300476 return cfg->passive[0] || cfg->active[0] ||
477 cfg->passive[1] || cfg->active[1] || cfg->dfs ||
478 cfg->passive[2] || cfg->active[2];
Luciano Coelho95feadc2011-05-10 14:38:59 +0300479}
480
Luciano Coelhofb553772011-09-02 14:28:21 +0300481/* Returns the scan type to be used or a negative value on error */
Luciano Coelhof9520792011-08-23 18:34:44 +0300482static int
483wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl,
484 struct cfg80211_sched_scan_request *req)
485{
486 struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL;
Luciano Coelhofb553772011-09-02 14:28:21 +0300487 struct cfg80211_match_set *sets = req->match_sets;
488 struct cfg80211_ssid *ssids = req->ssids;
Luciano Coelho20a33e52011-09-02 14:28:23 +0300489 int ret = 0, type, i, j, n_match_ssids = 0;
Luciano Coelhof9520792011-08-23 18:34:44 +0300490
491 wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list");
492
Luciano Coelho20a33e52011-09-02 14:28:23 +0300493 /* count the match sets that contain SSIDs */
494 for (i = 0; i < req->n_match_sets; i++)
495 if (sets[i].ssid.ssid_len > 0)
496 n_match_ssids++;
497
Luciano Coelhofb553772011-09-02 14:28:21 +0300498 /* No filter, no ssids or only bcast ssid */
Luciano Coelho20a33e52011-09-02 14:28:23 +0300499 if (!n_match_ssids &&
Luciano Coelhofb553772011-09-02 14:28:21 +0300500 (!req->n_ssids ||
501 (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) {
502 type = SCAN_SSID_FILTER_ANY;
503 goto out;
504 }
Luciano Coelhof9520792011-08-23 18:34:44 +0300505
Luciano Coelhofb553772011-09-02 14:28:21 +0300506 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
507 if (!cmd) {
508 ret = -ENOMEM;
509 goto out;
510 }
511
Luciano Coelho20a33e52011-09-02 14:28:23 +0300512 if (!n_match_ssids) {
Luciano Coelhofb553772011-09-02 14:28:21 +0300513 /* No filter, with ssids */
514 type = SCAN_SSID_FILTER_DISABLED;
515
516 for (i = 0; i < req->n_ssids; i++) {
517 cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ?
518 SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC;
519 cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len;
520 memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid,
521 ssids[i].ssid_len);
522 cmd->n_ssids++;
Luciano Coelhobd4932b2011-08-23 18:34:45 +0300523 }
Luciano Coelhofb553772011-09-02 14:28:21 +0300524 } else {
525 type = SCAN_SSID_FILTER_LIST;
526
527 /* Add all SSIDs from the filters */
528 for (i = 0; i < req->n_match_sets; i++) {
Luciano Coelho20a33e52011-09-02 14:28:23 +0300529 /* ignore sets without SSIDs */
530 if (!sets[i].ssid.ssid_len)
531 continue;
532
Luciano Coelhofb553772011-09-02 14:28:21 +0300533 cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC;
534 cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len;
535 memcpy(cmd->ssids[cmd->n_ssids].ssid,
536 sets[i].ssid.ssid, sets[i].ssid.ssid_len);
537 cmd->n_ssids++;
538 }
539 if ((req->n_ssids > 1) ||
540 (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) {
541 /*
542 * Mark all the SSIDs passed in the SSID list as HIDDEN,
543 * so they're used in probe requests.
544 */
545 for (i = 0; i < req->n_ssids; i++) {
546 for (j = 0; j < cmd->n_ssids; j++)
547 if (!memcmp(req->ssids[i].ssid,
548 cmd->ssids[j].ssid,
549 req->ssids[i].ssid_len)) {
550 cmd->ssids[j].type =
551 SCAN_SSID_TYPE_HIDDEN;
552 break;
553 }
554 /* Fail if SSID isn't present in the filters */
555 if (j == req->n_ssids) {
556 ret = -EINVAL;
557 goto out_free;
558 }
559 }
560 }
Luciano Coelhof9520792011-08-23 18:34:44 +0300561 }
562
563 wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd));
564
565 ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd,
566 sizeof(*cmd), 0);
567 if (ret < 0) {
568 wl1271_error("cmd sched scan ssid list failed");
Luciano Coelhofb553772011-09-02 14:28:21 +0300569 goto out_free;
Luciano Coelhof9520792011-08-23 18:34:44 +0300570 }
571
Luciano Coelhofb553772011-09-02 14:28:21 +0300572out_free:
Luciano Coelhof9520792011-08-23 18:34:44 +0300573 kfree(cmd);
Luciano Coelhofb553772011-09-02 14:28:21 +0300574out:
575 if (ret < 0)
576 return ret;
577 return type;
Luciano Coelhof9520792011-08-23 18:34:44 +0300578}
579
Luciano Coelho95feadc2011-05-10 14:38:59 +0300580int wl1271_scan_sched_scan_config(struct wl1271 *wl,
581 struct cfg80211_sched_scan_request *req,
582 struct ieee80211_sched_scan_ies *ies)
583{
584 struct wl1271_cmd_sched_scan_config *cfg = NULL;
585 struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300586 int i, ret;
Luciano Coelho66870b12011-05-27 15:34:48 +0300587 bool force_passive = !req->n_ssids;
Luciano Coelho95feadc2011-05-10 14:38:59 +0300588
589 wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
590
591 cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
592 if (!cfg)
593 return -ENOMEM;
594
595 cfg->rssi_threshold = c->rssi_threshold;
596 cfg->snr_threshold = c->snr_threshold;
597 cfg->n_probe_reqs = c->num_probe_reqs;
598 /* cycles set to 0 it means infinite (until manually stopped) */
599 cfg->cycles = 0;
600 /* report APs when at least 1 is found */
601 cfg->report_after = 1;
602 /* don't stop scanning automatically when something is found */
603 cfg->terminate = 0;
604 cfg->tag = WL1271_SCAN_DEFAULT_TAG;
605 /* don't filter on BSS type */
606 cfg->bss_type = SCAN_BSS_TYPE_ANY;
607 /* currently NL80211 supports only a single interval */
608 for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++)
609 cfg->intervals[i] = cpu_to_le32(req->interval);
610
Luciano Coelhof9520792011-08-23 18:34:44 +0300611 cfg->ssid_len = 0;
Luciano Coelhofb553772011-09-02 14:28:21 +0300612 ret = wl12xx_scan_sched_scan_ssid_list(wl, req);
613 if (ret < 0)
614 goto out;
615
616 cfg->filter_type = ret;
617
618 wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type);
Luciano Coelho95feadc2011-05-10 14:38:59 +0300619
Luciano Coelhod2c2bb9f2011-05-31 16:38:56 +0300620 if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300621 wl1271_error("scan channel list is empty");
622 ret = -EINVAL;
623 goto out;
624 }
625
Luciano Coelho66870b12011-05-27 15:34:48 +0300626 if (!force_passive && cfg->active[0]) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300627 ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid,
628 req->ssids[0].ssid_len,
629 ies->ie[IEEE80211_BAND_2GHZ],
630 ies->len[IEEE80211_BAND_2GHZ],
631 IEEE80211_BAND_2GHZ);
632 if (ret < 0) {
633 wl1271_error("2.4GHz PROBE request template failed");
634 goto out;
635 }
636 }
637
Luciano Coelho66870b12011-05-27 15:34:48 +0300638 if (!force_passive && cfg->active[1]) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300639 ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid,
640 req->ssids[0].ssid_len,
641 ies->ie[IEEE80211_BAND_5GHZ],
642 ies->len[IEEE80211_BAND_5GHZ],
643 IEEE80211_BAND_5GHZ);
644 if (ret < 0) {
645 wl1271_error("5GHz PROBE request template failed");
646 goto out;
647 }
648 }
649
650 wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg));
651
652 ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg,
653 sizeof(*cfg), 0);
654 if (ret < 0) {
655 wl1271_error("SCAN configuration failed");
656 goto out;
657 }
658out:
659 kfree(cfg);
660 return ret;
661}
662
663int wl1271_scan_sched_scan_start(struct wl1271 *wl)
664{
665 struct wl1271_cmd_sched_scan_start *start;
666 int ret = 0;
667
668 wl1271_debug(DEBUG_CMD, "cmd periodic scan start");
669
Luciano Coelho683c0022011-05-12 17:07:55 +0300670 if (wl->bss_type != BSS_TYPE_STA_BSS)
671 return -EOPNOTSUPP;
672
673 if (!test_bit(WL1271_FLAG_IDLE, &wl->flags))
674 return -EBUSY;
675
Luciano Coelho95feadc2011-05-10 14:38:59 +0300676 start = kzalloc(sizeof(*start), GFP_KERNEL);
677 if (!start)
678 return -ENOMEM;
679
680 start->tag = WL1271_SCAN_DEFAULT_TAG;
681
682 ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start,
683 sizeof(*start), 0);
684 if (ret < 0) {
685 wl1271_error("failed to send scan start command");
686 goto out_free;
687 }
688
689out_free:
690 kfree(start);
691 return ret;
692}
693
694void wl1271_scan_sched_scan_results(struct wl1271 *wl)
695{
696 wl1271_debug(DEBUG_SCAN, "got periodic scan results");
697
698 ieee80211_sched_scan_results(wl->hw);
699}
700
701void wl1271_scan_sched_scan_stop(struct wl1271 *wl)
702{
703 struct wl1271_cmd_sched_scan_stop *stop;
704 int ret = 0;
705
706 wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
707
708 /* FIXME: what to do if alloc'ing to stop fails? */
709 stop = kzalloc(sizeof(*stop), GFP_KERNEL);
710 if (!stop) {
711 wl1271_error("failed to alloc memory to send sched scan stop");
712 return;
713 }
714
715 stop->tag = WL1271_SCAN_DEFAULT_TAG;
716
717 ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop,
718 sizeof(*stop), 0);
Luciano Coelho33c2c062011-05-10 14:46:02 +0300719 if (ret < 0) {
Luciano Coelho95feadc2011-05-10 14:38:59 +0300720 wl1271_error("failed to send sched scan stop command");
Luciano Coelho33c2c062011-05-10 14:46:02 +0300721 goto out_free;
722 }
723 wl->sched_scanning = false;
Luciano Coelho95feadc2011-05-10 14:38:59 +0300724
Luciano Coelho33c2c062011-05-10 14:46:02 +0300725out_free:
Luciano Coelho95feadc2011-05-10 14:38:59 +0300726 kfree(stop);
727}