summaryrefslogtreecommitdiff
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2014-10-08 09:48:39 +0300
committerJohannes Berg <johannes.berg@intel.com>2014-10-09 11:30:09 +0200
commit0c21e6320f6ea7c4bd2fc0a8c1d8577b372f92d2 (patch)
tree928688796878b45d17d3138157f947384cec82ad /net/mac80211/mlme.c
parentf1d65583bc5bd43ace8abb9d4f4d9e8da407f708 (diff)
mac80211: wait for the first beacon on the new channel after CSA
Instead of immediately reopening the queues (in case of block_tx), calling the post_channel_switch operation and sending the notification, wait for the first beacon on the new channel. This makes sure that we don't lose packets if the AP/GO is not on the new channel yet. Signed-off-by: Luciano Coelho <luciano.coelho@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c45
1 files changed, 32 insertions, 13 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cb1a8c3bc73f..148253c1bd78 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1001,31 +1001,44 @@ static void ieee80211_chswitch_work(struct work_struct *work)
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->csa_chandef.chan;
- sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = true;
+
+ ieee80211_sta_reset_beacon_monitor(sdata);
+ ieee80211_sta_reset_conn_monitor(sdata);
+
+out:
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
+ sdata_unlock(sdata);
+}
+
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ret;
+
+ sdata_assert_lock(sdata);
+
+ WARN_ON(!sdata->vif.csa_active);
- /* XXX: wait for a beacon first? */
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
sdata->csa_block_tx = false;
}
+ sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
+
ret = drv_post_channel_switch(sdata);
if (ret) {
sdata_info(sdata,
"driver post channel switch failed, disconnecting\n");
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
- goto out;
+ return;
}
-
- ieee80211_sta_reset_beacon_monitor(sdata);
- ieee80211_sta_reset_conn_monitor(sdata);
-
-out:
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- sdata_unlock(sdata);
}
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
@@ -1943,6 +1956,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_vif_release_channel(sdata);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2191,6 +2205,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
true, frame_buf);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -3215,6 +3230,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (ifmgd->csa_waiting_bcn)
+ ieee80211_chswitch_post_beacon(sdata);
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -3687,11 +3705,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
sdata->u.mgd.connection_loss = false;
@@ -3709,7 +3728,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);