* published by the Free Software Foundation.
*/
-/* TODO: figure out how to avoid that the "current BSS" expires */
-
#include <linux/wireless.h>
#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"
bss->dtim_period = tim_ie->dtim_period;
}
- /* set default value for buggy APs */
- if (!elems->tim || bss->dtim_period == 0)
+ /* set default value for buggy AP/no TIM element */
+ if (bss->dtim_period == 0)
bss->dtim_period = 1;
bss->supp_rates_len = 0;
return RX_QUEUED;
}
+/* return false if no more work */
+static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
+{
+ struct cfg80211_scan_request *req = local->scan_req;
+ enum ieee80211_band band;
+ int i, ielen, n_chans;
+
+ do {
+ if (local->hw_scan_band == IEEE80211_NUM_BANDS)
+ return false;
+
+ band = local->hw_scan_band;
+ n_chans = 0;
+ for (i = 0; i < req->n_channels; i++) {
+ if (req->channels[i]->band == band) {
+ local->hw_scan_req->channels[n_chans] =
+ req->channels[i];
+ n_chans++;
+ }
+ }
+
+ local->hw_scan_band++;
+ } while (!n_chans);
+
+ 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);
+ local->hw_scan_req->ie_len = ielen;
+
+ return true;
+}
+
/*
* inform AP that we will go to sleep so that it will buffer the frames
* while we scan
}
}
-static void ieee80211_restore_scan_ies(struct ieee80211_local *local)
-{
- kfree(local->scan_req->ie);
- local->scan_req->ie = local->orig_ies;
- local->scan_req->ie_len = local->orig_ies_len;
-}
-
void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
{
struct ieee80211_local *local = hw_to_local(hw);
return;
}
- if (test_bit(SCAN_HW_SCANNING, &local->scanning))
- ieee80211_restore_scan_ies(local);
+ 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;
+ }
- if (local->scan_req != &local->int_scan_req)
+ kfree(local->hw_scan_req);
+ local->hw_scan_req = NULL;
+
+ 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 = test_bit(SCAN_HW_SCANNING, &local->scanning);
local->scanning = 0;
local->scan_channel = NULL;
if (was_hw_scan)
goto done;
- spin_lock_bh(&local->filter_lock);
- local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mc_count,
- local->mc_list);
- spin_unlock_bh(&local->filter_lock);
+ ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated) {
ieee80211_scan_ps_disable(sdata);
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
}
} else
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
/* re-enable beaconing */
if (sdata->vif.type == NL80211_IFTYPE_AP ||
* are handled in the scan state machine
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION)
- netif_tx_stop_all_queues(sdata->dev);
+ netif_stop_queue(sdata->dev);
}
mutex_unlock(&local->iflist_mtx);
- local->scan_state = SCAN_DECISION;
+ local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- spin_lock_bh(&local->filter_lock);
- local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mc_count,
- local->mc_list);
- spin_unlock_bh(&local->filter_lock);
+ 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;
}
if (local->ops->hw_scan) {
u8 *ies;
- int ielen;
- ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len + req->ie_len, GFP_KERNEL);
- if (!ies)
+ local->hw_scan_req = kmalloc(
+ sizeof(*local->hw_scan_req) +
+ req->n_channels * sizeof(req->channels[0]) +
+ 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
+ req->ie_len, GFP_KERNEL);
+ if (!local->hw_scan_req)
return -ENOMEM;
- ielen = ieee80211_build_preq_ies(local, ies,
- req->ie, req->ie_len);
- local->orig_ies = req->ie;
- local->orig_ies_len = req->ie_len;
- req->ie = ies;
- req->ie_len = ielen;
+ local->hw_scan_req->ssids = req->ssids;
+ local->hw_scan_req->n_ssids = req->n_ssids;
+ ies = (u8 *)local->hw_scan_req +
+ sizeof(*local->hw_scan_req) +
+ req->n_channels * sizeof(req->channels[0]);
+ local->hw_scan_req->ie = ies;
+
+ local->hw_scan_band = 0;
}
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 &&
!list_empty(&ifmgd->work_list)) {
/* actually wait for the work it's doing to finish/time out */
ieee80211_recalc_idle(local);
mutex_unlock(&local->scan_mtx);
- if (local->ops->hw_scan)
- rc = drv_hw_scan(local, local->scan_req);
- else
+ if (local->ops->hw_scan) {
+ WARN_ON(!ieee80211_prep_hw_scan(local));
+ rc = drv_hw_scan(local, local->hw_scan_req);
+ } else
rc = ieee80211_start_sw_scan(local);
mutex_lock(&local->scan_mtx);
if (rc) {
- if (local->ops->hw_scan)
- ieee80211_restore_scan_ies(local);
+ kfree(local->hw_scan_req);
+ local->hw_scan_req = NULL;
local->scanning = 0;
ieee80211_recalc_idle(local);
* next channel
*/
if (associated)
- local->scan_state = SCAN_ENTER_OPER_CHANNEL;
+ local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
else
- local->scan_state = SCAN_SET_CHANNEL;
+ 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->scan_state = SCAN_LEAVE_OPER_CHANNEL;
+ local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
}
*next_delay = 0;
continue;
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- netif_tx_stop_all_queues(sdata->dev);
+ netif_stop_queue(sdata->dev);
if (sdata->u.mgd.associated)
ieee80211_scan_ps_enable(sdata);
}
/* advance to the next channel to be scanned */
*next_delay = HZ / 10;
- local->scan_state = SCAN_SET_CHANNEL;
+ local->next_scan_state = SCAN_SET_CHANNEL;
}
static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated)
ieee80211_scan_ps_disable(sdata);
- netif_tx_wake_all_queues(sdata->dev);
+ netif_wake_queue(sdata->dev);
}
}
mutex_unlock(&local->iflist_mtx);
__clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
*next_delay = HZ / 5;
- local->scan_state = SCAN_DECISION;
+ local->next_scan_state = SCAN_DECISION;
}
static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
/* advance state machine to next channel/band */
local->scan_channel_idx++;
- if (skip)
+ 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
if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
!local->scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
- local->scan_state = SCAN_DECISION;
+ local->next_scan_state = SCAN_DECISION;
return;
}
/* active scan, send probes */
*next_delay = IEEE80211_PROBE_DELAY;
- local->scan_state = SCAN_SEND_PROBE;
+ local->next_scan_state = SCAN_SEND_PROBE;
}
static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
* on the channel.
*/
*next_delay = IEEE80211_CHANNEL_TIME;
- local->scan_state = SCAN_DECISION;
+ local->next_scan_state = SCAN_DECISION;
}
void ieee80211_scan_work(struct work_struct *work)
return;
}
+ if (local->hw_scan_req) {
+ int rc = drv_hw_scan(local, local->hw_scan_req);
+ mutex_unlock(&local->scan_mtx);
+ if (rc)
+ ieee80211_scan_completed(&local->hw, true);
+ return;
+ }
+
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);
* without scheduling a new work
*/
do {
- switch (local->scan_state) {
+ switch (local->next_scan_state) {
case SCAN_DECISION:
if (ieee80211_scan_state_decision(local, &next_delay))
return;
}
} while (next_delay == 0);
- 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,
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;
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
- bool swscan;
+ bool abortscan;
cancel_delayed_work_sync(&local->scan_work);
* queued -- mostly at suspend under RTNL.
*/
mutex_lock(&local->scan_mtx);
- swscan = test_bit(SCAN_SW_SCANNING, &local->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);
}