aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/cfg.c
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/mac80211/cfg.c
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/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c187
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
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};