summaryrefslogtreecommitdiffstats
path: root/net/mac80211/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r--net/mac80211/scan.c199
1 files changed, 114 insertions, 85 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index e1b0be7a57b9..fb274db77e3c 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -114,6 +114,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->dtim_period = tim_ie->dtim_period;
}
+ /* If the beacon had no TIM IE, or it was invalid, use 1 */
+ if (beacon && !bss->dtim_period)
+ bss->dtim_period = 1;
+
/* replace old supported rates if we get new values */
srlen = 0;
if (elems->supp_rates) {
@@ -238,20 +242,19 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
- req->ie, req->ie_len, band);
+ req->ie, req->ie_len, band, (u32) -1,
+ 0);
local->hw_scan_req->ie_len = ielen;
return true;
}
-void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
+static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
+ bool was_hw_scan)
{
struct ieee80211_local *local = hw_to_local(hw);
- bool was_hw_scan;
-
- trace_api_scan_completed(local, aborted);
- mutex_lock(&local->scan_mtx);
+ lockdep_assert_held(&local->mtx);
/*
* It's ok to abort a not-yet-running scan (that
@@ -262,17 +265,13 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (WARN_ON(!local->scanning && !aborted))
aborted = true;
- if (WARN_ON(!local->scan_req)) {
- mutex_unlock(&local->scan_mtx);
- return;
- }
+ if (WARN_ON(!local->scan_req))
+ return false;
- was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
- ieee80211_queue_delayed_work(&local->hw,
- &local->scan_work, 0);
- mutex_unlock(&local->scan_mtx);
- return;
+ int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req);
+ if (rc == 0)
+ return false;
}
kfree(local->hw_scan_req);
@@ -286,26 +285,42 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
local->scanning = 0;
local->scan_channel = NULL;
- /* we only have to protect scan_req and hw/sw scan */
- mutex_unlock(&local->scan_mtx);
-
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
- if (was_hw_scan)
- goto done;
-
- ieee80211_configure_filter(local);
+ return true;
+}
- drv_sw_scan_complete(local);
+static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw,
+ bool was_hw_scan)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
- ieee80211_offchannel_return(local, true);
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ if (!was_hw_scan) {
+ ieee80211_configure_filter(local);
+ drv_sw_scan_complete(local);
+ ieee80211_offchannel_return(local, true);
+ }
- done:
+ mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
+ mutex_unlock(&local->mtx);
+
ieee80211_mlme_notify_scan_completed(local);
ieee80211_ibss_notify_scan_completed(local);
ieee80211_mesh_notify_scan_completed(local);
ieee80211_queue_work(&local->hw, &local->work_work);
}
+
+void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ trace_api_scan_completed(local, aborted);
+
+ set_bit(SCAN_COMPLETED, &local->scanning);
+ if (aborted)
+ set_bit(SCAN_ABORTED, &local->scanning);
+ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+}
EXPORT_SYMBOL(ieee80211_scan_completed);
static int ieee80211_start_sw_scan(struct ieee80211_local *local)
@@ -349,6 +364,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
int rc;
+ lockdep_assert_held(&local->mtx);
+
if (local->scan_req)
return -EBUSY;
@@ -396,19 +413,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
else
__set_bit(SCAN_SW_SCANNING, &local->scanning);
- /*
- * Kicking off the scan need not be protected,
- * only the scan variable stuff, since now
- * local->scan_req is assigned and other callers
- * will abort their scan attempts.
- *
- * This avoids too many locking dependencies
- * so that the scan completed calls have more
- * locking freedom.
- */
-
ieee80211_recalc_idle(local);
- mutex_unlock(&local->scan_mtx);
if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
@@ -416,8 +421,6 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
} else
rc = ieee80211_start_sw_scan(local);
- mutex_lock(&local->scan_mtx);
-
if (rc) {
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;
@@ -444,8 +447,8 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
-static int ieee80211_scan_state_decision(struct ieee80211_local *local,
- unsigned long *next_delay)
+static void ieee80211_scan_state_decision(struct ieee80211_local *local,
+ unsigned long *next_delay)
{
bool associated = false;
bool tx_empty = true;
@@ -455,12 +458,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
- /* if no more bands/channels left, complete scan and advance to the idle state */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
- ieee80211_scan_completed(&local->hw, false);
- return 1;
- }
-
/*
* check if at least one STA interface is associated,
* check if at least one STA interface has pending tx frames
@@ -532,7 +529,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local,
}
*next_delay = 0;
- return 0;
}
static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
@@ -648,21 +644,18 @@ void ieee80211_scan_work(struct work_struct *work)
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
unsigned long next_delay = 0;
+ bool aborted, hw_scan, finish;
- mutex_lock(&local->scan_mtx);
- if (!sdata || !local->scan_req) {
- mutex_unlock(&local->scan_mtx);
- return;
- }
+ mutex_lock(&local->mtx);
- if (local->hw_scan_req) {
- int rc = drv_hw_scan(local, sdata, local->hw_scan_req);
- mutex_unlock(&local->scan_mtx);
- if (rc)
- ieee80211_scan_completed(&local->hw, true);
- return;
+ if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) {
+ aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning);
+ goto out_complete;
}
+ if (!sdata || !local->scan_req)
+ goto out;
+
if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;
@@ -671,21 +664,21 @@ void ieee80211_scan_work(struct work_struct *work)
local->scan_sdata = NULL;
rc = __ieee80211_start_scan(sdata, req);
- mutex_unlock(&local->scan_mtx);
-
- if (rc)
- ieee80211_scan_completed(&local->hw, true);
- return;
+ if (rc) {
+ /* need to complete scan in cfg80211 */
+ local->scan_req = req;
+ aborted = true;
+ goto out_complete;
+ } else
+ goto out;
}
- mutex_unlock(&local->scan_mtx);
-
/*
* Avoid re-scheduling when the sdata is going away.
*/
if (!ieee80211_sdata_running(sdata)) {
- ieee80211_scan_completed(&local->hw, true);
- return;
+ aborted = true;
+ goto out_complete;
}
/*
@@ -695,8 +688,12 @@ void ieee80211_scan_work(struct work_struct *work)
do {
switch (local->next_scan_state) {
case SCAN_DECISION:
- if (ieee80211_scan_state_decision(local, &next_delay))
- return;
+ /* if no more bands/channels left, complete scan */
+ if (local->scan_channel_idx >= local->scan_req->n_channels) {
+ aborted = false;
+ goto out_complete;
+ }
+ ieee80211_scan_state_decision(local, &next_delay);
break;
case SCAN_SET_CHANNEL:
ieee80211_scan_state_set_channel(local, &next_delay);
@@ -714,6 +711,19 @@ void ieee80211_scan_work(struct work_struct *work)
} while (next_delay == 0);
ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
+ mutex_unlock(&local->mtx);
+ return;
+
+out_complete:
+ hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
+ finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan);
+ mutex_unlock(&local->mtx);
+ if (finish)
+ __ieee80211_scan_completed_finish(&local->hw, hw_scan);
+ return;
+
+out:
+ mutex_unlock(&local->mtx);
}
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
@@ -721,9 +731,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
{
int res;
- mutex_lock(&sdata->local->scan_mtx);
+ mutex_lock(&sdata->local->mtx);
res = __ieee80211_start_scan(sdata, req);
- mutex_unlock(&sdata->local->scan_mtx);
+ mutex_unlock(&sdata->local->mtx);
return res;
}
@@ -734,9 +744,9 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
int ret = -EBUSY;
- enum nl80211_band band;
+ enum ieee80211_band band;
- mutex_lock(&local->scan_mtx);
+ mutex_lock(&local->mtx);
/* busy scanning */
if (local->scan_req)
@@ -771,25 +781,44 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
unlock:
- mutex_unlock(&local->scan_mtx);
+ mutex_unlock(&local->mtx);
return ret;
}
+/*
+ * Only call this function when a scan can't be queued -- under RTNL.
+ */
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
bool abortscan;
-
- cancel_delayed_work_sync(&local->scan_work);
+ bool finish = false;
/*
- * Only call this function when a scan can't be
- * queued -- mostly at suspend under RTNL.
+ * We are only canceling software scan, or deferred scan that was not
+ * yet really started (see __ieee80211_start_scan ).
+ *
+ * Regarding hardware scan:
+ * - we can not call __ieee80211_scan_completed() as when
+ * SCAN_HW_SCANNING bit is set this function change
+ * local->hw_scan_req to operate on 5G band, what race with
+ * driver which can use local->hw_scan_req
+ *
+ * - we can not cancel scan_work since driver can schedule it
+ * by ieee80211_scan_completed(..., true) to finish scan
+ *
+ * Hence low lever driver is responsible for canceling HW scan.
*/
- mutex_lock(&local->scan_mtx);
- abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- (!local->scanning && local->scan_req);
- mutex_unlock(&local->scan_mtx);
+ mutex_lock(&local->mtx);
+ abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning);
if (abortscan)
- ieee80211_scan_completed(&local->hw, true);
+ finish = __ieee80211_scan_completed(&local->hw, true, false);
+ mutex_unlock(&local->mtx);
+
+ if (abortscan) {
+ /* The scan is canceled, but stop work from being pending */
+ cancel_delayed_work_sync(&local->scan_work);
+ }
+ if (finish)
+ __ieee80211_scan_completed_finish(&local->hw, false);
}