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.c375
1 files changed, 243 insertions, 132 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 2a8d09a..0399011 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -18,7 +18,6 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <net/mac80211.h>
-#include <net/iw_handler.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -26,7 +25,7 @@
#define IEEE80211_PROBE_DELAY (HZ / 33)
#define IEEE80211_CHANNEL_TIME (HZ / 33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
+#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8)
struct ieee80211_bss *
ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
@@ -121,23 +120,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
return bss;
}
-void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid,
- int freq, u8 *ssid, u8 ssid_len)
-{
- struct ieee80211_bss *bss;
- struct ieee80211_local *local = sdata->local;
-
- bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len);
- if (bss) {
- cfg80211_unlink_bss(local->hw.wiphy, (void *)bss);
- ieee80211_rx_bss_put(local, bss);
- }
-}
-
ieee80211_rx_result
-ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_mgmt *mgmt;
struct ieee80211_bss *bss;
u8 *elements;
@@ -278,7 +264,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
mutex_lock(&local->scan_mtx);
- if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) {
+ if (WARN_ON(!local->scanning)) {
mutex_unlock(&local->scan_mtx);
return;
}
@@ -288,16 +274,16 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
return;
}
- if (local->hw_scanning)
+ if (test_bit(SCAN_HW_SCANNING, &local->scanning))
ieee80211_restore_scan_ies(local);
- if (local->scan_req != &local->int_scan_req)
+ if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
+ local->scan_sdata = NULL;
- was_hw_scan = local->hw_scanning;
- local->hw_scanning = false;
- local->sw_scanning = false;
+ was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
+ local->scanning = 0;
local->scan_channel = NULL;
/* we only have to protect scan_req and hw/sw scan */
@@ -307,16 +293,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (was_hw_scan)
goto done;
- netif_tx_lock_bh(local->mdev);
- netif_addr_lock(local->mdev);
- local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
-
- netif_addr_unlock(local->mdev);
- netif_tx_unlock_bh(local->mdev);
+ ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
@@ -327,7 +304,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* Tell AP we're back */
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
+ if (sdata->u.mgd.associated) {
ieee80211_scan_ps_disable(sdata);
netif_tx_wake_all_queues(sdata->dev);
}
@@ -382,30 +359,24 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_bss_info_change_notify(
sdata, BSS_CHANGED_BEACON_ENABLED);
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
- netif_tx_stop_all_queues(sdata->dev);
- ieee80211_scan_ps_enable(sdata);
- }
- } else
+ /*
+ * only handle non-STA interfaces here, STA interfaces
+ * are handled in the scan state machine
+ */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
netif_tx_stop_all_queues(sdata->dev);
}
mutex_unlock(&local->iflist_mtx);
- local->scan_state = SCAN_SET_CHANNEL;
+ local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- netif_addr_lock_bh(local->mdev);
- local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
- netif_addr_unlock_bh(local->mdev);
+ ieee80211_configure_filter(local);
/* TODO: start scan as soon as all nullfunc frames are ACKed */
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- IEEE80211_CHANNEL_TIME);
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->scan_work,
+ IEEE80211_CHANNEL_TIME);
return 0;
}
@@ -441,20 +412,18 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
local->scan_req = req;
local->scan_sdata = sdata;
- if (req != &local->int_scan_req &&
+ if (req != local->int_scan_req &&
sdata->vif.type == NL80211_IFTYPE_STATION &&
- (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE ||
- ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE)) {
- /* actually wait for the assoc to finish/time out */
+ !list_empty(&ifmgd->work_list)) {
+ /* actually wait for the work it's doing to finish/time out */
set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
return 0;
}
if (local->ops->hw_scan)
- local->hw_scanning = true;
+ __set_bit(SCAN_HW_SCANNING, &local->scanning);
else
- local->sw_scanning = true;
+ __set_bit(SCAN_SW_SCANNING, &local->scanning);
/*
* Kicking off the scan need not be protected,
* only the scan variable stuff, since now
@@ -477,11 +446,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->scan_mtx);
if (rc) {
- if (local->ops->hw_scan) {
- local->hw_scanning = false;
+ if (local->ops->hw_scan)
ieee80211_restore_scan_ies(local);
- } else
- local->sw_scanning = false;
+ local->scanning = 0;
ieee80211_recalc_idle(local);
@@ -492,13 +459,195 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
return rc;
}
+static int ieee80211_scan_state_decision(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ bool associated = false;
+ struct ieee80211_sub_if_data *sdata;
+
+ /* 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 */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.mgd.associated) {
+ associated = true;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ if (local->scan_channel) {
+ /*
+ * we're currently scanning a different channel, let's
+ * switch back to the operating channel now if at least
+ * one interface is associated. Otherwise just scan the
+ * next channel
+ */
+ if (associated)
+ local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
+ else
+ local->next_scan_state = SCAN_SET_CHANNEL;
+ } else {
+ /*
+ * we're on the operating channel currently, let's
+ * leave that channel now to scan another one
+ */
+ local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
+ }
+
+ *next_delay = 0;
+ return 0;
+}
+
+static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ /*
+ * notify the AP about us leaving the channel and stop all STA interfaces
+ */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ netif_tx_stop_all_queues(sdata->dev);
+ if (sdata->u.mgd.associated)
+ ieee80211_scan_ps_enable(sdata);
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
+
+ /* advance to the next channel to be scanned */
+ *next_delay = HZ / 10;
+ local->next_scan_state = SCAN_SET_CHANNEL;
+}
+
+static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ /* switch back to the operating channel */
+ local->scan_channel = NULL;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ /*
+ * notify the AP about us being back and restart all STA interfaces
+ */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ /* Tell AP we're back */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.mgd.associated)
+ ieee80211_scan_ps_disable(sdata);
+ netif_tx_wake_all_queues(sdata->dev);
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
+
+ *next_delay = HZ / 5;
+ local->next_scan_state = SCAN_DECISION;
+}
+
+static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ int skip;
+ struct ieee80211_channel *chan;
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ skip = 0;
+ chan = local->scan_req->channels[local->scan_channel_idx];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ chan->flags & IEEE80211_CHAN_NO_IBSS))
+ skip = 1;
+
+ if (!skip) {
+ local->scan_channel = chan;
+ if (ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_CHANNEL))
+ skip = 1;
+ }
+
+ /* advance state machine to next channel/band */
+ local->scan_channel_idx++;
+
+ if (skip) {
+ /* if we skip this channel return to the decision state */
+ local->next_scan_state = SCAN_DECISION;
+ return;
+ }
+
+ /*
+ * Probe delay is used to update the NAV, cf. 11.1.3.2.2
+ * (which unfortunately doesn't say _why_ step a) is done,
+ * but it waits for the probe delay or until a frame is
+ * received - and the received frame would update the NAV).
+ * For now, we do not support waiting until a frame is
+ * received.
+ *
+ * In any case, it is not necessary for a passive scan.
+ */
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
+ !local->scan_req->n_ssids) {
+ *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+ local->next_scan_state = SCAN_DECISION;
+ return;
+ }
+
+ /* active scan, send probes */
+ *next_delay = IEEE80211_PROBE_DELAY;
+ local->next_scan_state = SCAN_SEND_PROBE;
+}
+
+static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ int i;
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ for (i = 0; i < local->scan_req->n_ssids; i++)
+ ieee80211_send_probe_req(
+ sdata, NULL,
+ local->scan_req->ssids[i].ssid,
+ local->scan_req->ssids[i].ssid_len,
+ local->scan_req->ie, local->scan_req->ie_len);
+
+ /*
+ * After sending probe requests, wait for probe responses
+ * on the channel.
+ */
+ *next_delay = IEEE80211_CHANNEL_TIME;
+ local->next_scan_state = SCAN_DECISION;
+}
+
void ieee80211_scan_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
- struct ieee80211_channel *chan;
- int skip, i;
unsigned long next_delay = 0;
mutex_lock(&local->scan_mtx);
@@ -507,11 +656,12 @@ void ieee80211_scan_work(struct work_struct *work)
return;
}
- if (local->scan_req && !(local->sw_scanning || local->hw_scanning)) {
+ if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;
local->scan_req = NULL;
+ local->scan_sdata = NULL;
rc = __ieee80211_start_scan(sdata, req);
mutex_unlock(&local->scan_mtx);
@@ -531,72 +681,32 @@ void ieee80211_scan_work(struct work_struct *work)
return;
}
- switch (local->scan_state) {
- case SCAN_SET_CHANNEL:
- /* if no more bands/channels left, complete scan */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
- ieee80211_scan_completed(&local->hw, false);
- return;
- }
- skip = 0;
- chan = local->scan_req->channels[local->scan_channel_idx];
-
- if (chan->flags & IEEE80211_CHAN_DISABLED ||
- (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS))
- skip = 1;
-
- if (!skip) {
- local->scan_channel = chan;
- if (ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_CHANNEL))
- skip = 1;
- }
-
- /* advance state machine to next channel/band */
- local->scan_channel_idx++;
-
- if (skip)
+ /*
+ * as long as no delay is required advance immediately
+ * without scheduling a new work
+ */
+ do {
+ switch (local->next_scan_state) {
+ case SCAN_DECISION:
+ if (ieee80211_scan_state_decision(local, &next_delay))
+ return;
break;
-
- /*
- * Probe delay is used to update the NAV, cf. 11.1.3.2.2
- * (which unfortunately doesn't say _why_ step a) is done,
- * but it waits for the probe delay or until a frame is
- * received - and the received frame would update the NAV).
- * For now, we do not support waiting until a frame is
- * received.
- *
- * In any case, it is not necessary for a passive scan.
- */
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
- !local->scan_req->n_ssids) {
- next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+ case SCAN_SET_CHANNEL:
+ ieee80211_scan_state_set_channel(local, &next_delay);
+ break;
+ case SCAN_SEND_PROBE:
+ ieee80211_scan_state_send_probe(local, &next_delay);
+ break;
+ case SCAN_LEAVE_OPER_CHANNEL:
+ ieee80211_scan_state_leave_oper_channel(local, &next_delay);
+ break;
+ case SCAN_ENTER_OPER_CHANNEL:
+ ieee80211_scan_state_enter_oper_channel(local, &next_delay);
break;
}
+ } while (next_delay == 0);
- next_delay = IEEE80211_PROBE_DELAY;
- local->scan_state = SCAN_SEND_PROBE;
- break;
- case SCAN_SEND_PROBE:
- for (i = 0; i < local->scan_req->n_ssids; i++)
- ieee80211_send_probe_req(
- sdata, NULL,
- local->scan_req->ssids[i].ssid,
- local->scan_req->ssids[i].ssid_len,
- local->scan_req->ie, local->scan_req->ie_len);
-
- /*
- * After sending probe requests, wait for probe responses
- * on the channel.
- */
- next_delay = IEEE80211_CHANNEL_TIME;
- local->scan_state = SCAN_SET_CHANNEL;
- break;
- }
-
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- next_delay);
+ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
}
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
@@ -623,10 +733,10 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
if (local->scan_req)
goto unlock;
- memcpy(local->int_scan_req.ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
- local->int_scan_req.ssids[0].ssid_len = ssid_len;
+ memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
+ local->int_scan_req->ssids[0].ssid_len = ssid_len;
- ret = __ieee80211_start_scan(sdata, &sdata->local->int_scan_req);
+ ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
unlock:
mutex_unlock(&local->scan_mtx);
return ret;
@@ -634,7 +744,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
- bool swscan;
+ bool abortscan;
cancel_delayed_work_sync(&local->scan_work);
@@ -643,9 +753,10 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
* queued -- mostly at suspend under RTNL.
*/
mutex_lock(&local->scan_mtx);
- swscan = local->sw_scanning;
+ abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ (!local->scanning && local->scan_req);
mutex_unlock(&local->scan_mtx);
- if (swscan)
+ if (abortscan)
ieee80211_scan_completed(&local->hw, true);
}
OpenPOWER on IntegriCloud