diff options
author | Simon Wunderlich <simon.wunderlich@s2003.tu-chemnitz.de> | 2013-07-11 10:09:06 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-08-01 12:30:33 -0400 |
commit | 73da7d5bab79ad7e16ff44d67c3fe8b9c0b33e5b (patch) | |
tree | cb4eee7b96aae1d31a4841167a3f36c638bd0a11 /net | |
parent | 16ef1fe272332b2f7fd99236017b891db48d9cd6 (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.c | 187 | ||||
-rw-r--r-- | net/mac80211/chan.c | 58 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 13 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 17 | ||||
-rw-r--r-- | net/mac80211/iface.c | 9 | ||||
-rw-r--r-- | net/mac80211/trace.h | 26 | ||||
-rw-r--r-- | net/mac80211/tx.c | 78 |
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 | ||
863 | static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, | 863 | int 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 | ||
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(¶ms->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(¶ms->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, ¶ms->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, ¶ms->chandef); | ||
2956 | |||
2957 | return 0; | ||
2958 | } | ||
2959 | |||
2778 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, | 2960 | static 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 | ||
413 | int 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 | |||
413 | int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | 471 | int 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 | ||
1075 | static inline void | ||
1076 | drv_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); | |||
1372 | void ieee80211_sw_roc_work(struct work_struct *work); | 1379 | void ieee80211_sw_roc_work(struct work_struct *work); |
1373 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); | 1380 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); |
1374 | 1381 | ||
1382 | /* channel switch handling */ | ||
1383 | void ieee80211_csa_finalize_work(struct work_struct *work); | ||
1384 | |||
1375 | /* interface handling */ | 1385 | /* interface handling */ |
1376 | int ieee80211_iface_init(void); | 1386 | int ieee80211_iface_init(void); |
1377 | void ieee80211_iface_exit(void); | 1387 | void ieee80211_iface_exit(void); |
@@ -1393,6 +1403,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local); | |||
1393 | 1403 | ||
1394 | bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); | 1404 | bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); |
1395 | void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); | 1405 | void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); |
1406 | int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, | ||
1407 | struct cfg80211_beacon_data *params); | ||
1396 | 1408 | ||
1397 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) | 1409 | static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) |
1398 | { | 1410 | { |
@@ -1654,6 +1666,11 @@ int __must_check | |||
1654 | ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | 1666 | ieee80211_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 */ | ||
1670 | int __must_check | ||
1671 | ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | ||
1672 | const struct cfg80211_chan_def *chandef, | ||
1673 | u32 *changed); | ||
1657 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); | 1674 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); |
1658 | void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); | 1675 | void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata); |
1659 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | 1676 | void 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 | ||
1909 | TRACE_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 | ||
2341 | void 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 | } | ||
2348 | EXPORT_SYMBOL(ieee80211_csa_finish); | ||
2349 | |||
2350 | static 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 | |||
2378 | bool 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 | } | ||
2414 | EXPORT_SYMBOL(ieee80211_csa_is_complete); | ||
2415 | |||
2341 | struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | 2416 | struct 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 |