staging: wfx: rewrite wfx_hw_scan()
Scan requests from mac80211 must be splitted in a few hardware requests (it is necessary to split channels with active scan and channels with passive scan). Current code schedules a work_struct for each hardware request and one delayed_work to handle scan timeout. It is far simpler to run send all the hardware requests synchronously and replace delayed_work with a simple wait_for_completion_timeout(). Signed-off-by: Jérôme Pouiller <jerome.pouiller@silabs.com> Link: https://lore.kernel.org/r/20191217161318.31402-51-Jerome.Pouiller@silabs.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
094ecec9be
commit
d1c015b4ef
|
@ -206,7 +206,7 @@ static int hif_scan_complete_indication(struct wfx_dev *wdev,
|
|||
const struct hif_ind_scan_cmpl *body = buf;
|
||||
|
||||
WARN_ON(!wvif);
|
||||
wfx_scan_complete_cb(wvif, body);
|
||||
wfx_scan_complete(wvif, body);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -22,33 +22,6 @@ static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw,
|
|||
ieee80211_scan_completed(hw, &info);
|
||||
}
|
||||
|
||||
static void wfx_scan_restart_delayed(struct wfx_vif *wvif)
|
||||
{
|
||||
if (wvif->delayed_unjoin) {
|
||||
wvif->delayed_unjoin = false;
|
||||
if (!schedule_work(&wvif->unjoin_work))
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
} else if (wvif->delayed_link_loss) {
|
||||
wvif->delayed_link_loss = 0;
|
||||
wfx_cqm_bssloss_sm(wvif, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int wfx_scan_start(struct wfx_vif *wvif,
|
||||
int chan_start_idx, int chan_num)
|
||||
{
|
||||
int tmo;
|
||||
|
||||
if (wvif->state == WFX_STATE_PRE_STA)
|
||||
return -EBUSY;
|
||||
|
||||
atomic_set(&wvif->scan.in_progress, 1);
|
||||
|
||||
tmo = hif_scan(wvif, wvif->scan.req, chan_start_idx, chan_num);
|
||||
schedule_delayed_work(&wvif->scan.timeout, tmo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_probe_tmpl(struct wfx_vif *wvif,
|
||||
struct cfg80211_scan_request *req)
|
||||
{
|
||||
|
@ -65,153 +38,81 @@ static int update_probe_tmpl(struct wfx_vif *wvif,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int wfx_hw_scan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *hw_req)
|
||||
static int send_scan_req(struct wfx_vif *wvif,
|
||||
struct cfg80211_scan_request *req, int start_idx)
|
||||
{
|
||||
int i, ret, timeout;
|
||||
struct ieee80211_channel *ch_start, *ch_cur;
|
||||
|
||||
for (i = start_idx; i < req->n_channels; i++) {
|
||||
ch_start = req->channels[start_idx];
|
||||
ch_cur = req->channels[i];
|
||||
WARN(ch_cur->band != NL80211_BAND_2GHZ, "band not supported");
|
||||
if (ch_cur->max_power != ch_start->max_power)
|
||||
break;
|
||||
if ((ch_cur->flags ^ ch_start->flags) & IEEE80211_CHAN_NO_IR)
|
||||
break;
|
||||
}
|
||||
wfx_tx_lock_flush(wvif->wdev);
|
||||
reinit_completion(&wvif->scan_complete);
|
||||
ret = hif_scan(wvif, req, start_idx, i - start_idx);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
timeout = ret;
|
||||
ret = wait_for_completion_timeout(&wvif->scan_complete, timeout);
|
||||
if (req->channels[start_idx]->max_power != wvif->wdev->output_power)
|
||||
hif_set_output_power(wvif, wvif->wdev->output_power * 10);
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
if (!ret) {
|
||||
dev_notice(wvif->wdev->dev, "scan timeout\n");
|
||||
hif_stop_scan(wvif);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return i - start_idx;
|
||||
}
|
||||
|
||||
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;
|
||||
struct cfg80211_scan_request *req = &hw_req->req;
|
||||
int i, ret;
|
||||
int chan_cur, ret;
|
||||
|
||||
if (!wvif)
|
||||
return -EINVAL;
|
||||
WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
|
||||
|
||||
if (wvif->state == WFX_STATE_AP)
|
||||
if (vif->type == NL80211_IFTYPE_AP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
|
||||
req->n_ssids = 0;
|
||||
|
||||
if (req->n_ssids > HIF_API_MAX_NB_SSIDS)
|
||||
return -EINVAL;
|
||||
if (wvif->state == WFX_STATE_PRE_STA)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
mutex_lock(&wdev->conf_mutex);
|
||||
|
||||
ret = update_probe_tmpl(wvif, req);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = wfx_fwd_probe_req(wvif, true);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
wfx_tx_lock_flush(wdev);
|
||||
|
||||
WARN(wvif->scan.req, "unexpected concurrent scan");
|
||||
wvif->scan.req = req;
|
||||
wvif->scan.n_ssids = 0;
|
||||
wvif->scan.status = 0;
|
||||
wvif->scan.begin = &req->channels[0];
|
||||
wvif->scan.curr = wvif->scan.begin;
|
||||
wvif->scan.end = &req->channels[req->n_channels];
|
||||
wvif->scan.output_power = wdev->output_power;
|
||||
|
||||
for (i = 0; i < req->n_ssids; ++i) {
|
||||
struct hif_ssid_def *dst = &wvif->scan.ssids[wvif->scan.n_ssids];
|
||||
|
||||
memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
|
||||
dst->ssid_length = req->ssids[i].ssid_len;
|
||||
++wvif->scan.n_ssids;
|
||||
}
|
||||
schedule_work(&wvif->scan.work);
|
||||
|
||||
failed:
|
||||
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);
|
||||
return ret;
|
||||
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);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wfx_scan_work(struct work_struct *work)
|
||||
void wfx_scan_complete(struct wfx_vif *wvif,
|
||||
const struct hif_ind_scan_cmpl *arg)
|
||||
{
|
||||
struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.work);
|
||||
struct ieee80211_channel **it;
|
||||
struct ieee80211_channel *first;
|
||||
int i;
|
||||
|
||||
down(&wvif->scan.lock);
|
||||
mutex_lock(&wvif->wdev->conf_mutex);
|
||||
|
||||
|
||||
if (!wvif->scan.req || wvif->scan.curr == wvif->scan.end) {
|
||||
if (wvif->scan.output_power != wvif->wdev->output_power)
|
||||
hif_set_output_power(wvif,
|
||||
wvif->wdev->output_power * 10);
|
||||
|
||||
if (wvif->scan.status < 0)
|
||||
dev_warn(wvif->wdev->dev, "scan failed\n");
|
||||
else if (wvif->scan.req)
|
||||
dev_dbg(wvif->wdev->dev, "scan completed\n");
|
||||
else
|
||||
dev_dbg(wvif->wdev->dev, "scan canceled\n");
|
||||
|
||||
wvif->scan.req = NULL;
|
||||
wfx_scan_restart_delayed(wvif);
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
__ieee80211_scan_completed_compat(wvif->wdev->hw,
|
||||
wvif->scan.status ? 1 : 0);
|
||||
up(&wvif->scan.lock);
|
||||
return;
|
||||
}
|
||||
first = *wvif->scan.curr;
|
||||
|
||||
for (it = wvif->scan.curr + 1, i = 1;
|
||||
it != wvif->scan.end && i < HIF_API_MAX_NB_CHANNELS;
|
||||
++it, ++i) {
|
||||
if ((*it)->band != first->band)
|
||||
break;
|
||||
if (((*it)->flags ^ first->flags) &
|
||||
IEEE80211_CHAN_NO_IR)
|
||||
break;
|
||||
if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
|
||||
(*it)->max_power != first->max_power)
|
||||
break;
|
||||
}
|
||||
if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
|
||||
wvif->scan.output_power != first->max_power) {
|
||||
wvif->scan.output_power = first->max_power;
|
||||
hif_set_output_power(wvif, wvif->scan.output_power * 10);
|
||||
}
|
||||
wvif->scan.status = wfx_scan_start(wvif,
|
||||
wvif->scan.curr - wvif->scan.begin,
|
||||
it - wvif->scan.curr);
|
||||
if (wvif->scan.status)
|
||||
goto fail;
|
||||
wvif->scan.curr = it;
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
return;
|
||||
|
||||
fail:
|
||||
wvif->scan.curr = wvif->scan.end;
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
up(&wvif->scan.lock);
|
||||
schedule_work(&wvif->scan.work);
|
||||
}
|
||||
|
||||
void wfx_scan_complete_cb(struct wfx_vif *wvif,
|
||||
const struct hif_ind_scan_cmpl *arg)
|
||||
{
|
||||
if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) {
|
||||
wvif->scan.status = 1;
|
||||
schedule_work(&wvif->scan.timeout.work);
|
||||
}
|
||||
}
|
||||
|
||||
void wfx_scan_timeout(struct work_struct *work)
|
||||
{
|
||||
struct wfx_vif *wvif = container_of(work, struct wfx_vif,
|
||||
scan.timeout.work);
|
||||
|
||||
if (atomic_xchg(&wvif->scan.in_progress, 0)) {
|
||||
if (wvif->scan.status > 0) {
|
||||
wvif->scan.status = 0;
|
||||
} else if (!wvif->scan.status) {
|
||||
dev_warn(wvif->wdev->dev, "timeout waiting for scan complete notification\n");
|
||||
wvif->scan.status = -ETIMEDOUT;
|
||||
wvif->scan.curr = wvif->scan.end;
|
||||
hif_stop_scan(wvif);
|
||||
}
|
||||
up(&wvif->scan.lock);
|
||||
wfx_scan_work(&wvif->scan.work);
|
||||
}
|
||||
complete(&wvif->scan_complete);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#ifndef WFX_SCAN_H
|
||||
#define WFX_SCAN_H
|
||||
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <net/mac80211.h>
|
||||
|
||||
#include "hif_api_cmd.h"
|
||||
|
@ -17,26 +15,9 @@
|
|||
struct wfx_dev;
|
||||
struct wfx_vif;
|
||||
|
||||
struct wfx_scan {
|
||||
struct semaphore lock;
|
||||
struct work_struct work;
|
||||
struct delayed_work timeout;
|
||||
struct cfg80211_scan_request *req;
|
||||
struct ieee80211_channel **begin;
|
||||
struct ieee80211_channel **curr;
|
||||
struct ieee80211_channel **end;
|
||||
struct hif_ssid_def ssids[HIF_API_MAX_NB_SSIDS];
|
||||
int output_power;
|
||||
int n_ssids;
|
||||
int status;
|
||||
atomic_t in_progress;
|
||||
};
|
||||
|
||||
int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_scan_request *req);
|
||||
void wfx_scan_work(struct work_struct *work);
|
||||
void wfx_scan_timeout(struct work_struct *work);
|
||||
void wfx_scan_complete_cb(struct wfx_vif *wvif,
|
||||
const struct hif_ind_scan_cmpl *arg);
|
||||
void wfx_scan_complete(struct wfx_vif *wvif,
|
||||
const struct hif_ind_scan_cmpl *ind);
|
||||
|
||||
#endif /* WFX_SCAN_H */
|
||||
|
|
|
@ -277,13 +277,13 @@ void wfx_configure_filter(struct ieee80211_hw *hw,
|
|||
*total_flags &= FIF_OTHER_BSS | FIF_FCSFAIL | FIF_PROBE_REQ;
|
||||
|
||||
while ((wvif = wvif_iterate(wdev, wvif)) != NULL) {
|
||||
down(&wvif->scan.lock);
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
wvif->filter_bssid = (*total_flags &
|
||||
(FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 0 : 1;
|
||||
wvif->disable_beacon_filter = !(*total_flags & FIF_PROBE_REQ);
|
||||
wfx_fwd_probe_req(wvif, true);
|
||||
wfx_update_filtering(wvif);
|
||||
up(&wvif->scan.lock);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -433,9 +433,9 @@ static void wfx_event_handler_work(struct work_struct *work)
|
|||
switch (event->evt.event_id) {
|
||||
case HIF_EVENT_IND_BSSLOST:
|
||||
cancel_work_sync(&wvif->unjoin_work);
|
||||
if (!down_trylock(&wvif->scan.lock)) {
|
||||
if (mutex_trylock(&wvif->scan_lock)) {
|
||||
wfx_cqm_bssloss_sm(wvif, 1, 0, 0);
|
||||
up(&wvif->scan.lock);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
} else {
|
||||
/* Scan is in progress. Delay reporting.
|
||||
* Scan complete will trigger bss_loss_work
|
||||
|
@ -501,7 +501,7 @@ static void wfx_do_unjoin(struct wfx_vif *wvif)
|
|||
{
|
||||
mutex_lock(&wvif->wdev->conf_mutex);
|
||||
|
||||
if (atomic_read(&wvif->scan.in_progress)) {
|
||||
if (!mutex_trylock(&wvif->scan_lock)) {
|
||||
if (wvif->delayed_unjoin)
|
||||
dev_dbg(wvif->wdev->dev,
|
||||
"delayed unjoin is already scheduled\n");
|
||||
|
@ -509,6 +509,7 @@ static void wfx_do_unjoin(struct wfx_vif *wvif)
|
|||
wvif->delayed_unjoin = true;
|
||||
goto done;
|
||||
}
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
|
||||
wvif->delayed_link_loss = false;
|
||||
|
||||
|
@ -613,14 +614,6 @@ static void wfx_do_join(struct wfx_vif *wvif)
|
|||
|
||||
mutex_lock(&wvif->wdev->conf_mutex);
|
||||
|
||||
/* Under the conf lock: check scan status and
|
||||
* bail out if it is in progress.
|
||||
*/
|
||||
if (atomic_read(&wvif->scan.in_progress)) {
|
||||
wfx_tx_unlock(wvif->wdev);
|
||||
goto done_put;
|
||||
}
|
||||
|
||||
/* Sanity check basic rates */
|
||||
if (!join.basic_rate_set)
|
||||
join.basic_rate_set = 7;
|
||||
|
@ -684,7 +677,6 @@ static void wfx_do_join(struct wfx_vif *wvif)
|
|||
}
|
||||
wfx_update_filtering(wvif);
|
||||
|
||||
done_put:
|
||||
mutex_unlock(&wvif->wdev->conf_mutex);
|
||||
if (bss)
|
||||
cfg80211_put_bss(wvif->wdev->hw->wiphy, bss);
|
||||
|
@ -1346,7 +1338,7 @@ int wfx_config(struct ieee80211_hw *hw, u32 changed)
|
|||
return 0;
|
||||
}
|
||||
|
||||
down(&wvif->scan.lock);
|
||||
mutex_lock(&wvif->scan_lock);
|
||||
mutex_lock(&wdev->conf_mutex);
|
||||
if (changed & IEEE80211_CONF_CHANGE_POWER) {
|
||||
wdev->output_power = conf->power_level;
|
||||
|
@ -1361,7 +1353,7 @@ int wfx_config(struct ieee80211_hw *hw, u32 changed)
|
|||
}
|
||||
|
||||
mutex_unlock(&wdev->conf_mutex);
|
||||
up(&wvif->scan.lock);
|
||||
mutex_unlock(&wvif->scan_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1419,10 +1411,6 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|||
wvif->wep_default_key_id = -1;
|
||||
INIT_WORK(&wvif->wep_key_work, wfx_wep_key_work);
|
||||
|
||||
sema_init(&wvif->scan.lock, 1);
|
||||
INIT_WORK(&wvif->scan.work, wfx_scan_work);
|
||||
INIT_DELAYED_WORK(&wvif->scan.timeout, wfx_scan_timeout);
|
||||
|
||||
spin_lock_init(&wvif->event_queue_lock);
|
||||
INIT_LIST_HEAD(&wvif->event_queue);
|
||||
INIT_WORK(&wvif->event_handler_work, wfx_event_handler_work);
|
||||
|
@ -1435,8 +1423,11 @@ int wfx_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|||
INIT_WORK(&wvif->bss_params_work, wfx_bss_params_work);
|
||||
INIT_WORK(&wvif->set_cts_work, wfx_set_cts_work);
|
||||
INIT_WORK(&wvif->unjoin_work, wfx_unjoin_work);
|
||||
|
||||
INIT_WORK(&wvif->tx_policy_upload_work, wfx_tx_policy_upload_work);
|
||||
|
||||
mutex_init(&wvif->scan_lock);
|
||||
init_completion(&wvif->scan_complete);
|
||||
|
||||
mutex_unlock(&wdev->conf_mutex);
|
||||
|
||||
hif_set_macaddr(wvif, vif->addr);
|
||||
|
@ -1462,10 +1453,6 @@ void wfx_remove_interface(struct ieee80211_hw *hw,
|
|||
struct wfx_vif *wvif = (struct wfx_vif *) vif->drv_priv;
|
||||
int i;
|
||||
|
||||
// If scan is in progress, stop it
|
||||
while (down_trylock(&wvif->scan.lock))
|
||||
schedule();
|
||||
up(&wvif->scan.lock);
|
||||
wait_for_completion_timeout(&wvif->set_pm_mode_complete, msecs_to_jiffies(300));
|
||||
|
||||
mutex_lock(&wdev->conf_mutex);
|
||||
|
@ -1505,8 +1492,6 @@ void wfx_remove_interface(struct ieee80211_hw *hw,
|
|||
/* FIXME: In add to reset MAC address, try to reset interface */
|
||||
hif_set_macaddr(wvif, NULL);
|
||||
|
||||
cancel_delayed_work_sync(&wvif->scan.timeout);
|
||||
|
||||
wfx_cqm_bssloss_sm(wvif, 0, 0, 0);
|
||||
cancel_work_sync(&wvif->unjoin_work);
|
||||
cancel_delayed_work_sync(&wvif->link_id_gc_work);
|
||||
|
|
|
@ -125,7 +125,9 @@ struct wfx_vif {
|
|||
bool delayed_unjoin;
|
||||
struct work_struct unjoin_work;
|
||||
|
||||
struct wfx_scan scan;
|
||||
/* avoid some operations in parallel with scan */
|
||||
struct mutex scan_lock;
|
||||
struct completion scan_complete;
|
||||
|
||||
struct completion set_pm_mode_complete;
|
||||
|
||||
|
|
Loading…
Reference in a new issue