]>
Commit | Line | Data |
---|---|---|
2b27bdcc | 1 | // SPDX-License-Identifier: GPL-2.0-only |
34dd2aaa LC |
2 | /* |
3 | * This file is part of wl1271 | |
4 | * | |
5 | * Copyright (C) 2009-2010 Nokia Corporation | |
6 | * | |
7 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> | |
34dd2aaa LC |
8 | */ |
9 | ||
10 | #include <linux/ieee80211.h> | |
fa2648a3 | 11 | #include <linux/pm_runtime.h> |
34dd2aaa | 12 | |
c31be25a | 13 | #include "wlcore.h" |
0f4e3122 | 14 | #include "debug.h" |
00d20100 SL |
15 | #include "cmd.h" |
16 | #include "scan.h" | |
17 | #include "acx.h" | |
af7fbb28 | 18 | #include "tx.h" |
34dd2aaa | 19 | |
c454f1d9 JO |
20 | void wl1271_scan_complete_work(struct work_struct *work) |
21 | { | |
78abd320 JO |
22 | struct delayed_work *dwork; |
23 | struct wl1271 *wl; | |
536129c8 | 24 | struct wl12xx_vif *wlvif; |
7947d3e0 AS |
25 | struct cfg80211_scan_info info = { |
26 | .aborted = false, | |
27 | }; | |
251c177f | 28 | int ret; |
78abd320 | 29 | |
61383412 | 30 | dwork = to_delayed_work(work); |
78abd320 | 31 | wl = container_of(dwork, struct wl1271, scan_complete_work); |
c454f1d9 JO |
32 | |
33 | wl1271_debug(DEBUG_SCAN, "Scanning complete"); | |
34 | ||
35 | mutex_lock(&wl->mutex); | |
52a2a375 | 36 | |
4cc53383 | 37 | if (unlikely(wl->state != WLCORE_STATE_ON)) |
24225b37 AN |
38 | goto out; |
39 | ||
40 | if (wl->scan.state == WL1271_SCAN_STATE_IDLE) | |
41 | goto out; | |
52a2a375 | 42 | |
c50a2825 | 43 | wlvif = wl->scan_wlvif; |
536129c8 | 44 | |
55df5afb AN |
45 | /* |
46 | * Rearm the tx watchdog just before idling scan. This | |
47 | * prevents just-finished scans from triggering the watchdog | |
48 | */ | |
49 | wl12xx_rearm_tx_watchdog_locked(wl); | |
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; |
c50a2825 | 54 | wl->scan_wlvif = NULL; |
78abd320 | 55 | |
fa2648a3 TL |
56 | ret = pm_runtime_get_sync(wl->dev); |
57 | if (ret < 0) { | |
58 | pm_runtime_put_noidle(wl->dev); | |
251c177f | 59 | goto out; |
fa2648a3 | 60 | } |
251c177f | 61 | |
ba8447f6 | 62 | if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { |
251c177f | 63 | /* restore hardware connection monitoring template */ |
83587505 | 64 | wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq); |
227e81e1 EP |
65 | } |
66 | ||
78abd320 JO |
67 | if (wl->scan.failed) { |
68 | wl1271_info("Scan completed due to error."); | |
baacb9ae | 69 | wl12xx_queue_recovery_work(wl); |
78abd320 | 70 | } |
24225b37 | 71 | |
6b70e7eb VG |
72 | wlcore_cmd_regdomain_config_locked(wl); |
73 | ||
9b71578d TL |
74 | pm_runtime_mark_last_busy(wl->dev); |
75 | pm_runtime_put_autosuspend(wl->dev); | |
3ebbabea | 76 | |
7947d3e0 | 77 | ieee80211_scan_completed(wl->hw, &info); |
251c177f | 78 | |
24225b37 | 79 | out: |
b739a42c JO |
80 | mutex_unlock(&wl->mutex); |
81 | ||
c454f1d9 JO |
82 | } |
83 | ||
5d3a1603 ES |
84 | static void wlcore_started_vifs_iter(void *data, u8 *mac, |
85 | struct ieee80211_vif *vif) | |
86 | { | |
e9687ea9 EP |
87 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
88 | bool active = false; | |
5d3a1603 ES |
89 | int *count = (int *)data; |
90 | ||
e9687ea9 EP |
91 | /* |
92 | * count active interfaces according to interface type. | |
93 | * checking only bss_conf.idle is bad for some cases, e.g. | |
94 | * we don't want to count sta in p2p_find as active interface. | |
95 | */ | |
96 | switch (wlvif->bss_type) { | |
97 | case BSS_TYPE_STA_BSS: | |
98 | if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) | |
99 | active = true; | |
100 | break; | |
101 | ||
102 | case BSS_TYPE_AP_BSS: | |
103 | if (wlvif->wl->active_sta_count > 0) | |
104 | active = true; | |
105 | break; | |
106 | ||
107 | default: | |
108 | break; | |
109 | } | |
110 | ||
111 | if (active) | |
5d3a1603 ES |
112 | (*count)++; |
113 | } | |
114 | ||
115 | static int wlcore_count_started_vifs(struct wl1271 *wl) | |
116 | { | |
117 | int count = 0; | |
118 | ||
119 | ieee80211_iterate_active_interfaces_atomic(wl->hw, | |
120 | IEEE80211_IFACE_ITER_RESUME_ALL, | |
121 | wlcore_started_vifs_iter, &count); | |
122 | return count; | |
123 | } | |
124 | ||
95feadca | 125 | static int |
512c5385 EP |
126 | wlcore_scan_get_channels(struct wl1271 *wl, |
127 | struct ieee80211_channel *req_channels[], | |
128 | u32 n_channels, | |
129 | u32 n_ssids, | |
130 | struct conn_scan_ch_params *channels, | |
131 | u32 band, bool radar, bool passive, | |
132 | int start, int max_channels, | |
7c482c10 EP |
133 | u8 *n_pactive_ch, |
134 | int scan_type) | |
95feadca | 135 | { |
95feadca LC |
136 | int i, j; |
137 | u32 flags; | |
512c5385 | 138 | bool force_passive = !n_ssids; |
7c482c10 | 139 | u32 min_dwell_time_active, max_dwell_time_active; |
6f407e5b ES |
140 | u32 dwell_time_passive, dwell_time_dfs; |
141 | ||
7c482c10 EP |
142 | /* configure dwell times according to scan type */ |
143 | if (scan_type == SCAN_TYPE_SEARCH) { | |
144 | struct conf_scan_settings *c = &wl->conf.scan; | |
5d3a1603 ES |
145 | bool active_vif_exists = !!wlcore_count_started_vifs(wl); |
146 | ||
147 | min_dwell_time_active = active_vif_exists ? | |
148 | c->min_dwell_time_active : | |
149 | c->min_dwell_time_active_long; | |
150 | max_dwell_time_active = active_vif_exists ? | |
151 | c->max_dwell_time_active : | |
152 | c->max_dwell_time_active_long; | |
7c482c10 EP |
153 | dwell_time_passive = c->dwell_time_passive; |
154 | dwell_time_dfs = c->dwell_time_dfs; | |
155 | } else { | |
156 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | |
157 | u32 delta_per_probe; | |
158 | ||
57fbcce3 | 159 | if (band == NL80211_BAND_5GHZ) |
7c482c10 EP |
160 | delta_per_probe = c->dwell_time_delta_per_probe_5; |
161 | else | |
162 | delta_per_probe = c->dwell_time_delta_per_probe; | |
163 | ||
164 | min_dwell_time_active = c->base_dwell_time + | |
165 | n_ssids * c->num_probe_reqs * delta_per_probe; | |
166 | ||
167 | max_dwell_time_active = min_dwell_time_active + | |
168 | c->max_dwell_time_delta; | |
169 | dwell_time_passive = c->dwell_time_passive; | |
170 | dwell_time_dfs = c->dwell_time_dfs; | |
171 | } | |
6f407e5b ES |
172 | min_dwell_time_active = DIV_ROUND_UP(min_dwell_time_active, 1000); |
173 | max_dwell_time_active = DIV_ROUND_UP(max_dwell_time_active, 1000); | |
7c482c10 EP |
174 | dwell_time_passive = DIV_ROUND_UP(dwell_time_passive, 1000); |
175 | dwell_time_dfs = DIV_ROUND_UP(dwell_time_dfs, 1000); | |
95feadca LC |
176 | |
177 | for (i = 0, j = start; | |
512c5385 | 178 | i < n_channels && j < max_channels; |
95feadca | 179 | i++) { |
512c5385 | 180 | flags = req_channels[i]->flags; |
95feadca | 181 | |
66870b1c | 182 | if (force_passive) |
8fe02e16 | 183 | flags |= IEEE80211_CHAN_NO_IR; |
66870b1c | 184 | |
512c5385 | 185 | if ((req_channels[i]->band == band) && |
2497a246 | 186 | !(flags & IEEE80211_CHAN_DISABLED) && |
dd086821 | 187 | (!!(flags & IEEE80211_CHAN_RADAR) == radar) && |
2497a246 LC |
188 | /* if radar is set, we ignore the passive flag */ |
189 | (radar || | |
8fe02e16 | 190 | !!(flags & IEEE80211_CHAN_NO_IR) == passive)) { |
50a66d7f | 191 | if (flags & IEEE80211_CHAN_RADAR) { |
2497a246 | 192 | channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS; |
fea2a613 | 193 | |
50a66d7f | 194 | channels[j].passive_duration = |
6f407e5b | 195 | cpu_to_le16(dwell_time_dfs); |
fea2a613 | 196 | } else { |
95feadca | 197 | channels[j].passive_duration = |
6f407e5b | 198 | cpu_to_le16(dwell_time_passive); |
95feadca | 199 | } |
fea2a613 ES |
200 | |
201 | channels[j].min_duration = | |
6f407e5b | 202 | cpu_to_le16(min_dwell_time_active); |
fea2a613 | 203 | channels[j].max_duration = |
6f407e5b | 204 | cpu_to_le16(max_dwell_time_active); |
fea2a613 | 205 | |
512c5385 EP |
206 | channels[j].tx_power_att = req_channels[i]->max_power; |
207 | channels[j].channel = req_channels[i]->hw_value; | |
95feadca | 208 | |
512c5385 | 209 | if (n_pactive_ch && |
57fbcce3 | 210 | (band == NL80211_BAND_2GHZ) && |
97511b15 VG |
211 | (channels[j].channel >= 12) && |
212 | (channels[j].channel <= 14) && | |
8fe02e16 | 213 | (flags & IEEE80211_CHAN_NO_IR) && |
97511b15 VG |
214 | !force_passive) { |
215 | /* pactive channels treated as DFS */ | |
216 | channels[j].flags = SCAN_CHANNEL_FLAGS_DFS; | |
217 | ||
218 | /* | |
219 | * n_pactive_ch is counted down from the end of | |
220 | * the passive channel list | |
221 | */ | |
222 | (*n_pactive_ch)++; | |
223 | wl1271_debug(DEBUG_SCAN, "n_pactive_ch = %d", | |
224 | *n_pactive_ch); | |
225 | } | |
226 | ||
0fe72086 VG |
227 | wl1271_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s", |
228 | req_channels[i]->center_freq, | |
229 | req_channels[i]->hw_value, | |
230 | req_channels[i]->flags, | |
231 | req_channels[i]->max_power, | |
232 | min_dwell_time_active, | |
233 | max_dwell_time_active, | |
234 | flags & IEEE80211_CHAN_RADAR ? | |
235 | ", DFS" : "", | |
8fe02e16 LR |
236 | flags & IEEE80211_CHAN_NO_IR ? |
237 | ", NO-IR" : ""); | |
95feadca LC |
238 | j++; |
239 | } | |
240 | } | |
241 | ||
242 | return j - start; | |
243 | } | |
244 | ||
78e28062 | 245 | bool |
512c5385 | 246 | wlcore_set_scan_chan_params(struct wl1271 *wl, |
78e28062 | 247 | struct wlcore_scan_channels *cfg, |
512c5385 EP |
248 | struct ieee80211_channel *channels[], |
249 | u32 n_channels, | |
7c482c10 EP |
250 | u32 n_ssids, |
251 | int scan_type) | |
95feadca | 252 | { |
97511b15 VG |
253 | u8 n_pactive_ch = 0; |
254 | ||
95feadca | 255 | cfg->passive[0] = |
512c5385 EP |
256 | wlcore_scan_get_channels(wl, |
257 | channels, | |
258 | n_channels, | |
259 | n_ssids, | |
260 | cfg->channels_2, | |
57fbcce3 | 261 | NL80211_BAND_2GHZ, |
512c5385 EP |
262 | false, true, 0, |
263 | MAX_CHANNELS_2GHZ, | |
7c482c10 EP |
264 | &n_pactive_ch, |
265 | scan_type); | |
95feadca | 266 | cfg->active[0] = |
512c5385 EP |
267 | wlcore_scan_get_channels(wl, |
268 | channels, | |
269 | n_channels, | |
270 | n_ssids, | |
271 | cfg->channels_2, | |
57fbcce3 | 272 | NL80211_BAND_2GHZ, |
512c5385 EP |
273 | false, false, |
274 | cfg->passive[0], | |
275 | MAX_CHANNELS_2GHZ, | |
7c482c10 EP |
276 | &n_pactive_ch, |
277 | scan_type); | |
95feadca | 278 | cfg->passive[1] = |
512c5385 EP |
279 | wlcore_scan_get_channels(wl, |
280 | channels, | |
281 | n_channels, | |
282 | n_ssids, | |
283 | cfg->channels_5, | |
57fbcce3 | 284 | NL80211_BAND_5GHZ, |
512c5385 | 285 | false, true, 0, |
0a1c720c | 286 | wl->max_channels_5, |
7c482c10 EP |
287 | &n_pactive_ch, |
288 | scan_type); | |
2497a246 | 289 | cfg->dfs = |
512c5385 EP |
290 | wlcore_scan_get_channels(wl, |
291 | channels, | |
292 | n_channels, | |
293 | n_ssids, | |
294 | cfg->channels_5, | |
57fbcce3 | 295 | NL80211_BAND_5GHZ, |
512c5385 EP |
296 | true, true, |
297 | cfg->passive[1], | |
0a1c720c | 298 | wl->max_channels_5, |
7c482c10 EP |
299 | &n_pactive_ch, |
300 | scan_type); | |
2497a246 | 301 | cfg->active[1] = |
512c5385 EP |
302 | wlcore_scan_get_channels(wl, |
303 | channels, | |
304 | n_channels, | |
305 | n_ssids, | |
306 | cfg->channels_5, | |
57fbcce3 | 307 | NL80211_BAND_5GHZ, |
512c5385 EP |
308 | false, false, |
309 | cfg->passive[1] + cfg->dfs, | |
0a1c720c | 310 | wl->max_channels_5, |
7c482c10 EP |
311 | &n_pactive_ch, |
312 | scan_type); | |
512c5385 | 313 | |
d2c2bb9f LC |
314 | /* 802.11j channels are not supported yet */ |
315 | cfg->passive[2] = 0; | |
316 | cfg->active[2] = 0; | |
95feadca | 317 | |
78e28062 | 318 | cfg->passive_active = n_pactive_ch; |
97511b15 | 319 | |
95feadca LC |
320 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", |
321 | cfg->active[0], cfg->passive[0]); | |
322 | wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", | |
323 | cfg->active[1], cfg->passive[1]); | |
2497a246 | 324 | wl1271_debug(DEBUG_SCAN, " DFS: %d", cfg->dfs); |
95feadca | 325 | |
d2c2bb9f LC |
326 | return cfg->passive[0] || cfg->active[0] || |
327 | cfg->passive[1] || cfg->active[1] || cfg->dfs || | |
328 | cfg->passive[2] || cfg->active[2]; | |
95feadca | 329 | } |
78e28062 | 330 | EXPORT_SYMBOL_GPL(wlcore_set_scan_chan_params); |
95feadca | 331 | |
78e28062 EP |
332 | int wlcore_scan(struct wl1271 *wl, struct ieee80211_vif *vif, |
333 | const u8 *ssid, size_t ssid_len, | |
334 | struct cfg80211_scan_request *req) | |
335 | { | |
336 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); | |
337 | ||
338 | /* | |
339 | * cfg80211 should guarantee that we don't get more channels | |
340 | * than what we have registered. | |
341 | */ | |
342 | BUG_ON(req->n_channels > WL1271_MAX_CHANNELS); | |
343 | ||
344 | if (wl->scan.state != WL1271_SCAN_STATE_IDLE) | |
345 | return -EBUSY; | |
346 | ||
347 | wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE; | |
348 | ||
349 | if (ssid_len && ssid) { | |
350 | wl->scan.ssid_len = ssid_len; | |
351 | memcpy(wl->scan.ssid, ssid, ssid_len); | |
352 | } else { | |
353 | wl->scan.ssid_len = 0; | |
354 | } | |
355 | ||
c50a2825 | 356 | wl->scan_wlvif = wlvif; |
78e28062 EP |
357 | wl->scan.req = req; |
358 | memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); | |
359 | ||
360 | /* we assume failure so that timeout scenarios are handled correctly */ | |
361 | wl->scan.failed = true; | |
362 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | |
363 | msecs_to_jiffies(WL1271_SCAN_TIMEOUT)); | |
364 | ||
365 | wl->ops->scan_start(wl, wlvif, req); | |
366 | ||
367 | return 0; | |
368 | } | |
fb55377b | 369 | /* Returns the scan type to be used or a negative value on error */ |
78e28062 EP |
370 | int |
371 | wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, | |
78f85f50 | 372 | struct wl12xx_vif *wlvif, |
f952079a LC |
373 | struct cfg80211_sched_scan_request *req) |
374 | { | |
375 | struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL; | |
fb55377b LC |
376 | struct cfg80211_match_set *sets = req->match_sets; |
377 | struct cfg80211_ssid *ssids = req->ssids; | |
20a33e52 | 378 | int ret = 0, type, i, j, n_match_ssids = 0; |
f952079a | 379 | |
0fe72086 | 380 | wl1271_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list"); |
f952079a | 381 | |
20a33e52 LC |
382 | /* count the match sets that contain SSIDs */ |
383 | for (i = 0; i < req->n_match_sets; i++) | |
384 | if (sets[i].ssid.ssid_len > 0) | |
385 | n_match_ssids++; | |
386 | ||
fb55377b | 387 | /* No filter, no ssids or only bcast ssid */ |
20a33e52 | 388 | if (!n_match_ssids && |
fb55377b LC |
389 | (!req->n_ssids || |
390 | (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) { | |
391 | type = SCAN_SSID_FILTER_ANY; | |
392 | goto out; | |
393 | } | |
394 | ||
f952079a | 395 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); |
fb55377b LC |
396 | if (!cmd) { |
397 | ret = -ENOMEM; | |
398 | goto out; | |
399 | } | |
400 | ||
8dc57430 | 401 | cmd->role_id = wlvif->role_id; |
20a33e52 | 402 | if (!n_match_ssids) { |
fb55377b LC |
403 | /* No filter, with ssids */ |
404 | type = SCAN_SSID_FILTER_DISABLED; | |
405 | ||
406 | for (i = 0; i < req->n_ssids; i++) { | |
407 | cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ? | |
408 | SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC; | |
409 | cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len; | |
410 | memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid, | |
411 | ssids[i].ssid_len); | |
412 | cmd->n_ssids++; | |
413 | } | |
414 | } else { | |
415 | type = SCAN_SSID_FILTER_LIST; | |
f952079a | 416 | |
fb55377b LC |
417 | /* Add all SSIDs from the filters */ |
418 | for (i = 0; i < req->n_match_sets; i++) { | |
20a33e52 LC |
419 | /* ignore sets without SSIDs */ |
420 | if (!sets[i].ssid.ssid_len) | |
421 | continue; | |
422 | ||
bd4932b8 | 423 | cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC; |
fb55377b LC |
424 | cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len; |
425 | memcpy(cmd->ssids[cmd->n_ssids].ssid, | |
426 | sets[i].ssid.ssid, sets[i].ssid.ssid_len); | |
427 | cmd->n_ssids++; | |
428 | } | |
429 | if ((req->n_ssids > 1) || | |
430 | (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) { | |
431 | /* | |
432 | * Mark all the SSIDs passed in the SSID list as HIDDEN, | |
433 | * so they're used in probe requests. | |
434 | */ | |
435 | for (i = 0; i < req->n_ssids; i++) { | |
1b04b739 ES |
436 | if (!req->ssids[i].ssid_len) |
437 | continue; | |
438 | ||
fb55377b | 439 | for (j = 0; j < cmd->n_ssids; j++) |
587cc286 | 440 | if ((req->ssids[i].ssid_len == |
faae5aae | 441 | cmd->ssids[j].len) && |
587cc286 | 442 | !memcmp(req->ssids[i].ssid, |
fb55377b LC |
443 | cmd->ssids[j].ssid, |
444 | req->ssids[i].ssid_len)) { | |
445 | cmd->ssids[j].type = | |
446 | SCAN_SSID_TYPE_HIDDEN; | |
447 | break; | |
448 | } | |
449 | /* Fail if SSID isn't present in the filters */ | |
cc438fcc | 450 | if (j == cmd->n_ssids) { |
fb55377b LC |
451 | ret = -EINVAL; |
452 | goto out_free; | |
453 | } | |
454 | } | |
bd4932b8 | 455 | } |
f952079a LC |
456 | } |
457 | ||
f952079a LC |
458 | ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd, |
459 | sizeof(*cmd), 0); | |
460 | if (ret < 0) { | |
461 | wl1271_error("cmd sched scan ssid list failed"); | |
fb55377b | 462 | goto out_free; |
f952079a LC |
463 | } |
464 | ||
fb55377b | 465 | out_free: |
f952079a | 466 | kfree(cmd); |
fb55377b LC |
467 | out: |
468 | if (ret < 0) | |
469 | return ret; | |
470 | return type; | |
f952079a | 471 | } |
78e28062 | 472 | EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_ssid_list); |
95feadca | 473 | |
0b70078c | 474 | void wlcore_scan_sched_scan_results(struct wl1271 *wl) |
95feadca LC |
475 | { |
476 | wl1271_debug(DEBUG_SCAN, "got periodic scan results"); | |
477 | ||
478 | ieee80211_sched_scan_results(wl->hw); | |
479 | } | |
0b70078c | 480 | EXPORT_SYMBOL_GPL(wlcore_scan_sched_scan_results); |