aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/wfx/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/wfx/scan.c')
-rw-r--r--drivers/staging/wfx/scan.c321
1 files changed, 80 insertions, 241 deletions
diff --git a/drivers/staging/wfx/scan.c b/drivers/staging/wfx/scan.c
index 35fcf9119f96..6e1e50048651 100644
--- a/drivers/staging/wfx/scan.c
+++ b/drivers/staging/wfx/scan.c
@@ -16,279 +16,118 @@ static void __ieee80211_scan_completed_compat(struct ieee80211_hw *hw,
bool aborted)
{
struct cfg80211_scan_info info = {
- .aborted = aborted ? 1 : 0,
+ .aborted = aborted,
};
ieee80211_scan_completed(hw, &info);
}
-static void wfx_scan_restart_delayed(struct wfx_vif *wvif)
+static int update_probe_tmpl(struct wfx_vif *wvif,
+ struct cfg80211_scan_request *req)
{
- 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, struct wfx_scan_params *scan)
-{
- int ret;
- int tmo = 500;
-
- if (wvif->state == WFX_STATE_PRE_STA)
- return -EBUSY;
-
- tmo += scan->scan_req.num_of_channels *
- ((20 * (scan->scan_req.max_channel_time)) + 10);
- atomic_set(&wvif->scan.in_progress, 1);
- atomic_set(&wvif->wdev->scan_in_progress, 1);
-
- schedule_delayed_work(&wvif->scan.timeout, msecs_to_jiffies(tmo));
- ret = hif_scan(wvif, scan);
- if (ret) {
- wfx_scan_failed_cb(wvif);
- atomic_set(&wvif->scan.in_progress, 0);
- atomic_set(&wvif->wdev->scan_in_progress, 0);
- cancel_delayed_work_sync(&wvif->scan.timeout);
- wfx_scan_restart_delayed(wvif);
- }
- return ret;
-}
-
-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;
struct sk_buff *skb;
- int i, ret;
- struct hif_mib_template_frame *p;
-
- if (!wvif)
- return -EINVAL;
-
- if (wvif->state == WFX_STATE_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;
-
- skb = ieee80211_probereq_get(hw, wvif->vif->addr, NULL, 0, req->ie_len);
+ skb = ieee80211_probereq_get(wvif->wdev->hw, wvif->vif->addr,
+ NULL, 0, req->ie_len);
if (!skb)
return -ENOMEM;
- if (req->ie_len)
- memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
-
- mutex_lock(&wdev->conf_mutex);
-
- p = (struct hif_mib_template_frame *)skb_push(skb, 4);
- p->frame_type = HIF_TMPLT_PRBREQ;
- p->frame_length = cpu_to_le16(skb->len - 4);
- ret = hif_set_template_frame(wvif, p);
- skb_pull(skb, 4);
-
- if (!ret)
- /* Host want to be the probe responder. */
- ret = wfx_fwd_probe_req(wvif, true);
- if (ret) {
- mutex_unlock(&wdev->conf_mutex);
- dev_kfree_skb(skb);
- return ret;
- }
-
- 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;
- }
-
- mutex_unlock(&wdev->conf_mutex);
-
- if (skb)
- dev_kfree_skb(skb);
- schedule_work(&wvif->scan.work);
+ skb_put_data(skb, req->ie, req->ie_len);
+ hif_set_template_frame(wvif, skb, HIF_TMPLT_PRBREQ, 0);
+ dev_kfree_skb(skb);
return 0;
}
-void wfx_scan_work(struct work_struct *work)
+static int send_scan_req(struct wfx_vif *wvif,
+ struct cfg80211_scan_request *req, int start_idx)
{
- struct wfx_vif *wvif = container_of(work, struct wfx_vif, scan.work);
- struct ieee80211_channel **it;
- struct wfx_scan_params scan = {
- .scan_req.scan_type.type = 0, /* Foreground */
- };
- struct ieee80211_channel *first;
- bool first_run = (wvif->scan.begin == wvif->scan.curr &&
- wvif->scan.begin != wvif->scan.end);
- int i;
-
- down(&wvif->scan.lock);
- mutex_lock(&wvif->wdev->conf_mutex);
-
- if (first_run) {
- if (wvif->state == WFX_STATE_STA &&
- !(wvif->powersave_mode.pm_mode.enter_psm)) {
- struct hif_req_set_pm_mode pm = wvif->powersave_mode;
-
- pm.pm_mode.enter_psm = 1;
- wfx_set_pm(wvif, &pm);
- }
- }
-
- 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);
- if (wvif->state == WFX_STATE_STA &&
- !(wvif->powersave_mode.pm_mode.enter_psm))
- wfx_set_pm(wvif, &wvif->powersave_mode);
- 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)
+ 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 (!(first->flags & IEEE80211_CHAN_NO_IR) &&
- (*it)->max_power != first->max_power)
+ if ((ch_cur->flags ^ ch_start->flags) & IEEE80211_CHAN_NO_IR)
break;
}
- scan.scan_req.band = first->band;
-
- if (wvif->scan.req->no_cck)
- scan.scan_req.max_transmit_rate = API_RATE_INDEX_G_6MBPS;
- else
- scan.scan_req.max_transmit_rate = API_RATE_INDEX_B_1MBPS;
- scan.scan_req.num_of_probe_requests =
- (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
- scan.scan_req.num_of_ssi_ds = wvif->scan.n_ssids;
- scan.ssids = &wvif->scan.ssids[0];
- scan.scan_req.num_of_channels = it - wvif->scan.curr;
- scan.scan_req.probe_delay = 100;
- // FIXME: Check if FW can do active scan while joined.
- if (wvif->state == WFX_STATE_STA) {
- scan.scan_req.scan_type.type = 1;
- scan.scan_req.scan_flags.fbg = 1;
+ wfx_tx_lock_flush(wvif->wdev);
+ wvif->scan_abort = false;
+ reinit_completion(&wvif->scan_complete);
+ timeout = hif_scan(wvif, req, start_idx, i - start_idx);
+ if (timeout < 0)
+ return timeout;
+ ret = wait_for_completion_timeout(&wvif->scan_complete, timeout);
+ if (req->channels[start_idx]->max_power != wvif->vif->bss_conf.txpower)
+ hif_set_output_power(wvif, wvif->vif->bss_conf.txpower);
+ wfx_tx_unlock(wvif->wdev);
+ if (!ret) {
+ dev_notice(wvif->wdev->dev, "scan timeout\n");
+ hif_stop_scan(wvif);
+ return -ETIMEDOUT;
}
-
- scan.ch = kcalloc(scan.scan_req.num_of_channels,
- sizeof(u8), GFP_KERNEL);
-
- if (!scan.ch) {
- wvif->scan.status = -ENOMEM;
- goto fail;
+ if (wvif->scan_abort) {
+ dev_notice(wvif->wdev->dev, "scan abort\n");
+ return -ECONNABORTED;
}
- for (i = 0; i < scan.scan_req.num_of_channels; ++i)
- scan.ch[i] = wvif->scan.curr[i]->hw_value;
+ return i - start_idx;
+}
- if (wvif->scan.curr[0]->flags & IEEE80211_CHAN_NO_IR) {
- scan.scan_req.min_channel_time = 50;
- scan.scan_req.max_channel_time = 150;
- } else {
- scan.scan_req.min_channel_time = 10;
- scan.scan_req.max_channel_time = 50;
- }
- 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, &scan);
- kfree(scan.ch);
- if (wvif->scan.status)
- goto fail;
- wvif->scan.curr = it;
- mutex_unlock(&wvif->wdev->conf_mutex);
- return;
+/*
+ * 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;
-fail:
- wvif->scan.curr = wvif->scan.end;
+ 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);
- up(&wvif->scan.lock);
- schedule_work(&wvif->scan.work);
+ mutex_unlock(&wvif->scan_lock);
+ __ieee80211_scan_completed_compat(wvif->wdev->hw, ret < 0);
}
-static void wfx_scan_complete(struct wfx_vif *wvif)
+int wfx_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
{
- up(&wvif->scan.lock);
- atomic_set(&wvif->wdev->scan_in_progress, 0);
+ struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
- wfx_scan_work(&wvif->scan.work);
-}
+ WARN_ON(hw_req->req.n_channels > HIF_API_MAX_NB_CHANNELS);
-void wfx_scan_failed_cb(struct wfx_vif *wvif)
-{
- if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) {
- wvif->scan.status = -EIO;
- schedule_work(&wvif->scan.timeout.work);
- }
+ if (vif->type == NL80211_IFTYPE_AP)
+ return -EOPNOTSUPP;
+
+ if (wvif->state == WFX_STATE_PRE_STA)
+ return -EBUSY;
+
+ wvif->scan_req = hw_req;
+ schedule_work(&wvif->scan_work);
+ return 0;
}
-void wfx_scan_complete_cb(struct wfx_vif *wvif, struct hif_ind_scan_cmpl *arg)
+void wfx_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
- if (cancel_delayed_work_sync(&wvif->scan.timeout) > 0) {
- wvif->scan.status = 1;
- schedule_work(&wvif->scan.timeout.work);
- }
+ struct wfx_vif *wvif = (struct wfx_vif *)vif->drv_priv;
+
+ wvif->scan_abort = true;
+ hif_stop_scan(wvif);
}
-void wfx_scan_timeout(struct work_struct *work)
+void wfx_scan_complete(struct wfx_vif *wvif)
{
- 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);
- }
- wfx_scan_complete(wvif);
- }
+ complete(&wvif->scan_complete);
}