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