Commit b23b025f authored by Ben Greear's avatar Ben Greear Committed by John W. Linville

mac80211: Optimize scans on current operating channel.

This should decrease un-necessary flushes, on/off channel work,
and channel changes in cases where the only scanned channel is
the current operating channel.

* Removes SCAN_OFF_CHANNEL flag, uses SDATA_STATE_OFFCHANNEL
  and is-scanning flags instead.

* Add helper method to determine if we are currently configured
  for the operating channel.

* Do no blindly go off/on channel in work.c  Instead, only call
  appropriate on/off code when we really need to change channels.
  Always enable offchannel-ps mode when starting work,
  and disable it when we are done.

* Consolidate ieee80211_offchannel_stop_station and
  ieee80211_offchannel_stop_beaconing, call it
  ieee80211_offchannel_stop_vifs instead.

* Accept non-beacon frames when scanning on operating channel.

* Scan state machine optimized to minimize on/off channel
  transitions.  Also, when going on-channel, go ahead and
  re-enable beaconing.  We're going to be there for 200ms,
  so seems like some useful beaconing could happen.
  Always enable offchannel-ps mode when starting software
  scan, and disable it when we are done.

* Grab local->mtx earlier in __ieee80211_scan_completed_finish
  so that we are protected when calling hw_config(), etc.

* Pass probe-responses up the stack if scanning on local
  channel, so that mlme can take a look.
Signed-off-by: default avatarBen Greear <greearb@candelatech.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent cb8d61de
...@@ -655,8 +655,6 @@ struct tpt_led_trigger { ...@@ -655,8 +655,6 @@ struct tpt_led_trigger {
* well be on the operating channel * well be on the operating channel
* @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
* determine if we are on the operating channel or not * determine if we are on the operating channel or not
* @SCAN_OFF_CHANNEL: We're off our operating channel for scanning,
* gets only set in conjunction with SCAN_SW_SCANNING
* @SCAN_COMPLETED: Set for our scan work function when the driver reported * @SCAN_COMPLETED: Set for our scan work function when the driver reported
* that the scan completed. * that the scan completed.
* @SCAN_ABORTED: Set for our scan work function when the driver reported * @SCAN_ABORTED: Set for our scan work function when the driver reported
...@@ -665,7 +663,6 @@ struct tpt_led_trigger { ...@@ -665,7 +663,6 @@ struct tpt_led_trigger {
enum { enum {
SCAN_SW_SCANNING, SCAN_SW_SCANNING,
SCAN_HW_SCANNING, SCAN_HW_SCANNING,
SCAN_OFF_CHANNEL,
SCAN_COMPLETED, SCAN_COMPLETED,
SCAN_ABORTED, SCAN_ABORTED,
}; };
...@@ -1148,10 +1145,14 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local, ...@@ -1148,10 +1145,14 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss); struct ieee80211_bss *bss);
/* off-channel helpers */ /* off-channel helpers */
void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
void ieee80211_offchannel_stop_station(struct ieee80211_local *local); void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
bool tell_ap);
void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
bool offchannel_ps_enable);
void ieee80211_offchannel_return(struct ieee80211_local *local, void ieee80211_offchannel_return(struct ieee80211_local *local,
bool enable_beaconing); bool enable_beaconing,
bool offchannel_ps_disable);
void ieee80211_hw_roc_setup(struct ieee80211_local *local); void ieee80211_hw_roc_setup(struct ieee80211_local *local);
/* interface handling */ /* interface handling */
......
...@@ -98,6 +98,41 @@ static void ieee80211_reconfig_filter(struct work_struct *work) ...@@ -98,6 +98,41 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
} }
/*
* Returns true if we are logically configured to be on
* the operating channel AND the hardware-conf is currently
* configured on the operating channel. Compares channel-type
* as well.
*/
bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
{
struct ieee80211_channel *chan, *scan_chan;
enum nl80211_channel_type channel_type;
/* This logic needs to match logic in ieee80211_hw_config */
if (local->scan_channel) {
chan = local->scan_channel;
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
chan = scan_chan = local->tmp_channel;
channel_type = local->tmp_channel_type;
} else {
chan = local->oper_channel;
channel_type = local->_oper_channel_type;
}
if (chan != local->oper_channel ||
channel_type != local->_oper_channel_type)
return false;
/* Check current hardware-config against oper_channel. */
if ((local->oper_channel != local->hw.conf.channel) ||
(local->_oper_channel_type != local->hw.conf.channel_type))
return false;
return true;
}
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
{ {
struct ieee80211_channel *chan, *scan_chan; struct ieee80211_channel *chan, *scan_chan;
...@@ -110,21 +145,27 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) ...@@ -110,21 +145,27 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
scan_chan = local->scan_channel; scan_chan = local->scan_channel;
/* If this off-channel logic ever changes, ieee80211_on_oper_channel
* may need to change as well.
*/
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (scan_chan) { if (scan_chan) {
chan = scan_chan; chan = scan_chan;
channel_type = NL80211_CHAN_NO_HT; channel_type = NL80211_CHAN_NO_HT;
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; } else if (local->tmp_channel) {
} else if (local->tmp_channel &&
local->oper_channel != local->tmp_channel) {
chan = scan_chan = local->tmp_channel; chan = scan_chan = local->tmp_channel;
channel_type = local->tmp_channel_type; channel_type = local->tmp_channel_type;
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
} else { } else {
chan = local->oper_channel; chan = local->oper_channel;
channel_type = local->_oper_channel_type; channel_type = local->_oper_channel_type;
local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
} }
if (chan != local->oper_channel ||
channel_type != local->_oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (offchannel_flag || chan != local->hw.conf.channel || if (offchannel_flag || chan != local->hw.conf.channel ||
...@@ -231,7 +272,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, ...@@ -231,7 +272,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
if (changed & BSS_CHANGED_BEACON_ENABLED) { if (changed & BSS_CHANGED_BEACON_ENABLED) {
if (local->quiescing || !ieee80211_sdata_running(sdata) || if (local->quiescing || !ieee80211_sdata_running(sdata) ||
test_bit(SCAN_SW_SCANNING, &local->scanning)) { test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) {
sdata->vif.bss_conf.enable_beacon = false; sdata->vif.bss_conf.enable_beacon = false;
} else { } else {
/* /*
......
...@@ -17,10 +17,14 @@ ...@@ -17,10 +17,14 @@
#include "driver-trace.h" #include "driver-trace.h"
/* /*
* inform AP that we will go to sleep so that it will buffer the frames * Tell our hardware to disable PS.
* while we scan * Optionally inform AP that we will go to sleep so that it will buffer
* the frames while we are doing off-channel work. This is optional
* because we *may* be doing work on-operating channel, and want our
* hardware unconditionally awake, but still let the AP send us normal frames.
*/ */
static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata,
bool tell_ap)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
...@@ -41,8 +45,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) ...@@ -41,8 +45,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
} }
if (!(local->offchannel_ps_enabled) || if (tell_ap && (!local->offchannel_ps_enabled ||
!(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)))
/* /*
* If power save was enabled, no need to send a nullfunc * If power save was enabled, no need to send a nullfunc
* frame because AP knows that we are sleeping. But if the * frame because AP knows that we are sleeping. But if the
...@@ -77,6 +81,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) ...@@ -77,6 +81,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
* we are sleeping, let's just enable power save mode in * we are sleeping, let's just enable power save mode in
* hardware. * hardware.
*/ */
/* TODO: Only set hardware if CONF_PS changed?
* TODO: Should we set offchannel_ps_enabled to false?
*/
local->hw.conf.flags |= IEEE80211_CONF_PS; local->hw.conf.flags |= IEEE80211_CONF_PS;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
} else if (local->hw.conf.dynamic_ps_timeout > 0) { } else if (local->hw.conf.dynamic_ps_timeout > 0) {
...@@ -95,63 +102,61 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) ...@@ -95,63 +102,61 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
ieee80211_sta_reset_conn_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata);
} }
void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
bool offchannel_ps_enable)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
/*
* notify the AP about us leaving the channel and stop all
* STA interfaces.
*/
mutex_lock(&local->iflist_mtx); mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
continue; continue;
/* disable beaconing */ if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
/* Check to see if we should disable beaconing. */
if (sdata->vif.type == NL80211_IFTYPE_AP || if (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_ADHOC ||
sdata->vif.type == NL80211_IFTYPE_MESH_POINT) sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
ieee80211_bss_info_change_notify( ieee80211_bss_info_change_notify(
sdata, BSS_CHANGED_BEACON_ENABLED); sdata, BSS_CHANGED_BEACON_ENABLED);
/* if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
* only handle non-STA interfaces here, STA interfaces
* are handled in ieee80211_offchannel_stop_station(),
* e.g., from the background scan state machine.
*
* In addition, do not stop monitor interface to allow it to be
* used from user space controlled off-channel operations.
*/
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
sdata->vif.type != NL80211_IFTYPE_MONITOR) {
set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
netif_tx_stop_all_queues(sdata->dev); netif_tx_stop_all_queues(sdata->dev);
if (offchannel_ps_enable &&
(sdata->vif.type == NL80211_IFTYPE_STATION) &&
sdata->u.mgd.associated)
ieee80211_offchannel_ps_enable(sdata, true);
} }
} }
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
} }
void ieee80211_offchannel_stop_station(struct ieee80211_local *local) void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
bool tell_ap)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
/*
* notify the AP about us leaving the channel and stop all STA interfaces
*/
mutex_lock(&local->iflist_mtx); mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) { list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
continue; continue;
if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->vif.type == NL80211_IFTYPE_STATION &&
set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); sdata->u.mgd.associated)
netif_tx_stop_all_queues(sdata->dev); ieee80211_offchannel_ps_enable(sdata, tell_ap);
if (sdata->u.mgd.associated)
ieee80211_offchannel_ps_enable(sdata);
}
} }
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
} }
void ieee80211_offchannel_return(struct ieee80211_local *local, void ieee80211_offchannel_return(struct ieee80211_local *local,
bool enable_beaconing) bool enable_beaconing,
bool offchannel_ps_disable)
{ {
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
...@@ -161,7 +166,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, ...@@ -161,7 +166,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
continue; continue;
/* Tell AP we're back */ /* Tell AP we're back */
if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (offchannel_ps_disable &&
sdata->vif.type == NL80211_IFTYPE_STATION) {
if (sdata->u.mgd.associated) if (sdata->u.mgd.associated)
ieee80211_offchannel_ps_disable(sdata); ieee80211_offchannel_ps_disable(sdata);
} }
...@@ -181,7 +187,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, ...@@ -181,7 +187,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
netif_tx_wake_all_queues(sdata->dev); netif_tx_wake_all_queues(sdata->dev);
} }
/* re-enable beaconing */ /* Check to see if we should re-enable beaconing */
if (enable_beaconing && if (enable_beaconing &&
(sdata->vif.type == NL80211_IFTYPE_AP || (sdata->vif.type == NL80211_IFTYPE_AP ||
sdata->vif.type == NL80211_IFTYPE_ADHOC || sdata->vif.type == NL80211_IFTYPE_ADHOC ||
......
...@@ -409,16 +409,10 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) ...@@ -409,16 +409,10 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN))) if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
return RX_CONTINUE; return RX_CONTINUE;
if (test_bit(SCAN_HW_SCANNING, &local->scanning)) if (test_bit(SCAN_HW_SCANNING, &local->scanning) ||
test_bit(SCAN_SW_SCANNING, &local->scanning))
return ieee80211_scan_rx(rx->sdata, skb); return ieee80211_scan_rx(rx->sdata, skb);
if (test_bit(SCAN_SW_SCANNING, &local->scanning)) {
/* drop all the other packets during a software scan anyway */
if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
dev_kfree_skb(skb);
return RX_QUEUED;
}
/* scanning finished during invoking of handlers */ /* scanning finished during invoking of handlers */
I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
...@@ -2793,7 +2787,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ...@@ -2793,7 +2787,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
local->dot11ReceivedFragmentCount++; local->dot11ReceivedFragmentCount++;
if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
test_bit(SCAN_OFF_CHANNEL, &local->scanning))) test_bit(SCAN_SW_SCANNING, &local->scanning)))
status->rx_flags |= IEEE80211_RX_IN_SCAN; status->rx_flags |= IEEE80211_RX_IN_SCAN;
if (ieee80211_is_mgmt(fc)) if (ieee80211_is_mgmt(fc))
......
...@@ -212,6 +212,14 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) ...@@ -212,6 +212,14 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (bss) if (bss)
ieee80211_rx_bss_put(sdata->local, bss); ieee80211_rx_bss_put(sdata->local, bss);
/* If we are on-operating-channel, and this packet is for the
* current channel, pass the pkt on up the stack so that
* the rest of the stack can make use of it.
*/
if (ieee80211_cfg_on_oper_channel(sdata->local)
&& (channel == sdata->local->oper_channel))
return RX_CONTINUE;
dev_kfree_skb(skb); dev_kfree_skb(skb);
return RX_QUEUED; return RX_QUEUED;
} }
...@@ -293,15 +301,31 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, ...@@ -293,15 +301,31 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw,
bool was_hw_scan) bool was_hw_scan)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
bool on_oper_chan;
bool enable_beacons = false;
mutex_lock(&local->mtx);
on_oper_chan = ieee80211_cfg_on_oper_channel(local);
if (was_hw_scan || !on_oper_chan) {
if (WARN_ON(local->scan_channel))
local->scan_channel = NULL;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
if (!was_hw_scan) { if (!was_hw_scan) {
bool on_oper_chan2;
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
drv_sw_scan_complete(local); drv_sw_scan_complete(local);
ieee80211_offchannel_return(local, true); on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
/* We should always be on-channel at this point. */
WARN_ON(!on_oper_chan2);
if (on_oper_chan2 && (on_oper_chan != on_oper_chan2))
enable_beacons = true;
ieee80211_offchannel_return(local, enable_beacons, true);
} }
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local); ieee80211_recalc_idle(local);
mutex_unlock(&local->mtx); mutex_unlock(&local->mtx);
...@@ -341,13 +365,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) ...@@ -341,13 +365,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
*/ */
drv_sw_scan_start(local); drv_sw_scan_start(local);
ieee80211_offchannel_stop_beaconing(local);
local->leave_oper_channel_time = 0; local->leave_oper_channel_time = 0;
local->next_scan_state = SCAN_DECISION; local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0; local->scan_channel_idx = 0;
drv_flush(local, false); /* We always want to use off-channel PS, even if we
* are not really leaving oper-channel. Don't
* tell the AP though, as long as we are on-channel.
*/
ieee80211_offchannel_enable_all_ps(local, false);
ieee80211_configure_filter(local); ieee80211_configure_filter(local);
...@@ -487,7 +513,21 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -487,7 +513,21 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
} }
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
if (local->scan_channel) { next_chan = local->scan_req->channels[local->scan_channel_idx];
if (ieee80211_cfg_on_oper_channel(local)) {
/* We're currently on operating channel. */
if ((next_chan == local->oper_channel) &&
(local->_oper_channel_type == NL80211_CHAN_NO_HT))
/* We don't need to move off of operating channel. */
local->next_scan_state = SCAN_SET_CHANNEL;
else
/*
* We do need to leave operating channel, as next
* scan is somewhere else.
*/
local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
} else {
/* /*
* we're currently scanning a different channel, let's * we're currently scanning a different channel, let's
* see if we can scan another channel without interfering * see if we can scan another channel without interfering
...@@ -503,7 +543,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -503,7 +543,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
* *
* Otherwise switch back to the operating channel. * Otherwise switch back to the operating channel.
*/ */
next_chan = local->scan_req->channels[local->scan_channel_idx];
bad_latency = time_after(jiffies + bad_latency = time_after(jiffies +
ieee80211_scan_get_channel_time(next_chan), ieee80211_scan_get_channel_time(next_chan),
...@@ -521,12 +560,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -521,12 +560,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
else else
local->next_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->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
} }
*next_delay = 0; *next_delay = 0;
...@@ -535,9 +568,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, ...@@ -535,9 +568,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
unsigned long *next_delay) unsigned long *next_delay)
{ {
ieee80211_offchannel_stop_station(local); /* PS will already be in off-channel mode,
* we do that once at the beginning of scanning.
__set_bit(SCAN_OFF_CHANNEL, &local->scanning); */
ieee80211_offchannel_stop_vifs(local, false);
/* /*
* What if the nullfunc frames didn't arrive? * What if the nullfunc frames didn't arrive?
...@@ -560,15 +594,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca ...@@ -560,15 +594,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca
{ {
/* switch back to the operating channel */ /* switch back to the operating channel */
local->scan_channel = NULL; local->scan_channel = NULL;
if (!ieee80211_cfg_on_oper_channel(local))
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
/* /*
* Only re-enable station mode interface now; beaconing will be * Re-enable vifs and beaconing. Leave PS
* re-enabled once the full scan has been completed. * in off-channel state..will put that back
* on-channel at the end of scanning.
*/ */
ieee80211_offchannel_return(local, false); ieee80211_offchannel_return(local, true, false);
__clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
*next_delay = HZ / 5; *next_delay = HZ / 5;
local->next_scan_state = SCAN_DECISION; local->next_scan_state = SCAN_DECISION;
...@@ -584,6 +618,10 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, ...@@ -584,6 +618,10 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
chan = local->scan_req->channels[local->scan_channel_idx]; chan = local->scan_req->channels[local->scan_channel_idx];
local->scan_channel = chan; local->scan_channel = chan;
/* Only call hw-config if we really need to change channels. */
if ((chan != local->hw.conf.channel) ||
(local->hw.conf.channel_type != NL80211_CHAN_NO_HT))
if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1; skip = 1;
......
...@@ -257,7 +257,8 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) ...@@ -257,7 +257,8 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
return TX_CONTINUE; return TX_CONTINUE;
if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) && if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) &&
test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) &&
!ieee80211_is_probe_req(hdr->frame_control) && !ieee80211_is_probe_req(hdr->frame_control) &&
!ieee80211_is_nullfunc(hdr->frame_control)) !ieee80211_is_nullfunc(hdr->frame_control))
/* /*
......
...@@ -924,18 +924,44 @@ static void ieee80211_work_work(struct work_struct *work) ...@@ -924,18 +924,44 @@ static void ieee80211_work_work(struct work_struct *work)
} }
if (!started && !local->tmp_channel) { if (!started && !local->tmp_channel) {
/* bool on_oper_chan;
* TODO: could optimize this by leaving the bool tmp_chan_changed = false;
* station vifs in awake mode if they bool on_oper_chan2;
* happen to be on the same channel as on_oper_chan = ieee80211_cfg_on_oper_channel(local);
* the requested channel if (local->tmp_channel)
*/ if ((local->tmp_channel != wk->chan) ||
ieee80211_offchannel_stop_beaconing(local); (local->tmp_channel_type != wk->chan_type))
ieee80211_offchannel_stop_station(local); tmp_chan_changed = true;
local->tmp_channel = wk->chan; local->tmp_channel = wk->chan;
local->tmp_channel_type = wk->chan_type; local->tmp_channel_type = wk->chan_type;
/*
* Leave the station vifs in awake mode if they
* happen to be on the same channel as
* the requested channel.
*/
on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
if (on_oper_chan != on_oper_chan2) {
if (on_oper_chan2) {
/* going off oper channel, PS too */
ieee80211_offchannel_stop_vifs(local,
true);
ieee80211_hw_config(local, 0);
} else {
/* going on channel, but leave PS
* off-channel. */
ieee80211_hw_config(local, 0); ieee80211_hw_config(local, 0);
ieee80211_offchannel_return(local,
true,
false);
}
} else if (tmp_chan_changed)
/* Still off-channel, but on some other
* channel, so update hardware.
* PS should already be off-channel.
*/
ieee80211_hw_config(local, 0);
started = true; started = true;
wk->timeout = jiffies; wk->timeout = jiffies;
} }
...@@ -1011,9 +1037,27 @@ static void ieee80211_work_work(struct work_struct *work) ...@@ -1011,9 +1037,27 @@ static void ieee80211_work_work(struct work_struct *work)
} }
if (!remain_off_channel && local->tmp_channel) { if (!remain_off_channel && local->tmp_channel) {
bool on_oper_chan = ieee80211_cfg_on_oper_channel(local);
local->tmp_channel = NULL; local->tmp_channel = NULL;
/* If tmp_channel wasn't operating channel, then
* we need to go back on-channel.
* NOTE: If we can ever be here while scannning,
* or if the hw_config() channel config logic changes,
* then we may need to do a more thorough check to see if
* we still need to do a hardware config. Currently,
* we cannot be here while scanning, however.
*/
if (ieee80211_cfg_on_oper_channel(local) && !on_oper_chan)
ieee80211_hw_config(local, 0); ieee80211_hw_config(local, 0);
ieee80211_offchannel_return(local, true);
/* At the least, we need to disable offchannel_ps,
* so just go ahead and run the entire offchannel
* return logic here. We *could* skip enabling
* beaconing if we were already on-oper-channel
* as a future optimization.
*/
ieee80211_offchannel_return(local, true, true);
/* give connection some time to breathe */ /* give connection some time to breathe */
run_again(local, jiffies + HZ/2); run_again(local, jiffies + HZ/2);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment