Blame view

net/mac80211/scan.c 25.9 KB
0a51b27e9   Johannes Berg   mac80211: start m...
1
  /*
5484e2374   Johannes Berg   mac80211: move BS...
2
3
   * Scanning implementation
   *
0a51b27e9   Johannes Berg   mac80211: start m...
4
5
6
7
8
9
10
11
12
13
   * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
   * Copyright 2004, Instant802 Networks, Inc.
   * Copyright 2005, Devicescape Software, Inc.
   * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
   * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
0a51b27e9   Johannes Berg   mac80211: start m...
14
  #include <linux/if_arp.h>
888d04dfb   Felix Fietkau   mac80211: use com...
15
  #include <linux/etherdevice.h>
078e1e60d   Johannes Berg   mac80211: Add cap...
16
  #include <linux/rtnetlink.h>
e8db0be12   Jean Pihet   PM QoS: Move and ...
17
  #include <linux/pm_qos.h>
df13cce53   Helmut Schaa   mac80211: Improve...
18
  #include <net/sch_generic.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
20
  #include <linux/export.h>
0a51b27e9   Johannes Berg   mac80211: start m...
21
  #include <net/mac80211.h>
0a51b27e9   Johannes Berg   mac80211: start m...
22
23
  
  #include "ieee80211_i.h"
244879813   Johannes Berg   mac80211: add dri...
24
  #include "driver-ops.h"
5484e2374   Johannes Berg   mac80211: move BS...
25
  #include "mesh.h"
0a51b27e9   Johannes Berg   mac80211: start m...
26
27
28
  
  #define IEEE80211_PROBE_DELAY (HZ / 33)
  #define IEEE80211_CHANNEL_TIME (HZ / 33)
3f892b61a   Stanislaw Gruszka   mac80211: improve...
29
  #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)
0a51b27e9   Johannes Berg   mac80211: start m...
30

5484e2374   Johannes Berg   mac80211: move BS...
31
  void ieee80211_rx_bss_put(struct ieee80211_local *local,
c2b13452b   Johannes Berg   mac80211: clean u...
32
  			  struct ieee80211_bss *bss)
5484e2374   Johannes Berg   mac80211: move BS...
33
  {
0c1ad2cac   Johannes Berg   mac80211: proper ...
34
35
  	if (!bss)
  		return;
5b112d3d0   Johannes Berg   cfg80211: pass wi...
36
37
  	cfg80211_put_bss(local->hw.wiphy,
  			 container_of((void *)bss, struct cfg80211_bss, priv));
5484e2374   Johannes Berg   mac80211: move BS...
38
  }
ab13315af   Kalle Valo   mac80211: add U-A...
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
  static bool is_uapsd_supported(struct ieee802_11_elems *elems)
  {
  	u8 qos_info;
  
  	if (elems->wmm_info && elems->wmm_info_len == 7
  	    && elems->wmm_info[5] == 1)
  		qos_info = elems->wmm_info[6];
  	else if (elems->wmm_param && elems->wmm_param_len == 24
  		 && elems->wmm_param[5] == 1)
  		qos_info = elems->wmm_param[6];
  	else
  		/* no valid wmm information or parameter element found */
  		return false;
  
  	return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
  }
c2b13452b   Johannes Berg   mac80211: clean u...
55
  struct ieee80211_bss *
5484e2374   Johannes Berg   mac80211: move BS...
56
57
  ieee80211_bss_info_update(struct ieee80211_local *local,
  			  struct ieee80211_rx_status *rx_status,
d45c41722   Emmanuel Grumbach   mac82011: use fra...
58
  			  struct ieee80211_mgmt *mgmt, size_t len,
5484e2374   Johannes Berg   mac80211: move BS...
59
  			  struct ieee802_11_elems *elems,
d45c41722   Emmanuel Grumbach   mac82011: use fra...
60
  			  struct ieee80211_channel *channel)
5484e2374   Johannes Berg   mac80211: move BS...
61
  {
d45c41722   Emmanuel Grumbach   mac82011: use fra...
62
  	bool beacon = ieee80211_is_beacon(mgmt->frame_control);
0c1ad2cac   Johannes Berg   mac80211: proper ...
63
  	struct cfg80211_bss *cbss;
c2b13452b   Johannes Berg   mac80211: clean u...
64
  	struct ieee80211_bss *bss;
f0b058b61   Stanislaw Gruszka   mac80211: do not ...
65
  	int clen, srlen;
2a5193119   Johannes Berg   cfg80211/nl80211:...
66
  	s32 signal = 0;
77965c970   Johannes Berg   cfg80211: clean u...
67
  	if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
2a5193119   Johannes Berg   cfg80211/nl80211:...
68
  		signal = rx_status->signal * 100;
77965c970   Johannes Berg   cfg80211: clean u...
69
  	else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
2a5193119   Johannes Berg   cfg80211/nl80211:...
70
  		signal = (rx_status->signal * 100) / local->hw.max_signal;
2a5193119   Johannes Berg   cfg80211/nl80211:...
71

0c1ad2cac   Johannes Berg   mac80211: proper ...
72
73
  	cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
  					 mgmt, len, signal, GFP_ATOMIC);
0c1ad2cac   Johannes Berg   mac80211: proper ...
74
  	if (!cbss)
00d3f14cf   Johannes Berg   mac80211: use cfg...
75
  		return NULL;
0c1ad2cac   Johannes Berg   mac80211: proper ...
76
  	bss = (void *)cbss->priv;
5484e2374   Johannes Berg   mac80211: move BS...
77

ef429dadf   Johannes Berg   mac80211: introdu...
78
79
80
81
  	if (beacon)
  		bss->device_ts_beacon = rx_status->device_timestamp;
  	else
  		bss->device_ts_presp = rx_status->device_timestamp;
8c358bcd0   Johannes Berg   mac80211: add tim...
82

fcff4f108   Paul Stewart   mac80211: Filter ...
83
84
85
86
87
88
89
90
91
92
93
  	if (elems->parse_error) {
  		if (beacon)
  			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
  		else
  			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP;
  	} else {
  		if (beacon)
  			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON;
  		else
  			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP;
  	}
5484e2374   Johannes Berg   mac80211: move BS...
94
  	/* save the ERP value so that it is available at association time */
1946bed95   Johannes Berg   mac80211: check E...
95
96
  	if (elems->erp_info && (!elems->parse_error ||
  				!(bss->valid_data & IEEE80211_BSS_VALID_ERP))) {
5484e2374   Johannes Berg   mac80211: move BS...
97
  		bss->erp_value = elems->erp_info[0];
3db1cd5c0   Rusty Russell   net: fix assignme...
98
  		bss->has_erp_value = true;
fcff4f108   Paul Stewart   mac80211: Filter ...
99
100
  		if (!elems->parse_error)
  			bss->valid_data |= IEEE80211_BSS_VALID_ERP;
5484e2374   Johannes Berg   mac80211: move BS...
101
  	}
f0b058b61   Stanislaw Gruszka   mac80211: do not ...
102
  	/* replace old supported rates if we get new values */
fcff4f108   Paul Stewart   mac80211: Filter ...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  	if (!elems->parse_error ||
  	    !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
  		srlen = 0;
  		if (elems->supp_rates) {
  			clen = IEEE80211_MAX_SUPP_RATES;
  			if (clen > elems->supp_rates_len)
  				clen = elems->supp_rates_len;
  			memcpy(bss->supp_rates, elems->supp_rates, clen);
  			srlen += clen;
  		}
  		if (elems->ext_supp_rates) {
  			clen = IEEE80211_MAX_SUPP_RATES - srlen;
  			if (clen > elems->ext_supp_rates_len)
  				clen = elems->ext_supp_rates_len;
  			memcpy(bss->supp_rates + srlen, elems->ext_supp_rates,
  			       clen);
  			srlen += clen;
  		}
  		if (srlen) {
  			bss->supp_rates_len = srlen;
  			if (!elems->parse_error)
  				bss->valid_data |= IEEE80211_BSS_VALID_RATES;
  		}
5484e2374   Johannes Berg   mac80211: move BS...
126
  	}
5484e2374   Johannes Berg   mac80211: move BS...
127

fcff4f108   Paul Stewart   mac80211: Filter ...
128
129
130
131
132
133
134
  	if (!elems->parse_error ||
  	    !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) {
  		bss->wmm_used = elems->wmm_param || elems->wmm_info;
  		bss->uapsd_supported = is_uapsd_supported(elems);
  		if (!elems->parse_error)
  			bss->valid_data |= IEEE80211_BSS_VALID_WMM;
  	}
5484e2374   Johannes Berg   mac80211: move BS...
135

5484e2374   Johannes Berg   mac80211: move BS...
136
137
  	return bss;
  }
0a51b27e9   Johannes Berg   mac80211: start m...
138

d48b29685   Johannes Berg   mac80211: redesig...
139
  void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
98c8fccfa   Johannes Berg   mac80211: refacto...
140
  {
f1d58c252   Johannes Berg   mac80211: push rx...
141
  	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
d48b29685   Johannes Berg   mac80211: redesig...
142
143
  	struct ieee80211_sub_if_data *sdata1, *sdata2;
  	struct ieee80211_mgmt *mgmt = (void *)skb->data;
c2b13452b   Johannes Berg   mac80211: clean u...
144
  	struct ieee80211_bss *bss;
98c8fccfa   Johannes Berg   mac80211: refacto...
145
146
147
  	u8 *elements;
  	struct ieee80211_channel *channel;
  	size_t baselen;
98c8fccfa   Johannes Berg   mac80211: refacto...
148
  	struct ieee802_11_elems elems;
d48b29685   Johannes Berg   mac80211: redesig...
149
150
151
152
  	if (skb->len < 24 ||
  	    (!ieee80211_is_probe_resp(mgmt->frame_control) &&
  	     !ieee80211_is_beacon(mgmt->frame_control)))
  		return;
98c8fccfa   Johannes Berg   mac80211: refacto...
153

d48b29685   Johannes Berg   mac80211: redesig...
154
155
  	sdata1 = rcu_dereference(local->scan_sdata);
  	sdata2 = rcu_dereference(local->sched_scan_sdata);
98c8fccfa   Johannes Berg   mac80211: refacto...
156

d48b29685   Johannes Berg   mac80211: redesig...
157
158
  	if (likely(!sdata1 && !sdata2))
  		return;
98c8fccfa   Johannes Berg   mac80211: refacto...
159

d48b29685   Johannes Berg   mac80211: redesig...
160
  	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
98c8fccfa   Johannes Berg   mac80211: refacto...
161
  		/* ignore ProbeResp to foreign address */
d48b29685   Johannes Berg   mac80211: redesig...
162
163
164
  		if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
  		    (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
  			return;
98c8fccfa   Johannes Berg   mac80211: refacto...
165

98c8fccfa   Johannes Berg   mac80211: refacto...
166
167
168
  		elements = mgmt->u.probe_resp.variable;
  		baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
  	} else {
98c8fccfa   Johannes Berg   mac80211: refacto...
169
170
171
  		baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
  		elements = mgmt->u.beacon.variable;
  	}
98c8fccfa   Johannes Berg   mac80211: refacto...
172
  	if (baselen > skb->len)
d48b29685   Johannes Berg   mac80211: redesig...
173
  		return;
98c8fccfa   Johannes Berg   mac80211: refacto...
174

b2e506bfc   Johannes Berg   mac80211: parse V...
175
  	ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems);
98c8fccfa   Johannes Berg   mac80211: refacto...
176

0172bb750   Johannes Berg   cfg80211: use DS ...
177
  	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
98c8fccfa   Johannes Berg   mac80211: refacto...
178
179
  
  	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
d48b29685   Johannes Berg   mac80211: redesig...
180
  		return;
98c8fccfa   Johannes Berg   mac80211: refacto...
181

d48b29685   Johannes Berg   mac80211: redesig...
182
  	bss = ieee80211_bss_info_update(local, rx_status,
98c8fccfa   Johannes Berg   mac80211: refacto...
183
  					mgmt, skb->len, &elems,
d45c41722   Emmanuel Grumbach   mac82011: use fra...
184
  					channel);
d048e503a   Jouni Malinen   mac80211: Fix sca...
185
  	if (bss)
d48b29685   Johannes Berg   mac80211: redesig...
186
  		ieee80211_rx_bss_put(local, bss);
98c8fccfa   Johannes Berg   mac80211: refacto...
187
  }
4d36ec582   Johannes Berg   mac80211: split h...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  /* return false if no more work */
  static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
  {
  	struct cfg80211_scan_request *req = local->scan_req;
  	enum ieee80211_band band;
  	int i, ielen, n_chans;
  
  	do {
  		if (local->hw_scan_band == IEEE80211_NUM_BANDS)
  			return false;
  
  		band = local->hw_scan_band;
  		n_chans = 0;
  		for (i = 0; i < req->n_channels; i++) {
  			if (req->channels[i]->band == band) {
  				local->hw_scan_req->channels[n_chans] =
  							req->channels[i];
  				n_chans++;
  			}
  		}
  
  		local->hw_scan_band++;
  	} while (!n_chans);
  
  	local->hw_scan_req->n_channels = n_chans;
  
  	ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
c604b9f21   Johannes Berg   mac80211: make ie...
215
  					 local->hw_scan_ies_bufsize,
8ee310807   Johannes Berg   mac80211: restric...
216
  					 req->ie, req->ie_len, band,
85a237fe3   Johannes Berg   mac80211: impleme...
217
  					 req->rates[band], 0);
4d36ec582   Johannes Berg   mac80211: split h...
218
  	local->hw_scan_req->ie_len = ielen;
dcd83976b   Johannes Berg   mac80211: pass no...
219
  	local->hw_scan_req->no_cck = req->no_cck;
4d36ec582   Johannes Berg   mac80211: split h...
220
221
222
  
  	return true;
  }
d07bfd8b6   Johannes Berg   mac80211: fix sca...
223
  static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
224
  				       bool was_hw_scan)
0a51b27e9   Johannes Berg   mac80211: start m...
225
226
  {
  	struct ieee80211_local *local = hw_to_local(hw);
0a51b27e9   Johannes Berg   mac80211: start m...
227

e229f844d   Stanislaw Gruszka   mac80211: keep lo...
228
  	lockdep_assert_held(&local->mtx);
5bc75728f   Johannes Berg   mac80211: fix sca...
229

6d3560d4f   Johannes Berg   mac80211: fix sca...
230
231
232
233
234
235
236
237
  	/*
  	 * It's ok to abort a not-yet-running scan (that
  	 * we have one at all will be verified by checking
  	 * local->scan_req next), but not to complete it
  	 * successfully.
  	 */
  	if (WARN_ON(!local->scanning && !aborted))
  		aborted = true;
5bc75728f   Johannes Berg   mac80211: fix sca...
238

e229f844d   Stanislaw Gruszka   mac80211: keep lo...
239
  	if (WARN_ON(!local->scan_req))
d07bfd8b6   Johannes Berg   mac80211: fix sca...
240
  		return;
de95a54b1   Johannes Berg   mac80211: pass al...
241

4d36ec582   Johannes Berg   mac80211: split h...
242
  	if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
e2fd5dbc1   Johannes Berg   mac80211: make sc...
243
244
245
246
247
248
  		int rc;
  
  		rc = drv_hw_scan(local,
  			rcu_dereference_protected(local->scan_sdata,
  						  lockdep_is_held(&local->mtx)),
  			local->hw_scan_req);
6eb11a9a3   Stanislaw Gruszka   mac80211: do not ...
249
  		if (rc == 0)
d07bfd8b6   Johannes Berg   mac80211: fix sca...
250
  			return;
4d36ec582   Johannes Berg   mac80211: split h...
251
252
253
254
  	}
  
  	kfree(local->hw_scan_req);
  	local->hw_scan_req = NULL;
f3b85252f   Johannes Berg   mac80211: fix sca...
255

5ba63533b   Johannes Berg   cfg80211: fix ali...
256
  	if (local->scan_req != local->int_scan_req)
2a5193119   Johannes Berg   cfg80211/nl80211:...
257
258
  		cfg80211_scan_done(local->scan_req, aborted);
  	local->scan_req = NULL;
3aa569c3f   Johannes Berg   mac80211: fix sca...
259
  	rcu_assign_pointer(local->scan_sdata, NULL);
2a5193119   Johannes Berg   cfg80211/nl80211:...
260

fbe9c429f   Helmut Schaa   mac80211: Replace...
261
  	local->scanning = 0;
58905ca5b   Johannes Berg   mac80211: fix sca...
262
  	local->scan_channel = NULL;
f3b85252f   Johannes Berg   mac80211: fix sca...
263

07ef03ee8   Johannes Berg   mac80211: simplif...
264
265
  	/* Set power back to normal operating levels. */
  	ieee80211_hw_config(local, 0);
a0daa0e75   Luis R. Rodriguez   Revert "mac80211:...
266

e229f844d   Stanislaw Gruszka   mac80211: keep lo...
267
268
269
  	if (!was_hw_scan) {
  		ieee80211_configure_filter(local);
  		drv_sw_scan_complete(local);
aacde9ee4   Stanislaw Gruszka   mac80211: synchro...
270
  		ieee80211_offchannel_return(local);
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
271
  	}
0a51b27e9   Johannes Berg   mac80211: start m...
272

5cff20e6c   Johannes Berg   mac80211: tell dr...
273
  	ieee80211_recalc_idle(local);
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
274

0a51b27e9   Johannes Berg   mac80211: start m...
275
  	ieee80211_mlme_notify_scan_completed(local);
469002983   Johannes Berg   mac80211: split I...
276
  	ieee80211_ibss_notify_scan_completed(local);
472dbc45d   Johannes Berg   mac80211: split o...
277
  	ieee80211_mesh_notify_scan_completed(local);
2eb278e08   Johannes Berg   mac80211: unify S...
278
  	ieee80211_start_next_roc(local);
0a51b27e9   Johannes Berg   mac80211: start m...
279
  }
8789d459b   Johannes Berg   mac80211: allow s...
280
281
282
283
284
285
286
287
288
289
290
291
  
  void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
  {
  	struct ieee80211_local *local = hw_to_local(hw);
  
  	trace_api_scan_completed(local, aborted);
  
  	set_bit(SCAN_COMPLETED, &local->scanning);
  	if (aborted)
  		set_bit(SCAN_ABORTED, &local->scanning);
  	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
  }
0a51b27e9   Johannes Berg   mac80211: start m...
292
  EXPORT_SYMBOL(ieee80211_scan_completed);
f3b85252f   Johannes Berg   mac80211: fix sca...
293
294
  static int ieee80211_start_sw_scan(struct ieee80211_local *local)
  {
fe57d9f5c   Johannes Berg   mac80211: track w...
295
296
297
  	/* Software scan is not supported in multi-channel cases */
  	if (local->use_chanctx)
  		return -EOPNOTSUPP;
f3b85252f   Johannes Berg   mac80211: fix sca...
298
299
300
301
302
303
304
305
306
307
308
309
310
  	/*
  	 * Hardware/driver doesn't support hw_scan, so use software
  	 * scanning instead. First send a nullfunc frame with power save
  	 * bit on so that AP will buffer the frames for us while we are not
  	 * listening, then send probe requests to each channel and wait for
  	 * the responses. After all channels are scanned, tune back to the
  	 * original channel and send a nullfunc frame with power save bit
  	 * off to trigger the AP to send us all the buffered frames.
  	 *
  	 * Note that while local->sw_scanning is true everything else but
  	 * nullfunc frames and probe requests will be dropped in
  	 * ieee80211_tx_h_check_assoc().
  	 */
244879813   Johannes Berg   mac80211: add dri...
311
  	drv_sw_scan_start(local);
f3b85252f   Johannes Berg   mac80211: fix sca...
312

de312db34   Rajkumar Manoharan   mac80211: fix ope...
313
  	local->leave_oper_channel_time = jiffies;
977923b00   Helmut Schaa   mac80211: rename ...
314
  	local->next_scan_state = SCAN_DECISION;
f3b85252f   Johannes Berg   mac80211: fix sca...
315
  	local->scan_channel_idx = 0;
aacde9ee4   Stanislaw Gruszka   mac80211: synchro...
316
  	ieee80211_offchannel_stop_vifs(local);
a80f7c0b0   Johannes Berg   mac80211: introdu...
317

9c35d7d23   Seth Forshee   mac80211: Add flu...
318
  	/* ensure nullfunc is transmitted before leaving operating channel */
39ecc01d1   Johannes Berg   mac80211: pass qu...
319
  	ieee80211_flush_queues(local, NULL);
9c35d7d23   Seth Forshee   mac80211: Add flu...
320

3ac64beec   Johannes Berg   mac80211: allow c...
321
  	ieee80211_configure_filter(local);
f3b85252f   Johannes Berg   mac80211: fix sca...
322

59bdf3b0f   Ben Greear   mac80211: Ensure ...
323
324
  	/* We need to set power level at maximum rate for scanning. */
  	ieee80211_hw_config(local, 0);
42935ecaf   Luis R. Rodriguez   mac80211: redefin...
325
  	ieee80211_queue_delayed_work(&local->hw,
07ef03ee8   Johannes Berg   mac80211: simplif...
326
  				     &local->scan_work, 0);
f3b85252f   Johannes Berg   mac80211: fix sca...
327
328
329
  
  	return 0;
  }
133d40f9a   Stanislaw Gruszka   mac80211: do not ...
330
331
332
  static bool ieee80211_can_scan(struct ieee80211_local *local,
  			       struct ieee80211_sub_if_data *sdata)
  {
164eb02d0   Simon Wunderlich   mac80211: add rad...
333
334
  	if (local->radar_detect_enabled)
  		return false;
2eb278e08   Johannes Berg   mac80211: unify S...
335
  	if (!list_empty(&local->roc_list))
133d40f9a   Stanislaw Gruszka   mac80211: do not ...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
  		return false;
  
  	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
  	    sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
  				  IEEE80211_STA_CONNECTION_POLL))
  		return false;
  
  	return true;
  }
  
  void ieee80211_run_deferred_scan(struct ieee80211_local *local)
  {
  	lockdep_assert_held(&local->mtx);
  
  	if (!local->scan_req || local->scanning)
  		return;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
352
353
354
355
  	if (!ieee80211_can_scan(local,
  				rcu_dereference_protected(
  					local->scan_sdata,
  					lockdep_is_held(&local->mtx))))
133d40f9a   Stanislaw Gruszka   mac80211: do not ...
356
357
358
359
360
  		return;
  
  	ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
  				     round_jiffies_relative(0));
  }
f3b85252f   Johannes Berg   mac80211: fix sca...
361

8a690674e   Ben Greear   mac80211: Support...
362
363
364
365
  static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
  					    unsigned long *next_delay)
  {
  	int i;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
366
  	struct ieee80211_sub_if_data *sdata;
675a0b049   Karl Beldan   mac80211: Use a c...
367
  	enum ieee80211_band band = local->hw.conf.chandef.chan->band;
6c17b77b6   Seth Forshee   mac80211: Fix tx ...
368
369
370
371
372
  	u32 tx_flags;
  
  	tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
  	if (local->scan_req->no_cck)
  		tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
8a690674e   Ben Greear   mac80211: Support...
373

e2fd5dbc1   Johannes Berg   mac80211: make sc...
374
  	sdata = rcu_dereference_protected(local->scan_sdata,
316b6b5df   Peter Senna Tschudin   net/mac80211/scan...
375
  					  lockdep_is_held(&local->mtx));
e2fd5dbc1   Johannes Berg   mac80211: make sc...
376

8a690674e   Ben Greear   mac80211: Support...
377
378
379
380
381
382
383
  	for (i = 0; i < local->scan_req->n_ssids; i++)
  		ieee80211_send_probe_req(
  			sdata, NULL,
  			local->scan_req->ssids[i].ssid,
  			local->scan_req->ssids[i].ssid_len,
  			local->scan_req->ie, local->scan_req->ie_len,
  			local->scan_req->rates[band], false,
675a0b049   Karl Beldan   mac80211: Use a c...
384
  			tx_flags, local->hw.conf.chandef.chan, true);
8a690674e   Ben Greear   mac80211: Support...
385
386
387
388
389
390
391
392
  
  	/*
  	 * After sending probe requests, wait for probe responses
  	 * on the channel.
  	 */
  	*next_delay = IEEE80211_CHANNEL_TIME;
  	local->next_scan_state = SCAN_DECISION;
  }
f3b85252f   Johannes Berg   mac80211: fix sca...
393
394
395
396
  static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
  				  struct cfg80211_scan_request *req)
  {
  	struct ieee80211_local *local = sdata->local;
f3b85252f   Johannes Berg   mac80211: fix sca...
397
  	int rc;
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
398
  	lockdep_assert_held(&local->mtx);
f3b85252f   Johannes Berg   mac80211: fix sca...
399
400
  	if (local->scan_req)
  		return -EBUSY;
133d40f9a   Stanislaw Gruszka   mac80211: do not ...
401
  	if (!ieee80211_can_scan(local, sdata)) {
6e7e6213e   John W. Linville   Merge branch 'mas...
402
  		/* wait for the work to finish/time out */
c0ce77b83   Johannes Berg   mac80211: fix def...
403
  		local->scan_req = req;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
404
  		rcu_assign_pointer(local->scan_sdata, sdata);
c0ce77b83   Johannes Berg   mac80211: fix def...
405
406
  		return 0;
  	}
f3b85252f   Johannes Berg   mac80211: fix sca...
407
408
  	if (local->ops->hw_scan) {
  		u8 *ies;
f3b85252f   Johannes Berg   mac80211: fix sca...
409

c604b9f21   Johannes Berg   mac80211: make ie...
410
411
412
  		local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
  					     local->scan_ies_len +
  					     req->ie_len;
4d36ec582   Johannes Berg   mac80211: split h...
413
414
415
  		local->hw_scan_req = kmalloc(
  				sizeof(*local->hw_scan_req) +
  				req->n_channels * sizeof(req->channels[0]) +
c604b9f21   Johannes Berg   mac80211: make ie...
416
  				local->hw_scan_ies_bufsize, GFP_KERNEL);
4d36ec582   Johannes Berg   mac80211: split h...
417
  		if (!local->hw_scan_req)
f3b85252f   Johannes Berg   mac80211: fix sca...
418
  			return -ENOMEM;
4d36ec582   Johannes Berg   mac80211: split h...
419
420
421
422
423
424
  		local->hw_scan_req->ssids = req->ssids;
  		local->hw_scan_req->n_ssids = req->n_ssids;
  		ies = (u8 *)local->hw_scan_req +
  			sizeof(*local->hw_scan_req) +
  			req->n_channels * sizeof(req->channels[0]);
  		local->hw_scan_req->ie = ies;
cd2bb512c   Sam Leffler   mac80211: add sup...
425
  		local->hw_scan_req->flags = req->flags;
4d36ec582   Johannes Berg   mac80211: split h...
426
427
  
  		local->hw_scan_band = 0;
6e7e6213e   John W. Linville   Merge branch 'mas...
428
429
430
431
432
433
434
435
  
  		/*
  		 * After allocating local->hw_scan_req, we must
  		 * go through until ieee80211_prep_hw_scan(), so
  		 * anything that might be changed here and leave
  		 * this function early must not go after this
  		 * allocation.
  		 */
f3b85252f   Johannes Berg   mac80211: fix sca...
436
437
438
  	}
  
  	local->scan_req = req;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
439
  	rcu_assign_pointer(local->scan_sdata, sdata);
f3b85252f   Johannes Berg   mac80211: fix sca...
440

8a690674e   Ben Greear   mac80211: Support...
441
  	if (local->ops->hw_scan) {
fbe9c429f   Helmut Schaa   mac80211: Replace...
442
  		__set_bit(SCAN_HW_SCANNING, &local->scanning);
8a690674e   Ben Greear   mac80211: Support...
443
  	} else if ((req->n_channels == 1) &&
675a0b049   Karl Beldan   mac80211: Use a c...
444
  		   (req->channels[0] == local->_oper_chandef.chan)) {
9b8648704   Johannes Berg   mac80211: check o...
445
446
447
  		/*
  		 * If we are scanning only on the operating channel
  		 * then we do not need to stop normal activities
8a690674e   Ben Greear   mac80211: Support...
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  		 */
  		unsigned long next_delay;
  
  		__set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
  
  		ieee80211_recalc_idle(local);
  
  		/* Notify driver scan is starting, keep order of operations
  		 * same as normal software scan, in case that matters. */
  		drv_sw_scan_start(local);
  
  		ieee80211_configure_filter(local); /* accept probe-responses */
  
  		/* We need to ensure power level is at max for scanning. */
  		ieee80211_hw_config(local, 0);
  
  		if ((req->channels[0]->flags &
  		     IEEE80211_CHAN_PASSIVE_SCAN) ||
  		    !local->scan_req->n_ssids) {
  			next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
  		} else {
  			ieee80211_scan_state_send_probe(local, &next_delay);
  			next_delay = IEEE80211_CHANNEL_TIME;
  		}
  
  		/* Now, just wait a bit and we are all done! */
  		ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
  					     next_delay);
  		return 0;
  	} else {
  		/* Do normal software scan */
fbe9c429f   Helmut Schaa   mac80211: Replace...
479
  		__set_bit(SCAN_SW_SCANNING, &local->scanning);
8a690674e   Ben Greear   mac80211: Support...
480
  	}
6e7e6213e   John W. Linville   Merge branch 'mas...
481

5cff20e6c   Johannes Berg   mac80211: tell dr...
482
  	ieee80211_recalc_idle(local);
f3b85252f   Johannes Berg   mac80211: fix sca...
483

4d36ec582   Johannes Berg   mac80211: split h...
484
485
  	if (local->ops->hw_scan) {
  		WARN_ON(!ieee80211_prep_hw_scan(local));
a060bbfe4   Johannes Berg   mac80211: give vi...
486
  		rc = drv_hw_scan(local, sdata, local->hw_scan_req);
4d36ec582   Johannes Berg   mac80211: split h...
487
  	} else
f3b85252f   Johannes Berg   mac80211: fix sca...
488
  		rc = ieee80211_start_sw_scan(local);
f3b85252f   Johannes Berg   mac80211: fix sca...
489
  	if (rc) {
4d36ec582   Johannes Berg   mac80211: split h...
490
491
  		kfree(local->hw_scan_req);
  		local->hw_scan_req = NULL;
fbe9c429f   Helmut Schaa   mac80211: Replace...
492
  		local->scanning = 0;
f3b85252f   Johannes Berg   mac80211: fix sca...
493

5cff20e6c   Johannes Berg   mac80211: tell dr...
494
  		ieee80211_recalc_idle(local);
f3b85252f   Johannes Berg   mac80211: fix sca...
495
  		local->scan_req = NULL;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
496
  		rcu_assign_pointer(local->scan_sdata, NULL);
f3b85252f   Johannes Berg   mac80211: fix sca...
497
498
499
500
  	}
  
  	return rc;
  }
df13cce53   Helmut Schaa   mac80211: Improve...
501
502
503
504
505
506
507
508
509
510
511
  static unsigned long
  ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
  {
  	/*
  	 * TODO: channel switching also consumes quite some time,
  	 * add that delay as well to get a better estimation
  	 */
  	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
  		return IEEE80211_PASSIVE_CHANNEL_TIME;
  	return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
  }
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
512
513
  static void ieee80211_scan_state_decision(struct ieee80211_local *local,
  					  unsigned long *next_delay)
7d3be3cc4   Helmut Schaa   mac80211: refacto...
514
  {
142b9f507   Helmut Schaa   mac80211: impleme...
515
  	bool associated = false;
df13cce53   Helmut Schaa   mac80211: Improve...
516
517
  	bool tx_empty = true;
  	bool bad_latency;
142b9f507   Helmut Schaa   mac80211: impleme...
518
  	struct ieee80211_sub_if_data *sdata;
df13cce53   Helmut Schaa   mac80211: Improve...
519
  	struct ieee80211_channel *next_chan;
cd2bb512c   Sam Leffler   mac80211: add sup...
520
  	enum mac80211_scan_state next_scan_state;
142b9f507   Helmut Schaa   mac80211: impleme...
521

df13cce53   Helmut Schaa   mac80211: Improve...
522
523
524
525
526
  	/*
  	 * check if at least one STA interface is associated,
  	 * check if at least one STA interface has pending tx frames
  	 * and grab the lowest used beacon interval
  	 */
142b9f507   Helmut Schaa   mac80211: impleme...
527
528
  	mutex_lock(&local->iflist_mtx);
  	list_for_each_entry(sdata, &local->interfaces, list) {
9607e6b66   Johannes Berg   mac80211: add iee...
529
  		if (!ieee80211_sdata_running(sdata))
142b9f507   Helmut Schaa   mac80211: impleme...
530
531
532
533
534
  			continue;
  
  		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
  			if (sdata->u.mgd.associated) {
  				associated = true;
df13cce53   Helmut Schaa   mac80211: Improve...
535

df13cce53   Helmut Schaa   mac80211: Improve...
536
537
538
539
  				if (!qdisc_all_tx_empty(sdata->dev)) {
  					tx_empty = false;
  					break;
  				}
142b9f507   Helmut Schaa   mac80211: impleme...
540
541
542
543
  			}
  		}
  	}
  	mutex_unlock(&local->iflist_mtx);
b23b025fe   Ben Greear   mac80211: Optimiz...
544
  	next_chan = local->scan_req->channels[local->scan_channel_idx];
a80f7c0b0   Johannes Berg   mac80211: introdu...
545
  	/*
07ef03ee8   Johannes Berg   mac80211: simplif...
546
547
548
549
  	 * we're currently scanning a different channel, let's
  	 * see if we can scan another channel without interfering
  	 * with the current traffic situation.
  	 *
3f892b61a   Stanislaw Gruszka   mac80211: improve...
550
  	 * Keep good latency, do not stay off-channel more than 125 ms.
a80f7c0b0   Johannes Berg   mac80211: introdu...
551
  	 */
a80f7c0b0   Johannes Berg   mac80211: introdu...
552

07ef03ee8   Johannes Berg   mac80211: simplif...
553
  	bad_latency = time_after(jiffies +
3f892b61a   Stanislaw Gruszka   mac80211: improve...
554
555
  				 ieee80211_scan_get_channel_time(next_chan),
  				 local->leave_oper_channel_time + HZ / 8);
142b9f507   Helmut Schaa   mac80211: impleme...
556

cd2bb512c   Sam Leffler   mac80211: add sup...
557
558
559
560
561
  	if (associated && !tx_empty) {
  		if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
  			next_scan_state = SCAN_ABORT;
  		else
  			next_scan_state = SCAN_SUSPEND;
3f892b61a   Stanislaw Gruszka   mac80211: improve...
562
  	} else if (associated && bad_latency) {
cd2bb512c   Sam Leffler   mac80211: add sup...
563
564
565
566
567
568
  		next_scan_state = SCAN_SUSPEND;
  	} else {
  		next_scan_state = SCAN_SET_CHANNEL;
  	}
  
  	local->next_scan_state = next_scan_state;
142b9f507   Helmut Schaa   mac80211: impleme...
569

07ef03ee8   Johannes Berg   mac80211: simplif...
570
  	*next_delay = 0;
142b9f507   Helmut Schaa   mac80211: impleme...
571
  }
2fb3f028a   Helmut Schaa   mac80211: introdu...
572
573
574
575
576
  static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
  					     unsigned long *next_delay)
  {
  	int skip;
  	struct ieee80211_channel *chan;
2fb3f028a   Helmut Schaa   mac80211: introdu...
577

7d3be3cc4   Helmut Schaa   mac80211: refacto...
578
579
  	skip = 0;
  	chan = local->scan_req->channels[local->scan_channel_idx];
584991dcc   Johannes Berg   cfg80211: validat...
580
  	local->scan_channel = chan;
b23b025fe   Ben Greear   mac80211: Optimiz...
581

07ef03ee8   Johannes Berg   mac80211: simplif...
582
583
  	if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
  		skip = 1;
7d3be3cc4   Helmut Schaa   mac80211: refacto...
584

7d3be3cc4   Helmut Schaa   mac80211: refacto...
585
586
  	/* advance state machine to next channel/band */
  	local->scan_channel_idx++;
0ee9c13c7   Helmut Schaa   mac80211: fix an ...
587
588
589
  	if (skip) {
  		/* if we skip this channel return to the decision state */
  		local->next_scan_state = SCAN_DECISION;
2fb3f028a   Helmut Schaa   mac80211: introdu...
590
  		return;
0ee9c13c7   Helmut Schaa   mac80211: fix an ...
591
  	}
7d3be3cc4   Helmut Schaa   mac80211: refacto...
592
593
594
595
596
597
598
599
600
601
602
603
604
605
  
  	/*
  	 * Probe delay is used to update the NAV, cf. 11.1.3.2.2
  	 * (which unfortunately doesn't say _why_ step a) is done,
  	 * but it waits for the probe delay or until a frame is
  	 * received - and the received frame would update the NAV).
  	 * For now, we do not support waiting until a frame is
  	 * received.
  	 *
  	 * In any case, it is not necessary for a passive scan.
  	 */
  	if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
  	    !local->scan_req->n_ssids) {
  		*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
977923b00   Helmut Schaa   mac80211: rename ...
606
  		local->next_scan_state = SCAN_DECISION;
2fb3f028a   Helmut Schaa   mac80211: introdu...
607
  		return;
7d3be3cc4   Helmut Schaa   mac80211: refacto...
608
  	}
2fb3f028a   Helmut Schaa   mac80211: introdu...
609
  	/* active scan, send probes */
7d3be3cc4   Helmut Schaa   mac80211: refacto...
610
  	*next_delay = IEEE80211_PROBE_DELAY;
977923b00   Helmut Schaa   mac80211: rename ...
611
  	local->next_scan_state = SCAN_SEND_PROBE;
7d3be3cc4   Helmut Schaa   mac80211: refacto...
612
  }
07ef03ee8   Johannes Berg   mac80211: simplif...
613
614
615
616
617
618
  static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
  					 unsigned long *next_delay)
  {
  	/* switch back to the operating channel */
  	local->scan_channel = NULL;
  	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
aacde9ee4   Stanislaw Gruszka   mac80211: synchro...
619
620
  	/* disable PS */
  	ieee80211_offchannel_return(local);
07ef03ee8   Johannes Berg   mac80211: simplif...
621
622
623
624
625
626
627
628
629
  
  	*next_delay = HZ / 5;
  	/* afterwards, resume scan & go to next channel */
  	local->next_scan_state = SCAN_RESUME;
  }
  
  static void ieee80211_scan_state_resume(struct ieee80211_local *local,
  					unsigned long *next_delay)
  {
aacde9ee4   Stanislaw Gruszka   mac80211: synchro...
630
  	ieee80211_offchannel_stop_vifs(local);
07ef03ee8   Johannes Berg   mac80211: simplif...
631
632
  
  	if (local->ops->flush) {
39ecc01d1   Johannes Berg   mac80211: pass qu...
633
  		ieee80211_flush_queues(local, NULL);
07ef03ee8   Johannes Berg   mac80211: simplif...
634
635
636
637
638
639
640
641
  		*next_delay = 0;
  	} else
  		*next_delay = HZ / 10;
  
  	/* remember when we left the operating channel */
  	local->leave_oper_channel_time = jiffies;
  
  	/* advance to the next channel to be scanned */
de2ee84db   Mohammed Shafi Shajakhan   mac80211: fix sca...
642
  	local->next_scan_state = SCAN_SET_CHANNEL;
07ef03ee8   Johannes Berg   mac80211: simplif...
643
  }
c2b13452b   Johannes Berg   mac80211: clean u...
644
  void ieee80211_scan_work(struct work_struct *work)
0a51b27e9   Johannes Berg   mac80211: start m...
645
646
647
  {
  	struct ieee80211_local *local =
  		container_of(work, struct ieee80211_local, scan_work.work);
d07bfd8b6   Johannes Berg   mac80211: fix sca...
648
  	struct ieee80211_sub_if_data *sdata;
0a51b27e9   Johannes Berg   mac80211: start m...
649
  	unsigned long next_delay = 0;
d07bfd8b6   Johannes Berg   mac80211: fix sca...
650
  	bool aborted, hw_scan;
0a51b27e9   Johannes Berg   mac80211: start m...
651

259b62e35   Stanislaw Gruszka   mac80211: reduce ...
652
  	mutex_lock(&local->mtx);
8789d459b   Johannes Berg   mac80211: allow s...
653

e2fd5dbc1   Johannes Berg   mac80211: make sc...
654
655
  	sdata = rcu_dereference_protected(local->scan_sdata,
  					  lockdep_is_held(&local->mtx));
d07bfd8b6   Johannes Berg   mac80211: fix sca...
656

8a690674e   Ben Greear   mac80211: Support...
657
658
659
660
661
  	/* When scanning on-channel, the first-callback means completed. */
  	if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
  		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
  		goto out_complete;
  	}
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
662
  	if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
8789d459b   Johannes Berg   mac80211: allow s...
663
  		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
664
  		goto out_complete;
8789d459b   Johannes Berg   mac80211: allow s...
665
  	}
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
666
667
  	if (!sdata || !local->scan_req)
  		goto out;
f3b85252f   Johannes Berg   mac80211: fix sca...
668

fbe9c429f   Helmut Schaa   mac80211: Replace...
669
  	if (local->scan_req && !local->scanning) {
f3b85252f   Johannes Berg   mac80211: fix sca...
670
671
672
673
  		struct cfg80211_scan_request *req = local->scan_req;
  		int rc;
  
  		local->scan_req = NULL;
e2fd5dbc1   Johannes Berg   mac80211: make sc...
674
  		rcu_assign_pointer(local->scan_sdata, NULL);
f3b85252f   Johannes Berg   mac80211: fix sca...
675
676
  
  		rc = __ieee80211_start_scan(sdata, req);
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
677
  		if (rc) {
3aed49ef1   Stanislaw Gruszka   mac80211: compete...
678
679
  			/* need to complete scan in cfg80211 */
  			local->scan_req = req;
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
680
681
682
683
  			aborted = true;
  			goto out_complete;
  		} else
  			goto out;
f3b85252f   Johannes Berg   mac80211: fix sca...
684
  	}
5bc75728f   Johannes Berg   mac80211: fix sca...
685
686
687
  	/*
  	 * Avoid re-scheduling when the sdata is going away.
  	 */
9607e6b66   Johannes Berg   mac80211: add iee...
688
  	if (!ieee80211_sdata_running(sdata)) {
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
689
690
  		aborted = true;
  		goto out_complete;
f3b85252f   Johannes Berg   mac80211: fix sca...
691
  	}
0a51b27e9   Johannes Berg   mac80211: start m...
692

f502d09b7   Helmut Schaa   mac80211: advance...
693
694
695
696
697
  	/*
  	 * as long as no delay is required advance immediately
  	 * without scheduling a new work
  	 */
  	do {
c29acf201   Rajkumar Manoharan   mac80211: abort s...
698
699
700
701
  		if (!ieee80211_sdata_running(sdata)) {
  			aborted = true;
  			goto out_complete;
  		}
977923b00   Helmut Schaa   mac80211: rename ...
702
  		switch (local->next_scan_state) {
2fb3f028a   Helmut Schaa   mac80211: introdu...
703
  		case SCAN_DECISION:
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
704
705
706
707
708
709
  			/* if no more bands/channels left, complete scan */
  			if (local->scan_channel_idx >= local->scan_req->n_channels) {
  				aborted = false;
  				goto out_complete;
  			}
  			ieee80211_scan_state_decision(local, &next_delay);
f502d09b7   Helmut Schaa   mac80211: advance...
710
  			break;
2fb3f028a   Helmut Schaa   mac80211: introdu...
711
712
713
  		case SCAN_SET_CHANNEL:
  			ieee80211_scan_state_set_channel(local, &next_delay);
  			break;
f502d09b7   Helmut Schaa   mac80211: advance...
714
715
716
  		case SCAN_SEND_PROBE:
  			ieee80211_scan_state_send_probe(local, &next_delay);
  			break;
07ef03ee8   Johannes Berg   mac80211: simplif...
717
718
  		case SCAN_SUSPEND:
  			ieee80211_scan_state_suspend(local, &next_delay);
142b9f507   Helmut Schaa   mac80211: impleme...
719
  			break;
07ef03ee8   Johannes Berg   mac80211: simplif...
720
721
  		case SCAN_RESUME:
  			ieee80211_scan_state_resume(local, &next_delay);
142b9f507   Helmut Schaa   mac80211: impleme...
722
  			break;
cd2bb512c   Sam Leffler   mac80211: add sup...
723
724
725
  		case SCAN_ABORT:
  			aborted = true;
  			goto out_complete;
f502d09b7   Helmut Schaa   mac80211: advance...
726
727
  		}
  	} while (next_delay == 0);
0a51b27e9   Johannes Berg   mac80211: start m...
728

42935ecaf   Luis R. Rodriguez   mac80211: redefin...
729
  	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
d07bfd8b6   Johannes Berg   mac80211: fix sca...
730
  	goto out;
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
731
732
  
  out_complete:
e229f844d   Stanislaw Gruszka   mac80211: keep lo...
733
  	hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
d07bfd8b6   Johannes Berg   mac80211: fix sca...
734
  	__ieee80211_scan_completed(&local->hw, aborted, hw_scan);
259b62e35   Stanislaw Gruszka   mac80211: reduce ...
735
736
  out:
  	mutex_unlock(&local->mtx);
0a51b27e9   Johannes Berg   mac80211: start m...
737
  }
f3b85252f   Johannes Berg   mac80211: fix sca...
738
739
  int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
  			   struct cfg80211_scan_request *req)
0a51b27e9   Johannes Berg   mac80211: start m...
740
  {
f3b85252f   Johannes Berg   mac80211: fix sca...
741
  	int res;
de95a54b1   Johannes Berg   mac80211: pass al...
742

a1699b75a   Johannes Berg   mac80211: unify s...
743
  	mutex_lock(&sdata->local->mtx);
f3b85252f   Johannes Berg   mac80211: fix sca...
744
  	res = __ieee80211_start_scan(sdata, req);
a1699b75a   Johannes Berg   mac80211: unify s...
745
  	mutex_unlock(&sdata->local->mtx);
0a51b27e9   Johannes Berg   mac80211: start m...
746

f3b85252f   Johannes Berg   mac80211: fix sca...
747
  	return res;
0a51b27e9   Johannes Berg   mac80211: start m...
748
  }
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
749
750
751
  int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
  				const u8 *ssid, u8 ssid_len,
  				struct ieee80211_channel *chan)
0a51b27e9   Johannes Berg   mac80211: start m...
752
  {
0a51b27e9   Johannes Berg   mac80211: start m...
753
  	struct ieee80211_local *local = sdata->local;
f3b85252f   Johannes Berg   mac80211: fix sca...
754
  	int ret = -EBUSY;
fb63bc417   Gertjan van Wingerde   mac80211: Fix com...
755
  	enum ieee80211_band band;
2a5193119   Johannes Berg   cfg80211/nl80211:...
756

a1699b75a   Johannes Berg   mac80211: unify s...
757
  	mutex_lock(&local->mtx);
0a51b27e9   Johannes Berg   mac80211: start m...
758

f3b85252f   Johannes Berg   mac80211: fix sca...
759
760
761
  	/* busy scanning */
  	if (local->scan_req)
  		goto unlock;
9116dd011   Johannes Berg   mac80211: clarify...
762

be4a4b6a5   Johannes Berg   mac80211: improve...
763
764
  	/* fill internal scan request */
  	if (!chan) {
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
765
766
  		int i, max_n;
  		int n_ch = 0;
be4a4b6a5   Johannes Berg   mac80211: improve...
767
768
769
770
  
  		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
  			if (!local->hw.wiphy->bands[band])
  				continue;
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
771
772
773
774
  
  			max_n = local->hw.wiphy->bands[band]->n_channels;
  			for (i = 0; i < max_n; i++) {
  				struct ieee80211_channel *tmp_ch =
be4a4b6a5   Johannes Berg   mac80211: improve...
775
  				    &local->hw.wiphy->bands[band]->channels[i];
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
776
777
778
779
780
781
782
  
  				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
  						     IEEE80211_CHAN_DISABLED))
  					continue;
  
  				local->int_scan_req->channels[n_ch] = tmp_ch;
  				n_ch++;
be4a4b6a5   Johannes Berg   mac80211: improve...
783
784
  			}
  		}
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
785
786
787
788
  		if (WARN_ON_ONCE(n_ch == 0))
  			goto unlock;
  
  		local->int_scan_req->n_channels = n_ch;
be4a4b6a5   Johannes Berg   mac80211: improve...
789
  	} else {
34bcf7150   Stanislaw Gruszka   mac80211: fix ibs...
790
791
792
  		if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
  						IEEE80211_CHAN_DISABLED)))
  			goto unlock;
be4a4b6a5   Johannes Berg   mac80211: improve...
793
794
795
796
797
798
  		local->int_scan_req->channels[0] = chan;
  		local->int_scan_req->n_channels = 1;
  	}
  
  	local->int_scan_req->ssids = &local->scan_ssid;
  	local->int_scan_req->n_ssids = 1;
5ba63533b   Johannes Berg   cfg80211: fix ali...
799
800
  	memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
  	local->int_scan_req->ssids[0].ssid_len = ssid_len;
9116dd011   Johannes Berg   mac80211: clarify...
801

5ba63533b   Johannes Berg   cfg80211: fix ali...
802
  	ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
f3b85252f   Johannes Berg   mac80211: fix sca...
803
   unlock:
a1699b75a   Johannes Berg   mac80211: unify s...
804
  	mutex_unlock(&local->mtx);
f3b85252f   Johannes Berg   mac80211: fix sca...
805
  	return ret;
0a51b27e9   Johannes Berg   mac80211: start m...
806
  }
5bb644a0f   Johannes Berg   mac80211: cancel/...
807

4136c4224   Stanislaw Gruszka   mac80211: assure ...
808
809
810
  /*
   * Only call this function when a scan can't be queued -- under RTNL.
   */
5bb644a0f   Johannes Berg   mac80211: cancel/...
811
812
  void ieee80211_scan_cancel(struct ieee80211_local *local)
  {
5bb644a0f   Johannes Berg   mac80211: cancel/...
813
  	/*
b856439b1   Eliad Peller   mac80211: add can...
814
  	 * We are canceling software scan, or deferred scan that was not
4136c4224   Stanislaw Gruszka   mac80211: assure ...
815
816
817
818
819
820
821
822
823
824
825
  	 * yet really started (see __ieee80211_start_scan ).
  	 *
  	 * Regarding hardware scan:
  	 * - we can not call  __ieee80211_scan_completed() as when
  	 *   SCAN_HW_SCANNING bit is set this function change
  	 *   local->hw_scan_req to operate on 5G band, what race with
  	 *   driver which can use local->hw_scan_req
  	 *
  	 * - we can not cancel scan_work since driver can schedule it
  	 *   by ieee80211_scan_completed(..., true) to finish scan
  	 *
b856439b1   Eliad Peller   mac80211: add can...
826
827
828
  	 * Hence we only call the cancel_hw_scan() callback, but the low-level
  	 * driver is still responsible for calling ieee80211_scan_completed()
  	 * after the scan was completed/aborted.
5bb644a0f   Johannes Berg   mac80211: cancel/...
829
  	 */
4136c4224   Stanislaw Gruszka   mac80211: assure ...
830

a1699b75a   Johannes Berg   mac80211: unify s...
831
  	mutex_lock(&local->mtx);
b856439b1   Eliad Peller   mac80211: add can...
832
833
834
835
836
  	if (!local->scan_req)
  		goto out;
  
  	if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
  		if (local->ops->cancel_hw_scan)
e2fd5dbc1   Johannes Berg   mac80211: make sc...
837
838
839
  			drv_cancel_hw_scan(local,
  				rcu_dereference_protected(local->scan_sdata,
  						lockdep_is_held(&local->mtx)));
b856439b1   Eliad Peller   mac80211: add can...
840
  		goto out;
4136c4224   Stanislaw Gruszka   mac80211: assure ...
841
  	}
b856439b1   Eliad Peller   mac80211: add can...
842
843
844
845
846
847
848
849
850
851
  
  	/*
  	 * If the work is currently running, it must be blocked on
  	 * the mutex, but we'll set scan_sdata = NULL and it'll
  	 * simply exit once it acquires the mutex.
  	 */
  	cancel_delayed_work(&local->scan_work);
  	/* and clean up */
  	__ieee80211_scan_completed(&local->hw, true, false);
  out:
d07bfd8b6   Johannes Berg   mac80211: fix sca...
852
  	mutex_unlock(&local->mtx);
5bb644a0f   Johannes Berg   mac80211: cancel/...
853
  }
79f460ca4   Luciano Coelho   mac80211: add sup...
854
855
856
857
858
  
  int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
  				       struct cfg80211_sched_scan_request *req)
  {
  	struct ieee80211_local *local = sdata->local;
bca1e29fb   David Spinadel   mac80211: init sc...
859
  	struct ieee80211_sched_scan_ies sched_scan_ies = {};
c604b9f21   Johannes Berg   mac80211: make ie...
860
861
862
863
  	int ret, i, iebufsz;
  
  	iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
  		  local->scan_ies_len + req->ie_len;
79f460ca4   Luciano Coelho   mac80211: add sup...
864

5260a5b2c   Johannes Berg   mac80211: track s...
865
  	mutex_lock(&local->mtx);
79f460ca4   Luciano Coelho   mac80211: add sup...
866

5260a5b2c   Johannes Berg   mac80211: track s...
867
  	if (rcu_access_pointer(local->sched_scan_sdata)) {
79f460ca4   Luciano Coelho   mac80211: add sup...
868
869
870
871
872
873
874
875
876
877
  		ret = -EBUSY;
  		goto out;
  	}
  
  	if (!local->ops->sched_scan_start) {
  		ret = -ENOTSUPP;
  		goto out;
  	}
  
  	for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
d811b3d55   Arik Nemtsov   mac80211: fix inv...
878
879
  		if (!local->hw.wiphy->bands[i])
  			continue;
c604b9f21   Johannes Berg   mac80211: make ie...
880
  		sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
30dd3edf9   Johannes Berg   mac80211: don't h...
881
  		if (!sched_scan_ies.ie[i]) {
79f460ca4   Luciano Coelho   mac80211: add sup...
882
883
884
  			ret = -ENOMEM;
  			goto out_free;
  		}
30dd3edf9   Johannes Berg   mac80211: don't h...
885
886
  		sched_scan_ies.len[i] =
  			ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
c604b9f21   Johannes Berg   mac80211: make ie...
887
888
  						 iebufsz, req->ie, req->ie_len,
  						 i, (u32) -1, 0);
79f460ca4   Luciano Coelho   mac80211: add sup...
889
  	}
30dd3edf9   Johannes Berg   mac80211: don't h...
890
891
  	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
  	if (ret == 0)
5260a5b2c   Johannes Berg   mac80211: track s...
892
  		rcu_assign_pointer(local->sched_scan_sdata, sdata);
79f460ca4   Luciano Coelho   mac80211: add sup...
893
894
895
  
  out_free:
  	while (i > 0)
30dd3edf9   Johannes Berg   mac80211: don't h...
896
  		kfree(sched_scan_ies.ie[--i]);
79f460ca4   Luciano Coelho   mac80211: add sup...
897
  out:
5260a5b2c   Johannes Berg   mac80211: track s...
898
  	mutex_unlock(&local->mtx);
79f460ca4   Luciano Coelho   mac80211: add sup...
899
900
  	return ret;
  }
85a9994a0   Luciano Coelho   cfg80211/mac80211...
901
  int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
79f460ca4   Luciano Coelho   mac80211: add sup...
902
903
  {
  	struct ieee80211_local *local = sdata->local;
30dd3edf9   Johannes Berg   mac80211: don't h...
904
  	int ret = 0;
79f460ca4   Luciano Coelho   mac80211: add sup...
905

5260a5b2c   Johannes Berg   mac80211: track s...
906
  	mutex_lock(&local->mtx);
79f460ca4   Luciano Coelho   mac80211: add sup...
907
908
909
910
911
  
  	if (!local->ops->sched_scan_stop) {
  		ret = -ENOTSUPP;
  		goto out;
  	}
30dd3edf9   Johannes Berg   mac80211: don't h...
912
  	if (rcu_access_pointer(local->sched_scan_sdata))
85a9994a0   Luciano Coelho   cfg80211/mac80211...
913
  		drv_sched_scan_stop(local, sdata);
30dd3edf9   Johannes Berg   mac80211: don't h...
914

79f460ca4   Luciano Coelho   mac80211: add sup...
915
  out:
5260a5b2c   Johannes Berg   mac80211: track s...
916
  	mutex_unlock(&local->mtx);
79f460ca4   Luciano Coelho   mac80211: add sup...
917
918
919
920
921
922
923
924
925
926
927
928
929
  
  	return ret;
  }
  
  void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
  {
  	struct ieee80211_local *local = hw_to_local(hw);
  
  	trace_api_sched_scan_results(local);
  
  	cfg80211_sched_scan_results(hw->wiphy);
  }
  EXPORT_SYMBOL(ieee80211_sched_scan_results);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
930
931
932
933
934
  void ieee80211_sched_scan_stopped_work(struct work_struct *work)
  {
  	struct ieee80211_local *local =
  		container_of(work, struct ieee80211_local,
  			     sched_scan_stopped_work);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
935
936
  
  	mutex_lock(&local->mtx);
5260a5b2c   Johannes Berg   mac80211: track s...
937
  	if (!rcu_access_pointer(local->sched_scan_sdata)) {
85a9994a0   Luciano Coelho   cfg80211/mac80211...
938
939
940
  		mutex_unlock(&local->mtx);
  		return;
  	}
5260a5b2c   Johannes Berg   mac80211: track s...
941
  	rcu_assign_pointer(local->sched_scan_sdata, NULL);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
942
943
944
945
946
  
  	mutex_unlock(&local->mtx);
  
  	cfg80211_sched_scan_stopped(local->hw.wiphy);
  }
79f460ca4   Luciano Coelho   mac80211: add sup...
947
948
949
950
951
  void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
  {
  	struct ieee80211_local *local = hw_to_local(hw);
  
  	trace_api_sched_scan_stopped(local);
85a9994a0   Luciano Coelho   cfg80211/mac80211...
952
  	ieee80211_queue_work(&local->hw, &local->sched_scan_stopped_work);
79f460ca4   Luciano Coelho   mac80211: add sup...
953
954
  }
  EXPORT_SYMBOL(ieee80211_sched_scan_stopped);