From 67de50b811ecec877c8ca1a9309f0567141b9269 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 1 Dec 2011 09:10:14 -0500 Subject: [PATCH] Apply patch to revert mac80211 scan optimizations (rhbz #731365) --- kernel.spec | 11 +- mac80211_offchannel_rework_revert.patch | 594 ++++++++++++++++++++++++ 2 files changed, 604 insertions(+), 1 deletion(-) create mode 100644 mac80211_offchannel_rework_revert.patch diff --git a/kernel.spec b/kernel.spec index d60e4692a..ddef92524 100644 --- a/kernel.spec +++ b/kernel.spec @@ -54,7 +54,7 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 3 +%global baserelease 4 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -794,6 +794,9 @@ Patch21101: hpsa-add-irqf-shared.patch #rhbz 755154 Patch21200: rtlwifi-fix-lps_lock-deadlock.patch +#rhbz 731365 +Patch21220: mac80211_offchannel_rework_revert.patch + %endif BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root @@ -1457,6 +1460,9 @@ ApplyPatch hpsa-add-irqf-shared.patch #rhbz 755154 ApplyPatch rtlwifi-fix-lps_lock-deadlock.patch +#rhbz 731365 +ApplyPatch mac80211_offchannel_rework_revert.patch + # END OF PATCH APPLICATIONS %endif @@ -2188,6 +2194,9 @@ fi # and build. %changelog +* Thu Dec 01 2011 Josh Boyer +- Apply patch to revert mac80211 scan optimizations (rhbz #731365) + * Wed Nov 30 2011 Josh Boyer - Include commit 3940d6185 from JJ Ding in elantech.patch diff --git a/mac80211_offchannel_rework_revert.patch b/mac80211_offchannel_rework_revert.patch new file mode 100644 index 000000000..859799714 --- /dev/null +++ b/mac80211_offchannel_rework_revert.patch @@ -0,0 +1,594 @@ +diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h +index 9fab144..4f8cf7f 100644 +--- a/net/mac80211/ieee80211_i.h ++++ b/net/mac80211/ieee80211_i.h +@@ -694,6 +694,8 @@ struct tpt_led_trigger { + * well be on the operating channel + * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to + * 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 + * that the scan completed. + * @SCAN_ABORTED: Set for our scan work function when the driver reported +@@ -702,6 +704,7 @@ struct tpt_led_trigger { + enum { + SCAN_SW_SCANNING, + SCAN_HW_SCANNING, ++ SCAN_OFF_CHANNEL, + SCAN_COMPLETED, + SCAN_ABORTED, + }; +@@ -1197,14 +1200,10 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); + void ieee80211_sched_scan_stopped_work(struct work_struct *work); + + /* off-channel helpers */ +-bool ieee80211_cfg_on_oper_channel(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_stop_beaconing(struct ieee80211_local *local); ++void ieee80211_offchannel_stop_station(struct ieee80211_local *local); + void ieee80211_offchannel_return(struct ieee80211_local *local, +- bool enable_beaconing, +- bool offchannel_ps_disable); ++ bool enable_beaconing); + void ieee80211_hw_roc_setup(struct ieee80211_local *local); + + /* interface handling */ +diff --git a/net/mac80211/main.c b/net/mac80211/main.c +index acb4423..2d607e5 100644 +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -92,47 +92,6 @@ static void ieee80211_reconfig_filter(struct work_struct *work) + 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; +- /* If scanning on oper channel, use whatever channel-type +- * is currently in use. +- */ +- if (chan == local->oper_channel) +- channel_type = local->_oper_channel_type; +- else +- 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) + { + struct ieee80211_channel *chan, *scan_chan; +@@ -145,9 +104,6 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) + + 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; + if (scan_chan) { + chan = scan_chan; +@@ -158,19 +114,17 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) + channel_type = local->_oper_channel_type; + else + channel_type = NL80211_CHAN_NO_HT; +- } else if (local->tmp_channel) { ++ local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; ++ } else if (local->tmp_channel && ++ local->oper_channel != local->tmp_channel) { + chan = scan_chan = local->tmp_channel; + channel_type = local->tmp_channel_type; ++ local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; + } else { + chan = local->oper_channel; + channel_type = local->_oper_channel_type; +- } +- +- 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; + +@@ -279,7 +233,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + if (local->quiescing || !ieee80211_sdata_running(sdata) || +- test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) { ++ test_bit(SCAN_SW_SCANNING, &local->scanning)) { + sdata->vif.bss_conf.enable_beacon = false; + } else { + /* +diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c +index 13427b1..b4e5267 100644 +--- a/net/mac80211/offchannel.c ++++ b/net/mac80211/offchannel.c +@@ -17,14 +17,10 @@ + #include "driver-trace.h" + + /* +- * Tell our hardware to disable PS. +- * 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. ++ * inform AP that we will go to sleep so that it will buffer the frames ++ * while we scan + */ +-static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata, +- bool tell_ap) ++static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) + { + struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; +@@ -45,8 +41,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata, + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } + +- if (tell_ap && (!local->offchannel_ps_enabled || +- !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))) ++ if (!(local->offchannel_ps_enabled) || ++ !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) + /* + * If power save was enabled, no need to send a nullfunc + * frame because AP knows that we are sleeping. But if the +@@ -81,9 +77,6 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) + * we are sleeping, let's just enable power save mode in + * 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; + ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); + } else if (local->hw.conf.dynamic_ps_timeout > 0) { +@@ -102,61 +95,63 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) + ieee80211_sta_reset_conn_monitor(sdata); + } + +-void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local, +- bool offchannel_ps_enable) ++void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) + { + 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 (!ieee80211_sdata_running(sdata)) + continue; + +- if (sdata->vif.type != NL80211_IFTYPE_MONITOR) +- set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); +- +- /* Check to see if we should disable beaconing. */ ++ /* disable beaconing */ + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC || + sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + ieee80211_bss_info_change_notify( + 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); +- 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); + } + +-void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local, +- bool tell_ap) ++void ieee80211_offchannel_stop_station(struct ieee80211_local *local) + { + 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 (!ieee80211_sdata_running(sdata)) + continue; + +- if (sdata->vif.type == NL80211_IFTYPE_STATION && +- sdata->u.mgd.associated) +- ieee80211_offchannel_ps_enable(sdata, tell_ap); ++ if (sdata->vif.type == NL80211_IFTYPE_STATION) { ++ set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); ++ netif_tx_stop_all_queues(sdata->dev); ++ if (sdata->u.mgd.associated) ++ ieee80211_offchannel_ps_enable(sdata); ++ } + } + mutex_unlock(&local->iflist_mtx); + } + + void ieee80211_offchannel_return(struct ieee80211_local *local, +- bool enable_beaconing, +- bool offchannel_ps_disable) ++ bool enable_beaconing) + { + struct ieee80211_sub_if_data *sdata; + +@@ -166,8 +161,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, + continue; + + /* Tell AP we're back */ +- if (offchannel_ps_disable && +- sdata->vif.type == NL80211_IFTYPE_STATION) { ++ if (sdata->vif.type == NL80211_IFTYPE_STATION) { + if (sdata->u.mgd.associated) + ieee80211_offchannel_ps_disable(sdata); + } +@@ -187,7 +181,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, + netif_tx_wake_all_queues(sdata->dev); + } + +- /* Check to see if we should re-enable beaconing */ ++ /* re-enable beaconing */ + if (enable_beaconing && + (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_ADHOC || +diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c +index fe2c2a7..b46880e 100644 +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -417,10 +417,16 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx) + return RX_CONTINUE; + + if (test_bit(SCAN_HW_SCANNING, &local->scanning) || +- test_bit(SCAN_SW_SCANNING, &local->scanning) || + local->sched_scanning) + 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 */ + I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); + return RX_DROP_UNUSABLE; +@@ -2771,7 +2777,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, + local->dot11ReceivedFragmentCount++; + + if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || +- test_bit(SCAN_SW_SCANNING, &local->scanning))) ++ test_bit(SCAN_OFF_CHANNEL, &local->scanning))) + status->rx_flags |= IEEE80211_RX_IN_SCAN; + + if (ieee80211_is_mgmt(fc)) +diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c +index 6f09eca..2ba4977 100644 +--- a/net/mac80211/scan.c ++++ b/net/mac80211/scan.c +@@ -212,14 +212,6 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) + if (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); + return RX_QUEUED; + } +@@ -262,8 +254,6 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, + bool was_hw_scan) + { + struct ieee80211_local *local = hw_to_local(hw); +- bool on_oper_chan; +- bool enable_beacons = false; + + lockdep_assert_held(&local->mtx); + +@@ -296,25 +286,11 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, + local->scanning = 0; + local->scan_channel = NULL; + +- on_oper_chan = ieee80211_cfg_on_oper_channel(local); +- +- if (was_hw_scan || !on_oper_chan) +- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); +- else +- /* Set power back to normal operating levels. */ +- ieee80211_hw_config(local, 0); +- ++ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); + if (!was_hw_scan) { +- bool on_oper_chan2; + ieee80211_configure_filter(local); + drv_sw_scan_complete(local); +- 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); ++ ieee80211_offchannel_return(local, true); + } + + ieee80211_recalc_idle(local); +@@ -355,15 +331,13 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) + */ + drv_sw_scan_start(local); + ++ ieee80211_offchannel_stop_beaconing(local); ++ + local->leave_oper_channel_time = 0; + local->next_scan_state = SCAN_DECISION; + local->scan_channel_idx = 0; + +- /* 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); ++ drv_flush(local, false); + + ieee80211_configure_filter(local); + +@@ -506,20 +480,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, + } + mutex_unlock(&local->iflist_mtx); + +- 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) +- /* 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 { ++ if (local->scan_channel) { + /* + * we're currently scanning a different channel, let's + * see if we can scan another channel without interfering +@@ -535,6 +496,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, + * + * Otherwise switch back to the operating channel. + */ ++ next_chan = local->scan_req->channels[local->scan_channel_idx]; + + bad_latency = time_after(jiffies + + ieee80211_scan_get_channel_time(next_chan), +@@ -552,6 +514,12 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, + 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; +@@ -560,10 +528,9 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, + static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, + unsigned long *next_delay) + { +- /* PS will already be in off-channel mode, +- * we do that once at the beginning of scanning. +- */ +- ieee80211_offchannel_stop_vifs(local, false); ++ ieee80211_offchannel_stop_station(local); ++ ++ __set_bit(SCAN_OFF_CHANNEL, &local->scanning); + + /* + * What if the nullfunc frames didn't arrive? +@@ -586,15 +553,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca + { + /* switch back to the operating channel */ + 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); + + /* +- * Re-enable vifs and beaconing. Leave PS +- * in off-channel state..will put that back +- * on-channel at the end of scanning. ++ * Only re-enable station mode interface now; beaconing will be ++ * re-enabled once the full scan has been completed. + */ +- ieee80211_offchannel_return(local, true, false); ++ ieee80211_offchannel_return(local, false); ++ ++ __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); + + *next_delay = HZ / 5; + local->next_scan_state = SCAN_DECISION; +diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c +index 8cb0d2d..54ea022 100644 +--- a/net/mac80211/tx.c ++++ b/net/mac80211/tx.c +@@ -258,8 +258,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) + if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) + return TX_CONTINUE; + +- if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && +- test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && ++ if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) && + !ieee80211_is_probe_req(hdr->frame_control) && + !ieee80211_is_nullfunc(hdr->frame_control)) + /* +diff --git a/net/mac80211/work.c b/net/mac80211/work.c +index 7737f20..b76bf33 100644 +--- a/net/mac80211/work.c ++++ b/net/mac80211/work.c +@@ -901,26 +901,6 @@ static bool ieee80211_work_ct_coexists(enum nl80211_channel_type wk_ct, + return false; + } + +-static enum nl80211_channel_type +-ieee80211_calc_ct(enum nl80211_channel_type wk_ct, +- enum nl80211_channel_type oper_ct) +-{ +- switch (wk_ct) { +- case NL80211_CHAN_NO_HT: +- return oper_ct; +- case NL80211_CHAN_HT20: +- if (oper_ct != NL80211_CHAN_NO_HT) +- return oper_ct; +- return wk_ct; +- case NL80211_CHAN_HT40MINUS: +- case NL80211_CHAN_HT40PLUS: +- return wk_ct; +- } +- WARN_ON(1); /* shouldn't get here */ +- return wk_ct; +-} +- +- + static void ieee80211_work_timer(unsigned long data) + { + struct ieee80211_local *local = (void *) data; +@@ -971,52 +951,18 @@ static void ieee80211_work_work(struct work_struct *work) + } + + if (!started && !local->tmp_channel) { +- bool on_oper_chan; +- bool tmp_chan_changed = false; +- bool on_oper_chan2; +- enum nl80211_channel_type wk_ct; +- on_oper_chan = ieee80211_cfg_on_oper_channel(local); +- +- /* Work with existing channel type if possible. */ +- wk_ct = wk->chan_type; +- if (wk->chan == local->hw.conf.channel) +- wk_ct = ieee80211_calc_ct(wk->chan_type, +- local->hw.conf.channel_type); +- +- if (local->tmp_channel) +- if ((local->tmp_channel != wk->chan) || +- (local->tmp_channel_type != wk_ct)) +- tmp_chan_changed = true; +- +- local->tmp_channel = wk->chan; +- local->tmp_channel_type = wk_ct; + /* +- * Leave the station vifs in awake mode if they +- * happen to be on the same channel as +- * the requested channel. ++ * TODO: could optimize this by leaving 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_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); ++ ieee80211_offchannel_stop_beaconing(local); ++ ieee80211_offchannel_stop_station(local); + ++ local->tmp_channel = wk->chan; ++ local->tmp_channel_type = wk->chan_type; ++ ieee80211_hw_config(local, 0); + started = true; + wk->timeout = jiffies; + } +@@ -1102,8 +1048,7 @@ static void ieee80211_work_work(struct work_struct *work) + * we still need to do a hardware config. Currently, + * we cannot be here while scanning, however. + */ +- if (!ieee80211_cfg_on_oper_channel(local)) +- ieee80211_hw_config(local, 0); ++ ieee80211_hw_config(local, 0); + + /* At the least, we need to disable offchannel_ps, + * so just go ahead and run the entire offchannel +@@ -1111,7 +1056,7 @@ static void ieee80211_work_work(struct work_struct *work) + * beaconing if we were already on-oper-channel + * as a future optimization. + */ +- ieee80211_offchannel_return(local, true, true); ++ ieee80211_offchannel_return(local, true); + + /* give connection some time to breathe */ + run_again(local, jiffies + HZ/2);