xref: /linux/drivers/net/wireless/silabs/wfx/scan.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
14a5fb1bbSJérôme Pouiller // SPDX-License-Identifier: GPL-2.0-only
24a5fb1bbSJérôme Pouiller /*
34a5fb1bbSJérôme Pouiller  * Scan related functions.
44a5fb1bbSJérôme Pouiller  *
54a5fb1bbSJérôme Pouiller  * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
64a5fb1bbSJérôme Pouiller  * Copyright (c) 2010, ST-Ericsson
74a5fb1bbSJérôme Pouiller  */
84a5fb1bbSJérôme Pouiller #include <net/mac80211.h>
94a5fb1bbSJérôme Pouiller 
104a5fb1bbSJérôme Pouiller #include "scan.h"
114a5fb1bbSJérôme Pouiller #include "wfx.h"
124a5fb1bbSJérôme Pouiller #include "sta.h"
134a5fb1bbSJérôme Pouiller #include "hif_tx_mib.h"
144a5fb1bbSJérôme Pouiller 
wfx_ieee80211_scan_completed_compat(struct ieee80211_hw * hw,bool aborted)154a5fb1bbSJérôme Pouiller static void wfx_ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool aborted)
164a5fb1bbSJérôme Pouiller {
174a5fb1bbSJérôme Pouiller 	struct cfg80211_scan_info info = {
184a5fb1bbSJérôme Pouiller 		.aborted = aborted,
194a5fb1bbSJérôme Pouiller 	};
204a5fb1bbSJérôme Pouiller 
214a5fb1bbSJérôme Pouiller 	ieee80211_scan_completed(hw, &info);
224a5fb1bbSJérôme Pouiller }
234a5fb1bbSJérôme Pouiller 
update_probe_tmpl(struct wfx_vif * wvif,struct cfg80211_scan_request * req)244a5fb1bbSJérôme Pouiller static int update_probe_tmpl(struct wfx_vif *wvif, struct cfg80211_scan_request *req)
254a5fb1bbSJérôme Pouiller {
262c33360bSJaehee Park 	struct ieee80211_vif *vif = wvif_to_vif(wvif);
274a5fb1bbSJérôme Pouiller 	struct sk_buff *skb;
284a5fb1bbSJérôme Pouiller 
292c33360bSJaehee Park 	skb = ieee80211_probereq_get(wvif->wdev->hw, vif->addr, NULL, 0,
302c33360bSJaehee Park 				     req->ie_len);
314a5fb1bbSJérôme Pouiller 	if (!skb)
324a5fb1bbSJérôme Pouiller 		return -ENOMEM;
334a5fb1bbSJérôme Pouiller 
344a5fb1bbSJérôme Pouiller 	skb_put_data(skb, req->ie, req->ie_len);
354a5fb1bbSJérôme Pouiller 	wfx_hif_set_template_frame(wvif, skb, HIF_TMPLT_PRBREQ, 0);
364a5fb1bbSJérôme Pouiller 	dev_kfree_skb(skb);
374a5fb1bbSJérôme Pouiller 	return 0;
384a5fb1bbSJérôme Pouiller }
394a5fb1bbSJérôme Pouiller 
send_scan_req(struct wfx_vif * wvif,struct cfg80211_scan_request * req,int start_idx)404a5fb1bbSJérôme Pouiller static int send_scan_req(struct wfx_vif *wvif, struct cfg80211_scan_request *req, int start_idx)
414a5fb1bbSJérôme Pouiller {
422c33360bSJaehee Park 	struct ieee80211_vif *vif = wvif_to_vif(wvif);
434a5fb1bbSJérôme Pouiller 	struct ieee80211_channel *ch_start, *ch_cur;
442c33360bSJaehee Park 	int i, ret;
454a5fb1bbSJérôme Pouiller 
464a5fb1bbSJérôme Pouiller 	for (i = start_idx; i < req->n_channels; i++) {
474a5fb1bbSJérôme Pouiller 		ch_start = req->channels[start_idx];
484a5fb1bbSJérôme Pouiller 		ch_cur = req->channels[i];
494a5fb1bbSJérôme Pouiller 		WARN(ch_cur->band != NL80211_BAND_2GHZ, "band not supported");
504a5fb1bbSJérôme Pouiller 		if (ch_cur->max_power != ch_start->max_power)
514a5fb1bbSJérôme Pouiller 			break;
524a5fb1bbSJérôme Pouiller 		if ((ch_cur->flags ^ ch_start->flags) & IEEE80211_CHAN_NO_IR)
534a5fb1bbSJérôme Pouiller 			break;
544a5fb1bbSJérôme Pouiller 	}
554a5fb1bbSJérôme Pouiller 	wfx_tx_lock_flush(wvif->wdev);
564a5fb1bbSJérôme Pouiller 	wvif->scan_abort = false;
574a5fb1bbSJérôme Pouiller 	reinit_completion(&wvif->scan_complete);
584a5fb1bbSJérôme Pouiller 	ret = wfx_hif_scan(wvif, req, start_idx, i - start_idx);
594a5fb1bbSJérôme Pouiller 	if (ret) {
604a5fb1bbSJérôme Pouiller 		wfx_tx_unlock(wvif->wdev);
614a5fb1bbSJérôme Pouiller 		return -EIO;
624a5fb1bbSJérôme Pouiller 	}
634a5fb1bbSJérôme Pouiller 	ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
644a5fb1bbSJérôme Pouiller 	if (!ret) {
654a5fb1bbSJérôme Pouiller 		wfx_hif_stop_scan(wvif);
664a5fb1bbSJérôme Pouiller 		ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
674a5fb1bbSJérôme Pouiller 		dev_dbg(wvif->wdev->dev, "scan timeout (%d channels done)\n",
684a5fb1bbSJérôme Pouiller 			wvif->scan_nb_chan_done);
694a5fb1bbSJérôme Pouiller 	}
704a5fb1bbSJérôme Pouiller 	if (!ret) {
714a5fb1bbSJérôme Pouiller 		dev_err(wvif->wdev->dev, "scan didn't stop\n");
724a5fb1bbSJérôme Pouiller 		ret = -ETIMEDOUT;
734a5fb1bbSJérôme Pouiller 	} else if (wvif->scan_abort) {
744a5fb1bbSJérôme Pouiller 		dev_notice(wvif->wdev->dev, "scan abort\n");
754a5fb1bbSJérôme Pouiller 		ret = -ECONNABORTED;
764a5fb1bbSJérôme Pouiller 	} else if (wvif->scan_nb_chan_done > i - start_idx) {
774a5fb1bbSJérôme Pouiller 		ret = -EIO;
784a5fb1bbSJérôme Pouiller 	} else {
794a5fb1bbSJérôme Pouiller 		ret = wvif->scan_nb_chan_done;
804a5fb1bbSJérôme Pouiller 	}
812c33360bSJaehee Park 	if (req->channels[start_idx]->max_power != vif->bss_conf.txpower)
822c33360bSJaehee Park 		wfx_hif_set_output_power(wvif, vif->bss_conf.txpower);
834a5fb1bbSJérôme Pouiller 	wfx_tx_unlock(wvif->wdev);
844a5fb1bbSJérôme Pouiller 	return ret;
854a5fb1bbSJérôme Pouiller }
864a5fb1bbSJérôme Pouiller 
874a5fb1bbSJérôme Pouiller /* It is not really necessary to run scan request asynchronously. However,
884a5fb1bbSJérôme Pouiller  * there is a bug in "iw scan" when ieee80211_scan_completed() is called before
894a5fb1bbSJérôme Pouiller  * wfx_hw_scan() return
904a5fb1bbSJérôme Pouiller  */
wfx_hw_scan_work(struct work_struct * work)914a5fb1bbSJérôme Pouiller void wfx_hw_scan_work(struct work_struct *work)
924a5fb1bbSJérôme Pouiller {
934a5fb1bbSJérôme Pouiller 	struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
944a5fb1bbSJérôme Pouiller 	struct ieee80211_scan_request *hw_req = wvif->scan_req;
954a5fb1bbSJérôme Pouiller 	int chan_cur, ret, err;
964a5fb1bbSJérôme Pouiller 
974a5fb1bbSJérôme Pouiller 	mutex_lock(&wvif->wdev->conf_mutex);
9804106ec5SJérôme Pouiller 	mutex_lock(&wvif->wdev->scan_lock);
994a5fb1bbSJérôme Pouiller 	if (wvif->join_in_progress) {
1004a5fb1bbSJérôme Pouiller 		dev_info(wvif->wdev->dev, "abort in-progress REQ_JOIN");
1014a5fb1bbSJérôme Pouiller 		wfx_reset(wvif);
1024a5fb1bbSJérôme Pouiller 	}
1034a5fb1bbSJérôme Pouiller 	update_probe_tmpl(wvif, &hw_req->req);
1044a5fb1bbSJérôme Pouiller 	chan_cur = 0;
1054a5fb1bbSJérôme Pouiller 	err = 0;
1064a5fb1bbSJérôme Pouiller 	do {
1074a5fb1bbSJérôme Pouiller 		ret = send_scan_req(wvif, &hw_req->req, chan_cur);
1084a5fb1bbSJérôme Pouiller 		if (ret > 0) {
1094a5fb1bbSJérôme Pouiller 			chan_cur += ret;
1104a5fb1bbSJérôme Pouiller 			err = 0;
1114a5fb1bbSJérôme Pouiller 		}
1124a5fb1bbSJérôme Pouiller 		if (!ret)
1134a5fb1bbSJérôme Pouiller 			err++;
1144a5fb1bbSJérôme Pouiller 		if (err > 2) {
1154a5fb1bbSJérôme Pouiller 			dev_err(wvif->wdev->dev, "scan has not been able to start\n");
1164a5fb1bbSJérôme Pouiller 			ret = -ETIMEDOUT;
1174a5fb1bbSJérôme Pouiller 		}
1184a5fb1bbSJérôme Pouiller 	} while (ret >= 0 && chan_cur < hw_req->req.n_channels);
11904106ec5SJérôme Pouiller 	mutex_unlock(&wvif->wdev->scan_lock);
1204a5fb1bbSJérôme Pouiller 	mutex_unlock(&wvif->wdev->conf_mutex);
1214a5fb1bbSJérôme Pouiller 	wfx_ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
1224a5fb1bbSJérôme Pouiller }
1234a5fb1bbSJérôme Pouiller 
wfx_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * hw_req)1244a5fb1bbSJérôme Pouiller int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1254a5fb1bbSJérôme Pouiller 		struct ieee80211_scan_request *hw_req)
1264a5fb1bbSJérôme Pouiller {
1274a5fb1bbSJérôme Pouiller 	struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
1284a5fb1bbSJérôme Pouiller 
1294a5fb1bbSJérôme Pouiller 	WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
1304a5fb1bbSJérôme Pouiller 	wvif->scan_req = hw_req;
1314a5fb1bbSJérôme Pouiller 	schedule_work(&wvif->scan_work);
1324a5fb1bbSJérôme Pouiller 	return 0;
1334a5fb1bbSJérôme Pouiller }
1344a5fb1bbSJérôme Pouiller 
wfx_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)1354a5fb1bbSJérôme Pouiller void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
1364a5fb1bbSJérôme Pouiller {
1374a5fb1bbSJérôme Pouiller 	struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
1384a5fb1bbSJérôme Pouiller 
1394a5fb1bbSJérôme Pouiller 	wvif->scan_abort = true;
1404a5fb1bbSJérôme Pouiller 	wfx_hif_stop_scan(wvif);
1414a5fb1bbSJérôme Pouiller }
1424a5fb1bbSJérôme Pouiller 
wfx_scan_complete(struct wfx_vif * wvif,int nb_chan_done)1434a5fb1bbSJérôme Pouiller void wfx_scan_complete(struct wfx_vif *wvif, int nb_chan_done)
1444a5fb1bbSJérôme Pouiller {
1454a5fb1bbSJérôme Pouiller 	wvif->scan_nb_chan_done = nb_chan_done;
1464a5fb1bbSJérôme Pouiller 	complete(&wvif->scan_complete);
1474a5fb1bbSJérôme Pouiller }
148*fc627dadSJérôme Pouiller 
wfx_remain_on_channel_work(struct work_struct * work)149*fc627dadSJérôme Pouiller void wfx_remain_on_channel_work(struct work_struct *work)
150*fc627dadSJérôme Pouiller {
151*fc627dadSJérôme Pouiller 	struct wfx_vif *wvif = container_of(work, struct wfx_vif, remain_on_channel_work);
152*fc627dadSJérôme Pouiller 	struct ieee80211_channel *chan = wvif->remain_on_channel_chan;
153*fc627dadSJérôme Pouiller 	int duration = wvif->remain_on_channel_duration;
154*fc627dadSJérôme Pouiller 	int ret;
155*fc627dadSJérôme Pouiller 
156*fc627dadSJérôme Pouiller 	/* Hijack scan request to implement Remain-On-Channel */
157*fc627dadSJérôme Pouiller 	mutex_lock(&wvif->wdev->conf_mutex);
158*fc627dadSJérôme Pouiller 	mutex_lock(&wvif->wdev->scan_lock);
159*fc627dadSJérôme Pouiller 	if (wvif->join_in_progress) {
160*fc627dadSJérôme Pouiller 		dev_info(wvif->wdev->dev, "abort in-progress REQ_JOIN");
161*fc627dadSJérôme Pouiller 		wfx_reset(wvif);
162*fc627dadSJérôme Pouiller 	}
163*fc627dadSJérôme Pouiller 	wfx_tx_flush(wvif->wdev);
164*fc627dadSJérôme Pouiller 
165*fc627dadSJérôme Pouiller 	reinit_completion(&wvif->scan_complete);
166*fc627dadSJérôme Pouiller 	ret = wfx_hif_scan_uniq(wvif, chan, duration);
167*fc627dadSJérôme Pouiller 	if (ret)
168*fc627dadSJérôme Pouiller 		goto end;
169*fc627dadSJérôme Pouiller 	ieee80211_ready_on_channel(wvif->wdev->hw);
170*fc627dadSJérôme Pouiller 	ret = wait_for_completion_timeout(&wvif->scan_complete,
171*fc627dadSJérôme Pouiller 					  msecs_to_jiffies(duration * 120 / 100));
172*fc627dadSJérôme Pouiller 	if (!ret) {
173*fc627dadSJérôme Pouiller 		wfx_hif_stop_scan(wvif);
174*fc627dadSJérôme Pouiller 		ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
175*fc627dadSJérôme Pouiller 		dev_dbg(wvif->wdev->dev, "roc timeout\n");
176*fc627dadSJérôme Pouiller 	}
177*fc627dadSJérôme Pouiller 	if (!ret)
178*fc627dadSJérôme Pouiller 		dev_err(wvif->wdev->dev, "roc didn't stop\n");
179*fc627dadSJérôme Pouiller 	ieee80211_remain_on_channel_expired(wvif->wdev->hw);
180*fc627dadSJérôme Pouiller end:
181*fc627dadSJérôme Pouiller 	mutex_unlock(&wvif->wdev->scan_lock);
182*fc627dadSJérôme Pouiller 	mutex_unlock(&wvif->wdev->conf_mutex);
183*fc627dadSJérôme Pouiller 	wfx_bh_request_tx(wvif->wdev);
184*fc627dadSJérôme Pouiller }
185*fc627dadSJérôme Pouiller 
wfx_remain_on_channel(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_channel * chan,int duration,enum ieee80211_roc_type type)186*fc627dadSJérôme Pouiller int wfx_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
187*fc627dadSJérôme Pouiller 			  struct ieee80211_channel *chan, int duration,
188*fc627dadSJérôme Pouiller 			  enum ieee80211_roc_type type)
189*fc627dadSJérôme Pouiller {
190*fc627dadSJérôme Pouiller 	struct wfx_dev *wdev = hw->priv;
191*fc627dadSJérôme Pouiller 	struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
192*fc627dadSJérôme Pouiller 
193*fc627dadSJérôme Pouiller 	if (wfx_api_older_than(wdev, 3, 10))
194*fc627dadSJérôme Pouiller 		return -EOPNOTSUPP;
195*fc627dadSJérôme Pouiller 
196*fc627dadSJérôme Pouiller 	wvif->remain_on_channel_duration = duration;
197*fc627dadSJérôme Pouiller 	wvif->remain_on_channel_chan = chan;
198*fc627dadSJérôme Pouiller 	schedule_work(&wvif->remain_on_channel_work);
199*fc627dadSJérôme Pouiller 	return 0;
200*fc627dadSJérôme Pouiller }
201*fc627dadSJérôme Pouiller 
wfx_cancel_remain_on_channel(struct ieee80211_hw * hw,struct ieee80211_vif * vif)202*fc627dadSJérôme Pouiller int wfx_cancel_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
203*fc627dadSJérôme Pouiller {
204*fc627dadSJérôme Pouiller 	struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
205*fc627dadSJérôme Pouiller 
206*fc627dadSJérôme Pouiller 	wfx_hif_stop_scan(wvif);
207*fc627dadSJérôme Pouiller 	flush_work(&wvif->remain_on_channel_work);
208*fc627dadSJérôme Pouiller 	return 0;
209*fc627dadSJérôme Pouiller }
210