@@ -860,8 +860,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
860
860
return 0 ;
861
861
}
862
862
863
- static int ieee80211_assign_beacon (struct ieee80211_sub_if_data * sdata ,
864
- struct cfg80211_beacon_data * params )
863
+ int ieee80211_assign_beacon (struct ieee80211_sub_if_data * sdata ,
864
+ struct cfg80211_beacon_data * params )
865
865
{
866
866
struct beacon_data * new , * old ;
867
867
int new_head_len , new_tail_len ;
@@ -1024,6 +1024,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
1024
1024
1025
1025
sdata = IEEE80211_DEV_TO_SUB_IF (dev );
1026
1026
1027
+ /* don't allow changing the beacon while CSA is in place - offset
1028
+ * of channel switch counter may change
1029
+ */
1030
+ if (sdata -> vif .csa_active )
1031
+ return - EBUSY ;
1032
+
1027
1033
old = rtnl_dereference (sdata -> u .ap .beacon );
1028
1034
if (!old )
1029
1035
return - ENOENT ;
@@ -1048,6 +1054,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
1048
1054
return - ENOENT ;
1049
1055
old_probe_resp = rtnl_dereference (sdata -> u .ap .probe_resp );
1050
1056
1057
+ /* abort any running channel switch */
1058
+ sdata -> vif .csa_active = false;
1059
+ cancel_work_sync (& sdata -> csa_finalize_work );
1060
+
1051
1061
/* turn off carrier for this interface and dependent VLANs */
1052
1062
list_for_each_entry (vlan , & sdata -> u .ap .vlans , u .vlan .list )
1053
1063
netif_carrier_off (vlan -> dev );
@@ -2775,6 +2785,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
2775
2785
return 0 ;
2776
2786
}
2777
2787
2788
+ static struct cfg80211_beacon_data *
2789
+ cfg80211_beacon_dup (struct cfg80211_beacon_data * beacon )
2790
+ {
2791
+ struct cfg80211_beacon_data * new_beacon ;
2792
+ u8 * pos ;
2793
+ int len ;
2794
+
2795
+ len = beacon -> head_len + beacon -> tail_len + beacon -> beacon_ies_len +
2796
+ beacon -> proberesp_ies_len + beacon -> assocresp_ies_len +
2797
+ beacon -> probe_resp_len ;
2798
+
2799
+ new_beacon = kzalloc (sizeof (* new_beacon ) + len , GFP_KERNEL );
2800
+ if (!new_beacon )
2801
+ return NULL ;
2802
+
2803
+ pos = (u8 * )(new_beacon + 1 );
2804
+ if (beacon -> head_len ) {
2805
+ new_beacon -> head_len = beacon -> head_len ;
2806
+ new_beacon -> head = pos ;
2807
+ memcpy (pos , beacon -> head , beacon -> head_len );
2808
+ pos += beacon -> head_len ;
2809
+ }
2810
+ if (beacon -> tail_len ) {
2811
+ new_beacon -> tail_len = beacon -> tail_len ;
2812
+ new_beacon -> tail = pos ;
2813
+ memcpy (pos , beacon -> tail , beacon -> tail_len );
2814
+ pos += beacon -> tail_len ;
2815
+ }
2816
+ if (beacon -> beacon_ies_len ) {
2817
+ new_beacon -> beacon_ies_len = beacon -> beacon_ies_len ;
2818
+ new_beacon -> beacon_ies = pos ;
2819
+ memcpy (pos , beacon -> beacon_ies , beacon -> beacon_ies_len );
2820
+ pos += beacon -> beacon_ies_len ;
2821
+ }
2822
+ if (beacon -> proberesp_ies_len ) {
2823
+ new_beacon -> proberesp_ies_len = beacon -> proberesp_ies_len ;
2824
+ new_beacon -> proberesp_ies = pos ;
2825
+ memcpy (pos , beacon -> proberesp_ies , beacon -> proberesp_ies_len );
2826
+ pos += beacon -> proberesp_ies_len ;
2827
+ }
2828
+ if (beacon -> assocresp_ies_len ) {
2829
+ new_beacon -> assocresp_ies_len = beacon -> assocresp_ies_len ;
2830
+ new_beacon -> assocresp_ies = pos ;
2831
+ memcpy (pos , beacon -> assocresp_ies , beacon -> assocresp_ies_len );
2832
+ pos += beacon -> assocresp_ies_len ;
2833
+ }
2834
+ if (beacon -> probe_resp_len ) {
2835
+ new_beacon -> probe_resp_len = beacon -> probe_resp_len ;
2836
+ beacon -> probe_resp = pos ;
2837
+ memcpy (pos , beacon -> probe_resp , beacon -> probe_resp_len );
2838
+ pos += beacon -> probe_resp_len ;
2839
+ }
2840
+
2841
+ return new_beacon ;
2842
+ }
2843
+
2844
+ void ieee80211_csa_finalize_work (struct work_struct * work )
2845
+ {
2846
+ struct ieee80211_sub_if_data * sdata =
2847
+ container_of (work , struct ieee80211_sub_if_data ,
2848
+ csa_finalize_work );
2849
+ struct ieee80211_local * local = sdata -> local ;
2850
+ int err , changed ;
2851
+
2852
+ if (!ieee80211_sdata_running (sdata ))
2853
+ return ;
2854
+
2855
+ if (WARN_ON (sdata -> vif .type != NL80211_IFTYPE_AP ))
2856
+ return ;
2857
+
2858
+ sdata -> radar_required = sdata -> csa_radar_required ;
2859
+ err = ieee80211_vif_change_channel (sdata , & local -> csa_chandef ,
2860
+ & changed );
2861
+ if (WARN_ON (err < 0 ))
2862
+ return ;
2863
+
2864
+ err = ieee80211_assign_beacon (sdata , sdata -> u .ap .next_beacon );
2865
+ if (err < 0 )
2866
+ return ;
2867
+
2868
+ changed |= err ;
2869
+ kfree (sdata -> u .ap .next_beacon );
2870
+ sdata -> u .ap .next_beacon = NULL ;
2871
+ sdata -> vif .csa_active = false;
2872
+
2873
+ ieee80211_wake_queues_by_reason (& sdata -> local -> hw ,
2874
+ IEEE80211_MAX_QUEUE_MAP ,
2875
+ IEEE80211_QUEUE_STOP_REASON_CSA );
2876
+
2877
+ ieee80211_bss_info_change_notify (sdata , changed );
2878
+
2879
+ cfg80211_ch_switch_notify (sdata -> dev , & local -> csa_chandef );
2880
+ }
2881
+
2882
+ static int ieee80211_channel_switch (struct wiphy * wiphy , struct net_device * dev ,
2883
+ struct cfg80211_csa_settings * params )
2884
+ {
2885
+ struct ieee80211_sub_if_data * sdata = IEEE80211_DEV_TO_SUB_IF (dev );
2886
+ struct ieee80211_local * local = sdata -> local ;
2887
+ struct ieee80211_chanctx_conf * chanctx_conf ;
2888
+ struct ieee80211_chanctx * chanctx ;
2889
+ int err , num_chanctx ;
2890
+
2891
+ if (!list_empty (& local -> roc_list ) || local -> scanning )
2892
+ return - EBUSY ;
2893
+
2894
+ if (sdata -> wdev .cac_started )
2895
+ return - EBUSY ;
2896
+
2897
+ if (cfg80211_chandef_identical (& params -> chandef ,
2898
+ & sdata -> vif .bss_conf .chandef ))
2899
+ return - EINVAL ;
2900
+
2901
+ rcu_read_lock ();
2902
+ chanctx_conf = rcu_dereference (sdata -> vif .chanctx_conf );
2903
+ if (!chanctx_conf ) {
2904
+ rcu_read_unlock ();
2905
+ return - EBUSY ;
2906
+ }
2907
+
2908
+ /* don't handle for multi-VIF cases */
2909
+ chanctx = container_of (chanctx_conf , struct ieee80211_chanctx , conf );
2910
+ if (chanctx -> refcount > 1 ) {
2911
+ rcu_read_unlock ();
2912
+ return - EBUSY ;
2913
+ }
2914
+ num_chanctx = 0 ;
2915
+ list_for_each_entry_rcu (chanctx , & local -> chanctx_list , list )
2916
+ num_chanctx ++ ;
2917
+ rcu_read_unlock ();
2918
+
2919
+ if (num_chanctx > 1 )
2920
+ return - EBUSY ;
2921
+
2922
+ /* don't allow another channel switch if one is already active. */
2923
+ if (sdata -> vif .csa_active )
2924
+ return - EBUSY ;
2925
+
2926
+ /* only handle AP for now. */
2927
+ switch (sdata -> vif .type ) {
2928
+ case NL80211_IFTYPE_AP :
2929
+ break ;
2930
+ default :
2931
+ return - EOPNOTSUPP ;
2932
+ }
2933
+
2934
+ sdata -> u .ap .next_beacon = cfg80211_beacon_dup (& params -> beacon_after );
2935
+ if (!sdata -> u .ap .next_beacon )
2936
+ return - ENOMEM ;
2937
+
2938
+ sdata -> csa_counter_offset_beacon = params -> counter_offset_beacon ;
2939
+ sdata -> csa_counter_offset_presp = params -> counter_offset_presp ;
2940
+ sdata -> csa_radar_required = params -> radar_required ;
2941
+
2942
+ if (params -> block_tx )
2943
+ ieee80211_stop_queues_by_reason (& local -> hw ,
2944
+ IEEE80211_MAX_QUEUE_MAP ,
2945
+ IEEE80211_QUEUE_STOP_REASON_CSA );
2946
+
2947
+ err = ieee80211_assign_beacon (sdata , & params -> beacon_csa );
2948
+ if (err < 0 )
2949
+ return err ;
2950
+
2951
+ local -> csa_chandef = params -> chandef ;
2952
+ sdata -> vif .csa_active = true;
2953
+
2954
+ ieee80211_bss_info_change_notify (sdata , err );
2955
+ drv_channel_switch_beacon (sdata , & params -> chandef );
2956
+
2957
+ return 0 ;
2958
+ }
2959
+
2778
2960
static int ieee80211_mgmt_tx (struct wiphy * wiphy , struct wireless_dev * wdev ,
2779
2961
struct ieee80211_channel * chan , bool offchan ,
2780
2962
unsigned int wait , const u8 * buf , size_t len ,
@@ -3492,4 +3674,5 @@ struct cfg80211_ops mac80211_config_ops = {
3492
3674
.get_et_strings = ieee80211_get_et_strings ,
3493
3675
.get_channel = ieee80211_cfg_get_channel ,
3494
3676
.start_radar_detection = ieee80211_start_radar_detection ,
3677
+ .channel_switch = ieee80211_channel_switch ,
3495
3678
};
0 commit comments