11a61af0fSJérôme Pouiller // SPDX-License-Identifier: GPL-2.0-only
21a61af0fSJérôme Pouiller /*
31a61af0fSJérôme Pouiller * Scan related functions.
41a61af0fSJérôme Pouiller *
5f9dc9f37SJérôme Pouiller * Copyright (c) 2017-2020, Silicon Laboratories, Inc.
61a61af0fSJérôme Pouiller * Copyright (c) 2010, ST-Ericsson
71a61af0fSJérôme Pouiller */
81a61af0fSJérôme Pouiller #include <net/mac80211.h>
91a61af0fSJérôme Pouiller
101a61af0fSJérôme Pouiller #include "scan.h"
111a61af0fSJérôme Pouiller #include "wfx.h"
121a61af0fSJérôme Pouiller #include "sta.h"
131a61af0fSJérôme Pouiller #include "hif_tx_mib.h"
141a61af0fSJérôme Pouiller
wfx_ieee80211_scan_completed_compat(struct ieee80211_hw * hw,bool aborted)15381d3295SJérôme Pouiller static void wfx_ieee80211_scan_completed_compat(struct ieee80211_hw *hw, bool aborted)
161a61af0fSJérôme Pouiller {
171a61af0fSJérôme Pouiller struct cfg80211_scan_info info = {
18faffec88SJérôme Pouiller .aborted = aborted,
191a61af0fSJérôme Pouiller };
201a61af0fSJérôme Pouiller
211a61af0fSJérôme Pouiller ieee80211_scan_completed(hw, &info);
221a61af0fSJérôme Pouiller }
231a61af0fSJérôme Pouiller
update_probe_tmpl(struct wfx_vif * wvif,struct cfg80211_scan_request * req)24381d3295SJérôme Pouiller static int update_probe_tmpl(struct wfx_vif *wvif, struct cfg80211_scan_request *req)
25397f36c1SJérôme Pouiller {
262c33360bSJaehee Park struct ieee80211_vif *vif = wvif_to_vif(wvif);
27397f36c1SJérôme Pouiller struct sk_buff *skb;
28397f36c1SJérôme Pouiller
292c33360bSJaehee Park skb = ieee80211_probereq_get(wvif->wdev->hw, vif->addr, NULL, 0,
302c33360bSJaehee Park req->ie_len);
31397f36c1SJérôme Pouiller if (!skb)
32397f36c1SJérôme Pouiller return -ENOMEM;
33397f36c1SJérôme Pouiller
34397f36c1SJérôme Pouiller skb_put_data(skb, req->ie, req->ie_len);
351c780482SJérôme Pouiller wfx_hif_set_template_frame(wvif, skb, HIF_TMPLT_PRBREQ, 0);
36397f36c1SJérôme Pouiller dev_kfree_skb(skb);
37397f36c1SJérôme Pouiller return 0;
38397f36c1SJérôme Pouiller }
39397f36c1SJérôme Pouiller
send_scan_req(struct wfx_vif * wvif,struct cfg80211_scan_request * req,int start_idx)40381d3295SJérôme Pouiller static int send_scan_req(struct wfx_vif *wvif, struct cfg80211_scan_request *req, int start_idx)
41d1c015b4SJérôme Pouiller {
422c33360bSJaehee Park struct ieee80211_vif *vif = wvif_to_vif(wvif);
43d1c015b4SJérôme Pouiller struct ieee80211_channel *ch_start, *ch_cur;
442c33360bSJaehee Park int i, ret;
45d1c015b4SJérôme Pouiller
46d1c015b4SJérôme Pouiller for (i = start_idx; i < req->n_channels; i++) {
47d1c015b4SJérôme Pouiller ch_start = req->channels[start_idx];
48d1c015b4SJérôme Pouiller ch_cur = req->channels[i];
49d1c015b4SJérôme Pouiller WARN(ch_cur->band != NL80211_BAND_2GHZ, "band not supported");
50d1c015b4SJérôme Pouiller if (ch_cur->max_power != ch_start->max_power)
51d1c015b4SJérôme Pouiller break;
52d1c015b4SJérôme Pouiller if ((ch_cur->flags ^ ch_start->flags) & IEEE80211_CHAN_NO_IR)
53d1c015b4SJérôme Pouiller break;
54d1c015b4SJérôme Pouiller }
55d1c015b4SJérôme Pouiller wfx_tx_lock_flush(wvif->wdev);
564337074bSJérôme Pouiller wvif->scan_abort = false;
57d1c015b4SJérôme Pouiller reinit_completion(&wvif->scan_complete);
581c780482SJérôme Pouiller ret = wfx_hif_scan(wvif, req, start_idx, i - start_idx);
5929de523aSJérôme Pouiller if (ret) {
608bce06b0SJérôme Pouiller wfx_tx_unlock(wvif->wdev);
618bce06b0SJérôme Pouiller return -EIO;
62f0b9d875SDan Carpenter }
638bce06b0SJérôme Pouiller ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
64d1c015b4SJérôme Pouiller if (!ret) {
651c780482SJérôme Pouiller wfx_hif_stop_scan(wvif);
6610b72a7cSJérôme Pouiller ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
678bce06b0SJérôme Pouiller dev_dbg(wvif->wdev->dev, "scan timeout (%d channels done)\n",
688bce06b0SJérôme Pouiller wvif->scan_nb_chan_done);
698bce06b0SJérôme Pouiller }
708bce06b0SJérôme Pouiller if (!ret) {
7110b72a7cSJérôme Pouiller dev_err(wvif->wdev->dev, "scan didn't stop\n");
7210b72a7cSJérôme Pouiller ret = -ETIMEDOUT;
738bce06b0SJérôme Pouiller } else if (wvif->scan_abort) {
744337074bSJérôme Pouiller dev_notice(wvif->wdev->dev, "scan abort\n");
7510b72a7cSJérôme Pouiller ret = -ECONNABORTED;
768bce06b0SJérôme Pouiller } else if (wvif->scan_nb_chan_done > i - start_idx) {
778bce06b0SJérôme Pouiller ret = -EIO;
788bce06b0SJérôme Pouiller } else {
798bce06b0SJérôme Pouiller ret = wvif->scan_nb_chan_done;
804337074bSJé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);
8310b72a7cSJérôme Pouiller wfx_tx_unlock(wvif->wdev);
8410b72a7cSJérôme Pouiller return ret;
85d1c015b4SJérôme Pouiller }
86d1c015b4SJérôme Pouiller
8758de6994SJérôme Pouiller /* It is not really necessary to run scan request asynchronously. However,
883827e33dSJérôme Pouiller * there is a bug in "iw scan" when ieee80211_scan_completed() is called before
893827e33dSJérôme Pouiller * wfx_hw_scan() return
903827e33dSJérôme Pouiller */
wfx_hw_scan_work(struct work_struct * work)913827e33dSJérôme Pouiller void wfx_hw_scan_work(struct work_struct *work)
923827e33dSJérôme Pouiller {
933827e33dSJérôme Pouiller struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
943827e33dSJérôme Pouiller struct ieee80211_scan_request *hw_req = wvif->scan_req;
958bce06b0SJérôme Pouiller int chan_cur, ret, err;
963827e33dSJérôme Pouiller
973827e33dSJérôme Pouiller mutex_lock(&wvif->wdev->conf_mutex);
9804106ec5SJérôme Pouiller mutex_lock(&wvif->wdev->scan_lock);
99f214b7b6SJérôme Pouiller if (wvif->join_in_progress) {
100ea174822SJérôme Pouiller dev_info(wvif->wdev->dev, "abort in-progress REQ_JOIN");
101f214b7b6SJérôme Pouiller wfx_reset(wvif);
102f214b7b6SJérôme Pouiller }
1033827e33dSJérôme Pouiller update_probe_tmpl(wvif, &hw_req->req);
1043827e33dSJérôme Pouiller chan_cur = 0;
1058bce06b0SJérôme Pouiller err = 0;
1063827e33dSJérôme Pouiller do {
1073827e33dSJérôme Pouiller ret = send_scan_req(wvif, &hw_req->req, chan_cur);
1088bce06b0SJérôme Pouiller if (ret > 0) {
1093827e33dSJérôme Pouiller chan_cur += ret;
1108bce06b0SJérôme Pouiller err = 0;
1118bce06b0SJérôme Pouiller }
1128bce06b0SJérôme Pouiller if (!ret)
1138bce06b0SJérôme Pouiller err++;
1148bce06b0SJérôme Pouiller if (err > 2) {
1158bce06b0SJérôme Pouiller dev_err(wvif->wdev->dev, "scan has not been able to start\n");
1168bce06b0SJérôme Pouiller ret = -ETIMEDOUT;
1178bce06b0SJérôme Pouiller }
1188bce06b0SJérôme Pouiller } while (ret >= 0 && chan_cur < hw_req->req.n_channels);
11904106ec5SJérôme Pouiller mutex_unlock(&wvif->wdev->scan_lock);
1200f66c31eSJérôme Pouiller mutex_unlock(&wvif->wdev->conf_mutex);
12174507433SJérôme Pouiller wfx_ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
1223827e33dSJérôme Pouiller }
1233827e33dSJérôme Pouiller
wfx_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * hw_req)124d1c015b4SJérôme Pouiller int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1251a61af0fSJérôme Pouiller struct ieee80211_scan_request *hw_req)
1261a61af0fSJérôme Pouiller {
1271a61af0fSJérôme Pouiller struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
1281a61af0fSJérôme Pouiller
129d1c015b4SJérôme Pouiller WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
1303827e33dSJérôme Pouiller wvif->scan_req = hw_req;
1313827e33dSJérôme Pouiller schedule_work(&wvif->scan_work);
132d1c015b4SJérôme Pouiller return 0;
1331a61af0fSJérôme Pouiller }
1341a61af0fSJérôme Pouiller
wfx_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)1354337074bSJérôme Pouiller void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
1364337074bSJérôme Pouiller {
1374337074bSJérôme Pouiller struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
1384337074bSJérôme Pouiller
1394337074bSJérôme Pouiller wvif->scan_abort = true;
1401c780482SJérôme Pouiller wfx_hif_stop_scan(wvif);
1414337074bSJérôme Pouiller }
1424337074bSJérôme Pouiller
wfx_scan_complete(struct wfx_vif * wvif,int nb_chan_done)1438bce06b0SJérôme Pouiller void wfx_scan_complete(struct wfx_vif *wvif, int nb_chan_done)
1441a61af0fSJérôme Pouiller {
1458bce06b0SJérôme Pouiller wvif->scan_nb_chan_done = nb_chan_done;
146d1c015b4SJérôme Pouiller complete(&wvif->scan_complete);
1471a61af0fSJérôme Pouiller }
148fc627dadSJérôme Pouiller
wfx_remain_on_channel_work(struct work_struct * work)149fc627dadSJérôme Pouiller void wfx_remain_on_channel_work(struct work_struct *work)
150fc627dadSJérôme Pouiller {
151fc627dadSJérôme Pouiller struct wfx_vif *wvif = container_of(work, struct wfx_vif, remain_on_channel_work);
152fc627dadSJérôme Pouiller struct ieee80211_channel *chan = wvif->remain_on_channel_chan;
153fc627dadSJérôme Pouiller int duration = wvif->remain_on_channel_duration;
154fc627dadSJérôme Pouiller int ret;
155fc627dadSJérôme Pouiller
156fc627dadSJérôme Pouiller /* Hijack scan request to implement Remain-On-Channel */
157fc627dadSJérôme Pouiller mutex_lock(&wvif->wdev->conf_mutex);
158fc627dadSJérôme Pouiller mutex_lock(&wvif->wdev->scan_lock);
159fc627dadSJérôme Pouiller if (wvif->join_in_progress) {
160fc627dadSJérôme Pouiller dev_info(wvif->wdev->dev, "abort in-progress REQ_JOIN");
161fc627dadSJérôme Pouiller wfx_reset(wvif);
162fc627dadSJérôme Pouiller }
163fc627dadSJérôme Pouiller wfx_tx_flush(wvif->wdev);
164fc627dadSJérôme Pouiller
165fc627dadSJérôme Pouiller reinit_completion(&wvif->scan_complete);
166fc627dadSJérôme Pouiller ret = wfx_hif_scan_uniq(wvif, chan, duration);
167fc627dadSJérôme Pouiller if (ret)
168fc627dadSJérôme Pouiller goto end;
169fc627dadSJérôme Pouiller ieee80211_ready_on_channel(wvif->wdev->hw);
170fc627dadSJérôme Pouiller ret = wait_for_completion_timeout(&wvif->scan_complete,
171fc627dadSJérôme Pouiller msecs_to_jiffies(duration * 120 / 100));
172fc627dadSJérôme Pouiller if (!ret) {
173fc627dadSJérôme Pouiller wfx_hif_stop_scan(wvif);
174fc627dadSJérôme Pouiller ret = wait_for_completion_timeout(&wvif->scan_complete, 1 * HZ);
175fc627dadSJérôme Pouiller dev_dbg(wvif->wdev->dev, "roc timeout\n");
176fc627dadSJérôme Pouiller }
177fc627dadSJérôme Pouiller if (!ret)
178fc627dadSJérôme Pouiller dev_err(wvif->wdev->dev, "roc didn't stop\n");
179fc627dadSJérôme Pouiller ieee80211_remain_on_channel_expired(wvif->wdev->hw);
180fc627dadSJérôme Pouiller end:
181fc627dadSJérôme Pouiller mutex_unlock(&wvif->wdev->scan_lock);
182fc627dadSJérôme Pouiller mutex_unlock(&wvif->wdev->conf_mutex);
183fc627dadSJérôme Pouiller wfx_bh_request_tx(wvif->wdev);
184fc627dadSJérôme Pouiller }
185fc627dadSJé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)186fc627dadSJérôme Pouiller int wfx_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
187fc627dadSJérôme Pouiller struct ieee80211_channel *chan, int duration,
188fc627dadSJérôme Pouiller enum ieee80211_roc_type type)
189fc627dadSJérôme Pouiller {
190fc627dadSJérôme Pouiller struct wfx_dev *wdev = hw->priv;
191fc627dadSJérôme Pouiller struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
192fc627dadSJérôme Pouiller
193fc627dadSJérôme Pouiller if (wfx_api_older_than(wdev, 3, 10))
194fc627dadSJérôme Pouiller return -EOPNOTSUPP;
195fc627dadSJérôme Pouiller
196fc627dadSJérôme Pouiller wvif->remain_on_channel_duration = duration;
197fc627dadSJérôme Pouiller wvif->remain_on_channel_chan = chan;
198fc627dadSJérôme Pouiller schedule_work(&wvif->remain_on_channel_work);
199fc627dadSJérôme Pouiller return 0;
200fc627dadSJérôme Pouiller }
201fc627dadSJérôme Pouiller
wfx_cancel_remain_on_channel(struct ieee80211_hw * hw,struct ieee80211_vif * vif)202fc627dadSJérôme Pouiller int wfx_cancel_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
203fc627dadSJérôme Pouiller {
204fc627dadSJérôme Pouiller struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
205fc627dadSJérôme Pouiller
206fc627dadSJérôme Pouiller wfx_hif_stop_scan(wvif);
207fc627dadSJérôme Pouiller flush_work(&wvif->remain_on_channel_work);
208fc627dadSJérôme Pouiller return 0;
209fc627dadSJérôme Pouiller }
210