xref: /linux/net/mac80211/scan.c (revision 57fbcce37be7c1d2622b56587c10ade00e96afa3)
10a51b27eSJohannes Berg /*
25484e237SJohannes Berg  * Scanning implementation
35484e237SJohannes Berg  *
40a51b27eSJohannes Berg  * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi>
50a51b27eSJohannes Berg  * Copyright 2004, Instant802 Networks, Inc.
60a51b27eSJohannes Berg  * Copyright 2005, Devicescape Software, Inc.
70a51b27eSJohannes Berg  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
80a51b27eSJohannes Berg  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
974d803b6SSara Sharon  * Copyright 2013-2015  Intel Mobile Communications GmbH
100a51b27eSJohannes Berg  *
110a51b27eSJohannes Berg  * This program is free software; you can redistribute it and/or modify
120a51b27eSJohannes Berg  * it under the terms of the GNU General Public License version 2 as
130a51b27eSJohannes Berg  * published by the Free Software Foundation.
140a51b27eSJohannes Berg  */
150a51b27eSJohannes Berg 
160a51b27eSJohannes Berg #include <linux/if_arp.h>
17888d04dfSFelix Fietkau #include <linux/etherdevice.h>
18078e1e60SJohannes Berg #include <linux/rtnetlink.h>
19df13cce5SHelmut Schaa #include <net/sch_generic.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
21bc3b2d7fSPaul Gortmaker #include <linux/export.h>
220a51b27eSJohannes Berg #include <net/mac80211.h>
230a51b27eSJohannes Berg 
240a51b27eSJohannes Berg #include "ieee80211_i.h"
2524487981SJohannes Berg #include "driver-ops.h"
265484e237SJohannes Berg #include "mesh.h"
270a51b27eSJohannes Berg 
280a51b27eSJohannes Berg #define IEEE80211_PROBE_DELAY (HZ / 33)
290a51b27eSJohannes Berg #define IEEE80211_CHANNEL_TIME (HZ / 33)
303f892b61SStanislaw Gruszka #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 9)
310a51b27eSJohannes Berg 
325484e237SJohannes Berg void ieee80211_rx_bss_put(struct ieee80211_local *local,
33c2b13452SJohannes Berg 			  struct ieee80211_bss *bss)
345484e237SJohannes Berg {
350c1ad2caSJohannes Berg 	if (!bss)
360c1ad2caSJohannes Berg 		return;
375b112d3dSJohannes Berg 	cfg80211_put_bss(local->hw.wiphy,
385b112d3dSJohannes Berg 			 container_of((void *)bss, struct cfg80211_bss, priv));
395484e237SJohannes Berg }
405484e237SJohannes Berg 
41ab13315aSKalle Valo static bool is_uapsd_supported(struct ieee802_11_elems *elems)
42ab13315aSKalle Valo {
43ab13315aSKalle Valo 	u8 qos_info;
44ab13315aSKalle Valo 
45ab13315aSKalle Valo 	if (elems->wmm_info && elems->wmm_info_len == 7
46ab13315aSKalle Valo 	    && elems->wmm_info[5] == 1)
47ab13315aSKalle Valo 		qos_info = elems->wmm_info[6];
48ab13315aSKalle Valo 	else if (elems->wmm_param && elems->wmm_param_len == 24
49ab13315aSKalle Valo 		 && elems->wmm_param[5] == 1)
50ab13315aSKalle Valo 		qos_info = elems->wmm_param[6];
51ab13315aSKalle Valo 	else
52ab13315aSKalle Valo 		/* no valid wmm information or parameter element found */
53ab13315aSKalle Valo 		return false;
54ab13315aSKalle Valo 
55ab13315aSKalle Valo 	return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD;
56ab13315aSKalle Valo }
57ab13315aSKalle Valo 
58c2b13452SJohannes Berg struct ieee80211_bss *
595484e237SJohannes Berg ieee80211_bss_info_update(struct ieee80211_local *local,
605484e237SJohannes Berg 			  struct ieee80211_rx_status *rx_status,
61d45c4172SEmmanuel Grumbach 			  struct ieee80211_mgmt *mgmt, size_t len,
625484e237SJohannes Berg 			  struct ieee802_11_elems *elems,
63d45c4172SEmmanuel Grumbach 			  struct ieee80211_channel *channel)
645484e237SJohannes Berg {
65d45c4172SEmmanuel Grumbach 	bool beacon = ieee80211_is_beacon(mgmt->frame_control);
660c1ad2caSJohannes Berg 	struct cfg80211_bss *cbss;
67c2b13452SJohannes Berg 	struct ieee80211_bss *bss;
68f0b058b6SStanislaw Gruszka 	int clen, srlen;
69162dd6a7SJohannes Berg 	struct cfg80211_inform_bss bss_meta = {
70162dd6a7SJohannes Berg 		.boottime_ns = rx_status->boottime_ns,
71162dd6a7SJohannes Berg 	};
7274d803b6SSara Sharon 	bool signal_valid;
732a519311SJohannes Berg 
7430686bf7SJohannes Berg 	if (ieee80211_hw_check(&local->hw, SIGNAL_DBM))
7561f6bba0SJohannes Berg 		bss_meta.signal = rx_status->signal * 100;
7630686bf7SJohannes Berg 	else if (ieee80211_hw_check(&local->hw, SIGNAL_UNSPEC))
7761f6bba0SJohannes Berg 		bss_meta.signal = (rx_status->signal * 100) / local->hw.max_signal;
782a519311SJohannes Berg 
7961f6bba0SJohannes Berg 	bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_20;
807ca15a0aSSimon Wunderlich 	if (rx_status->flag & RX_FLAG_5MHZ)
8161f6bba0SJohannes Berg 		bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_5;
827ca15a0aSSimon Wunderlich 	if (rx_status->flag & RX_FLAG_10MHZ)
8361f6bba0SJohannes Berg 		bss_meta.scan_width = NL80211_BSS_CHAN_WIDTH_10;
847ca15a0aSSimon Wunderlich 
8561f6bba0SJohannes Berg 	bss_meta.chan = channel;
8661f6bba0SJohannes Berg 	cbss = cfg80211_inform_bss_frame_data(local->hw.wiphy, &bss_meta,
8761f6bba0SJohannes Berg 					      mgmt, len, GFP_ATOMIC);
880c1ad2caSJohannes Berg 	if (!cbss)
895484e237SJohannes Berg 		return NULL;
9074d803b6SSara Sharon 	/* In case the signal is invalid update the status */
9174d803b6SSara Sharon 	signal_valid = abs(channel->center_freq - cbss->channel->center_freq)
9274d803b6SSara Sharon 		<= local->hw.wiphy->max_adj_channel_rssi_comp;
9374d803b6SSara Sharon 	if (!signal_valid)
9474d803b6SSara Sharon 		rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
9500d3f14cSJohannes Berg 
960c1ad2caSJohannes Berg 	bss = (void *)cbss->priv;
975484e237SJohannes Berg 
98ef429dadSJohannes Berg 	if (beacon)
99ef429dadSJohannes Berg 		bss->device_ts_beacon = rx_status->device_timestamp;
100ef429dadSJohannes Berg 	else
101ef429dadSJohannes Berg 		bss->device_ts_presp = rx_status->device_timestamp;
1028c358bcdSJohannes Berg 
103fcff4f10SPaul Stewart 	if (elems->parse_error) {
104fcff4f10SPaul Stewart 		if (beacon)
105fcff4f10SPaul Stewart 			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON;
106fcff4f10SPaul Stewart 		else
107fcff4f10SPaul Stewart 			bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP;
108fcff4f10SPaul Stewart 	} else {
109fcff4f10SPaul Stewart 		if (beacon)
110fcff4f10SPaul Stewart 			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON;
111fcff4f10SPaul Stewart 		else
112fcff4f10SPaul Stewart 			bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP;
1135484e237SJohannes Berg 	}
1145484e237SJohannes Berg 
115fcff4f10SPaul Stewart 	/* save the ERP value so that it is available at association time */
1161946bed9SJohannes Berg 	if (elems->erp_info && (!elems->parse_error ||
117fcff4f10SPaul Stewart 				!(bss->valid_data & IEEE80211_BSS_VALID_ERP))) {
118fcff4f10SPaul Stewart 		bss->erp_value = elems->erp_info[0];
119fcff4f10SPaul Stewart 		bss->has_erp_value = true;
120fcff4f10SPaul Stewart 		if (!elems->parse_error)
121fcff4f10SPaul Stewart 			bss->valid_data |= IEEE80211_BSS_VALID_ERP;
122fcff4f10SPaul Stewart 	}
123fcff4f10SPaul Stewart 
124f0b058b6SStanislaw Gruszka 	/* replace old supported rates if we get new values */
125fcff4f10SPaul Stewart 	if (!elems->parse_error ||
126fcff4f10SPaul Stewart 	    !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
127f0b058b6SStanislaw Gruszka 		srlen = 0;
1285484e237SJohannes Berg 		if (elems->supp_rates) {
129f0b058b6SStanislaw Gruszka 			clen = IEEE80211_MAX_SUPP_RATES;
1305484e237SJohannes Berg 			if (clen > elems->supp_rates_len)
1315484e237SJohannes Berg 				clen = elems->supp_rates_len;
132f0b058b6SStanislaw Gruszka 			memcpy(bss->supp_rates, elems->supp_rates, clen);
133f0b058b6SStanislaw Gruszka 			srlen += clen;
1345484e237SJohannes Berg 		}
1355484e237SJohannes Berg 		if (elems->ext_supp_rates) {
136f0b058b6SStanislaw Gruszka 			clen = IEEE80211_MAX_SUPP_RATES - srlen;
1375484e237SJohannes Berg 			if (clen > elems->ext_supp_rates_len)
1385484e237SJohannes Berg 				clen = elems->ext_supp_rates_len;
139fcff4f10SPaul Stewart 			memcpy(bss->supp_rates + srlen, elems->ext_supp_rates,
140fcff4f10SPaul Stewart 			       clen);
141f0b058b6SStanislaw Gruszka 			srlen += clen;
1425484e237SJohannes Berg 		}
143fcff4f10SPaul Stewart 		if (srlen) {
144f0b058b6SStanislaw Gruszka 			bss->supp_rates_len = srlen;
145fcff4f10SPaul Stewart 			if (!elems->parse_error)
146fcff4f10SPaul Stewart 				bss->valid_data |= IEEE80211_BSS_VALID_RATES;
147fcff4f10SPaul Stewart 		}
148fcff4f10SPaul Stewart 	}
1495484e237SJohannes Berg 
150fcff4f10SPaul Stewart 	if (!elems->parse_error ||
151fcff4f10SPaul Stewart 	    !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) {
1525484e237SJohannes Berg 		bss->wmm_used = elems->wmm_param || elems->wmm_info;
153ab13315aSKalle Valo 		bss->uapsd_supported = is_uapsd_supported(elems);
154fcff4f10SPaul Stewart 		if (!elems->parse_error)
155fcff4f10SPaul Stewart 			bss->valid_data |= IEEE80211_BSS_VALID_WMM;
156fcff4f10SPaul Stewart 	}
1575484e237SJohannes Berg 
158817cee76SAlexander Bondar 	if (beacon) {
159817cee76SAlexander Bondar 		struct ieee80211_supported_band *sband =
160817cee76SAlexander Bondar 			local->hw.wiphy->bands[rx_status->band];
161817cee76SAlexander Bondar 		if (!(rx_status->flag & RX_FLAG_HT) &&
162817cee76SAlexander Bondar 		    !(rx_status->flag & RX_FLAG_VHT))
163817cee76SAlexander Bondar 			bss->beacon_rate =
164817cee76SAlexander Bondar 				&sband->bitrates[rx_status->rate_idx];
165817cee76SAlexander Bondar 	}
166817cee76SAlexander Bondar 
1675484e237SJohannes Berg 	return bss;
1685484e237SJohannes Berg }
1690a51b27eSJohannes Berg 
170d48b2968SJohannes Berg void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
17198c8fccfSJohannes Berg {
172f1d58c25SJohannes Berg 	struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
173d48b2968SJohannes Berg 	struct ieee80211_sub_if_data *sdata1, *sdata2;
174d48b2968SJohannes Berg 	struct ieee80211_mgmt *mgmt = (void *)skb->data;
175c2b13452SJohannes Berg 	struct ieee80211_bss *bss;
17698c8fccfSJohannes Berg 	u8 *elements;
17798c8fccfSJohannes Berg 	struct ieee80211_channel *channel;
17898c8fccfSJohannes Berg 	size_t baselen;
17998c8fccfSJohannes Berg 	struct ieee802_11_elems elems;
18098c8fccfSJohannes Berg 
181d48b2968SJohannes Berg 	if (skb->len < 24 ||
182d48b2968SJohannes Berg 	    (!ieee80211_is_probe_resp(mgmt->frame_control) &&
183d48b2968SJohannes Berg 	     !ieee80211_is_beacon(mgmt->frame_control)))
184d48b2968SJohannes Berg 		return;
18598c8fccfSJohannes Berg 
186d48b2968SJohannes Berg 	sdata1 = rcu_dereference(local->scan_sdata);
187d48b2968SJohannes Berg 	sdata2 = rcu_dereference(local->sched_scan_sdata);
18898c8fccfSJohannes Berg 
189d48b2968SJohannes Berg 	if (likely(!sdata1 && !sdata2))
190d48b2968SJohannes Berg 		return;
19198c8fccfSJohannes Berg 
192d48b2968SJohannes Berg 	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
193a344d677SJohannes Berg 		struct cfg80211_scan_request *scan_req;
194a344d677SJohannes Berg 		struct cfg80211_sched_scan_request *sched_scan_req;
195a344d677SJohannes Berg 
196a344d677SJohannes Berg 		scan_req = rcu_dereference(local->scan_req);
197a344d677SJohannes Berg 		sched_scan_req = rcu_dereference(local->sched_scan_req);
198a344d677SJohannes Berg 
199a344d677SJohannes Berg 		/* ignore ProbeResp to foreign address unless scanning
200a344d677SJohannes Berg 		 * with randomised address
201a344d677SJohannes Berg 		 */
202a344d677SJohannes Berg 		if (!(sdata1 &&
203a344d677SJohannes Berg 		      (ether_addr_equal(mgmt->da, sdata1->vif.addr) ||
204a344d677SJohannes Berg 		       scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) &&
205a344d677SJohannes Berg 		    !(sdata2 &&
206a344d677SJohannes Berg 		      (ether_addr_equal(mgmt->da, sdata2->vif.addr) ||
207a344d677SJohannes Berg 		       sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)))
208d48b2968SJohannes Berg 			return;
20998c8fccfSJohannes Berg 
21098c8fccfSJohannes Berg 		elements = mgmt->u.probe_resp.variable;
21198c8fccfSJohannes Berg 		baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
21298c8fccfSJohannes Berg 	} else {
21398c8fccfSJohannes Berg 		baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable);
21498c8fccfSJohannes Berg 		elements = mgmt->u.beacon.variable;
21598c8fccfSJohannes Berg 	}
21698c8fccfSJohannes Berg 
21798c8fccfSJohannes Berg 	if (baselen > skb->len)
218d48b2968SJohannes Berg 		return;
21998c8fccfSJohannes Berg 
220b2e506bfSJohannes Berg 	ieee802_11_parse_elems(elements, skb->len - baselen, false, &elems);
22198c8fccfSJohannes Berg 
2220172bb75SJohannes Berg 	channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
22398c8fccfSJohannes Berg 
22498c8fccfSJohannes Berg 	if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
225d48b2968SJohannes Berg 		return;
22698c8fccfSJohannes Berg 
227d48b2968SJohannes Berg 	bss = ieee80211_bss_info_update(local, rx_status,
22898c8fccfSJohannes Berg 					mgmt, skb->len, &elems,
229d45c4172SEmmanuel Grumbach 					channel);
230d048e503SJouni Malinen 	if (bss)
231d48b2968SJohannes Berg 		ieee80211_rx_bss_put(local, bss);
23298c8fccfSJohannes Berg }
23398c8fccfSJohannes Berg 
2342103dec1SSimon Wunderlich static void
2352103dec1SSimon Wunderlich ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
2362103dec1SSimon Wunderlich 			       enum nl80211_bss_scan_width scan_width)
2372103dec1SSimon Wunderlich {
2382103dec1SSimon Wunderlich 	memset(chandef, 0, sizeof(*chandef));
2392103dec1SSimon Wunderlich 	switch (scan_width) {
2402103dec1SSimon Wunderlich 	case NL80211_BSS_CHAN_WIDTH_5:
2412103dec1SSimon Wunderlich 		chandef->width = NL80211_CHAN_WIDTH_5;
2422103dec1SSimon Wunderlich 		break;
2432103dec1SSimon Wunderlich 	case NL80211_BSS_CHAN_WIDTH_10:
2442103dec1SSimon Wunderlich 		chandef->width = NL80211_CHAN_WIDTH_10;
2452103dec1SSimon Wunderlich 		break;
2462103dec1SSimon Wunderlich 	default:
2472103dec1SSimon Wunderlich 		chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
2482103dec1SSimon Wunderlich 		break;
2492103dec1SSimon Wunderlich 	}
2502103dec1SSimon Wunderlich }
2512103dec1SSimon Wunderlich 
2524d36ec58SJohannes Berg /* return false if no more work */
2534d36ec58SJohannes Berg static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
2544d36ec58SJohannes Berg {
2556ea0a69cSJohannes Berg 	struct cfg80211_scan_request *req;
2562103dec1SSimon Wunderlich 	struct cfg80211_chan_def chandef;
257c56ef672SDavid Spinadel 	u8 bands_used = 0;
2584d36ec58SJohannes Berg 	int i, ielen, n_chans;
2594d36ec58SJohannes Berg 
2606ea0a69cSJohannes Berg 	req = rcu_dereference_protected(local->scan_req,
2616ea0a69cSJohannes Berg 					lockdep_is_held(&local->mtx));
2626ea0a69cSJohannes Berg 
263a754055aSEmmanuel Grumbach 	if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
264a754055aSEmmanuel Grumbach 		return false;
265a754055aSEmmanuel Grumbach 
26630686bf7SJohannes Berg 	if (ieee80211_hw_check(&local->hw, SINGLE_SCAN_ON_ALL_BANDS)) {
267c56ef672SDavid Spinadel 		for (i = 0; i < req->n_channels; i++) {
268c56ef672SDavid Spinadel 			local->hw_scan_req->req.channels[i] = req->channels[i];
269c56ef672SDavid Spinadel 			bands_used |= BIT(req->channels[i]->band);
270c56ef672SDavid Spinadel 		}
271c56ef672SDavid Spinadel 
272c56ef672SDavid Spinadel 		n_chans = req->n_channels;
273c56ef672SDavid Spinadel 	} else {
2744d36ec58SJohannes Berg 		do {
275*57fbcce3SJohannes Berg 			if (local->hw_scan_band == NUM_NL80211_BANDS)
2764d36ec58SJohannes Berg 				return false;
2774d36ec58SJohannes Berg 
2784d36ec58SJohannes Berg 			n_chans = 0;
279c56ef672SDavid Spinadel 
2804d36ec58SJohannes Berg 			for (i = 0; i < req->n_channels; i++) {
281c56ef672SDavid Spinadel 				if (req->channels[i]->band !=
282c56ef672SDavid Spinadel 				    local->hw_scan_band)
283c56ef672SDavid Spinadel 					continue;
284c56ef672SDavid Spinadel 				local->hw_scan_req->req.channels[n_chans] =
2854d36ec58SJohannes Berg 							req->channels[i];
2864d36ec58SJohannes Berg 				n_chans++;
287c56ef672SDavid Spinadel 				bands_used |= BIT(req->channels[i]->band);
2884d36ec58SJohannes Berg 			}
2894d36ec58SJohannes Berg 
2904d36ec58SJohannes Berg 			local->hw_scan_band++;
2914d36ec58SJohannes Berg 		} while (!n_chans);
292c56ef672SDavid Spinadel 	}
2934d36ec58SJohannes Berg 
294c56ef672SDavid Spinadel 	local->hw_scan_req->req.n_channels = n_chans;
2952103dec1SSimon Wunderlich 	ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
2964d36ec58SJohannes Berg 
297c56ef672SDavid Spinadel 	ielen = ieee80211_build_preq_ies(local,
298c56ef672SDavid Spinadel 					 (u8 *)local->hw_scan_req->req.ie,
299c604b9f2SJohannes Berg 					 local->hw_scan_ies_bufsize,
300c56ef672SDavid Spinadel 					 &local->hw_scan_req->ies,
301c56ef672SDavid Spinadel 					 req->ie, req->ie_len,
302c56ef672SDavid Spinadel 					 bands_used, req->rates, &chandef);
303c56ef672SDavid Spinadel 	local->hw_scan_req->req.ie_len = ielen;
304c56ef672SDavid Spinadel 	local->hw_scan_req->req.no_cck = req->no_cck;
305a344d677SJohannes Berg 	ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
306a344d677SJohannes Berg 	ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
307a344d677SJohannes Berg 			req->mac_addr_mask);
308e345f44fSJouni Malinen 	ether_addr_copy(local->hw_scan_req->req.bssid, req->bssid);
3094d36ec58SJohannes Berg 
3104d36ec58SJohannes Berg 	return true;
3114d36ec58SJohannes Berg }
3124d36ec58SJohannes Berg 
3138bd2a248SEliad Peller static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
314f3b85252SJohannes Berg {
315f3b85252SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
3168bd2a248SEliad Peller 	bool hw_scan = local->ops->hw_scan;
317a2b70e83SEliad Peller 	bool was_scanning = local->scanning;
3186ea0a69cSJohannes Berg 	struct cfg80211_scan_request *scan_req;
319a344d677SJohannes Berg 	struct ieee80211_sub_if_data *scan_sdata;
3204fa11ec7SSachin Kulkarni 	struct ieee80211_sub_if_data *sdata;
321f3b85252SJohannes Berg 
322e229f844SStanislaw Gruszka 	lockdep_assert_held(&local->mtx);
323f3b85252SJohannes Berg 
3246d3560d4SJohannes Berg 	/*
3256d3560d4SJohannes Berg 	 * It's ok to abort a not-yet-running scan (that
3266d3560d4SJohannes Berg 	 * we have one at all will be verified by checking
3276d3560d4SJohannes Berg 	 * local->scan_req next), but not to complete it
3286d3560d4SJohannes Berg 	 * successfully.
3296d3560d4SJohannes Berg 	 */
3306d3560d4SJohannes Berg 	if (WARN_ON(!local->scanning && !aborted))
3316d3560d4SJohannes Berg 		aborted = true;
332f3b85252SJohannes Berg 
333e229f844SStanislaw Gruszka 	if (WARN_ON(!local->scan_req))
334d07bfd8bSJohannes Berg 		return;
335f3b85252SJohannes Berg 
336c56ef672SDavid Spinadel 	if (hw_scan && !aborted &&
33730686bf7SJohannes Berg 	    !ieee80211_hw_check(&local->hw, SINGLE_SCAN_ON_ALL_BANDS) &&
338c56ef672SDavid Spinadel 	    ieee80211_prep_hw_scan(local)) {
339e2fd5dbcSJohannes Berg 		int rc;
340e2fd5dbcSJohannes Berg 
341e2fd5dbcSJohannes Berg 		rc = drv_hw_scan(local,
342e2fd5dbcSJohannes Berg 			rcu_dereference_protected(local->scan_sdata,
343e2fd5dbcSJohannes Berg 						  lockdep_is_held(&local->mtx)),
344e2fd5dbcSJohannes Berg 			local->hw_scan_req);
345e2fd5dbcSJohannes Berg 
3466eb11a9aSStanislaw Gruszka 		if (rc == 0)
347d07bfd8bSJohannes Berg 			return;
3484d36ec58SJohannes Berg 	}
3494d36ec58SJohannes Berg 
3504d36ec58SJohannes Berg 	kfree(local->hw_scan_req);
3514d36ec58SJohannes Berg 	local->hw_scan_req = NULL;
352f3b85252SJohannes Berg 
3536ea0a69cSJohannes Berg 	scan_req = rcu_dereference_protected(local->scan_req,
3546ea0a69cSJohannes Berg 					     lockdep_is_held(&local->mtx));
3556ea0a69cSJohannes Berg 
3566ea0a69cSJohannes Berg 	if (scan_req != local->int_scan_req)
3576ea0a69cSJohannes Berg 		cfg80211_scan_done(scan_req, aborted);
3586ea0a69cSJohannes Berg 	RCU_INIT_POINTER(local->scan_req, NULL);
359a344d677SJohannes Berg 
360a344d677SJohannes Berg 	scan_sdata = rcu_dereference_protected(local->scan_sdata,
361a344d677SJohannes Berg 					       lockdep_is_held(&local->mtx));
3620c2bef46SMonam Agarwal 	RCU_INIT_POINTER(local->scan_sdata, NULL);
3632a519311SJohannes Berg 
364fbe9c429SHelmut Schaa 	local->scanning = 0;
3657ca15a0aSSimon Wunderlich 	local->scan_chandef.chan = NULL;
366f3b85252SJohannes Berg 
36759bdf3b0SBen Greear 	/* Set power back to normal operating levels. */
36859bdf3b0SBen Greear 	ieee80211_hw_config(local, 0);
369b23b025fSBen Greear 
3708bd2a248SEliad Peller 	if (!hw_scan) {
371b23b025fSBen Greear 		ieee80211_configure_filter(local);
372a344d677SJohannes Berg 		drv_sw_scan_complete(local, scan_sdata);
373aacde9eeSStanislaw Gruszka 		ieee80211_offchannel_return(local);
374b23b025fSBen Greear 	}
375b23b025fSBen Greear 
3765cff20e6SJohannes Berg 	ieee80211_recalc_idle(local);
377e229f844SStanislaw Gruszka 
3780a51b27eSJohannes Berg 	ieee80211_mlme_notify_scan_completed(local);
37946900298SJohannes Berg 	ieee80211_ibss_notify_scan_completed(local);
3804fa11ec7SSachin Kulkarni 
3814fa11ec7SSachin Kulkarni 	/* Requeue all the work that might have been ignored while
3824fa11ec7SSachin Kulkarni 	 * the scan was in progress; if there was none this will
3834fa11ec7SSachin Kulkarni 	 * just be a no-op for the particular interface.
3844fa11ec7SSachin Kulkarni 	 */
3854fa11ec7SSachin Kulkarni 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
3864fa11ec7SSachin Kulkarni 		if (ieee80211_sdata_running(sdata))
3874fa11ec7SSachin Kulkarni 			ieee80211_queue_work(&sdata->local->hw, &sdata->work);
3884fa11ec7SSachin Kulkarni 	}
3894fa11ec7SSachin Kulkarni 
390a2b70e83SEliad Peller 	if (was_scanning)
3912eb278e0SJohannes Berg 		ieee80211_start_next_roc(local);
3920a51b27eSJohannes Berg }
3938789d459SJohannes Berg 
3948789d459SJohannes Berg void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
3958789d459SJohannes Berg {
3968789d459SJohannes Berg 	struct ieee80211_local *local = hw_to_local(hw);
3978789d459SJohannes Berg 
3988789d459SJohannes Berg 	trace_api_scan_completed(local, aborted);
3998789d459SJohannes Berg 
4008789d459SJohannes Berg 	set_bit(SCAN_COMPLETED, &local->scanning);
4018789d459SJohannes Berg 	if (aborted)
4028789d459SJohannes Berg 		set_bit(SCAN_ABORTED, &local->scanning);
4038789d459SJohannes Berg 	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
4048789d459SJohannes Berg }
4050a51b27eSJohannes Berg EXPORT_SYMBOL(ieee80211_scan_completed);
4060a51b27eSJohannes Berg 
407a344d677SJohannes Berg static int ieee80211_start_sw_scan(struct ieee80211_local *local,
408a344d677SJohannes Berg 				   struct ieee80211_sub_if_data *sdata)
409f3b85252SJohannes Berg {
410fe57d9f5SJohannes Berg 	/* Software scan is not supported in multi-channel cases */
411fe57d9f5SJohannes Berg 	if (local->use_chanctx)
412fe57d9f5SJohannes Berg 		return -EOPNOTSUPP;
413fe57d9f5SJohannes Berg 
414f3b85252SJohannes Berg 	/*
415f3b85252SJohannes Berg 	 * Hardware/driver doesn't support hw_scan, so use software
416f3b85252SJohannes Berg 	 * scanning instead. First send a nullfunc frame with power save
417f3b85252SJohannes Berg 	 * bit on so that AP will buffer the frames for us while we are not
418f3b85252SJohannes Berg 	 * listening, then send probe requests to each channel and wait for
419f3b85252SJohannes Berg 	 * the responses. After all channels are scanned, tune back to the
420f3b85252SJohannes Berg 	 * original channel and send a nullfunc frame with power save bit
421f3b85252SJohannes Berg 	 * off to trigger the AP to send us all the buffered frames.
422f3b85252SJohannes Berg 	 *
423f3b85252SJohannes Berg 	 * Note that while local->sw_scanning is true everything else but
424f3b85252SJohannes Berg 	 * nullfunc frames and probe requests will be dropped in
425f3b85252SJohannes Berg 	 * ieee80211_tx_h_check_assoc().
426f3b85252SJohannes Berg 	 */
427a344d677SJohannes Berg 	drv_sw_scan_start(local, sdata, local->scan_addr);
428f3b85252SJohannes Berg 
429de312db3SRajkumar Manoharan 	local->leave_oper_channel_time = jiffies;
430977923b0SHelmut Schaa 	local->next_scan_state = SCAN_DECISION;
431f3b85252SJohannes Berg 	local->scan_channel_idx = 0;
432f3b85252SJohannes Berg 
433aacde9eeSStanislaw Gruszka 	ieee80211_offchannel_stop_vifs(local);
434a80f7c0bSJohannes Berg 
4359c35d7d2SSeth Forshee 	/* ensure nullfunc is transmitted before leaving operating channel */
4363b24f4c6SEmmanuel Grumbach 	ieee80211_flush_queues(local, NULL, false);
4379c35d7d2SSeth Forshee 
4383ac64beeSJohannes Berg 	ieee80211_configure_filter(local);
439f3b85252SJohannes Berg 
44059bdf3b0SBen Greear 	/* We need to set power level at maximum rate for scanning. */
44159bdf3b0SBen Greear 	ieee80211_hw_config(local, 0);
44259bdf3b0SBen Greear 
44342935ecaSLuis R. Rodriguez 	ieee80211_queue_delayed_work(&local->hw,
44407ef03eeSJohannes Berg 				     &local->scan_work, 0);
445f3b85252SJohannes Berg 
446f3b85252SJohannes Berg 	return 0;
447f3b85252SJohannes Berg }
448f3b85252SJohannes Berg 
449133d40f9SStanislaw Gruszka static bool ieee80211_can_scan(struct ieee80211_local *local,
450133d40f9SStanislaw Gruszka 			       struct ieee80211_sub_if_data *sdata)
451133d40f9SStanislaw Gruszka {
4525cbc95a7SEliad Peller 	if (ieee80211_is_radar_required(local))
453164eb02dSSimon Wunderlich 		return false;
454164eb02dSSimon Wunderlich 
4552eb278e0SJohannes Berg 	if (!list_empty(&local->roc_list))
456133d40f9SStanislaw Gruszka 		return false;
457133d40f9SStanislaw Gruszka 
458133d40f9SStanislaw Gruszka 	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
459392b9ffbSStanislaw Gruszka 	    sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
460133d40f9SStanislaw Gruszka 		return false;
461133d40f9SStanislaw Gruszka 
462133d40f9SStanislaw Gruszka 	return true;
463133d40f9SStanislaw Gruszka }
464133d40f9SStanislaw Gruszka 
465133d40f9SStanislaw Gruszka void ieee80211_run_deferred_scan(struct ieee80211_local *local)
466133d40f9SStanislaw Gruszka {
467133d40f9SStanislaw Gruszka 	lockdep_assert_held(&local->mtx);
468133d40f9SStanislaw Gruszka 
469133d40f9SStanislaw Gruszka 	if (!local->scan_req || local->scanning)
470133d40f9SStanislaw Gruszka 		return;
471133d40f9SStanislaw Gruszka 
472e2fd5dbcSJohannes Berg 	if (!ieee80211_can_scan(local,
473e2fd5dbcSJohannes Berg 				rcu_dereference_protected(
474e2fd5dbcSJohannes Berg 					local->scan_sdata,
475e2fd5dbcSJohannes Berg 					lockdep_is_held(&local->mtx))))
476133d40f9SStanislaw Gruszka 		return;
477133d40f9SStanislaw Gruszka 
478133d40f9SStanislaw Gruszka 	ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
479133d40f9SStanislaw Gruszka 				     round_jiffies_relative(0));
480133d40f9SStanislaw Gruszka }
481f3b85252SJohannes Berg 
4828a690674SBen Greear static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
4838a690674SBen Greear 					    unsigned long *next_delay)
4848a690674SBen Greear {
4858a690674SBen Greear 	int i;
486e2fd5dbcSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
4876ea0a69cSJohannes Berg 	struct cfg80211_scan_request *scan_req;
488*57fbcce3SJohannes Berg 	enum nl80211_band band = local->hw.conf.chandef.chan->band;
4896c17b77bSSeth Forshee 	u32 tx_flags;
4906c17b77bSSeth Forshee 
4916ea0a69cSJohannes Berg 	scan_req = rcu_dereference_protected(local->scan_req,
4926ea0a69cSJohannes Berg 					     lockdep_is_held(&local->mtx));
4936ea0a69cSJohannes Berg 
4946c17b77bSSeth Forshee 	tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
4956ea0a69cSJohannes Berg 	if (scan_req->no_cck)
4966c17b77bSSeth Forshee 		tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
4978a690674SBen Greear 
498e2fd5dbcSJohannes Berg 	sdata = rcu_dereference_protected(local->scan_sdata,
499316b6b5dSPeter Senna Tschudin 					  lockdep_is_held(&local->mtx));
500e2fd5dbcSJohannes Berg 
5016ea0a69cSJohannes Berg 	for (i = 0; i < scan_req->n_ssids; i++)
5028a690674SBen Greear 		ieee80211_send_probe_req(
503e345f44fSJouni Malinen 			sdata, local->scan_addr, scan_req->bssid,
5046ea0a69cSJohannes Berg 			scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
5056ea0a69cSJohannes Berg 			scan_req->ie, scan_req->ie_len,
5066ea0a69cSJohannes Berg 			scan_req->rates[band], false,
507675a0b04SKarl Beldan 			tx_flags, local->hw.conf.chandef.chan, true);
5088a690674SBen Greear 
5098a690674SBen Greear 	/*
5108a690674SBen Greear 	 * After sending probe requests, wait for probe responses
5118a690674SBen Greear 	 * on the channel.
5128a690674SBen Greear 	 */
5138a690674SBen Greear 	*next_delay = IEEE80211_CHANNEL_TIME;
5148a690674SBen Greear 	local->next_scan_state = SCAN_DECISION;
5158a690674SBen Greear }
5168a690674SBen Greear 
517f3b85252SJohannes Berg static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
518f3b85252SJohannes Berg 				  struct cfg80211_scan_request *req)
519f3b85252SJohannes Berg {
520f3b85252SJohannes Berg 	struct ieee80211_local *local = sdata->local;
521f3b85252SJohannes Berg 	int rc;
522f3b85252SJohannes Berg 
523e229f844SStanislaw Gruszka 	lockdep_assert_held(&local->mtx);
524e229f844SStanislaw Gruszka 
5252726f23dSEliad Peller 	if (local->scan_req || ieee80211_is_radar_required(local))
526f3b85252SJohannes Berg 		return -EBUSY;
527f3b85252SJohannes Berg 
528133d40f9SStanislaw Gruszka 	if (!ieee80211_can_scan(local, sdata)) {
5296e7e6213SJohn W. Linville 		/* wait for the work to finish/time out */
5306ea0a69cSJohannes Berg 		rcu_assign_pointer(local->scan_req, req);
531e2fd5dbcSJohannes Berg 		rcu_assign_pointer(local->scan_sdata, sdata);
532c0ce77b8SJohannes Berg 		return 0;
533c0ce77b8SJohannes Berg 	}
534c0ce77b8SJohannes Berg 
535f3b85252SJohannes Berg 	if (local->ops->hw_scan) {
536f3b85252SJohannes Berg 		u8 *ies;
537f3b85252SJohannes Berg 
538e4dcbb37SDavid Spinadel 		local->hw_scan_ies_bufsize = local->scan_ies_len + req->ie_len;
539c56ef672SDavid Spinadel 
54030686bf7SJohannes Berg 		if (ieee80211_hw_check(&local->hw, SINGLE_SCAN_ON_ALL_BANDS)) {
541c56ef672SDavid Spinadel 			int i, n_bands = 0;
542c56ef672SDavid Spinadel 			u8 bands_counted = 0;
543c56ef672SDavid Spinadel 
544c56ef672SDavid Spinadel 			for (i = 0; i < req->n_channels; i++) {
545c56ef672SDavid Spinadel 				if (bands_counted & BIT(req->channels[i]->band))
546c56ef672SDavid Spinadel 					continue;
547c56ef672SDavid Spinadel 				bands_counted |= BIT(req->channels[i]->band);
548c56ef672SDavid Spinadel 				n_bands++;
549c56ef672SDavid Spinadel 			}
550c56ef672SDavid Spinadel 
551c56ef672SDavid Spinadel 			local->hw_scan_ies_bufsize *= n_bands;
552c56ef672SDavid Spinadel 		}
553c56ef672SDavid Spinadel 
5544d36ec58SJohannes Berg 		local->hw_scan_req = kmalloc(
5554d36ec58SJohannes Berg 				sizeof(*local->hw_scan_req) +
5564d36ec58SJohannes Berg 				req->n_channels * sizeof(req->channels[0]) +
557c604b9f2SJohannes Berg 				local->hw_scan_ies_bufsize, GFP_KERNEL);
5584d36ec58SJohannes Berg 		if (!local->hw_scan_req)
559f3b85252SJohannes Berg 			return -ENOMEM;
560f3b85252SJohannes Berg 
561c56ef672SDavid Spinadel 		local->hw_scan_req->req.ssids = req->ssids;
562c56ef672SDavid Spinadel 		local->hw_scan_req->req.n_ssids = req->n_ssids;
5634d36ec58SJohannes Berg 		ies = (u8 *)local->hw_scan_req +
5644d36ec58SJohannes Berg 			sizeof(*local->hw_scan_req) +
5654d36ec58SJohannes Berg 			req->n_channels * sizeof(req->channels[0]);
566c56ef672SDavid Spinadel 		local->hw_scan_req->req.ie = ies;
567c56ef672SDavid Spinadel 		local->hw_scan_req->req.flags = req->flags;
568e345f44fSJouni Malinen 		eth_broadcast_addr(local->hw_scan_req->req.bssid);
5694d36ec58SJohannes Berg 
5704d36ec58SJohannes Berg 		local->hw_scan_band = 0;
5716e7e6213SJohn W. Linville 
5726e7e6213SJohn W. Linville 		/*
5736e7e6213SJohn W. Linville 		 * After allocating local->hw_scan_req, we must
5746e7e6213SJohn W. Linville 		 * go through until ieee80211_prep_hw_scan(), so
5756e7e6213SJohn W. Linville 		 * anything that might be changed here and leave
5766e7e6213SJohn W. Linville 		 * this function early must not go after this
5776e7e6213SJohn W. Linville 		 * allocation.
5786e7e6213SJohn W. Linville 		 */
579f3b85252SJohannes Berg 	}
580f3b85252SJohannes Berg 
5816ea0a69cSJohannes Berg 	rcu_assign_pointer(local->scan_req, req);
582e2fd5dbcSJohannes Berg 	rcu_assign_pointer(local->scan_sdata, sdata);
583f3b85252SJohannes Berg 
584a344d677SJohannes Berg 	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
585a344d677SJohannes Berg 		get_random_mask_addr(local->scan_addr,
586a344d677SJohannes Berg 				     req->mac_addr,
587a344d677SJohannes Berg 				     req->mac_addr_mask);
588a344d677SJohannes Berg 	else
589a344d677SJohannes Berg 		memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
590a344d677SJohannes Berg 
5918a690674SBen Greear 	if (local->ops->hw_scan) {
592fbe9c429SHelmut Schaa 		__set_bit(SCAN_HW_SCANNING, &local->scanning);
5938a690674SBen Greear 	} else if ((req->n_channels == 1) &&
594675a0b04SKarl Beldan 		   (req->channels[0] == local->_oper_chandef.chan)) {
5959b864870SJohannes Berg 		/*
5969b864870SJohannes Berg 		 * If we are scanning only on the operating channel
5979b864870SJohannes Berg 		 * then we do not need to stop normal activities
5988a690674SBen Greear 		 */
5998a690674SBen Greear 		unsigned long next_delay;
6008a690674SBen Greear 
6018a690674SBen Greear 		__set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning);
6028a690674SBen Greear 
6038a690674SBen Greear 		ieee80211_recalc_idle(local);
6048a690674SBen Greear 
6058a690674SBen Greear 		/* Notify driver scan is starting, keep order of operations
6068a690674SBen Greear 		 * same as normal software scan, in case that matters. */
607a344d677SJohannes Berg 		drv_sw_scan_start(local, sdata, local->scan_addr);
6088a690674SBen Greear 
6098a690674SBen Greear 		ieee80211_configure_filter(local); /* accept probe-responses */
6108a690674SBen Greear 
6118a690674SBen Greear 		/* We need to ensure power level is at max for scanning. */
6128a690674SBen Greear 		ieee80211_hw_config(local, 0);
6138a690674SBen Greear 
6144e39ccacSAntonio Quartulli 		if ((req->channels[0]->flags & (IEEE80211_CHAN_NO_IR |
6154e39ccacSAntonio Quartulli 						IEEE80211_CHAN_RADAR)) ||
6166ea0a69cSJohannes Berg 		    !req->n_ssids) {
6178a690674SBen Greear 			next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
6188a690674SBen Greear 		} else {
6198a690674SBen Greear 			ieee80211_scan_state_send_probe(local, &next_delay);
6208a690674SBen Greear 			next_delay = IEEE80211_CHANNEL_TIME;
6218a690674SBen Greear 		}
6228a690674SBen Greear 
6238a690674SBen Greear 		/* Now, just wait a bit and we are all done! */
6248a690674SBen Greear 		ieee80211_queue_delayed_work(&local->hw, &local->scan_work,
6258a690674SBen Greear 					     next_delay);
6268a690674SBen Greear 		return 0;
6278a690674SBen Greear 	} else {
6288a690674SBen Greear 		/* Do normal software scan */
629fbe9c429SHelmut Schaa 		__set_bit(SCAN_SW_SCANNING, &local->scanning);
6308a690674SBen Greear 	}
6316e7e6213SJohn W. Linville 
6325cff20e6SJohannes Berg 	ieee80211_recalc_idle(local);
633f3b85252SJohannes Berg 
6344d36ec58SJohannes Berg 	if (local->ops->hw_scan) {
6354d36ec58SJohannes Berg 		WARN_ON(!ieee80211_prep_hw_scan(local));
636a060bbfeSJohannes Berg 		rc = drv_hw_scan(local, sdata, local->hw_scan_req);
637a344d677SJohannes Berg 	} else {
638a344d677SJohannes Berg 		rc = ieee80211_start_sw_scan(local, sdata);
639a344d677SJohannes Berg 	}
640f3b85252SJohannes Berg 
641f3b85252SJohannes Berg 	if (rc) {
6424d36ec58SJohannes Berg 		kfree(local->hw_scan_req);
6434d36ec58SJohannes Berg 		local->hw_scan_req = NULL;
644fbe9c429SHelmut Schaa 		local->scanning = 0;
645f3b85252SJohannes Berg 
6465cff20e6SJohannes Berg 		ieee80211_recalc_idle(local);
6475cff20e6SJohannes Berg 
648f3b85252SJohannes Berg 		local->scan_req = NULL;
6490c2bef46SMonam Agarwal 		RCU_INIT_POINTER(local->scan_sdata, NULL);
650f3b85252SJohannes Berg 	}
651f3b85252SJohannes Berg 
652f3b85252SJohannes Berg 	return rc;
653f3b85252SJohannes Berg }
654f3b85252SJohannes Berg 
655df13cce5SHelmut Schaa static unsigned long
656df13cce5SHelmut Schaa ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
657df13cce5SHelmut Schaa {
658df13cce5SHelmut Schaa 	/*
659df13cce5SHelmut Schaa 	 * TODO: channel switching also consumes quite some time,
660df13cce5SHelmut Schaa 	 * add that delay as well to get a better estimation
661df13cce5SHelmut Schaa 	 */
6624e39ccacSAntonio Quartulli 	if (chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR))
663df13cce5SHelmut Schaa 		return IEEE80211_PASSIVE_CHANNEL_TIME;
664df13cce5SHelmut Schaa 	return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
665df13cce5SHelmut Schaa }
666df13cce5SHelmut Schaa 
667e229f844SStanislaw Gruszka static void ieee80211_scan_state_decision(struct ieee80211_local *local,
6682fb3f028SHelmut Schaa 					  unsigned long *next_delay)
6692fb3f028SHelmut Schaa {
670142b9f50SHelmut Schaa 	bool associated = false;
671df13cce5SHelmut Schaa 	bool tx_empty = true;
672df13cce5SHelmut Schaa 	bool bad_latency;
673142b9f50SHelmut Schaa 	struct ieee80211_sub_if_data *sdata;
674df13cce5SHelmut Schaa 	struct ieee80211_channel *next_chan;
675cd2bb512SSam Leffler 	enum mac80211_scan_state next_scan_state;
6766ea0a69cSJohannes Berg 	struct cfg80211_scan_request *scan_req;
677142b9f50SHelmut Schaa 
678df13cce5SHelmut Schaa 	/*
679df13cce5SHelmut Schaa 	 * check if at least one STA interface is associated,
680df13cce5SHelmut Schaa 	 * check if at least one STA interface has pending tx frames
681df13cce5SHelmut Schaa 	 * and grab the lowest used beacon interval
682df13cce5SHelmut Schaa 	 */
683142b9f50SHelmut Schaa 	mutex_lock(&local->iflist_mtx);
684142b9f50SHelmut Schaa 	list_for_each_entry(sdata, &local->interfaces, list) {
6859607e6b6SJohannes Berg 		if (!ieee80211_sdata_running(sdata))
686142b9f50SHelmut Schaa 			continue;
687142b9f50SHelmut Schaa 
688142b9f50SHelmut Schaa 		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
689142b9f50SHelmut Schaa 			if (sdata->u.mgd.associated) {
690142b9f50SHelmut Schaa 				associated = true;
691df13cce5SHelmut Schaa 
692df13cce5SHelmut Schaa 				if (!qdisc_all_tx_empty(sdata->dev)) {
693df13cce5SHelmut Schaa 					tx_empty = false;
694142b9f50SHelmut Schaa 					break;
695142b9f50SHelmut Schaa 				}
696142b9f50SHelmut Schaa 			}
697142b9f50SHelmut Schaa 		}
698df13cce5SHelmut Schaa 	}
699142b9f50SHelmut Schaa 	mutex_unlock(&local->iflist_mtx);
700142b9f50SHelmut Schaa 
7016ea0a69cSJohannes Berg 	scan_req = rcu_dereference_protected(local->scan_req,
7026ea0a69cSJohannes Berg 					     lockdep_is_held(&local->mtx));
7036ea0a69cSJohannes Berg 
7046ea0a69cSJohannes Berg 	next_chan = scan_req->channels[local->scan_channel_idx];
705b23b025fSBen Greear 
706142b9f50SHelmut Schaa 	/*
707142b9f50SHelmut Schaa 	 * we're currently scanning a different channel, let's
708df13cce5SHelmut Schaa 	 * see if we can scan another channel without interfering
709df13cce5SHelmut Schaa 	 * with the current traffic situation.
710df13cce5SHelmut Schaa 	 *
7113f892b61SStanislaw Gruszka 	 * Keep good latency, do not stay off-channel more than 125 ms.
712142b9f50SHelmut Schaa 	 */
713df13cce5SHelmut Schaa 
714df13cce5SHelmut Schaa 	bad_latency = time_after(jiffies +
715df13cce5SHelmut Schaa 				 ieee80211_scan_get_channel_time(next_chan),
7163f892b61SStanislaw Gruszka 				 local->leave_oper_channel_time + HZ / 8);
717df13cce5SHelmut Schaa 
718cd2bb512SSam Leffler 	if (associated && !tx_empty) {
7196ea0a69cSJohannes Berg 		if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
720cd2bb512SSam Leffler 			next_scan_state = SCAN_ABORT;
721142b9f50SHelmut Schaa 		else
722cd2bb512SSam Leffler 			next_scan_state = SCAN_SUSPEND;
7233f892b61SStanislaw Gruszka 	} else if (associated && bad_latency) {
724cd2bb512SSam Leffler 		next_scan_state = SCAN_SUSPEND;
725cd2bb512SSam Leffler 	} else {
726cd2bb512SSam Leffler 		next_scan_state = SCAN_SET_CHANNEL;
727cd2bb512SSam Leffler 	}
728cd2bb512SSam Leffler 
729cd2bb512SSam Leffler 	local->next_scan_state = next_scan_state;
730142b9f50SHelmut Schaa 
731142b9f50SHelmut Schaa 	*next_delay = 0;
7322fb3f028SHelmut Schaa }
7332fb3f028SHelmut Schaa 
7342fb3f028SHelmut Schaa static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
7357d3be3ccSHelmut Schaa 					     unsigned long *next_delay)
7367d3be3ccSHelmut Schaa {
7377d3be3ccSHelmut Schaa 	int skip;
7387d3be3ccSHelmut Schaa 	struct ieee80211_channel *chan;
7397ca15a0aSSimon Wunderlich 	enum nl80211_bss_scan_width oper_scan_width;
7406ea0a69cSJohannes Berg 	struct cfg80211_scan_request *scan_req;
7416ea0a69cSJohannes Berg 
7426ea0a69cSJohannes Berg 	scan_req = rcu_dereference_protected(local->scan_req,
7436ea0a69cSJohannes Berg 					     lockdep_is_held(&local->mtx));
7447d3be3ccSHelmut Schaa 
7457d3be3ccSHelmut Schaa 	skip = 0;
7466ea0a69cSJohannes Berg 	chan = scan_req->channels[local->scan_channel_idx];
7477d3be3ccSHelmut Schaa 
7487ca15a0aSSimon Wunderlich 	local->scan_chandef.chan = chan;
7497ca15a0aSSimon Wunderlich 	local->scan_chandef.center_freq1 = chan->center_freq;
7507ca15a0aSSimon Wunderlich 	local->scan_chandef.center_freq2 = 0;
7516ea0a69cSJohannes Berg 	switch (scan_req->scan_width) {
7527ca15a0aSSimon Wunderlich 	case NL80211_BSS_CHAN_WIDTH_5:
7537ca15a0aSSimon Wunderlich 		local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
7547ca15a0aSSimon Wunderlich 		break;
7557ca15a0aSSimon Wunderlich 	case NL80211_BSS_CHAN_WIDTH_10:
7567ca15a0aSSimon Wunderlich 		local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
7577ca15a0aSSimon Wunderlich 		break;
7587ca15a0aSSimon Wunderlich 	case NL80211_BSS_CHAN_WIDTH_20:
7597ca15a0aSSimon Wunderlich 		/* If scanning on oper channel, use whatever channel-type
7607ca15a0aSSimon Wunderlich 		 * is currently in use.
7617ca15a0aSSimon Wunderlich 		 */
7627ca15a0aSSimon Wunderlich 		oper_scan_width = cfg80211_chandef_to_scan_width(
7637ca15a0aSSimon Wunderlich 					&local->_oper_chandef);
7647ca15a0aSSimon Wunderlich 		if (chan == local->_oper_chandef.chan &&
7656ea0a69cSJohannes Berg 		    oper_scan_width == scan_req->scan_width)
7667ca15a0aSSimon Wunderlich 			local->scan_chandef = local->_oper_chandef;
7677ca15a0aSSimon Wunderlich 		else
7687ca15a0aSSimon Wunderlich 			local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
7697ca15a0aSSimon Wunderlich 		break;
7707ca15a0aSSimon Wunderlich 	}
771b23b025fSBen Greear 
772584991dcSJohannes Berg 	if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
7737d3be3ccSHelmut Schaa 		skip = 1;
7747d3be3ccSHelmut Schaa 
7757d3be3ccSHelmut Schaa 	/* advance state machine to next channel/band */
7767d3be3ccSHelmut Schaa 	local->scan_channel_idx++;
7777d3be3ccSHelmut Schaa 
7780ee9c13cSHelmut Schaa 	if (skip) {
7790ee9c13cSHelmut Schaa 		/* if we skip this channel return to the decision state */
7800ee9c13cSHelmut Schaa 		local->next_scan_state = SCAN_DECISION;
7812fb3f028SHelmut Schaa 		return;
7820ee9c13cSHelmut Schaa 	}
7837d3be3ccSHelmut Schaa 
7847d3be3ccSHelmut Schaa 	/*
7857d3be3ccSHelmut Schaa 	 * Probe delay is used to update the NAV, cf. 11.1.3.2.2
7867d3be3ccSHelmut Schaa 	 * (which unfortunately doesn't say _why_ step a) is done,
7877d3be3ccSHelmut Schaa 	 * but it waits for the probe delay or until a frame is
7887d3be3ccSHelmut Schaa 	 * received - and the received frame would update the NAV).
7897d3be3ccSHelmut Schaa 	 * For now, we do not support waiting until a frame is
7907d3be3ccSHelmut Schaa 	 * received.
7917d3be3ccSHelmut Schaa 	 *
7927d3be3ccSHelmut Schaa 	 * In any case, it is not necessary for a passive scan.
7937d3be3ccSHelmut Schaa 	 */
7944e39ccacSAntonio Quartulli 	if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) ||
7954e39ccacSAntonio Quartulli 	    !scan_req->n_ssids) {
7967d3be3ccSHelmut Schaa 		*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
797977923b0SHelmut Schaa 		local->next_scan_state = SCAN_DECISION;
7982fb3f028SHelmut Schaa 		return;
7997d3be3ccSHelmut Schaa 	}
8007d3be3ccSHelmut Schaa 
8012fb3f028SHelmut Schaa 	/* active scan, send probes */
8027d3be3ccSHelmut Schaa 	*next_delay = IEEE80211_PROBE_DELAY;
803977923b0SHelmut Schaa 	local->next_scan_state = SCAN_SEND_PROBE;
8047d3be3ccSHelmut Schaa }
8057d3be3ccSHelmut Schaa 
80607ef03eeSJohannes Berg static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
80707ef03eeSJohannes Berg 					 unsigned long *next_delay)
80807ef03eeSJohannes Berg {
80907ef03eeSJohannes Berg 	/* switch back to the operating channel */
8107ca15a0aSSimon Wunderlich 	local->scan_chandef.chan = NULL;
81107ef03eeSJohannes Berg 	ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
81207ef03eeSJohannes Berg 
813aacde9eeSStanislaw Gruszka 	/* disable PS */
814aacde9eeSStanislaw Gruszka 	ieee80211_offchannel_return(local);
81507ef03eeSJohannes Berg 
81607ef03eeSJohannes Berg 	*next_delay = HZ / 5;
81707ef03eeSJohannes Berg 	/* afterwards, resume scan & go to next channel */
81807ef03eeSJohannes Berg 	local->next_scan_state = SCAN_RESUME;
81907ef03eeSJohannes Berg }
82007ef03eeSJohannes Berg 
82107ef03eeSJohannes Berg static void ieee80211_scan_state_resume(struct ieee80211_local *local,
82207ef03eeSJohannes Berg 					unsigned long *next_delay)
82307ef03eeSJohannes Berg {
824aacde9eeSStanislaw Gruszka 	ieee80211_offchannel_stop_vifs(local);
82507ef03eeSJohannes Berg 
82607ef03eeSJohannes Berg 	if (local->ops->flush) {
8273b24f4c6SEmmanuel Grumbach 		ieee80211_flush_queues(local, NULL, false);
82807ef03eeSJohannes Berg 		*next_delay = 0;
82907ef03eeSJohannes Berg 	} else
83007ef03eeSJohannes Berg 		*next_delay = HZ / 10;
83107ef03eeSJohannes Berg 
83207ef03eeSJohannes Berg 	/* remember when we left the operating channel */
83307ef03eeSJohannes Berg 	local->leave_oper_channel_time = jiffies;
83407ef03eeSJohannes Berg 
83507ef03eeSJohannes Berg 	/* advance to the next channel to be scanned */
836de2ee84dSMohammed Shafi Shajakhan 	local->next_scan_state = SCAN_SET_CHANNEL;
83707ef03eeSJohannes Berg }
83807ef03eeSJohannes Berg 
839c2b13452SJohannes Berg void ieee80211_scan_work(struct work_struct *work)
8400a51b27eSJohannes Berg {
8410a51b27eSJohannes Berg 	struct ieee80211_local *local =
8420a51b27eSJohannes Berg 		container_of(work, struct ieee80211_local, scan_work.work);
843d07bfd8bSJohannes Berg 	struct ieee80211_sub_if_data *sdata;
8446ea0a69cSJohannes Berg 	struct cfg80211_scan_request *scan_req;
8450a51b27eSJohannes Berg 	unsigned long next_delay = 0;
8468bd2a248SEliad Peller 	bool aborted;
8478789d459SJohannes Berg 
848259b62e3SStanislaw Gruszka 	mutex_lock(&local->mtx);
849259b62e3SStanislaw Gruszka 
850332ff7feSLuciano Coelho 	if (!ieee80211_can_run_worker(local)) {
851332ff7feSLuciano Coelho 		aborted = true;
852332ff7feSLuciano Coelho 		goto out_complete;
853332ff7feSLuciano Coelho 	}
854332ff7feSLuciano Coelho 
855e2fd5dbcSJohannes Berg 	sdata = rcu_dereference_protected(local->scan_sdata,
856e2fd5dbcSJohannes Berg 					  lockdep_is_held(&local->mtx));
8576ea0a69cSJohannes Berg 	scan_req = rcu_dereference_protected(local->scan_req,
8586ea0a69cSJohannes Berg 					     lockdep_is_held(&local->mtx));
859d07bfd8bSJohannes Berg 
8608a690674SBen Greear 	/* When scanning on-channel, the first-callback means completed. */
8618a690674SBen Greear 	if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
8628a690674SBen Greear 		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
8638a690674SBen Greear 		goto out_complete;
8648a690674SBen Greear 	}
8658a690674SBen Greear 
866259b62e3SStanislaw Gruszka 	if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
8678789d459SJohannes Berg 		aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
868259b62e3SStanislaw Gruszka 		goto out_complete;
8698789d459SJohannes Berg 	}
8708789d459SJohannes Berg 
8716ea0a69cSJohannes Berg 	if (!sdata || !scan_req)
872259b62e3SStanislaw Gruszka 		goto out;
873f3b85252SJohannes Berg 
874ff5db439SEliad Peller 	if (!local->scanning) {
875f3b85252SJohannes Berg 		int rc;
876f3b85252SJohannes Berg 
8776ea0a69cSJohannes Berg 		RCU_INIT_POINTER(local->scan_req, NULL);
8780c2bef46SMonam Agarwal 		RCU_INIT_POINTER(local->scan_sdata, NULL);
879f3b85252SJohannes Berg 
8806ea0a69cSJohannes Berg 		rc = __ieee80211_start_scan(sdata, scan_req);
881259b62e3SStanislaw Gruszka 		if (rc) {
8823aed49efSStanislaw Gruszka 			/* need to complete scan in cfg80211 */
8836ea0a69cSJohannes Berg 			rcu_assign_pointer(local->scan_req, scan_req);
884259b62e3SStanislaw Gruszka 			aborted = true;
885259b62e3SStanislaw Gruszka 			goto out_complete;
886259b62e3SStanislaw Gruszka 		} else
887259b62e3SStanislaw Gruszka 			goto out;
888f3b85252SJohannes Berg 	}
889f3b85252SJohannes Berg 
8905bc75728SJohannes Berg 	/*
891f502d09bSHelmut Schaa 	 * as long as no delay is required advance immediately
892f502d09bSHelmut Schaa 	 * without scheduling a new work
893f502d09bSHelmut Schaa 	 */
894f502d09bSHelmut Schaa 	do {
895c29acf20SRajkumar Manoharan 		if (!ieee80211_sdata_running(sdata)) {
896c29acf20SRajkumar Manoharan 			aborted = true;
897c29acf20SRajkumar Manoharan 			goto out_complete;
898c29acf20SRajkumar Manoharan 		}
899c29acf20SRajkumar Manoharan 
900977923b0SHelmut Schaa 		switch (local->next_scan_state) {
9012fb3f028SHelmut Schaa 		case SCAN_DECISION:
902e229f844SStanislaw Gruszka 			/* if no more bands/channels left, complete scan */
9036ea0a69cSJohannes Berg 			if (local->scan_channel_idx >= scan_req->n_channels) {
904e229f844SStanislaw Gruszka 				aborted = false;
905e229f844SStanislaw Gruszka 				goto out_complete;
906e229f844SStanislaw Gruszka 			}
907e229f844SStanislaw Gruszka 			ieee80211_scan_state_decision(local, &next_delay);
9080a51b27eSJohannes Berg 			break;
9092fb3f028SHelmut Schaa 		case SCAN_SET_CHANNEL:
9102fb3f028SHelmut Schaa 			ieee80211_scan_state_set_channel(local, &next_delay);
9112fb3f028SHelmut Schaa 			break;
9120a51b27eSJohannes Berg 		case SCAN_SEND_PROBE:
9137d3be3ccSHelmut Schaa 			ieee80211_scan_state_send_probe(local, &next_delay);
9140a51b27eSJohannes Berg 			break;
91507ef03eeSJohannes Berg 		case SCAN_SUSPEND:
91607ef03eeSJohannes Berg 			ieee80211_scan_state_suspend(local, &next_delay);
917142b9f50SHelmut Schaa 			break;
91807ef03eeSJohannes Berg 		case SCAN_RESUME:
91907ef03eeSJohannes Berg 			ieee80211_scan_state_resume(local, &next_delay);
920142b9f50SHelmut Schaa 			break;
921cd2bb512SSam Leffler 		case SCAN_ABORT:
922cd2bb512SSam Leffler 			aborted = true;
923cd2bb512SSam Leffler 			goto out_complete;
9240a51b27eSJohannes Berg 		}
925f502d09bSHelmut Schaa 	} while (next_delay == 0);
9260a51b27eSJohannes Berg 
92742935ecaSLuis R. Rodriguez 	ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
928d07bfd8bSJohannes Berg 	goto out;
929259b62e3SStanislaw Gruszka 
930259b62e3SStanislaw Gruszka out_complete:
9318bd2a248SEliad Peller 	__ieee80211_scan_completed(&local->hw, aborted);
932259b62e3SStanislaw Gruszka out:
933259b62e3SStanislaw Gruszka 	mutex_unlock(&local->mtx);
9340a51b27eSJohannes Berg }
9350a51b27eSJohannes Berg 
936c2b13452SJohannes Berg int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
9372a519311SJohannes Berg 			   struct cfg80211_scan_request *req)
9380a51b27eSJohannes Berg {
939f3b85252SJohannes Berg 	int res;
9400a51b27eSJohannes Berg 
941a1699b75SJohannes Berg 	mutex_lock(&sdata->local->mtx);
942f3b85252SJohannes Berg 	res = __ieee80211_start_scan(sdata, req);
943a1699b75SJohannes Berg 	mutex_unlock(&sdata->local->mtx);
9442a519311SJohannes Berg 
945f3b85252SJohannes Berg 	return res;
9460a51b27eSJohannes Berg }
9470a51b27eSJohannes Berg 
94834bcf715SStanislaw Gruszka int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
949be4a4b6aSJohannes Berg 				const u8 *ssid, u8 ssid_len,
95076bed0f4SJanusz.Dziedzic@tieto.com 				struct ieee80211_channel **channels,
95176bed0f4SJanusz.Dziedzic@tieto.com 				unsigned int n_channels,
9527ca15a0aSSimon Wunderlich 				enum nl80211_bss_scan_width scan_width)
953f3b85252SJohannes Berg {
954f3b85252SJohannes Berg 	struct ieee80211_local *local = sdata->local;
95576bed0f4SJanusz.Dziedzic@tieto.com 	int ret = -EBUSY, i, n_ch = 0;
956*57fbcce3SJohannes Berg 	enum nl80211_band band;
9579116dd01SJohannes Berg 
958a1699b75SJohannes Berg 	mutex_lock(&local->mtx);
959f3b85252SJohannes Berg 
960f3b85252SJohannes Berg 	/* busy scanning */
961f3b85252SJohannes Berg 	if (local->scan_req)
962f3b85252SJohannes Berg 		goto unlock;
963f3b85252SJohannes Berg 
964be4a4b6aSJohannes Berg 	/* fill internal scan request */
96576bed0f4SJanusz.Dziedzic@tieto.com 	if (!channels) {
96676bed0f4SJanusz.Dziedzic@tieto.com 		int max_n;
967be4a4b6aSJohannes Berg 
968*57fbcce3SJohannes Berg 		for (band = 0; band < NUM_NL80211_BANDS; band++) {
969be4a4b6aSJohannes Berg 			if (!local->hw.wiphy->bands[band])
970be4a4b6aSJohannes Berg 				continue;
97134bcf715SStanislaw Gruszka 
97234bcf715SStanislaw Gruszka 			max_n = local->hw.wiphy->bands[band]->n_channels;
97334bcf715SStanislaw Gruszka 			for (i = 0; i < max_n; i++) {
97434bcf715SStanislaw Gruszka 				struct ieee80211_channel *tmp_ch =
975be4a4b6aSJohannes Berg 				    &local->hw.wiphy->bands[band]->channels[i];
97634bcf715SStanislaw Gruszka 
9778fe02e16SLuis R. Rodriguez 				if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
97834bcf715SStanislaw Gruszka 						     IEEE80211_CHAN_DISABLED))
97934bcf715SStanislaw Gruszka 					continue;
98034bcf715SStanislaw Gruszka 
98134bcf715SStanislaw Gruszka 				local->int_scan_req->channels[n_ch] = tmp_ch;
98234bcf715SStanislaw Gruszka 				n_ch++;
983be4a4b6aSJohannes Berg 			}
984be4a4b6aSJohannes Berg 		}
985be4a4b6aSJohannes Berg 
98634bcf715SStanislaw Gruszka 		if (WARN_ON_ONCE(n_ch == 0))
98734bcf715SStanislaw Gruszka 			goto unlock;
98834bcf715SStanislaw Gruszka 
98934bcf715SStanislaw Gruszka 		local->int_scan_req->n_channels = n_ch;
990be4a4b6aSJohannes Berg 	} else {
99176bed0f4SJanusz.Dziedzic@tieto.com 		for (i = 0; i < n_channels; i++) {
99276bed0f4SJanusz.Dziedzic@tieto.com 			if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
99376bed0f4SJanusz.Dziedzic@tieto.com 						  IEEE80211_CHAN_DISABLED))
99476bed0f4SJanusz.Dziedzic@tieto.com 				continue;
99576bed0f4SJanusz.Dziedzic@tieto.com 
99676bed0f4SJanusz.Dziedzic@tieto.com 			local->int_scan_req->channels[n_ch] = channels[i];
99776bed0f4SJanusz.Dziedzic@tieto.com 			n_ch++;
99876bed0f4SJanusz.Dziedzic@tieto.com 		}
99976bed0f4SJanusz.Dziedzic@tieto.com 
100076bed0f4SJanusz.Dziedzic@tieto.com 		if (WARN_ON_ONCE(n_ch == 0))
100134bcf715SStanislaw Gruszka 			goto unlock;
100234bcf715SStanislaw Gruszka 
100376bed0f4SJanusz.Dziedzic@tieto.com 		local->int_scan_req->n_channels = n_ch;
1004be4a4b6aSJohannes Berg 	}
1005be4a4b6aSJohannes Berg 
1006be4a4b6aSJohannes Berg 	local->int_scan_req->ssids = &local->scan_ssid;
1007be4a4b6aSJohannes Berg 	local->int_scan_req->n_ssids = 1;
10087ca15a0aSSimon Wunderlich 	local->int_scan_req->scan_width = scan_width;
10095ba63533SJohannes Berg 	memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
10105ba63533SJohannes Berg 	local->int_scan_req->ssids[0].ssid_len = ssid_len;
1011f3b85252SJohannes Berg 
10125ba63533SJohannes Berg 	ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
1013f3b85252SJohannes Berg  unlock:
1014a1699b75SJohannes Berg 	mutex_unlock(&local->mtx);
1015f3b85252SJohannes Berg 	return ret;
10160a51b27eSJohannes Berg }
10175bb644a0SJohannes Berg 
10184136c422SStanislaw Gruszka /*
10194136c422SStanislaw Gruszka  * Only call this function when a scan can't be queued -- under RTNL.
10204136c422SStanislaw Gruszka  */
10215bb644a0SJohannes Berg void ieee80211_scan_cancel(struct ieee80211_local *local)
10225bb644a0SJohannes Berg {
10235bb644a0SJohannes Berg 	/*
1024b856439bSEliad Peller 	 * We are canceling software scan, or deferred scan that was not
10254136c422SStanislaw Gruszka 	 * yet really started (see __ieee80211_start_scan ).
10264136c422SStanislaw Gruszka 	 *
10274136c422SStanislaw Gruszka 	 * Regarding hardware scan:
10284136c422SStanislaw Gruszka 	 * - we can not call  __ieee80211_scan_completed() as when
10294136c422SStanislaw Gruszka 	 *   SCAN_HW_SCANNING bit is set this function change
10304136c422SStanislaw Gruszka 	 *   local->hw_scan_req to operate on 5G band, what race with
10314136c422SStanislaw Gruszka 	 *   driver which can use local->hw_scan_req
10324136c422SStanislaw Gruszka 	 *
10334136c422SStanislaw Gruszka 	 * - we can not cancel scan_work since driver can schedule it
10344136c422SStanislaw Gruszka 	 *   by ieee80211_scan_completed(..., true) to finish scan
10354136c422SStanislaw Gruszka 	 *
1036b856439bSEliad Peller 	 * Hence we only call the cancel_hw_scan() callback, but the low-level
1037b856439bSEliad Peller 	 * driver is still responsible for calling ieee80211_scan_completed()
1038b856439bSEliad Peller 	 * after the scan was completed/aborted.
10395bb644a0SJohannes Berg 	 */
10404136c422SStanislaw Gruszka 
1041a1699b75SJohannes Berg 	mutex_lock(&local->mtx);
1042b856439bSEliad Peller 	if (!local->scan_req)
1043b856439bSEliad Peller 		goto out;
1044b856439bSEliad Peller 
1045a754055aSEmmanuel Grumbach 	/*
1046a754055aSEmmanuel Grumbach 	 * We have a scan running and the driver already reported completion,
1047a754055aSEmmanuel Grumbach 	 * but the worker hasn't run yet or is stuck on the mutex - mark it as
1048a754055aSEmmanuel Grumbach 	 * cancelled.
1049a754055aSEmmanuel Grumbach 	 */
1050a754055aSEmmanuel Grumbach 	if (test_bit(SCAN_HW_SCANNING, &local->scanning) &&
1051a754055aSEmmanuel Grumbach 	    test_bit(SCAN_COMPLETED, &local->scanning)) {
1052a754055aSEmmanuel Grumbach 		set_bit(SCAN_HW_CANCELLED, &local->scanning);
1053a754055aSEmmanuel Grumbach 		goto out;
1054a754055aSEmmanuel Grumbach 	}
1055a754055aSEmmanuel Grumbach 
1056b856439bSEliad Peller 	if (test_bit(SCAN_HW_SCANNING, &local->scanning)) {
1057a754055aSEmmanuel Grumbach 		/*
1058a754055aSEmmanuel Grumbach 		 * Make sure that __ieee80211_scan_completed doesn't trigger a
1059a754055aSEmmanuel Grumbach 		 * scan on another band.
1060a754055aSEmmanuel Grumbach 		 */
1061a754055aSEmmanuel Grumbach 		set_bit(SCAN_HW_CANCELLED, &local->scanning);
1062b856439bSEliad Peller 		if (local->ops->cancel_hw_scan)
1063e2fd5dbcSJohannes Berg 			drv_cancel_hw_scan(local,
1064e2fd5dbcSJohannes Berg 				rcu_dereference_protected(local->scan_sdata,
1065e2fd5dbcSJohannes Berg 						lockdep_is_held(&local->mtx)));
1066b856439bSEliad Peller 		goto out;
1067b856439bSEliad Peller 	}
1068b856439bSEliad Peller 
1069d07bfd8bSJohannes Berg 	/*
1070d07bfd8bSJohannes Berg 	 * If the work is currently running, it must be blocked on
1071d07bfd8bSJohannes Berg 	 * the mutex, but we'll set scan_sdata = NULL and it'll
1072d07bfd8bSJohannes Berg 	 * simply exit once it acquires the mutex.
1073d07bfd8bSJohannes Berg 	 */
1074d07bfd8bSJohannes Berg 	cancel_delayed_work(&local->scan_work);
1075d07bfd8bSJohannes Berg 	/* and clean up */
10768bd2a248SEliad Peller 	__ieee80211_scan_completed(&local->hw, true);
1077b856439bSEliad Peller out:
1078d07bfd8bSJohannes Berg 	mutex_unlock(&local->mtx);
10795bb644a0SJohannes Berg }
108079f460caSLuciano Coelho 
1081d43c6b6eSDavid Spinadel int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
108279f460caSLuciano Coelho 					struct cfg80211_sched_scan_request *req)
108379f460caSLuciano Coelho {
108479f460caSLuciano Coelho 	struct ieee80211_local *local = sdata->local;
1085633e2713SDavid Spinadel 	struct ieee80211_scan_ies sched_scan_ies = {};
10862103dec1SSimon Wunderlich 	struct cfg80211_chan_def chandef;
1087633e2713SDavid Spinadel 	int ret, i, iebufsz, num_bands = 0;
1088*57fbcce3SJohannes Berg 	u32 rate_masks[NUM_NL80211_BANDS] = {};
1089633e2713SDavid Spinadel 	u8 bands_used = 0;
1090633e2713SDavid Spinadel 	u8 *ie;
1091633e2713SDavid Spinadel 	size_t len;
1092c604b9f2SJohannes Berg 
1093e4dcbb37SDavid Spinadel 	iebufsz = local->scan_ies_len + req->ie_len;
109479f460caSLuciano Coelho 
1095d43c6b6eSDavid Spinadel 	lockdep_assert_held(&local->mtx);
109679f460caSLuciano Coelho 
1097d43c6b6eSDavid Spinadel 	if (!local->ops->sched_scan_start)
1098d43c6b6eSDavid Spinadel 		return -ENOTSUPP;
109979f460caSLuciano Coelho 
1100*57fbcce3SJohannes Berg 	for (i = 0; i < NUM_NL80211_BANDS; i++) {
1101633e2713SDavid Spinadel 		if (local->hw.wiphy->bands[i]) {
1102633e2713SDavid Spinadel 			bands_used |= BIT(i);
1103633e2713SDavid Spinadel 			rate_masks[i] = (u32) -1;
1104633e2713SDavid Spinadel 			num_bands++;
1105633e2713SDavid Spinadel 		}
1106633e2713SDavid Spinadel 	}
1107c56ef672SDavid Spinadel 
1108633e2713SDavid Spinadel 	ie = kzalloc(num_bands * iebufsz, GFP_KERNEL);
1109633e2713SDavid Spinadel 	if (!ie) {
111079f460caSLuciano Coelho 		ret = -ENOMEM;
1111633e2713SDavid Spinadel 		goto out;
111279f460caSLuciano Coelho 	}
111379f460caSLuciano Coelho 
11142103dec1SSimon Wunderlich 	ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
11152103dec1SSimon Wunderlich 
1116633e2713SDavid Spinadel 	len = ieee80211_build_preq_ies(local, ie, num_bands * iebufsz,
1117633e2713SDavid Spinadel 				       &sched_scan_ies, req->ie,
1118633e2713SDavid Spinadel 				       req->ie_len, bands_used,
1119c56ef672SDavid Spinadel 				       rate_masks, &chandef);
112079f460caSLuciano Coelho 
112130dd3edfSJohannes Berg 	ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
1122d43c6b6eSDavid Spinadel 	if (ret == 0) {
11235260a5b2SJohannes Berg 		rcu_assign_pointer(local->sched_scan_sdata, sdata);
11246ea0a69cSJohannes Berg 		rcu_assign_pointer(local->sched_scan_req, req);
1125d43c6b6eSDavid Spinadel 	}
112679f460caSLuciano Coelho 
1127633e2713SDavid Spinadel 	kfree(ie);
1128d43c6b6eSDavid Spinadel 
1129633e2713SDavid Spinadel out:
1130d43c6b6eSDavid Spinadel 	if (ret) {
1131d43c6b6eSDavid Spinadel 		/* Clean in case of failure after HW restart or upon resume. */
11320c2bef46SMonam Agarwal 		RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
11336ea0a69cSJohannes Berg 		RCU_INIT_POINTER(local->sched_scan_req, NULL);
1134d43c6b6eSDavid Spinadel 	}
1135d43c6b6eSDavid Spinadel 
1136d43c6b6eSDavid Spinadel 	return ret;
1137d43c6b6eSDavid Spinadel }
1138d43c6b6eSDavid Spinadel 
1139d43c6b6eSDavid Spinadel int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
1140d43c6b6eSDavid Spinadel 				       struct cfg80211_sched_scan_request *req)
1141d43c6b6eSDavid Spinadel {
1142d43c6b6eSDavid Spinadel 	struct ieee80211_local *local = sdata->local;
1143d43c6b6eSDavid Spinadel 	int ret;
1144d43c6b6eSDavid Spinadel 
1145d43c6b6eSDavid Spinadel 	mutex_lock(&local->mtx);
1146d43c6b6eSDavid Spinadel 
1147d43c6b6eSDavid Spinadel 	if (rcu_access_pointer(local->sched_scan_sdata)) {
1148d43c6b6eSDavid Spinadel 		mutex_unlock(&local->mtx);
1149d43c6b6eSDavid Spinadel 		return -EBUSY;
1150d43c6b6eSDavid Spinadel 	}
1151d43c6b6eSDavid Spinadel 
1152d43c6b6eSDavid Spinadel 	ret = __ieee80211_request_sched_scan_start(sdata, req);
1153d43c6b6eSDavid Spinadel 
11545260a5b2SJohannes Berg 	mutex_unlock(&local->mtx);
115579f460caSLuciano Coelho 	return ret;
115679f460caSLuciano Coelho }
115779f460caSLuciano Coelho 
11580d440ea2SEliad Peller int ieee80211_request_sched_scan_stop(struct ieee80211_local *local)
115979f460caSLuciano Coelho {
11600d440ea2SEliad Peller 	struct ieee80211_sub_if_data *sched_scan_sdata;
11610d440ea2SEliad Peller 	int ret = -ENOENT;
116279f460caSLuciano Coelho 
11635260a5b2SJohannes Berg 	mutex_lock(&local->mtx);
116479f460caSLuciano Coelho 
116579f460caSLuciano Coelho 	if (!local->ops->sched_scan_stop) {
116679f460caSLuciano Coelho 		ret = -ENOTSUPP;
116779f460caSLuciano Coelho 		goto out;
116879f460caSLuciano Coelho 	}
116979f460caSLuciano Coelho 
1170d43c6b6eSDavid Spinadel 	/* We don't want to restart sched scan anymore. */
11716ea0a69cSJohannes Berg 	RCU_INIT_POINTER(local->sched_scan_req, NULL);
1172d43c6b6eSDavid Spinadel 
11730d440ea2SEliad Peller 	sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
11740d440ea2SEliad Peller 						lockdep_is_held(&local->mtx));
11750d440ea2SEliad Peller 	if (sched_scan_sdata) {
11760d440ea2SEliad Peller 		ret = drv_sched_scan_stop(local, sched_scan_sdata);
117771228a1eSAlexander Bondar 		if (!ret)
1178ad053a96SAndreea-Cristina Bernat 			RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
117971228a1eSAlexander Bondar 	}
118079f460caSLuciano Coelho out:
11815260a5b2SJohannes Berg 	mutex_unlock(&local->mtx);
118279f460caSLuciano Coelho 
118379f460caSLuciano Coelho 	return ret;
118479f460caSLuciano Coelho }
118579f460caSLuciano Coelho 
118679f460caSLuciano Coelho void ieee80211_sched_scan_results(struct ieee80211_hw *hw)
118779f460caSLuciano Coelho {
118879f460caSLuciano Coelho 	struct ieee80211_local *local = hw_to_local(hw);
118979f460caSLuciano Coelho 
119079f460caSLuciano Coelho 	trace_api_sched_scan_results(local);
119179f460caSLuciano Coelho 
119279f460caSLuciano Coelho 	cfg80211_sched_scan_results(hw->wiphy);
119379f460caSLuciano Coelho }
119479f460caSLuciano Coelho EXPORT_SYMBOL(ieee80211_sched_scan_results);
119579f460caSLuciano Coelho 
1196f6837ba8SJohannes Berg void ieee80211_sched_scan_end(struct ieee80211_local *local)
119785a9994aSLuciano Coelho {
119885a9994aSLuciano Coelho 	mutex_lock(&local->mtx);
119985a9994aSLuciano Coelho 
12005260a5b2SJohannes Berg 	if (!rcu_access_pointer(local->sched_scan_sdata)) {
120185a9994aSLuciano Coelho 		mutex_unlock(&local->mtx);
120285a9994aSLuciano Coelho 		return;
120385a9994aSLuciano Coelho 	}
120485a9994aSLuciano Coelho 
12050c2bef46SMonam Agarwal 	RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
120685a9994aSLuciano Coelho 
1207d43c6b6eSDavid Spinadel 	/* If sched scan was aborted by the driver. */
12086ea0a69cSJohannes Berg 	RCU_INIT_POINTER(local->sched_scan_req, NULL);
1209d43c6b6eSDavid Spinadel 
121085a9994aSLuciano Coelho 	mutex_unlock(&local->mtx);
121185a9994aSLuciano Coelho 
121285a9994aSLuciano Coelho 	cfg80211_sched_scan_stopped(local->hw.wiphy);
121385a9994aSLuciano Coelho }
121485a9994aSLuciano Coelho 
1215f6837ba8SJohannes Berg void ieee80211_sched_scan_stopped_work(struct work_struct *work)
1216f6837ba8SJohannes Berg {
1217f6837ba8SJohannes Berg 	struct ieee80211_local *local =
1218f6837ba8SJohannes Berg 		container_of(work, struct ieee80211_local,
1219f6837ba8SJohannes Berg 			     sched_scan_stopped_work);
1220f6837ba8SJohannes Berg 
1221f6837ba8SJohannes Berg 	ieee80211_sched_scan_end(local);
1222f6837ba8SJohannes Berg }
1223f6837ba8SJohannes Berg 
122479f460caSLuciano Coelho void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw)
122579f460caSLuciano Coelho {
122679f460caSLuciano Coelho 	struct ieee80211_local *local = hw_to_local(hw);
122779f460caSLuciano Coelho 
122879f460caSLuciano Coelho 	trace_api_sched_scan_stopped(local);
122979f460caSLuciano Coelho 
12302bc533bdSEliad Peller 	/*
12312bc533bdSEliad Peller 	 * this shouldn't really happen, so for simplicity
12322bc533bdSEliad Peller 	 * simply ignore it, and let mac80211 reconfigure
12332bc533bdSEliad Peller 	 * the sched scan later on.
12342bc533bdSEliad Peller 	 */
12352bc533bdSEliad Peller 	if (local->in_reconfig)
12362bc533bdSEliad Peller 		return;
12372bc533bdSEliad Peller 
123818db594aSJohannes Berg 	schedule_work(&local->sched_scan_stopped_work);
123979f460caSLuciano Coelho }
124079f460caSLuciano Coelho EXPORT_SYMBOL(ieee80211_sched_scan_stopped);
1241