aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorSimon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de>2013-07-11 10:09:06 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-08-01 12:30:33 -0400
commit73da7d5bab79ad7e16ff44d67c3fe8b9c0b33e5b (patch)
treecb4eee7b96aae1d31a4841167a3f36c638bd0a11 /net
parent16ef1fe272332b2f7fd99236017b891db48d9cd6 (diff)
mac80211: add channel switch command and beacon callbacks
The count field in CSA must be decremented with each beacon transmitted. This patch implements the functionality for drivers using ieee80211_beacon_get(). Other drivers must call back manually after reaching count == 0. This patch also contains the handling and finish worker for the channel switch command, and mac80211/chanctx code to allow to change a channel definition of an active channel context. Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de> Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de> [small cleanups, catch identical chandef] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c187
-rw-r--r--net/mac80211/chan.c58
-rw-r--r--net/mac80211/driver-ops.h13
-rw-r--r--net/mac80211/ieee80211_i.h17
-rw-r--r--net/mac80211/iface.c9
-rw-r--r--net/mac80211/trace.h26
-rw-r--r--net/mac80211/tx.c78
7 files changed, 386 insertions, 2 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b82fff6c0b30..44449ceb7966 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -860,8 +860,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
860 return 0; 860 return 0;
861} 861}
862 862
863static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, 863int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
864 struct cfg80211_beacon_data *params) 864 struct cfg80211_beacon_data *params)
865{ 865{
866 struct beacon_data *new, *old; 866 struct beacon_data *new, *old;
867 int new_head_len, new_tail_len; 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 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 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 old = rtnl_dereference(sdata->u.ap.beacon); 1033 old = rtnl_dereference(sdata->u.ap.beacon);
1028 if (!old) 1034 if (!old)
1029 return -ENOENT; 1035 return -ENOENT;
@@ -1048,6 +1054,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
1048 return -ENOENT; 1054 return -ENOENT;
1049 old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); 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 /* turn off carrier for this interface and dependent VLANs */ 1061 /* turn off carrier for this interface and dependent VLANs */
1052 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) 1062 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
1053 netif_carrier_off(vlan->dev); 1063 netif_carrier_off(vlan->dev);
@@ -2775,6 +2785,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
2775 return 0; 2785 return 0;
2776} 2786}
2777 2787
2788static struct cfg80211_beacon_data *
2789cfg80211_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
2844void 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
2882static 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
2778static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 2960static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
2779 struct ieee80211_channel *chan, bool offchan, 2961 struct ieee80211_channel *chan, bool offchan,
2780 unsigned int wait, const u8 *buf, size_t len, 2962 unsigned int wait, const u8 *buf, size_t len,
@@ -3492,4 +3674,5 @@ struct cfg80211_ops mac80211_config_ops = {
3492 .get_et_strings = ieee80211_get_et_strings, 3674 .get_et_strings = ieee80211_get_et_strings,
3493 .get_channel = ieee80211_cfg_get_channel, 3675 .get_channel = ieee80211_cfg_get_channel,
3494 .start_radar_detection = ieee80211_start_radar_detection, 3676 .start_radar_detection = ieee80211_start_radar_detection,
3677 .channel_switch = ieee80211_channel_switch,
3495}; 3678};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 03e8d2e3270e..3a4764b2869e 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -410,6 +410,64 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
410 return ret; 410 return ret;
411} 411}
412 412
413int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
414 const struct cfg80211_chan_def *chandef,
415 u32 *changed)
416{
417 struct ieee80211_local *local = sdata->local;
418 struct ieee80211_chanctx_conf *conf;
419 struct ieee80211_chanctx *ctx;
420 int ret;
421 u32 chanctx_changed = 0;
422
423 /* should never be called if not performing a channel switch. */
424 if (WARN_ON(!sdata->vif.csa_active))
425 return -EINVAL;
426
427 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
428 IEEE80211_CHAN_DISABLED))
429 return -EINVAL;
430
431 mutex_lock(&local->chanctx_mtx);
432 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
433 lockdep_is_held(&local->chanctx_mtx));
434 if (!conf) {
435 ret = -EINVAL;
436 goto out;
437 }
438
439 ctx = container_of(conf, struct ieee80211_chanctx, conf);
440 if (ctx->refcount != 1) {
441 ret = -EINVAL;
442 goto out;
443 }
444
445 if (sdata->vif.bss_conf.chandef.width != chandef->width) {
446 chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
447 *changed |= BSS_CHANGED_BANDWIDTH;
448 }
449
450 sdata->vif.bss_conf.chandef = *chandef;
451 ctx->conf.def = *chandef;
452
453 chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
454 drv_change_chanctx(local, ctx, chanctx_changed);
455
456 if (!local->use_chanctx) {
457 local->_oper_chandef = *chandef;
458 ieee80211_hw_config(local, 0);
459 }
460
461 ieee80211_recalc_chanctx_chantype(local, ctx);
462 ieee80211_recalc_smps_chanctx(local, ctx);
463 ieee80211_recalc_radar_chanctx(local, ctx);
464
465 ret = 0;
466 out:
467 mutex_unlock(&local->chanctx_mtx);
468 return ret;
469}
470
413int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 471int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
414 const struct cfg80211_chan_def *chandef, 472 const struct cfg80211_chan_def *chandef,
415 u32 *changed) 473 u32 *changed)
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index b931c96a596f..b3ea11f3d526 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1072,4 +1072,17 @@ static inline void drv_ipv6_addr_change(struct ieee80211_local *local,
1072} 1072}
1073#endif 1073#endif
1074 1074
1075static inline void
1076drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
1077 struct cfg80211_chan_def *chandef)
1078{
1079 struct ieee80211_local *local = sdata->local;
1080
1081 if (local->ops->channel_switch_beacon) {
1082 trace_drv_channel_switch_beacon(local, sdata, chandef);
1083 local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
1084 chandef);
1085 }
1086}
1087
1075#endif /* __MAC80211_DRIVER_OPS */ 1088#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index d779383c52d2..e94c84050e9c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -259,6 +259,8 @@ struct ieee80211_if_ap {
259 struct beacon_data __rcu *beacon; 259 struct beacon_data __rcu *beacon;
260 struct probe_resp __rcu *probe_resp; 260 struct probe_resp __rcu *probe_resp;
261 261
262 /* to be used after channel switch. */
263 struct cfg80211_beacon_data *next_beacon;
262 struct list_head vlans; 264 struct list_head vlans;
263 265
264 struct ps_data ps; 266 struct ps_data ps;
@@ -716,6 +718,11 @@ struct ieee80211_sub_if_data {
716 718
717 struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; 719 struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
718 720
721 struct work_struct csa_finalize_work;
722 int csa_counter_offset_beacon;
723 int csa_counter_offset_presp;
724 bool csa_radar_required;
725
719 /* used to reconfigure hardware SM PS */ 726 /* used to reconfigure hardware SM PS */
720 struct work_struct recalc_smps; 727 struct work_struct recalc_smps;
721 728
@@ -1372,6 +1379,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
1372void ieee80211_sw_roc_work(struct work_struct *work); 1379void ieee80211_sw_roc_work(struct work_struct *work);
1373void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); 1380void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
1374 1381
1382/* channel switch handling */
1383void ieee80211_csa_finalize_work(struct work_struct *work);
1384
1375/* interface handling */ 1385/* interface handling */
1376int ieee80211_iface_init(void); 1386int ieee80211_iface_init(void);
1377void ieee80211_iface_exit(void); 1387void ieee80211_iface_exit(void);
@@ -1393,6 +1403,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local);
1393 1403
1394bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); 1404bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
1395void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); 1405void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
1406int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
1407 struct cfg80211_beacon_data *params);
1396 1408
1397static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) 1409static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
1398{ 1410{
@@ -1654,6 +1666,11 @@ int __must_check
1654ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, 1666ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1655 const struct cfg80211_chan_def *chandef, 1667 const struct cfg80211_chan_def *chandef,
1656 u32 *changed); 1668 u32 *changed);
1669/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
1670int __must_check
1671ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
1672 const struct cfg80211_chan_def *chandef,
1673 u32 *changed);
1657void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); 1674void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
1658void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); 1675void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
1659void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, 1676void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4c41c11958c8..7ca534bf4cea 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -274,6 +274,12 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
274 if (iftype == NL80211_IFTYPE_ADHOC && 274 if (iftype == NL80211_IFTYPE_ADHOC &&
275 nsdata->vif.type == NL80211_IFTYPE_ADHOC) 275 nsdata->vif.type == NL80211_IFTYPE_ADHOC)
276 return -EBUSY; 276 return -EBUSY;
277 /*
278 * will not add another interface while any channel
279 * switch is active.
280 */
281 if (nsdata->vif.csa_active)
282 return -EBUSY;
277 283
278 /* 284 /*
279 * The remaining checks are only performed for interfaces 285 * The remaining checks are only performed for interfaces
@@ -804,6 +810,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
804 cancel_work_sync(&local->dynamic_ps_enable_work); 810 cancel_work_sync(&local->dynamic_ps_enable_work);
805 811
806 cancel_work_sync(&sdata->recalc_smps); 812 cancel_work_sync(&sdata->recalc_smps);
813 sdata->vif.csa_active = false;
814 cancel_work_sync(&sdata->csa_finalize_work);
807 815
808 cancel_delayed_work_sync(&sdata->dfs_cac_timer_work); 816 cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
809 817
@@ -1267,6 +1275,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
1267 skb_queue_head_init(&sdata->skb_queue); 1275 skb_queue_head_init(&sdata->skb_queue);
1268 INIT_WORK(&sdata->work, ieee80211_iface_work); 1276 INIT_WORK(&sdata->work, ieee80211_iface_work);
1269 INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work); 1277 INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
1278 INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
1270 1279
1271 switch (type) { 1280 switch (type) {
1272 case NL80211_IFTYPE_P2P_GO: 1281 case NL80211_IFTYPE_P2P_GO:
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index c215fafd7a2f..1aba645882bd 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1906,6 +1906,32 @@ TRACE_EVENT(api_radar_detected,
1906 ) 1906 )
1907); 1907);
1908 1908
1909TRACE_EVENT(drv_channel_switch_beacon,
1910 TP_PROTO(struct ieee80211_local *local,
1911 struct ieee80211_sub_if_data *sdata,
1912 struct cfg80211_chan_def *chandef),
1913
1914 TP_ARGS(local, sdata, chandef),
1915
1916 TP_STRUCT__entry(
1917 LOCAL_ENTRY
1918 VIF_ENTRY
1919 CHANDEF_ENTRY
1920 ),
1921
1922 TP_fast_assign(
1923 LOCAL_ASSIGN;
1924 VIF_ASSIGN;
1925 CHANDEF_ASSIGN(chandef);
1926 ),
1927
1928 TP_printk(
1929 LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
1930 LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
1931 )
1932);
1933
1934
1909#ifdef CONFIG_MAC80211_MESSAGE_TRACING 1935#ifdef CONFIG_MAC80211_MESSAGE_TRACING
1910#undef TRACE_SYSTEM 1936#undef TRACE_SYSTEM
1911#define TRACE_SYSTEM mac80211_msg 1937#define TRACE_SYSTEM mac80211_msg
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index f65873f0c89f..0e42322aa6b1 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2338,6 +2338,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
2338 return 0; 2338 return 0;
2339} 2339}
2340 2340
2341void ieee80211_csa_finish(struct ieee80211_vif *vif)
2342{
2343 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2344
2345 ieee80211_queue_work(&sdata->local->hw,
2346 &sdata->csa_finalize_work);
2347}
2348EXPORT_SYMBOL(ieee80211_csa_finish);
2349
2350static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
2351 struct beacon_data *beacon)
2352{
2353 struct probe_resp *resp;
2354 int counter_offset_beacon = sdata->csa_counter_offset_beacon;
2355 int counter_offset_presp = sdata->csa_counter_offset_presp;
2356
2357 /* warn if the driver did not check for/react to csa completeness */
2358 if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
2359 return;
2360
2361 ((u8 *)beacon->tail)[counter_offset_beacon]--;
2362
2363 if (sdata->vif.type == NL80211_IFTYPE_AP &&
2364 counter_offset_presp) {
2365 rcu_read_lock();
2366 resp = rcu_dereference(sdata->u.ap.probe_resp);
2367
2368 /* if nl80211 accepted the offset, this should not happen. */
2369 if (WARN_ON(!resp)) {
2370 rcu_read_unlock();
2371 return;
2372 }
2373 resp->data[counter_offset_presp]--;
2374 rcu_read_unlock();
2375 }
2376}
2377
2378bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
2379{
2380 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
2381 struct beacon_data *beacon = NULL;
2382 u8 *beacon_data;
2383 size_t beacon_data_len;
2384 int counter_beacon = sdata->csa_counter_offset_beacon;
2385 int ret = false;
2386
2387 if (!ieee80211_sdata_running(sdata))
2388 return false;
2389
2390 rcu_read_lock();
2391 if (vif->type == NL80211_IFTYPE_AP) {
2392 struct ieee80211_if_ap *ap = &sdata->u.ap;
2393
2394 beacon = rcu_dereference(ap->beacon);
2395 if (WARN_ON(!beacon || !beacon->tail))
2396 goto out;
2397 beacon_data = beacon->tail;
2398 beacon_data_len = beacon->tail_len;
2399 } else {
2400 WARN_ON(1);
2401 goto out;
2402 }
2403
2404 if (WARN_ON(counter_beacon > beacon_data_len))
2405 goto out;
2406
2407 if (beacon_data[counter_beacon] == 0)
2408 ret = true;
2409 out:
2410 rcu_read_unlock();
2411
2412 return ret;
2413}
2414EXPORT_SYMBOL(ieee80211_csa_is_complete);
2415
2341struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, 2416struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2342 struct ieee80211_vif *vif, 2417 struct ieee80211_vif *vif,
2343 u16 *tim_offset, u16 *tim_length) 2418 u16 *tim_offset, u16 *tim_length)
@@ -2368,6 +2443,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
2368 struct beacon_data *beacon = rcu_dereference(ap->beacon); 2443 struct beacon_data *beacon = rcu_dereference(ap->beacon);
2369 2444
2370 if (beacon) { 2445 if (beacon) {
2446 if (sdata->vif.csa_active)
2447 ieee80211_update_csa(sdata, beacon);
2448
2371 /* 2449 /*
2372 * headroom, head length, 2450 * headroom, head length,
2373 * tail length and maximum TIM length 2451 * tail length and maximum TIM length