diff options
Diffstat (limited to 'drivers/net/wireless/ti/wl18xx/scan.c')
-rw-r--r-- | drivers/net/wireless/ti/wl18xx/scan.c | 326 |
1 files changed, 326 insertions, 0 deletions
diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c new file mode 100644 index 000000000000..09d944505ac0 --- /dev/null +++ b/drivers/net/wireless/ti/wl18xx/scan.c | |||
@@ -0,0 +1,326 @@ | |||
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, | ||
37 | sizeof(cmd->channels_2)); | ||
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 | |||
54 | cmd->role_id = wlvif->role_id; | ||
55 | |||
56 | if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { | ||
57 | ret = -EINVAL; | ||
58 | goto out; | ||
59 | } | ||
60 | |||
61 | cmd->scan_type = SCAN_TYPE_SEARCH; | ||
62 | cmd->rssi_threshold = -127; | ||
63 | cmd->snr_threshold = 0; | ||
64 | |||
65 | cmd->bss_type = SCAN_BSS_TYPE_ANY; | ||
66 | |||
67 | cmd->ssid_from_list = 0; | ||
68 | cmd->filter = 0; | ||
69 | cmd->add_broadcast = 0; | ||
70 | |||
71 | cmd->urgency = 0; | ||
72 | cmd->protect = 0; | ||
73 | |||
74 | cmd->n_probe_reqs = wl->conf.scan.num_probe_reqs; | ||
75 | cmd->terminate_after = 0; | ||
76 | |||
77 | /* configure channels */ | ||
78 | WARN_ON(req->n_ssids > 1); | ||
79 | |||
80 | cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); | ||
81 | if (!cmd_channels) { | ||
82 | ret = -ENOMEM; | ||
83 | goto out; | ||
84 | } | ||
85 | |||
86 | wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, | ||
87 | req->n_channels, req->n_ssids, | ||
88 | SCAN_TYPE_SEARCH); | ||
89 | wl18xx_adjust_channels(cmd, cmd_channels); | ||
90 | |||
91 | /* | ||
92 | * all the cycles params (except total cycles) should | ||
93 | * remain 0 for normal scan | ||
94 | */ | ||
95 | cmd->total_cycles = 1; | ||
96 | |||
97 | if (req->no_cck) | ||
98 | cmd->rate = WL18XX_SCAN_RATE_6; | ||
99 | |||
100 | cmd->tag = WL1271_SCAN_DEFAULT_TAG; | ||
101 | |||
102 | if (req->n_ssids) { | ||
103 | cmd->ssid_len = req->ssids[0].ssid_len; | ||
104 | memcpy(cmd->ssid, req->ssids[0].ssid, cmd->ssid_len); | ||
105 | } | ||
106 | |||
107 | /* TODO: per-band ies? */ | ||
108 | if (cmd->active[0]) { | ||
109 | u8 band = IEEE80211_BAND_2GHZ; | ||
110 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, | ||
111 | cmd->role_id, band, | ||
112 | req->ssids ? req->ssids[0].ssid : NULL, | ||
113 | req->ssids ? req->ssids[0].ssid_len : 0, | ||
114 | req->ie, | ||
115 | req->ie_len, | ||
116 | false); | ||
117 | if (ret < 0) { | ||
118 | wl1271_error("2.4GHz PROBE request template failed"); | ||
119 | goto out; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (cmd->active[1] || cmd->dfs) { | ||
124 | u8 band = IEEE80211_BAND_5GHZ; | ||
125 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, | ||
126 | cmd->role_id, band, | ||
127 | req->ssids ? req->ssids[0].ssid : NULL, | ||
128 | req->ssids ? req->ssids[0].ssid_len : 0, | ||
129 | req->ie, | ||
130 | req->ie_len, | ||
131 | false); | ||
132 | if (ret < 0) { | ||
133 | wl1271_error("5GHz PROBE request template failed"); | ||
134 | goto out; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); | ||
139 | |||
140 | ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); | ||
141 | if (ret < 0) { | ||
142 | wl1271_error("SCAN failed"); | ||
143 | goto out; | ||
144 | } | ||
145 | |||
146 | out: | ||
147 | kfree(cmd_channels); | ||
148 | kfree(cmd); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif) | ||
153 | { | ||
154 | wl->scan.failed = false; | ||
155 | cancel_delayed_work(&wl->scan_complete_work); | ||
156 | ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work, | ||
157 | msecs_to_jiffies(0)); | ||
158 | } | ||
159 | |||
160 | static | ||
161 | int wl18xx_scan_sched_scan_config(struct wl1271 *wl, | ||
162 | struct wl12xx_vif *wlvif, | ||
163 | struct cfg80211_sched_scan_request *req, | ||
164 | struct ieee80211_sched_scan_ies *ies) | ||
165 | { | ||
166 | struct wl18xx_cmd_scan_params *cmd; | ||
167 | struct wlcore_scan_channels *cmd_channels = NULL; | ||
168 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | ||
169 | int ret; | ||
170 | int filter_type; | ||
171 | |||
172 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | ||
173 | |||
174 | filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req); | ||
175 | if (filter_type < 0) | ||
176 | return filter_type; | ||
177 | |||
178 | cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); | ||
179 | if (!cmd) { | ||
180 | ret = -ENOMEM; | ||
181 | goto out; | ||
182 | } | ||
183 | |||
184 | cmd->role_id = wlvif->role_id; | ||
185 | |||
186 | if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { | ||
187 | ret = -EINVAL; | ||
188 | goto out; | ||
189 | } | ||
190 | |||
191 | cmd->scan_type = SCAN_TYPE_PERIODIC; | ||
192 | cmd->rssi_threshold = c->rssi_threshold; | ||
193 | cmd->snr_threshold = c->snr_threshold; | ||
194 | |||
195 | /* don't filter on BSS type */ | ||
196 | cmd->bss_type = SCAN_BSS_TYPE_ANY; | ||
197 | |||
198 | cmd->ssid_from_list = 1; | ||
199 | if (filter_type == SCAN_SSID_FILTER_LIST) | ||
200 | cmd->filter = 1; | ||
201 | cmd->add_broadcast = 0; | ||
202 | |||
203 | cmd->urgency = 0; | ||
204 | cmd->protect = 0; | ||
205 | |||
206 | cmd->n_probe_reqs = c->num_probe_reqs; | ||
207 | /* don't stop scanning automatically when something is found */ | ||
208 | cmd->terminate_after = 0; | ||
209 | |||
210 | cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL); | ||
211 | if (!cmd_channels) { | ||
212 | ret = -ENOMEM; | ||
213 | goto out; | ||
214 | } | ||
215 | |||
216 | /* configure channels */ | ||
217 | wlcore_set_scan_chan_params(wl, cmd_channels, req->channels, | ||
218 | req->n_channels, req->n_ssids, | ||
219 | SCAN_TYPE_PERIODIC); | ||
220 | wl18xx_adjust_channels(cmd, cmd_channels); | ||
221 | |||
222 | cmd->short_cycles_sec = 0; | ||
223 | cmd->long_cycles_sec = cpu_to_le16(req->interval); | ||
224 | cmd->short_cycles_count = 0; | ||
225 | |||
226 | cmd->total_cycles = 0; | ||
227 | |||
228 | cmd->tag = WL1271_SCAN_DEFAULT_TAG; | ||
229 | |||
230 | /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ | ||
231 | cmd->report_threshold = 1; | ||
232 | cmd->terminate_on_report = 0; | ||
233 | |||
234 | if (cmd->active[0]) { | ||
235 | u8 band = IEEE80211_BAND_2GHZ; | ||
236 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, | ||
237 | cmd->role_id, band, | ||
238 | req->ssids ? req->ssids[0].ssid : NULL, | ||
239 | req->ssids ? req->ssids[0].ssid_len : 0, | ||
240 | ies->ie[band], | ||
241 | ies->len[band], | ||
242 | true); | ||
243 | if (ret < 0) { | ||
244 | wl1271_error("2.4GHz PROBE request template failed"); | ||
245 | goto out; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | if (cmd->active[1] || cmd->dfs) { | ||
250 | u8 band = IEEE80211_BAND_5GHZ; | ||
251 | ret = wl12xx_cmd_build_probe_req(wl, wlvif, | ||
252 | cmd->role_id, band, | ||
253 | req->ssids ? req->ssids[0].ssid : NULL, | ||
254 | req->ssids ? req->ssids[0].ssid_len : 0, | ||
255 | ies->ie[band], | ||
256 | ies->len[band], | ||
257 | true); | ||
258 | if (ret < 0) { | ||
259 | wl1271_error("5GHz PROBE request template failed"); | ||
260 | goto out; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); | ||
265 | |||
266 | ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); | ||
267 | if (ret < 0) { | ||
268 | wl1271_error("SCAN failed"); | ||
269 | goto out; | ||
270 | } | ||
271 | |||
272 | out: | ||
273 | kfree(cmd_channels); | ||
274 | kfree(cmd); | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, | ||
279 | struct cfg80211_sched_scan_request *req, | ||
280 | struct ieee80211_sched_scan_ies *ies) | ||
281 | { | ||
282 | return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies); | ||
283 | } | ||
284 | |||
285 | static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif, | ||
286 | u8 scan_type) | ||
287 | { | ||
288 | struct wl18xx_cmd_scan_stop *stop; | ||
289 | int ret; | ||
290 | |||
291 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | ||
292 | |||
293 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | ||
294 | if (!stop) { | ||
295 | wl1271_error("failed to alloc memory to send sched scan stop"); | ||
296 | return -ENOMEM; | ||
297 | } | ||
298 | |||
299 | stop->role_id = wlvif->role_id; | ||
300 | stop->scan_type = scan_type; | ||
301 | |||
302 | ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0); | ||
303 | if (ret < 0) { | ||
304 | wl1271_error("failed to send sched scan stop command"); | ||
305 | goto out_free; | ||
306 | } | ||
307 | |||
308 | out_free: | ||
309 | kfree(stop); | ||
310 | return ret; | ||
311 | } | ||
312 | |||
313 | void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) | ||
314 | { | ||
315 | __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC); | ||
316 | } | ||
317 | int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, | ||
318 | struct cfg80211_scan_request *req) | ||
319 | { | ||
320 | return wl18xx_scan_send(wl, wlvif, req); | ||
321 | } | ||
322 | |||
323 | int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif) | ||
324 | { | ||
325 | return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH); | ||
326 | } | ||