diff options
Diffstat (limited to 'net/mac80211/ibss.c')
-rw-r--r-- | net/mac80211/ibss.c | 90 |
1 files changed, 33 insertions, 57 deletions
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 27a39de89679..2796a198728f 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -293,14 +293,17 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
293 | radar_required = true; | 293 | radar_required = true; |
294 | } | 294 | } |
295 | 295 | ||
296 | mutex_lock(&local->mtx); | ||
296 | ieee80211_vif_release_channel(sdata); | 297 | ieee80211_vif_release_channel(sdata); |
297 | if (ieee80211_vif_use_channel(sdata, &chandef, | 298 | if (ieee80211_vif_use_channel(sdata, &chandef, |
298 | ifibss->fixed_channel ? | 299 | ifibss->fixed_channel ? |
299 | IEEE80211_CHANCTX_SHARED : | 300 | IEEE80211_CHANCTX_SHARED : |
300 | IEEE80211_CHANCTX_EXCLUSIVE)) { | 301 | IEEE80211_CHANCTX_EXCLUSIVE)) { |
301 | sdata_info(sdata, "Failed to join IBSS, no channel context\n"); | 302 | sdata_info(sdata, "Failed to join IBSS, no channel context\n"); |
303 | mutex_unlock(&local->mtx); | ||
302 | return; | 304 | return; |
303 | } | 305 | } |
306 | mutex_unlock(&local->mtx); | ||
304 | 307 | ||
305 | memcpy(ifibss->bssid, bssid, ETH_ALEN); | 308 | memcpy(ifibss->bssid, bssid, ETH_ALEN); |
306 | 309 | ||
@@ -363,7 +366,9 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
363 | sdata->vif.bss_conf.ssid_len = 0; | 366 | sdata->vif.bss_conf.ssid_len = 0; |
364 | RCU_INIT_POINTER(ifibss->presp, NULL); | 367 | RCU_INIT_POINTER(ifibss->presp, NULL); |
365 | kfree_rcu(presp, rcu_head); | 368 | kfree_rcu(presp, rcu_head); |
369 | mutex_lock(&local->mtx); | ||
366 | ieee80211_vif_release_channel(sdata); | 370 | ieee80211_vif_release_channel(sdata); |
371 | mutex_unlock(&local->mtx); | ||
367 | sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", | 372 | sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", |
368 | err); | 373 | err); |
369 | return; | 374 | return; |
@@ -522,7 +527,7 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, | |||
522 | if (csa_settings) | 527 | if (csa_settings) |
523 | ieee80211_send_action_csa(sdata, csa_settings); | 528 | ieee80211_send_action_csa(sdata, csa_settings); |
524 | 529 | ||
525 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); | 530 | return BSS_CHANGED_BEACON; |
526 | out: | 531 | out: |
527 | return ret; | 532 | return ret; |
528 | } | 533 | } |
@@ -534,7 +539,8 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) | |||
534 | int err; | 539 | int err; |
535 | u16 capability; | 540 | u16 capability; |
536 | 541 | ||
537 | sdata_lock(sdata); | 542 | sdata_assert_lock(sdata); |
543 | |||
538 | /* update cfg80211 bss information with the new channel */ | 544 | /* update cfg80211 bss information with the new channel */ |
539 | if (!is_zero_ether_addr(ifibss->bssid)) { | 545 | if (!is_zero_ether_addr(ifibss->bssid)) { |
540 | capability = WLAN_CAPABILITY_IBSS; | 546 | capability = WLAN_CAPABILITY_IBSS; |
@@ -550,19 +556,21 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) | |||
550 | capability); | 556 | capability); |
551 | /* XXX: should not really modify cfg80211 data */ | 557 | /* XXX: should not really modify cfg80211 data */ |
552 | if (cbss) { | 558 | if (cbss) { |
553 | cbss->channel = sdata->local->csa_chandef.chan; | 559 | cbss->channel = sdata->csa_chandef.chan; |
554 | cfg80211_put_bss(sdata->local->hw.wiphy, cbss); | 560 | cfg80211_put_bss(sdata->local->hw.wiphy, cbss); |
555 | } | 561 | } |
556 | } | 562 | } |
557 | 563 | ||
558 | ifibss->chandef = sdata->local->csa_chandef; | 564 | ifibss->chandef = sdata->csa_chandef; |
559 | 565 | ||
560 | /* generate the beacon */ | 566 | /* generate the beacon */ |
561 | err = ieee80211_ibss_csa_beacon(sdata, NULL); | 567 | err = ieee80211_ibss_csa_beacon(sdata, NULL); |
562 | sdata_unlock(sdata); | ||
563 | if (err < 0) | 568 | if (err < 0) |
564 | return err; | 569 | return err; |
565 | 570 | ||
571 | if (err) | ||
572 | ieee80211_bss_info_change_notify(sdata, err); | ||
573 | |||
566 | return 0; | 574 | return 0; |
567 | } | 575 | } |
568 | 576 | ||
@@ -687,12 +695,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) | |||
687 | struct cfg80211_bss *cbss; | 695 | struct cfg80211_bss *cbss; |
688 | struct beacon_data *presp; | 696 | struct beacon_data *presp; |
689 | struct sta_info *sta; | 697 | struct sta_info *sta; |
690 | int active_ibss; | ||
691 | u16 capability; | 698 | u16 capability; |
692 | 699 | ||
693 | active_ibss = ieee80211_sta_active_ibss(sdata); | 700 | if (!is_zero_ether_addr(ifibss->bssid)) { |
694 | |||
695 | if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { | ||
696 | capability = WLAN_CAPABILITY_IBSS; | 701 | capability = WLAN_CAPABILITY_IBSS; |
697 | 702 | ||
698 | if (ifibss->privacy) | 703 | if (ifibss->privacy) |
@@ -744,7 +749,9 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) | |||
744 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | | 749 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | |
745 | BSS_CHANGED_IBSS); | 750 | BSS_CHANGED_IBSS); |
746 | drv_leave_ibss(local, sdata); | 751 | drv_leave_ibss(local, sdata); |
752 | mutex_lock(&local->mtx); | ||
747 | ieee80211_vif_release_channel(sdata); | 753 | ieee80211_vif_release_channel(sdata); |
754 | mutex_unlock(&local->mtx); | ||
748 | } | 755 | } |
749 | 756 | ||
750 | static void ieee80211_csa_connection_drop_work(struct work_struct *work) | 757 | 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) | |||
753 | container_of(work, struct ieee80211_sub_if_data, | 760 | container_of(work, struct ieee80211_sub_if_data, |
754 | u.ibss.csa_connection_drop_work); | 761 | u.ibss.csa_connection_drop_work); |
755 | 762 | ||
763 | sdata_lock(sdata); | ||
764 | |||
756 | ieee80211_ibss_disconnect(sdata); | 765 | ieee80211_ibss_disconnect(sdata); |
757 | synchronize_rcu(); | 766 | synchronize_rcu(); |
758 | skb_queue_purge(&sdata->skb_queue); | 767 | skb_queue_purge(&sdata->skb_queue); |
759 | 768 | ||
760 | /* trigger a scan to find another IBSS network to join */ | 769 | /* trigger a scan to find another IBSS network to join */ |
761 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); | 770 | ieee80211_queue_work(&sdata->local->hw, &sdata->work); |
771 | |||
772 | sdata_unlock(sdata); | ||
762 | } | 773 | } |
763 | 774 | ||
764 | static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) | 775 | 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, | |||
784 | struct cfg80211_csa_settings params; | 795 | struct cfg80211_csa_settings params; |
785 | struct ieee80211_csa_ie csa_ie; | 796 | struct ieee80211_csa_ie csa_ie; |
786 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; | 797 | struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; |
787 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
788 | struct ieee80211_chanctx *chanctx; | ||
789 | enum nl80211_channel_type ch_type; | 798 | enum nl80211_channel_type ch_type; |
790 | int err, num_chanctx; | 799 | int err; |
791 | u32 sta_flags; | 800 | u32 sta_flags; |
792 | 801 | ||
793 | if (sdata->vif.csa_active) | ||
794 | return true; | ||
795 | |||
796 | if (!sdata->vif.bss_conf.ibss_joined) | ||
797 | return false; | ||
798 | |||
799 | sta_flags = IEEE80211_STA_DISABLE_VHT; | 802 | sta_flags = IEEE80211_STA_DISABLE_VHT; |
800 | switch (ifibss->chandef.width) { | 803 | switch (ifibss->chandef.width) { |
801 | case NL80211_CHAN_WIDTH_5: | 804 | case NL80211_CHAN_WIDTH_5: |
@@ -830,9 +833,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
830 | params.count = csa_ie.count; | 833 | params.count = csa_ie.count; |
831 | params.chandef = csa_ie.chandef; | 834 | params.chandef = csa_ie.chandef; |
832 | 835 | ||
833 | if (ifibss->chandef.chan->band != params.chandef.chan->band) | ||
834 | goto disconnect; | ||
835 | |||
836 | switch (ifibss->chandef.width) { | 836 | switch (ifibss->chandef.width) { |
837 | case NL80211_CHAN_WIDTH_20_NOHT: | 837 | case NL80211_CHAN_WIDTH_20_NOHT: |
838 | case NL80211_CHAN_WIDTH_20: | 838 | case NL80211_CHAN_WIDTH_20: |
@@ -888,28 +888,12 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
888 | params.radar_required = true; | 888 | params.radar_required = true; |
889 | } | 889 | } |
890 | 890 | ||
891 | rcu_read_lock(); | 891 | if (cfg80211_chandef_identical(¶ms.chandef, |
892 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | 892 | &sdata->vif.bss_conf.chandef)) { |
893 | if (!chanctx_conf) { | 893 | ibss_dbg(sdata, |
894 | rcu_read_unlock(); | 894 | "received csa with an identical chandef, ignoring\n"); |
895 | goto disconnect; | 895 | return true; |
896 | } | ||
897 | |||
898 | /* don't handle for multi-VIF cases */ | ||
899 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
900 | if (chanctx->refcount > 1) { | ||
901 | rcu_read_unlock(); | ||
902 | goto disconnect; | ||
903 | } | ||
904 | num_chanctx = 0; | ||
905 | list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) | ||
906 | num_chanctx++; | ||
907 | |||
908 | if (num_chanctx > 1) { | ||
909 | rcu_read_unlock(); | ||
910 | goto disconnect; | ||
911 | } | 896 | } |
912 | rcu_read_unlock(); | ||
913 | 897 | ||
914 | /* all checks done, now perform the channel switch. */ | 898 | /* all checks done, now perform the channel switch. */ |
915 | ibss_dbg(sdata, | 899 | ibss_dbg(sdata, |
@@ -918,19 +902,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, | |||
918 | 902 | ||
919 | params.block_tx = !!csa_ie.mode; | 903 | params.block_tx = !!csa_ie.mode; |
920 | 904 | ||
921 | ieee80211_ibss_csa_beacon(sdata, ¶ms); | 905 | if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, |
922 | sdata->csa_radar_required = params.radar_required; | 906 | ¶ms)) |
923 | 907 | goto disconnect; | |
924 | if (params.block_tx) | ||
925 | ieee80211_stop_queues_by_reason(&sdata->local->hw, | ||
926 | IEEE80211_MAX_QUEUE_MAP, | ||
927 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
928 | |||
929 | sdata->local->csa_chandef = params.chandef; | ||
930 | sdata->vif.csa_active = true; | ||
931 | |||
932 | ieee80211_bss_info_change_notify(sdata, err); | ||
933 | drv_channel_switch_beacon(sdata, ¶ms.chandef); | ||
934 | 908 | ||
935 | ieee80211_ibss_csa_mark_radar(sdata); | 909 | ieee80211_ibss_csa_mark_radar(sdata); |
936 | 910 | ||
@@ -966,7 +940,8 @@ ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, | |||
966 | if (len < required_len) | 940 | if (len < required_len) |
967 | return; | 941 | return; |
968 | 942 | ||
969 | ieee80211_ibss_process_chanswitch(sdata, elems, false); | 943 | if (!sdata->vif.csa_active) |
944 | ieee80211_ibss_process_chanswitch(sdata, elems, false); | ||
970 | } | 945 | } |
971 | 946 | ||
972 | static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, | 947 | 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, | |||
1147 | goto put_bss; | 1122 | goto put_bss; |
1148 | 1123 | ||
1149 | /* process channel switch */ | 1124 | /* process channel switch */ |
1150 | if (ieee80211_ibss_process_chanswitch(sdata, elems, true)) | 1125 | if (sdata->vif.csa_active || |
1126 | ieee80211_ibss_process_chanswitch(sdata, elems, true)) | ||
1151 | goto put_bss; | 1127 | goto put_bss; |
1152 | 1128 | ||
1153 | /* same BSSID */ | 1129 | /* same BSSID */ |