mac80211: allow channel switch with multiple channel contexts
[deliverable/linux.git] / net / mac80211 / mlme.c
index f2e048fa250c556b9070d36e8cd693c16ada6d66..fb6561509cafc1d0ded7fd0c1a4e4241abf4eac3 100644 (file)
@@ -1001,14 +1001,7 @@ 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;
-
-       /* 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;
-       }
+       ifmgd->csa_waiting_bcn = true;
 
        ieee80211_sta_reset_beacon_monitor(sdata);
        ieee80211_sta_reset_conn_monitor(sdata);
@@ -1019,6 +1012,35 @@ out:
        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);
+
+       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);
+               return;
+       }
+}
+
 void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
 {
        struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -1057,6 +1079,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_chanctx *chanctx;
        enum ieee80211_band current_band;
        struct ieee80211_csa_ie csa_ie;
+       struct ieee80211_channel_switch ch_switch;
        int res;
 
        sdata_assert_lock(sdata);
@@ -1111,21 +1134,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        chanctx = container_of(conf, struct ieee80211_chanctx, conf);
 
-       if (local->use_chanctx) {
-               u32 num_chanctx = 0;
-               list_for_each_entry(chanctx, &local->chanctx_list, list)
-                      num_chanctx++;
+       if (local->use_chanctx &&
+           !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+               sdata_info(sdata,
+                          "driver doesn't support chan-switch with channel contexts\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
+       }
 
-               if (num_chanctx > 1 ||
-                   !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
-                       sdata_info(sdata,
-                                  "not handling chan-switch with channel contexts\n");
-                       ieee80211_queue_work(&local->hw,
-                                            &ifmgd->csa_connection_drop_work);
-                       mutex_unlock(&local->chanctx_mtx);
-                       mutex_unlock(&local->mtx);
-                       return;
-               }
+       ch_switch.timestamp = timestamp;
+       ch_switch.device_timestamp = device_timestamp;
+       ch_switch.block_tx = csa_ie.mode;
+       ch_switch.chandef = csa_ie.chandef;
+       ch_switch.count = csa_ie.count;
+
+       if (drv_pre_channel_switch(sdata, &ch_switch)) {
+               sdata_info(sdata,
+                          "preparing for channel switch failed, disconnecting\n");
+               ieee80211_queue_work(&local->hw,
+                                    &ifmgd->csa_connection_drop_work);
+               mutex_unlock(&local->chanctx_mtx);
+               mutex_unlock(&local->mtx);
+               return;
        }
 
        res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1153,15 +1186,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        if (local->ops->channel_switch) {
                /* use driver's channel switch callback */
-               struct ieee80211_channel_switch ch_switch = {
-                       .timestamp = timestamp,
-                       .device_timestamp = device_timestamp,
-                       .block_tx = csa_ie.mode,
-                       .chandef = csa_ie.chandef,
-                       .count = csa_ie.count,
-               };
-
-               drv_channel_switch(local, &ch_switch);
+               drv_channel_switch(local, sdata, &ch_switch);
                return;
        }
 
@@ -1925,6 +1950,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);
@@ -2173,6 +2199,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);
@@ -3197,6 +3224,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;
@@ -3669,11 +3699,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;
@@ -3691,7 +3722,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);
This page took 0.038761 seconds and 5 git commands to generate.