mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 04:11:49 +00:00
staging: wfx: workaround bug with "iw scan"
mac80211 specification does not forbid hw_scan() to call ieee80211_scan_completed(). However, from userspace point of view, not all applications support this behavior. In particular, the code of iw contains a big fat warning: /* * This code has a bug, which requires creating a separate * nl80211 socket to fix: * It is possible for a NL80211_CMD_NEW_SCAN_RESULTS or * NL80211_CMD_SCAN_ABORTED message to be sent by the kernel * before (!) we listen to it, because we only start listening * after we send our scan request. [...] * Alas, the kernel doesn't do that (yet). */ So, we have to avoid to call ieee80211_scan_completed() from hw_scan() (it's a kind of unwritten rule). This patch relocates the hw_scan() process to a work_struct to fix the problem. Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com> Link: https://lore.kernel.org/r/20191217161318.31402-52-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
d1c015b4ef
commit
3827e33d37
@ -71,12 +71,45 @@ static int send_scan_req(struct wfx_vif *wvif,
|
||||
return i - start_idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is not really necessary to run scan request asynchronously. However,
|
||||
* there is a bug in "iw scan" when ieee80211_scan_completed() is called before
|
||||
* wfx_hw_scan() return
|
||||
*/
|
||||
void wfx_hw_scan_work(struct work_struct *work)
|
||||
{
|
||||
struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan_work);
|
||||
struct ieee80211_scan_request *hw_req = wvif->scan_req;
|
||||
int chan_cur, ret;
|
||||
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
mutex_lock(&wvif->wdev->conf_mutex);
|
||||
update_probe_tmpl(wvif, &hw_req->req);
|
||||
wfx_fwd_probe_req(wvif, true);
|
||||
chan_cur = 0;
|
||||
do {
|
||||
ret = send_scan_req(wvif, &hw_req->req, chan_cur);
|
||||
if (ret > 0)
|
||||
chan_cur += ret;
|
||||
} while (ret > 0 && chan_cur < hw_req->req.n_channels);
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
__ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
|
||||
if (wvif->delayed_unjoin) {
|
||||
wvif->delayed_unjoin = false;
|
||||
wfx_tx_lock(wvif->wdev);
|
||||
if (!schedule_work(&wvif->unjoin_work))
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
} else if (wvif->delayed_link_loss) {
|
||||
wvif->delayed_link_loss = false;
|
||||
wfx_cqm_bssloss_sm(wvif, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
{
|
||||
struct wfx_dev *wdev = hw->priv;
|
||||
struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv;
|
||||
int chan_cur, ret;
|
||||
struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
|
||||
|
||||
WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
|
||||
|
||||
@ -86,28 +119,8 @@ int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
if (wvif->state == WFX_STATE_PRE_STA)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
mutex_lock(&wdev->conf_mutex);
|
||||
update_probe_tmpl(wvif, &hw_req->req);
|
||||
wfx_fwd_probe_req(wvif, true);
|
||||
chan_cur = 0;
|
||||
do {
|
||||
ret = send_scan_req(wvif, &hw_req->req, chan_cur);
|
||||
if (ret > 0)
|
||||
chan_cur += ret;
|
||||
} while (ret > 0 && chan_cur < hw_req->req.n_channels);
|
||||
__ieee80211_scan_completed_compat(hw, ret < 0);
|
||||
mutex_unlock(&wdev->conf_mutex);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
if (wvif->delayed_unjoin) {
|
||||
wvif->delayed_unjoin = false;
|
||||
wfx_tx_lock(wdev);
|
||||
if (!schedule_work(&wvif->unjoin_work))
|
||||
wfx_tx_unlock(wdev);
|
||||
} else if (wvif->delayed_link_loss) {
|
||||
wvif->delayed_link_loss = false;
|
||||
wfx_cqm_bssloss_sm(wvif, 1, 0, 0);
|
||||
}
|
||||
wvif->scan_req = hw_req;
|
||||
schedule_work(&wvif->scan_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
struct wfx_dev;
|
||||
struct wfx_vif;
|
||||
|
||||
void wfx_hw_scan_work(struct work_struct *work);
|
||||
int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *req);
|
||||
void wfx_scan_complete(struct wfx_vif *wvif,
|
||||
|
@ -1427,6 +1427,7 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
||||
|
||||
mutex_init(&wvif->scan_lock);
|
||||
init_completion(&wvif->scan_complete);
|
||||
INIT_WORK(&wvif->scan_work, wfx_hw_scan_work);
|
||||
|
||||
mutex_unlock(&wdev->conf_mutex);
|
||||
|
||||
|
@ -127,7 +127,9 @@ struct wfx_vif {
|
||||
|
||||
/* avoid some operations in parallel with scan */
|
||||
struct mutex scan_lock;
|
||||
struct work_struct scan_work;
|
||||
struct completion scan_complete;
|
||||
struct ieee80211_scan_request *scan_req;
|
||||
|
||||
struct completion set_pm_mode_complete;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user