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