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/mac80211/cfg.c | |
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/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 187 |
1 files changed, 185 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 | }; |