]>
Commit | Line | Data |
---|---|---|
78e28062 EP |
1 | /* |
2 | * This file is part of wl18xx | |
3 | * | |
4 | * Copyright (C) 2012 Texas Instruments. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
18 | * 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <linux/ieee80211.h> | |
23 | #include "scan.h" | |
24 | #include "../wlcore/debug.h" | |
25 | ||
26 | static void wl18xx_adjust_channels(struct wl18xx_cmd_scan_params *cmd, | |
27 | struct wlcore_scan_channels *cmd_channels) | |
28 | { | |
29 | memcpy(cmd->passive, cmd_channels->passive, sizeof(cmd->passive)); | |
30 | memcpy(cmd->active, cmd_channels->active, sizeof(cmd->active)); | |
31 | cmd->dfs = cmd_channels->dfs; | |
32 | cmd->passive_active = cmd_channels->passive_active; | |
33 | ||
34 | memcpy(cmd->channels_2, cmd_channels->channels_2, | |
35 | sizeof(cmd->channels_2)); | |
36 | memcpy(cmd->channels_5, cmd_channels->channels_5, | |
a805de4d | 37 | sizeof(cmd->channels_5)); |
78e28062 EP |
38 | /* channels_4 are not supported, so no need to copy them */ |
39 | } | |
40 | ||
41 | static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif, | |
42 | struct cfg80211_scan_request *req) | |
43 | { | |
44 | struct wl18xx_cmd_scan_params *cmd; | |
45 | struct wlcore_scan_channels *cmd_channels = NULL; | |
46 | int ret; | |
47 | ||
48 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
49 | if (!cmd) { | |
50 | ret = -ENOMEM; | |
51 | goto out; | |
52 | } | |
53 | ||
7845af35 EP |
54 | /* scan on the dev role if the regular one is not started */ |
55 | if (wlcore_is_p2p_mgmt(wlvif)) | |
56 | cmd->role_id = wlvif->dev_role_id; | |
57 | else | |
58 | cmd->role_id = wlvif->role_id; | |
78e28062 EP |
59 | |
60 | if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { | |
61 | ret = -EINVAL; | |
62 | goto out; | |
63 | } | |
64 | ||
65 | cmd->scan_type = SCAN_TYPE_SEARCH; | |
66 | cmd->rssi_threshold = -127; | |
67 | cmd->snr_threshold = 0; | |
68 | ||
69 | cmd->bss_type = SCAN_BSS_TYPE_ANY; | |
70 | ||
71 | cmd->ssid_from_list = 0; | |
72 | cmd->filter = 0; | |
73 | cmd->add_broadcast = 0; | |
74 | ||
75 | cmd->urgency = 0; | |
76 | cmd->protect = 0; | |
77 | ||
78 | cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs; | |
79 | cmd->terminate_after = 0; | |
80 | ||
81 | /* configure channels */ | |
82 | WARN_ON(req->n_ssids > 1); | |
83 | ||
84 | cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); | |
85 | if (!cmd_channels) { | |
86 | ret = -ENOMEM; | |
87 | goto out; | |
88 | } | |
89 | ||
90 | wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, | |
7c482c10 EP |
91 | req->n_channels, req->n_ssids, |
92 | SCAN_TYPE_SEARCH); | |
78e28062 EP |
93 | wl18xx_adjust_channels(cmd, cmd_channels); |
94 | ||
95 | /* | |
96 | * all the cycles params (except total cycles) should | |
97 | * remain 0 for normal scan | |
98 | */ | |
99 | cmd->total_cycles = 1; | |
100 | ||
101 | if (req->no_cck) | |
102 | cmd->rate = WL18XX_SCAN_RATE_6; | |
103 | ||
104 | cmd->tag = WL1271_SCAN_DEFAULT_TAG; | |
105 | ||
106 | if (req->n_ssids) { | |
107 | cmd->ssid_len = req->ssids[0].ssid_len; | |
108 | memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len); | |
109 | } | |
110 | ||
111 | /* TODO: per-band ies? */ | |
112 | if (cmd->active[0]) { | |
57fbcce3 | 113 | u8 band = NL80211_BAND_2GHZ; |
78e28062 | 114 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, |
c23280eb AN |
115 | cmd->role_id, band, |
116 | req->ssids ? req->ssids[0].ssid : NULL, | |
117 | req->ssids ? req->ssids[0].ssid_len : 0, | |
118 | req->ie, | |
119 | req->ie_len, | |
633e2713 DS |
120 | NULL, |
121 | 0, | |
c23280eb | 122 | false); |
78e28062 EP |
123 | if (ret < 0) { |
124 | wl1271_error("2.4GHz PROBE request template failed"); | |
125 | goto out; | |
126 | } | |
127 | } | |
128 | ||
c23280eb | 129 | if (cmd->active[1] || cmd->dfs) { |
57fbcce3 | 130 | u8 band = NL80211_BAND_5GHZ; |
78e28062 | 131 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, |
c23280eb AN |
132 | cmd->role_id, band, |
133 | req->ssids ? req->ssids[0].ssid : NULL, | |
134 | req->ssids ? req->ssids[0].ssid_len : 0, | |
135 | req->ie, | |
136 | req->ie_len, | |
633e2713 DS |
137 | NULL, |
138 | 0, | |
c23280eb | 139 | false); |
78e28062 EP |
140 | if (ret < 0) { |
141 | wl1271_error("5GHz PROBE request template failed"); | |
142 | goto out; | |
143 | } | |
144 | } | |
145 | ||
146 | wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); | |
147 | ||
148 | ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); | |
149 | if (ret < 0) { | |
150 | wl1271_error("SCAN failed"); | |
151 | goto out; | |
152 | } | |
153 | ||
154 | out: | |
155 | kfree(cmd_channels); | |
156 | kfree(cmd); | |
157 | return ret; | |
158 | } | |
159 | ||
160 | void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) | |
161 | { | |
162 | wl->scan.failed = false; | |
163 | cancel_delayed_work(&wl->scan_complete_work); | |
164 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | |
165 | msecs_to_jiffies(0)); | |
166 | } | |
167 | ||
168 | static | |
169 | int wl18xx_scan_sched_scan_config(struct wl1271 *wl, | |
170 | struct wl12xx_vif *wlvif, | |
171 | struct cfg80211_sched_scan_request *req, | |
633e2713 | 172 | struct ieee80211_scan_ies *ies) |
78e28062 EP |
173 | { |
174 | struct wl18xx_cmd_scan_params *cmd; | |
175 | struct wlcore_scan_channels *cmd_channels = NULL; | |
176 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | |
177 | int ret; | |
178 | int filter_type; | |
179 | ||
180 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | |
181 | ||
182 | filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); | |
183 | if (filter_type < 0) | |
184 | return filter_type; | |
185 | ||
186 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | |
187 | if (!cmd) { | |
188 | ret = -ENOMEM; | |
189 | goto out; | |
190 | } | |
191 | ||
192 | cmd->role_id = wlvif->role_id; | |
193 | ||
194 | if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { | |
195 | ret = -EINVAL; | |
196 | goto out; | |
197 | } | |
198 | ||
199 | cmd->scan_type = SCAN_TYPE_PERIODIC; | |
200 | cmd->rssi_threshold = c->rssi_threshold; | |
201 | cmd->snr_threshold = c->snr_threshold; | |
202 | ||
203 | /* don't filter on BSS type */ | |
204 | cmd->bss_type = SCAN_BSS_TYPE_ANY; | |
205 | ||
206 | cmd->ssid_from_list = 1; | |
207 | if (filter_type == SCAN_SSID_FILTER_LIST) | |
208 | cmd->filter = 1; | |
209 | cmd->add_broadcast = 0; | |
210 | ||
211 | cmd->urgency = 0; | |
212 | cmd->protect = 0; | |
213 | ||
214 | cmd->n_probe_reqs = c->num_probe_reqs; | |
215 | /* don't stop scanning automatically when something is found */ | |
216 | cmd->terminate_after = 0; | |
217 | ||
218 | cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); | |
219 | if (!cmd_channels) { | |
220 | ret = -ENOMEM; | |
221 | goto out; | |
222 | } | |
223 | ||
224 | /* configure channels */ | |
225 | wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, | |
7c482c10 EP |
226 | req->n_channels, req->n_ssids, |
227 | SCAN_TYPE_PERIODIC); | |
78e28062 EP |
228 | wl18xx_adjust_channels(cmd, cmd_channels); |
229 | ||
8698a3a4 | 230 | if (c->num_short_intervals && c->long_interval && |
3b06d277 AS |
231 | c->long_interval > req->scan_plans[0].interval * MSEC_PER_SEC) { |
232 | cmd->short_cycles_msec = | |
233 | cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); | |
8698a3a4 EP |
234 | cmd->long_cycles_msec = cpu_to_le16(c->long_interval); |
235 | cmd->short_cycles_count = c->num_short_intervals; | |
236 | } else { | |
237 | cmd->short_cycles_msec = 0; | |
3b06d277 AS |
238 | cmd->long_cycles_msec = |
239 | cpu_to_le16(req->scan_plans[0].interval * MSEC_PER_SEC); | |
8698a3a4 EP |
240 | cmd->short_cycles_count = 0; |
241 | } | |
242 | wl1271_debug(DEBUG_SCAN, "short_interval: %d, long_interval: %d, num_short: %d", | |
243 | le16_to_cpu(cmd->short_cycles_msec), | |
244 | le16_to_cpu(cmd->long_cycles_msec), | |
245 | cmd->short_cycles_count); | |
78e28062 EP |
246 | |
247 | cmd->total_cycles = 0; | |
248 | ||
249 | cmd->tag = WL1271_SCAN_DEFAULT_TAG; | |
250 | ||
0b70078c ES |
251 | /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ |
252 | cmd->report_threshold = 1; | |
253 | cmd->terminate_on_report = 0; | |
254 | ||
78e28062 | 255 | if (cmd->active[0]) { |
57fbcce3 | 256 | u8 band = NL80211_BAND_2GHZ; |
78e28062 | 257 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, |
c23280eb AN |
258 | cmd->role_id, band, |
259 | req->ssids ? req->ssids[0].ssid : NULL, | |
260 | req->ssids ? req->ssids[0].ssid_len : 0, | |
633e2713 | 261 | ies->ies[band], |
c23280eb | 262 | ies->len[band], |
633e2713 DS |
263 | ies->common_ies, |
264 | ies->common_ie_len, | |
c23280eb | 265 | true); |
78e28062 EP |
266 | if (ret < 0) { |
267 | wl1271_error("2.4GHz PROBE request template failed"); | |
268 | goto out; | |
269 | } | |
270 | } | |
271 | ||
c23280eb | 272 | if (cmd->active[1] || cmd->dfs) { |
57fbcce3 | 273 | u8 band = NL80211_BAND_5GHZ; |
78e28062 | 274 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, |
c23280eb AN |
275 | cmd->role_id, band, |
276 | req->ssids ? req->ssids[0].ssid : NULL, | |
277 | req->ssids ? req->ssids[0].ssid_len : 0, | |
633e2713 | 278 | ies->ies[band], |
c23280eb | 279 | ies->len[band], |
633e2713 DS |
280 | ies->common_ies, |
281 | ies->common_ie_len, | |
c23280eb | 282 | true); |
78e28062 EP |
283 | if (ret < 0) { |
284 | wl1271_error("5GHz PROBE request template failed"); | |
285 | goto out; | |
286 | } | |
287 | } | |
288 | ||
289 | wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); | |
290 | ||
291 | ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); | |
292 | if (ret < 0) { | |
293 | wl1271_error("SCAN failed"); | |
294 | goto out; | |
295 | } | |
296 | ||
297 | out: | |
298 | kfree(cmd_channels); | |
299 | kfree(cmd); | |
300 | return ret; | |
301 | } | |
302 | ||
303 | int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, | |
304 | struct cfg80211_sched_scan_request *req, | |
633e2713 | 305 | struct ieee80211_scan_ies *ies) |
78e28062 EP |
306 | { |
307 | return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); | |
308 | } | |
309 | ||
310 | static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif, | |
311 | u8 scan_type) | |
312 | { | |
313 | struct wl18xx_cmd_scan_stop *stop; | |
314 | int ret; | |
315 | ||
316 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | |
317 | ||
318 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | |
319 | if (!stop) { | |
320 | wl1271_error("failed to alloc memory to send sched scan stop"); | |
321 | return -ENOMEM; | |
322 | } | |
323 | ||
324 | stop->role_id = wlvif->role_id; | |
325 | stop->scan_type = scan_type; | |
326 | ||
327 | ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0); | |
328 | if (ret < 0) { | |
329 | wl1271_error("failed to send sched scan stop command"); | |
330 | goto out_free; | |
331 | } | |
332 | ||
333 | out_free: | |
334 | kfree(stop); | |
335 | return ret; | |
336 | } | |
337 | ||
338 | void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) | |
339 | { | |
340 | __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC); | |
341 | } | |
342 | int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, | |
343 | struct cfg80211_scan_request *req) | |
344 | { | |
345 | return wl18xx_scan_send(wl, wlvif, req); | |
346 | } | |
347 | ||
348 | int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) | |
349 | { | |
350 | return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH); | |
351 | } |