|
@@ -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,
|
|
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)
|
|
*/
|
|
*/
|
|
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,
|
|
}
|
|
}
|
|
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,
|
|
*
|
|
*
|
|
* 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,
|
|
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,
|
|
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);
|
|
|
|
-
|
|
|
|
- __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
|
|
|
|
|
|
+ /* PS will already be in off-channel mode,
|
|
|
|
+ * we do that once at the beginning of 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
|
|
{
|
|
{
|
|
/* switch back to the operating channel */
|
|
/* switch back to the operating channel */
|
|
local->scan_channel = NULL;
|
|
local->scan_channel = NULL;
|
|
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
|
|
|
|
|
+ if (!ieee80211_cfg_on_oper_channel(local))
|
|
|
|
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Only re-enable station mode interface now; beaconing will be
|
|
|
|
- * re-enabled once the full scan has been completed.
|
|
|
|
|
|
+ * Re-enable vifs and beaconing. Leave PS
|
|
|
|
+ * in off-channel state..will put that back
|
|
|
|
+ * on-channel at the end of scanning.
|
|
*/
|
|
*/
|
|
- ieee80211_offchannel_return(local, false);
|
|
|
|
-
|
|
|
|
- __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
|
|
|
|
|
|
+ ieee80211_offchannel_return(local, true, false);
|
|
|
|
|
|
*next_delay = HZ / 5;
|
|
*next_delay = HZ / 5;
|
|
local->next_scan_state = SCAN_DECISION;
|
|
local->next_scan_state = SCAN_DECISION;
|
|
@@ -584,8 +618,12 @@ 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;
|
|
- if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
|
|
|
|
- skip = 1;
|
|
|
|
|
|
+
|
|
|
|
+ /* 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))
|
|
|
|
+ skip = 1;
|
|
|
|
|
|
/* advance state machine to next channel/band */
|
|
/* advance state machine to next channel/band */
|
|
local->scan_channel_idx++;
|
|
local->scan_channel_idx++;
|