]>
Commit | Line | Data |
---|---|---|
34dd2aaa LC |
1 | /* |
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 | ||
00d20100 SL |
26 | #include "wl12xx.h" |
27 | #include "cmd.h" | |
28 | #include "scan.h" | |
29 | #include "acx.h" | |
24225b37 | 30 | #include "ps.h" |
34dd2aaa | 31 | |
c454f1d9 JO |
32 | void wl1271_scan_complete_work(struct work_struct *work) |
33 | { | |
78abd320 JO |
34 | struct delayed_work *dwork; |
35 | struct wl1271 *wl; | |
251c177f | 36 | int ret; |
78abd320 JO |
37 | |
38 | dwork = container_of(work, struct delayed_work, work); | |
39 | wl = container_of(dwork, struct wl1271, scan_complete_work); | |
c454f1d9 JO |
40 | |
41 | wl1271_debug(DEBUG_SCAN, "Scanning complete"); | |
42 | ||
43 | mutex_lock(&wl->mutex); | |
52a2a375 | 44 | |
24225b37 AN |
45 | if (wl->state == WL1271_STATE_OFF) |
46 | goto out; | |
47 | ||
48 | if (wl->scan.state == WL1271_SCAN_STATE_IDLE) | |
49 | goto out; | |
52a2a375 | 50 | |
c454f1d9 | 51 | wl->scan.state = WL1271_SCAN_STATE_IDLE; |
4a31c11c | 52 | memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); |
b739a42c | 53 | wl->scan.req = NULL; |
78abd320 | 54 | |
251c177f EP |
55 | ret = wl1271_ps_elp_wakeup(wl); |
56 | if (ret < 0) | |
57 | goto out; | |
58 | ||
24225b37 | 59 | if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { |
251c177f EP |
60 | /* restore hardware connection monitoring template */ |
61 | wl1271_cmd_build_ap_probe_req(wl, wl->probereq); | |
62 | } else { | |
63 | /* restore remain on channel */ | |
64 | wl12xx_cmd_role_start_dev(wl); | |
65 | wl12xx_roc(wl, wl->dev_role_id); | |
24225b37 | 66 | } |
251c177f | 67 | wl1271_ps_elp_sleep(wl); |
2f6724b2 | 68 | |
78abd320 JO |
69 | if (wl->scan.failed) { |
70 | wl1271_info("Scan completed due to error."); | |
baacb9ae | 71 | wl12xx_queue_recovery_work(wl); |
78abd320 | 72 | } |
24225b37 | 73 | |
251c177f EP |
74 | ieee80211_scan_completed(wl->hw, false); |
75 | ||
24225b37 | 76 | out: |
b739a42c JO |
77 | mutex_unlock(&wl->mutex); |
78 | ||
c454f1d9 JO |
79 | } |
80 | ||
81 | ||
08688d6b LC |
82 | static int wl1271_get_scan_channels(struct wl1271 *wl, |
83 | struct cfg80211_scan_request *req, | |
84 | struct basic_scan_channel_params *channels, | |
85 | enum ieee80211_band band, bool passive) | |
34dd2aaa | 86 | { |
bea39d6a | 87 | struct conf_scan_settings *c = &wl->conf.scan; |
08688d6b LC |
88 | int i, j; |
89 | u32 flags; | |
34dd2aaa | 90 | |
08688d6b LC |
91 | for (i = 0, j = 0; |
92 | i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; | |
93 | i++) { | |
34dd2aaa | 94 | |
08688d6b | 95 | flags = req->channels[i]->flags; |
34dd2aaa | 96 | |
4a31c11c | 97 | if (!test_bit(i, wl->scan.scanned_ch) && |
08688d6b LC |
98 | !(flags & IEEE80211_CHAN_DISABLED) && |
99 | ((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) && | |
100 | (req->channels[i]->band == band)) { | |
34dd2aaa | 101 | |
08688d6b LC |
102 | wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", |
103 | req->channels[i]->band, | |
104 | req->channels[i]->center_freq); | |
105 | wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", | |
106 | req->channels[i]->hw_value, | |
107 | req->channels[i]->flags); | |
108 | wl1271_debug(DEBUG_SCAN, | |
109 | "max_antenna_gain %d, max_power %d", | |
110 | req->channels[i]->max_antenna_gain, | |
111 | req->channels[i]->max_power); | |
112 | wl1271_debug(DEBUG_SCAN, "beacon_found %d", | |
113 | req->channels[i]->beacon_found); | |
34dd2aaa | 114 | |
bea39d6a JO |
115 | if (!passive) { |
116 | channels[j].min_duration = | |
117 | cpu_to_le32(c->min_dwell_time_active); | |
118 | channels[j].max_duration = | |
119 | cpu_to_le32(c->max_dwell_time_active); | |
120 | } else { | |
121 | channels[j].min_duration = | |
122 | cpu_to_le32(c->min_dwell_time_passive); | |
123 | channels[j].max_duration = | |
124 | cpu_to_le32(c->max_dwell_time_passive); | |
125 | } | |
08688d6b | 126 | channels[j].early_termination = 0; |
3cc7b544 | 127 | channels[j].tx_power_att = req->channels[i]->max_power; |
08688d6b LC |
128 | channels[j].channel = req->channels[i]->hw_value; |
129 | ||
130 | memset(&channels[j].bssid_lsb, 0xff, 4); | |
131 | memset(&channels[j].bssid_msb, 0xff, 2); | |
132 | ||
133 | /* Mark the channels we already used */ | |
4a31c11c | 134 | set_bit(i, wl->scan.scanned_ch); |
08688d6b | 135 | |
34dd2aaa LC |
136 | j++; |
137 | } | |
138 | } | |
139 | ||
08688d6b LC |
140 | return j; |
141 | } | |
34dd2aaa | 142 | |
08688d6b LC |
143 | #define WL1271_NOTHING_TO_SCAN 1 |
144 | ||
145 | static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band, | |
146 | bool passive, u32 basic_rate) | |
147 | { | |
148 | struct wl1271_cmd_scan *cmd; | |
149 | struct wl1271_cmd_trigger_scan_to *trigger; | |
150 | int ret; | |
151 | u16 scan_options = 0; | |
152 | ||
153 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
154 | trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); | |
155 | if (!cmd || !trigger) { | |
156 | ret = -ENOMEM; | |
157 | goto out; | |
34dd2aaa LC |
158 | } |
159 | ||
08688d6b LC |
160 | /* We always use high priority scans */ |
161 | scan_options = WL1271_SCAN_OPT_PRIORITY_HIGH; | |
4f35c025 LC |
162 | |
163 | /* No SSIDs means that we have a forced passive scan */ | |
164 | if (passive || wl->scan.req->n_ssids == 0) | |
08688d6b | 165 | scan_options |= WL1271_SCAN_OPT_PASSIVE; |
4f35c025 | 166 | |
a4e02f33 EP |
167 | if (WARN_ON(wl->role_id == WL12XX_INVALID_ROLE_ID)) { |
168 | ret = -EINVAL; | |
169 | goto out; | |
170 | } | |
171 | cmd->params.role_id = wl->role_id; | |
08688d6b LC |
172 | cmd->params.scan_options = cpu_to_le16(scan_options); |
173 | ||
174 | cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req, | |
175 | cmd->channels, | |
176 | band, passive); | |
177 | if (cmd->params.n_ch == 0) { | |
178 | ret = WL1271_NOTHING_TO_SCAN; | |
34dd2aaa LC |
179 | goto out; |
180 | } | |
181 | ||
08688d6b | 182 | cmd->params.tx_rate = cpu_to_le32(basic_rate); |
bea39d6a | 183 | cmd->params.n_probe_reqs = wl->conf.scan.num_probe_reqs; |
08688d6b LC |
184 | cmd->params.tx_rate = cpu_to_le32(basic_rate); |
185 | cmd->params.tid_trigger = 0; | |
186 | cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG; | |
187 | ||
188 | if (band == IEEE80211_BAND_2GHZ) | |
189 | cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ; | |
190 | else | |
191 | cmd->params.band = WL1271_SCAN_BAND_5_GHZ; | |
192 | ||
193 | if (wl->scan.ssid_len && wl->scan.ssid) { | |
194 | cmd->params.ssid_len = wl->scan.ssid_len; | |
195 | memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len); | |
196 | } | |
197 | ||
a4e02f33 EP |
198 | memcpy(cmd->addr, wl->mac_addr, ETH_ALEN); |
199 | ||
08688d6b LC |
200 | ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len, |
201 | wl->scan.req->ie, wl->scan.req->ie_len, | |
202 | band); | |
203 | if (ret < 0) { | |
204 | wl1271_error("PROBE request template failed"); | |
34dd2aaa LC |
205 | goto out; |
206 | } | |
207 | ||
208 | /* disable the timeout */ | |
209 | trigger->timeout = 0; | |
34dd2aaa LC |
210 | ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, |
211 | sizeof(*trigger), 0); | |
212 | if (ret < 0) { | |
213 | wl1271_error("trigger scan to failed for hw scan"); | |
214 | goto out; | |
215 | } | |
216 | ||
08688d6b | 217 | wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); |
34dd2aaa | 218 | |
08688d6b | 219 | ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); |
34dd2aaa LC |
220 | if (ret < 0) { |
221 | wl1271_error("SCAN failed"); | |
34dd2aaa LC |
222 | goto out; |
223 | } | |
224 | ||
225 | out: | |
08688d6b | 226 | kfree(cmd); |
34dd2aaa LC |
227 | kfree(trigger); |
228 | return ret; | |
229 | } | |
230 | ||
08688d6b | 231 | void wl1271_scan_stm(struct wl1271 *wl) |
34dd2aaa | 232 | { |
78abd320 | 233 | int ret = 0; |
08688d6b LC |
234 | |
235 | switch (wl->scan.state) { | |
236 | case WL1271_SCAN_STATE_IDLE: | |
237 | break; | |
238 | ||
239 | case WL1271_SCAN_STATE_2GHZ_ACTIVE: | |
240 | ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, false, | |
241 | wl->conf.tx.basic_rate); | |
242 | if (ret == WL1271_NOTHING_TO_SCAN) { | |
243 | wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; | |
244 | wl1271_scan_stm(wl); | |
245 | } | |
246 | ||
247 | break; | |
248 | ||
249 | case WL1271_SCAN_STATE_2GHZ_PASSIVE: | |
250 | ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true, | |
251 | wl->conf.tx.basic_rate); | |
252 | if (ret == WL1271_NOTHING_TO_SCAN) { | |
02fabb0e | 253 | if (wl->enable_11a) |
08688d6b LC |
254 | wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; |
255 | else | |
256 | wl->scan.state = WL1271_SCAN_STATE_DONE; | |
257 | wl1271_scan_stm(wl); | |
34dd2aaa | 258 | } |
08688d6b LC |
259 | |
260 | break; | |
261 | ||
262 | case WL1271_SCAN_STATE_5GHZ_ACTIVE: | |
263 | ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, false, | |
264 | wl->conf.tx.basic_rate_5); | |
265 | if (ret == WL1271_NOTHING_TO_SCAN) { | |
266 | wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; | |
267 | wl1271_scan_stm(wl); | |
268 | } | |
269 | ||
270 | break; | |
271 | ||
272 | case WL1271_SCAN_STATE_5GHZ_PASSIVE: | |
273 | ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, true, | |
274 | wl->conf.tx.basic_rate_5); | |
275 | if (ret == WL1271_NOTHING_TO_SCAN) { | |
276 | wl->scan.state = WL1271_SCAN_STATE_DONE; | |
277 | wl1271_scan_stm(wl); | |
278 | } | |
279 | ||
280 | break; | |
281 | ||
282 | case WL1271_SCAN_STATE_DONE: | |
78abd320 JO |
283 | wl->scan.failed = false; |
284 | cancel_delayed_work(&wl->scan_complete_work); | |
285 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | |
286 | msecs_to_jiffies(0)); | |
08688d6b LC |
287 | break; |
288 | ||
289 | default: | |
290 | wl1271_error("invalid scan state"); | |
291 | break; | |
34dd2aaa | 292 | } |
78abd320 JO |
293 | |
294 | if (ret < 0) { | |
295 | cancel_delayed_work(&wl->scan_complete_work); | |
296 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | |
297 | msecs_to_jiffies(0)); | |
298 | } | |
08688d6b LC |
299 | } |
300 | ||
301 | int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, | |
302 | struct cfg80211_scan_request *req) | |
303 | { | |
4a31c11c LC |
304 | /* |
305 | * cfg80211 should guarantee that we don't get more channels | |
306 | * than what we have registered. | |
307 | */ | |
308 | BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); | |
309 | ||
08688d6b LC |
310 | if (wl->scan.state != WL1271_SCAN_STATE_IDLE) |
311 | return -EBUSY; | |
312 | ||
313 | wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; | |
314 | ||
315 | if (ssid_len && ssid) { | |
316 | wl->scan.ssid_len = ssid_len; | |
317 | memcpy(wl->scan.ssid, ssid, ssid_len); | |
318 | } else { | |
319 | wl->scan.ssid_len = 0; | |
320 | } | |
321 | ||
322 | wl->scan.req = req; | |
4a31c11c | 323 | memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); |
08688d6b | 324 | |
78abd320 JO |
325 | /* we assume failure so that timeout scenarios are handled correctly */ |
326 | wl->scan.failed = true; | |
327 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | |
328 | msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); | |
329 | ||
08688d6b LC |
330 | wl1271_scan_stm(wl); |
331 | ||
34dd2aaa LC |
332 | return 0; |
333 | } | |
95feadca | 334 | |
2aa01597 EP |
335 | int wl1271_scan_stop(struct wl1271 *wl) |
336 | { | |
337 | struct wl1271_cmd_header *cmd = NULL; | |
338 | int ret = 0; | |
339 | ||
340 | if (WARN_ON(wl->scan.state == WL1271_SCAN_STATE_IDLE)) | |
341 | return -EINVAL; | |
342 | ||
343 | wl1271_debug(DEBUG_CMD, "cmd scan stop"); | |
344 | ||
345 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
346 | if (!cmd) { | |
347 | ret = -ENOMEM; | |
348 | goto out; | |
349 | } | |
350 | ||
351 | ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, cmd, | |
352 | sizeof(*cmd), 0); | |
353 | if (ret < 0) { | |
354 | wl1271_error("cmd stop_scan failed"); | |
355 | goto out; | |
356 | } | |
357 | out: | |
358 | kfree(cmd); | |
359 | return ret; | |
360 | } | |
361 | ||
95feadca LC |
362 | static int |
363 | wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | |
364 | struct cfg80211_sched_scan_request *req, | |
365 | struct conn_scan_ch_params *channels, | |
366 | u32 band, bool radar, bool passive, | |
d2c2bb9f | 367 | int start, int max_channels) |
95feadca LC |
368 | { |
369 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | |
370 | int i, j; | |
371 | u32 flags; | |
66870b1c | 372 | bool force_passive = !req->n_ssids; |
95feadca LC |
373 | |
374 | for (i = 0, j = start; | |
d2c2bb9f | 375 | i < req->n_channels && j < max_channels; |
95feadca LC |
376 | i++) { |
377 | flags = req->channels[i]->flags; | |
378 | ||
66870b1c LC |
379 | if (force_passive) |
380 | flags |= IEEE80211_CHAN_PASSIVE_SCAN; | |
381 | ||
2497a246 LC |
382 | if ((req->channels[i]->band == band) && |
383 | !(flags & IEEE80211_CHAN_DISABLED) && | |
dd086821 | 384 | (!!(flags & IEEE80211_CHAN_RADAR) == radar) && |
2497a246 LC |
385 | /* if radar is set, we ignore the passive flag */ |
386 | (radar || | |
387 | !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { | |
95feadca LC |
388 | wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", |
389 | req->channels[i]->band, | |
390 | req->channels[i]->center_freq); | |
391 | wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", | |
392 | req->channels[i]->hw_value, | |
393 | req->channels[i]->flags); | |
394 | wl1271_debug(DEBUG_SCAN, "max_power %d", | |
395 | req->channels[i]->max_power); | |
396 | ||
50a66d7f | 397 | if (flags & IEEE80211_CHAN_RADAR) { |
2497a246 | 398 | channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS; |
50a66d7f LC |
399 | channels[j].passive_duration = |
400 | cpu_to_le16(c->dwell_time_dfs); | |
401 | } | |
402 | else if (flags & IEEE80211_CHAN_PASSIVE_SCAN) { | |
95feadca LC |
403 | channels[j].passive_duration = |
404 | cpu_to_le16(c->dwell_time_passive); | |
405 | } else { | |
406 | channels[j].min_duration = | |
407 | cpu_to_le16(c->min_dwell_time_active); | |
408 | channels[j].max_duration = | |
409 | cpu_to_le16(c->max_dwell_time_active); | |
410 | } | |
2497a246 | 411 | channels[j].tx_power_att = req->channels[i]->max_power; |
95feadca LC |
412 | channels[j].channel = req->channels[i]->hw_value; |
413 | ||
414 | j++; | |
415 | } | |
416 | } | |
417 | ||
418 | return j - start; | |
419 | } | |
420 | ||
d2c2bb9f | 421 | static bool |
95feadca LC |
422 | wl1271_scan_sched_scan_channels(struct wl1271 *wl, |
423 | struct cfg80211_sched_scan_request *req, | |
424 | struct wl1271_cmd_sched_scan_config *cfg) | |
425 | { | |
95feadca | 426 | cfg->passive[0] = |
d2c2bb9f | 427 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, |
95feadca | 428 | IEEE80211_BAND_2GHZ, |
d2c2bb9f LC |
429 | false, true, 0, |
430 | MAX_CHANNELS_2GHZ); | |
95feadca | 431 | cfg->active[0] = |
d2c2bb9f | 432 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_2, |
95feadca | 433 | IEEE80211_BAND_2GHZ, |
d2c2bb9f LC |
434 | false, false, |
435 | cfg->passive[0], | |
436 | MAX_CHANNELS_2GHZ); | |
95feadca | 437 | cfg->passive[1] = |
d2c2bb9f | 438 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
95feadca | 439 | IEEE80211_BAND_5GHZ, |
d2c2bb9f LC |
440 | false, true, 0, |
441 | MAX_CHANNELS_5GHZ); | |
2497a246 | 442 | cfg->dfs = |
d2c2bb9f | 443 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
95feadca | 444 | IEEE80211_BAND_5GHZ, |
d2c2bb9f LC |
445 | true, true, |
446 | cfg->passive[1], | |
447 | MAX_CHANNELS_5GHZ); | |
2497a246 | 448 | cfg->active[1] = |
d2c2bb9f | 449 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels_5, |
95feadca | 450 | IEEE80211_BAND_5GHZ, |
d2c2bb9f LC |
451 | false, false, |
452 | cfg->passive[1] + cfg->dfs, | |
453 | MAX_CHANNELS_5GHZ); | |
454 | /* 802.11j channels are not supported yet */ | |
455 | cfg->passive[2] = 0; | |
456 | cfg->active[2] = 0; | |
95feadca LC |
457 | |
458 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", | |
459 | cfg->active[0], cfg->passive[0]); | |
460 | wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", | |
461 | cfg->active[1], cfg->passive[1]); | |
2497a246 | 462 | wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); |
95feadca | 463 | |
d2c2bb9f LC |
464 | return cfg->passive[0] || cfg->active[0] || |
465 | cfg->passive[1] || cfg->active[1] || cfg->dfs || | |
466 | cfg->passive[2] || cfg->active[2]; | |
95feadca LC |
467 | } |
468 | ||
469 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | |
470 | struct cfg80211_sched_scan_request *req, | |
471 | struct ieee80211_sched_scan_ies *ies) | |
472 | { | |
473 | struct wl1271_cmd_sched_scan_config *cfg = NULL; | |
474 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | |
d2c2bb9f | 475 | int i, ret; |
66870b1c | 476 | bool force_passive = !req->n_ssids; |
95feadca LC |
477 | |
478 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | |
479 | ||
480 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | |
481 | if (!cfg) | |
482 | return -ENOMEM; | |
483 | ||
484 | cfg->rssi_threshold = c->rssi_threshold; | |
485 | cfg->snr_threshold = c->snr_threshold; | |
486 | cfg->n_probe_reqs = c->num_probe_reqs; | |
487 | /* cycles set to 0 it means infinite (until manually stopped) */ | |
488 | cfg->cycles = 0; | |
489 | /* report APs when at least 1 is found */ | |
490 | cfg->report_after = 1; | |
491 | /* don't stop scanning automatically when something is found */ | |
492 | cfg->terminate = 0; | |
493 | cfg->tag = WL1271_SCAN_DEFAULT_TAG; | |
494 | /* don't filter on BSS type */ | |
495 | cfg->bss_type = SCAN_BSS_TYPE_ANY; | |
496 | /* currently NL80211 supports only a single interval */ | |
497 | for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) | |
498 | cfg->intervals[i] = cpu_to_le32(req->interval); | |
499 | ||
66870b1c | 500 | if (!force_passive && req->ssids[0].ssid_len && req->ssids[0].ssid) { |
95feadca LC |
501 | cfg->filter_type = SCAN_SSID_FILTER_SPECIFIC; |
502 | cfg->ssid_len = req->ssids[0].ssid_len; | |
503 | memcpy(cfg->ssid, req->ssids[0].ssid, | |
504 | req->ssids[0].ssid_len); | |
505 | } else { | |
506 | cfg->filter_type = SCAN_SSID_FILTER_ANY; | |
507 | cfg->ssid_len = 0; | |
508 | } | |
509 | ||
d2c2bb9f | 510 | if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { |
95feadca LC |
511 | wl1271_error("scan channel list is empty"); |
512 | ret = -EINVAL; | |
513 | goto out; | |
514 | } | |
515 | ||
66870b1c | 516 | if (!force_passive && cfg->active[0]) { |
95feadca LC |
517 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, |
518 | req->ssids[0].ssid_len, | |
519 | ies->ie[IEEE80211_BAND_2GHZ], | |
520 | ies->len[IEEE80211_BAND_2GHZ], | |
521 | IEEE80211_BAND_2GHZ); | |
522 | if (ret < 0) { | |
523 | wl1271_error("2.4GHz PROBE request template failed"); | |
524 | goto out; | |
525 | } | |
526 | } | |
527 | ||
66870b1c | 528 | if (!force_passive && cfg->active[1]) { |
95feadca LC |
529 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, |
530 | req->ssids[0].ssid_len, | |
531 | ies->ie[IEEE80211_BAND_5GHZ], | |
532 | ies->len[IEEE80211_BAND_5GHZ], | |
533 | IEEE80211_BAND_5GHZ); | |
534 | if (ret < 0) { | |
535 | wl1271_error("5GHz PROBE request template failed"); | |
536 | goto out; | |
537 | } | |
538 | } | |
539 | ||
540 | wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); | |
541 | ||
542 | ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, | |
543 | sizeof(*cfg), 0); | |
544 | if (ret < 0) { | |
545 | wl1271_error("SCAN configuration failed"); | |
546 | goto out; | |
547 | } | |
548 | out: | |
549 | kfree(cfg); | |
550 | return ret; | |
551 | } | |
552 | ||
553 | int wl1271_scan_sched_scan_start(struct wl1271 *wl) | |
554 | { | |
555 | struct wl1271_cmd_sched_scan_start *start; | |
556 | int ret = 0; | |
557 | ||
558 | wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); | |
559 | ||
683c0024 LC |
560 | if (wl->bss_type != BSS_TYPE_STA_BSS) |
561 | return -EOPNOTSUPP; | |
562 | ||
563 | if (!test_bit(WL1271_FLAG_IDLE, &wl->flags)) | |
564 | return -EBUSY; | |
565 | ||
95feadca LC |
566 | start = kzalloc(sizeof(*start), GFP_KERNEL); |
567 | if (!start) | |
568 | return -ENOMEM; | |
569 | ||
570 | start->tag = WL1271_SCAN_DEFAULT_TAG; | |
571 | ||
572 | ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, | |
573 | sizeof(*start), 0); | |
574 | if (ret < 0) { | |
575 | wl1271_error("failed to send scan start command"); | |
576 | goto out_free; | |
577 | } | |
578 | ||
579 | out_free: | |
580 | kfree(start); | |
581 | return ret; | |
582 | } | |
583 | ||
584 | void wl1271_scan_sched_scan_results(struct wl1271 *wl) | |
585 | { | |
586 | wl1271_debug(DEBUG_SCAN, "got periodic scan results"); | |
587 | ||
588 | ieee80211_sched_scan_results(wl->hw); | |
589 | } | |
590 | ||
591 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl) | |
592 | { | |
593 | struct wl1271_cmd_sched_scan_stop *stop; | |
594 | int ret = 0; | |
595 | ||
596 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | |
597 | ||
598 | /* FIXME: what to do if alloc'ing to stop fails? */ | |
599 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | |
600 | if (!stop) { | |
601 | wl1271_error("failed to alloc memory to send sched scan stop"); | |
602 | return; | |
603 | } | |
604 | ||
605 | stop->tag = WL1271_SCAN_DEFAULT_TAG; | |
606 | ||
607 | ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, | |
608 | sizeof(*stop), 0); | |
33c2c06c | 609 | if (ret < 0) { |
95feadca | 610 | wl1271_error("failed to send sched scan stop command"); |
33c2c06c LC |
611 | goto out_free; |
612 | } | |
613 | wl->sched_scanning = false; | |
95feadca | 614 | |
33c2c06c | 615 | out_free: |
95feadca LC |
616 | kfree(stop); |
617 | } |