]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/mac80211/ibss.c
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211
[karo-tx-linux.git] / net / mac80211 / ibss.c
index 27a39de89679b7d3710fea18a65b6d5df6d003d0..2796a198728fd12bab4625ae1b112123988794f0 100644 (file)
@@ -293,14 +293,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                radar_required = true;
        }
 
+       mutex_lock(&local->mtx);
        ieee80211_vif_release_channel(sdata);
        if (ieee80211_vif_use_channel(sdata, &chandef,
                                      ifibss->fixed_channel ?
                                        IEEE80211_CHANCTX_SHARED :
                                        IEEE80211_CHANCTX_EXCLUSIVE)) {
                sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+               mutex_unlock(&local->mtx);
                return;
        }
+       mutex_unlock(&local->mtx);
 
        memcpy(ifibss->bssid, bssid, ETH_ALEN);
 
@@ -363,7 +366,9 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
                sdata->vif.bss_conf.ssid_len = 0;
                RCU_INIT_POINTER(ifibss->presp, NULL);
                kfree_rcu(presp, rcu_head);
+               mutex_lock(&local->mtx);
                ieee80211_vif_release_channel(sdata);
+               mutex_unlock(&local->mtx);
                sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
                           err);
                return;
@@ -522,7 +527,7 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
        if (csa_settings)
                ieee80211_send_action_csa(sdata, csa_settings);
 
-       ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
+       return BSS_CHANGED_BEACON;
  out:
        return ret;
 }
@@ -534,7 +539,8 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
        int err;
        u16 capability;
 
-       sdata_lock(sdata);
+       sdata_assert_lock(sdata);
+
        /* update cfg80211 bss information with the new channel */
        if (!is_zero_ether_addr(ifibss->bssid)) {
                capability = WLAN_CAPABILITY_IBSS;
@@ -550,19 +556,21 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
                                        capability);
                /* XXX: should not really modify cfg80211 data */
                if (cbss) {
-                       cbss->channel = sdata->local->csa_chandef.chan;
+                       cbss->channel = sdata->csa_chandef.chan;
                        cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
                }
        }
 
-       ifibss->chandef = sdata->local->csa_chandef;
+       ifibss->chandef = sdata->csa_chandef;
 
        /* generate the beacon */
        err = ieee80211_ibss_csa_beacon(sdata, NULL);
-       sdata_unlock(sdata);
        if (err < 0)
                return err;
 
+       if (err)
+               ieee80211_bss_info_change_notify(sdata, err);
+
        return 0;
 }
 
@@ -687,12 +695,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
        struct cfg80211_bss *cbss;
        struct beacon_data *presp;
        struct sta_info *sta;
-       int active_ibss;
        u16 capability;
 
-       active_ibss = ieee80211_sta_active_ibss(sdata);
-
-       if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
+       if (!is_zero_ether_addr(ifibss->bssid)) {
                capability = WLAN_CAPABILITY_IBSS;
 
                if (ifibss->privacy)
@@ -744,7 +749,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
        ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
                                                BSS_CHANGED_IBSS);
        drv_leave_ibss(local, sdata);
+       mutex_lock(&local->mtx);
        ieee80211_vif_release_channel(sdata);
+       mutex_unlock(&local->mtx);
 }
 
 static void ieee80211_csa_connection_drop_work(struct work_struct *work)
@@ -753,12 +760,16 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work)
                container_of(work, struct ieee80211_sub_if_data,
                             u.ibss.csa_connection_drop_work);
 
+       sdata_lock(sdata);
+
        ieee80211_ibss_disconnect(sdata);
        synchronize_rcu();
        skb_queue_purge(&sdata->skb_queue);
 
        /* trigger a scan to find another IBSS network to join */
        ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+
+       sdata_unlock(sdata);
 }
 
 static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata)
@@ -784,18 +795,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        struct cfg80211_csa_settings params;
        struct ieee80211_csa_ie csa_ie;
        struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
-       struct ieee80211_chanctx_conf *chanctx_conf;
-       struct ieee80211_chanctx *chanctx;
        enum nl80211_channel_type ch_type;
-       int err, num_chanctx;
+       int err;
        u32 sta_flags;
 
-       if (sdata->vif.csa_active)
-               return true;
-
-       if (!sdata->vif.bss_conf.ibss_joined)
-               return false;
-
        sta_flags = IEEE80211_STA_DISABLE_VHT;
        switch (ifibss->chandef.width) {
        case NL80211_CHAN_WIDTH_5:
@@ -830,9 +833,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
        params.count = csa_ie.count;
        params.chandef = csa_ie.chandef;
 
-       if (ifibss->chandef.chan->band != params.chandef.chan->band)
-               goto disconnect;
-
        switch (ifibss->chandef.width) {
        case NL80211_CHAN_WIDTH_20_NOHT:
        case NL80211_CHAN_WIDTH_20:
@@ -888,28 +888,12 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
                params.radar_required = true;
        }
 
-       rcu_read_lock();
-       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-       if (!chanctx_conf) {
-               rcu_read_unlock();
-               goto disconnect;
-       }
-
-       /* don't handle for multi-VIF cases */
-       chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
-       if (chanctx->refcount > 1) {
-               rcu_read_unlock();
-               goto disconnect;
-       }
-       num_chanctx = 0;
-       list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
-               num_chanctx++;
-
-       if (num_chanctx > 1) {
-               rcu_read_unlock();
-               goto disconnect;
+       if (cfg80211_chandef_identical(&params.chandef,
+                                      &sdata->vif.bss_conf.chandef)) {
+               ibss_dbg(sdata,
+                        "received csa with an identical chandef, ignoring\n");
+               return true;
        }
-       rcu_read_unlock();
 
        /* all checks done, now perform the channel switch. */
        ibss_dbg(sdata,
@@ -918,19 +902,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
 
        params.block_tx = !!csa_ie.mode;
 
-       ieee80211_ibss_csa_beacon(sdata, &params);
-       sdata->csa_radar_required = params.radar_required;
-
-       if (params.block_tx)
-               ieee80211_stop_queues_by_reason(&sdata->local->hw,
-                               IEEE80211_MAX_QUEUE_MAP,
-                               IEEE80211_QUEUE_STOP_REASON_CSA);
-
-       sdata->local->csa_chandef = params.chandef;
-       sdata->vif.csa_active = true;
-
-       ieee80211_bss_info_change_notify(sdata, err);
-       drv_channel_switch_beacon(sdata, &params.chandef);
+       if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev,
+                                    &params))
+               goto disconnect;
 
        ieee80211_ibss_csa_mark_radar(sdata);
 
@@ -966,7 +940,8 @@ ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
        if (len < required_len)
                return;
 
-       ieee80211_ibss_process_chanswitch(sdata, elems, false);
+       if (!sdata->vif.csa_active)
+               ieee80211_ibss_process_chanswitch(sdata, elems, false);
 }
 
 static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
@@ -1147,7 +1122,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
                goto put_bss;
 
        /* process channel switch */
-       if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
+       if (sdata->vif.csa_active ||
+           ieee80211_ibss_process_chanswitch(sdata, elems, true))
                goto put_bss;
 
        /* same BSSID */