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
26static 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_5));
38	/* channels_4 are not supported, so no need to copy them */
39}
40
41static 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				 NULL,
117				 0,
118				 false);
119		if (ret < 0) {
120			wl1271_error("2.4GHz PROBE request template failed");
121			goto out;
122		}
123	}
124
125	if (cmd->active[1] || cmd->dfs) {
126		u8 band = IEEE80211_BAND_5GHZ;
127		ret = wl12xx_cmd_build_probe_req(wl, wlvif,
128				 cmd->role_id, band,
129				 req->ssids ? req->ssids[0].ssid : NULL,
130				 req->ssids ? req->ssids[0].ssid_len : 0,
131				 req->ie,
132				 req->ie_len,
133				 NULL,
134				 0,
135				 false);
136		if (ret < 0) {
137			wl1271_error("5GHz PROBE request template failed");
138			goto out;
139		}
140	}
141
142	wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
143
144	ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
145	if (ret < 0) {
146		wl1271_error("SCAN failed");
147		goto out;
148	}
149
150out:
151	kfree(cmd_channels);
152	kfree(cmd);
153	return ret;
154}
155
156void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif)
157{
158	wl->scan.failed = false;
159	cancel_delayed_work(&wl->scan_complete_work);
160	ieee80211_queue_delayed_work(wl->hw, &wl->scan_complete_work,
161				     msecs_to_jiffies(0));
162}
163
164static
165int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
166				  struct wl12xx_vif *wlvif,
167				  struct cfg80211_sched_scan_request *req,
168				  struct ieee80211_scan_ies *ies)
169{
170	struct wl18xx_cmd_scan_params *cmd;
171	struct wlcore_scan_channels *cmd_channels = NULL;
172	struct conf_sched_scan_settings *c = &wl->conf.sched_scan;
173	int ret;
174	int filter_type;
175
176	wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config");
177
178	filter_type = wlcore_scan_sched_scan_ssid_list(wl, wlvif, req);
179	if (filter_type < 0)
180		return filter_type;
181
182	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
183	if (!cmd) {
184		ret = -ENOMEM;
185		goto out;
186	}
187
188	cmd->role_id = wlvif->role_id;
189
190	if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) {
191		ret = -EINVAL;
192		goto out;
193	}
194
195	cmd->scan_type = SCAN_TYPE_PERIODIC;
196	cmd->rssi_threshold = c->rssi_threshold;
197	cmd->snr_threshold = c->snr_threshold;
198
199	/* don't filter on BSS type */
200	cmd->bss_type = SCAN_BSS_TYPE_ANY;
201
202	cmd->ssid_from_list = 1;
203	if (filter_type == SCAN_SSID_FILTER_LIST)
204		cmd->filter = 1;
205	cmd->add_broadcast = 0;
206
207	cmd->urgency = 0;
208	cmd->protect = 0;
209
210	cmd->n_probe_reqs = c->num_probe_reqs;
211	/* don't stop scanning automatically when something is found */
212	cmd->terminate_after = 0;
213
214	cmd_channels = kzalloc(sizeof(*cmd_channels), GFP_KERNEL);
215	if (!cmd_channels) {
216		ret = -ENOMEM;
217		goto out;
218	}
219
220	/* configure channels */
221	wlcore_set_scan_chan_params(wl, cmd_channels, req->channels,
222				    req->n_channels, req->n_ssids,
223				    SCAN_TYPE_PERIODIC);
224	wl18xx_adjust_channels(cmd, cmd_channels);
225
226	cmd->short_cycles_sec = 0;
227	cmd->long_cycles_sec = cpu_to_le16(req->interval);
228	cmd->short_cycles_count = 0;
229
230	cmd->total_cycles = 0;
231
232	cmd->tag = WL1271_SCAN_DEFAULT_TAG;
233
234	/* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */
235	cmd->report_threshold = 1;
236	cmd->terminate_on_report = 0;
237
238	if (cmd->active[0]) {
239		u8 band = IEEE80211_BAND_2GHZ;
240		ret = wl12xx_cmd_build_probe_req(wl, wlvif,
241				 cmd->role_id, band,
242				 req->ssids ? req->ssids[0].ssid : NULL,
243				 req->ssids ? req->ssids[0].ssid_len : 0,
244				 ies->ies[band],
245				 ies->len[band],
246				 ies->common_ies,
247				 ies->common_ie_len,
248				 true);
249		if (ret < 0) {
250			wl1271_error("2.4GHz PROBE request template failed");
251			goto out;
252		}
253	}
254
255	if (cmd->active[1] || cmd->dfs) {
256		u8 band = IEEE80211_BAND_5GHZ;
257		ret = wl12xx_cmd_build_probe_req(wl, wlvif,
258				 cmd->role_id, band,
259				 req->ssids ? req->ssids[0].ssid : NULL,
260				 req->ssids ? req->ssids[0].ssid_len : 0,
261				 ies->ies[band],
262				 ies->len[band],
263				 ies->common_ies,
264				 ies->common_ie_len,
265				 true);
266		if (ret < 0) {
267			wl1271_error("5GHz PROBE request template failed");
268			goto out;
269		}
270	}
271
272	wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
273
274	ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
275	if (ret < 0) {
276		wl1271_error("SCAN failed");
277		goto out;
278	}
279
280out:
281	kfree(cmd_channels);
282	kfree(cmd);
283	return ret;
284}
285
286int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
287			    struct cfg80211_sched_scan_request *req,
288			    struct ieee80211_scan_ies *ies)
289{
290	return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
291}
292
293static int __wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif,
294			       u8 scan_type)
295{
296	struct wl18xx_cmd_scan_stop *stop;
297	int ret;
298
299	wl1271_debug(DEBUG_CMD, "cmd periodic scan stop");
300
301	stop = kzalloc(sizeof(*stop), GFP_KERNEL);
302	if (!stop) {
303		wl1271_error("failed to alloc memory to send sched scan stop");
304		return -ENOMEM;
305	}
306
307	stop->role_id = wlvif->role_id;
308	stop->scan_type = scan_type;
309
310	ret = wl1271_cmd_send(wl, CMD_STOP_SCAN, stop, sizeof(*stop), 0);
311	if (ret < 0) {
312		wl1271_error("failed to send sched scan stop command");
313		goto out_free;
314	}
315
316out_free:
317	kfree(stop);
318	return ret;
319}
320
321void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
322{
323	__wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_PERIODIC);
324}
325int wl18xx_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
326		      struct cfg80211_scan_request *req)
327{
328	return wl18xx_scan_send(wl, wlvif, req);
329}
330
331int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif)
332{
333	return __wl18xx_scan_stop(wl, wlvif, SCAN_TYPE_SEARCH);
334}
335