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