diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-08-09 15:08:10 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-08-09 15:08:10 -0400 |
commit | fa5978447cb0144411df3a588e3d01459c12d855 (patch) | |
tree | 998e58c515def864c8cd87511625d1e7184a7a21 /net/mac80211 | |
parent | 2437f3c5d6bc07252c6d7d24448755e0c35ed91c (diff) | |
parent | 73da7d5bab79ad7e16ff44d67c3fe8b9c0b33e5b (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/cfg.c | 187 | ||||
-rw-r--r-- | net/mac80211/chan.c | 58 | ||||
-rw-r--r-- | net/mac80211/debugfs_sta.c | 9 | ||||
-rw-r--r-- | net/mac80211/driver-ops.h | 13 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 30 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 18 | ||||
-rw-r--r-- | net/mac80211/iface.c | 9 | ||||
-rw-r--r-- | net/mac80211/led.c | 19 | ||||
-rw-r--r-- | net/mac80211/led.h | 4 | ||||
-rw-r--r-- | net/mac80211/status.c | 78 | ||||
-rw-r--r-- | net/mac80211/trace.h | 26 | ||||
-rw-r--r-- | net/mac80211/tx.c | 79 |
12 files changed, 497 insertions, 33 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 973594b229f4..31fc2247bc37 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -862,8 +862,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, | |||
862 | return 0; | 862 | return 0; |
863 | } | 863 | } |
864 | 864 | ||
865 | static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, | 865 | int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, |
866 | struct cfg80211_beacon_data *params) | 866 | struct cfg80211_beacon_data *params) |
867 | { | 867 | { |
868 | struct beacon_data *new, *old; | 868 | struct beacon_data *new, *old; |
869 | int new_head_len, new_tail_len; | 869 | int new_head_len, new_tail_len; |
@@ -1026,6 +1026,12 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, | |||
1026 | 1026 | ||
1027 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 1027 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
1028 | 1028 | ||
1029 | /* don't allow changing the beacon while CSA is in place - offset | ||
1030 | * of channel switch counter may change | ||
1031 | */ | ||
1032 | if (sdata->vif.csa_active) | ||
1033 | return -EBUSY; | ||
1034 | |||
1029 | old = rtnl_dereference(sdata->u.ap.beacon); | 1035 | old = rtnl_dereference(sdata->u.ap.beacon); |
1030 | if (!old) | 1036 | if (!old) |
1031 | return -ENOENT; | 1037 | return -ENOENT; |
@@ -1050,6 +1056,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) | |||
1050 | return -ENOENT; | 1056 | return -ENOENT; |
1051 | old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); | 1057 | old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); |
1052 | 1058 | ||
1059 | /* abort any running channel switch */ | ||
1060 | sdata->vif.csa_active = false; | ||
1061 | cancel_work_sync(&sdata->csa_finalize_work); | ||
1062 | |||
1053 | /* turn off carrier for this interface and dependent VLANs */ | 1063 | /* turn off carrier for this interface and dependent VLANs */ |
1054 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | 1064 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) |
1055 | netif_carrier_off(vlan->dev); | 1065 | netif_carrier_off(vlan->dev); |
@@ -2777,6 +2787,178 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy, | |||
2777 | return 0; | 2787 | return 0; |
2778 | } | 2788 | } |
2779 | 2789 | ||
2790 | static struct cfg80211_beacon_data * | ||
2791 | cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon) | ||
2792 | { | ||
2793 | struct cfg80211_beacon_data *new_beacon; | ||
2794 | u8 *pos; | ||
2795 | int len; | ||
2796 | |||
2797 | len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len + | ||
2798 | beacon->proberesp_ies_len + beacon->assocresp_ies_len + | ||
2799 | beacon->probe_resp_len; | ||
2800 | |||
2801 | new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL); | ||
2802 | if (!new_beacon) | ||
2803 | return NULL; | ||
2804 | |||
2805 | pos = (u8 *)(new_beacon + 1); | ||
2806 | if (beacon->head_len) { | ||
2807 | new_beacon->head_len = beacon->head_len; | ||
2808 | new_beacon->head = pos; | ||
2809 | memcpy(pos, beacon->head, beacon->head_len); | ||
2810 | pos += beacon->head_len; | ||
2811 | } | ||
2812 | if (beacon->tail_len) { | ||
2813 | new_beacon->tail_len = beacon->tail_len; | ||
2814 | new_beacon->tail = pos; | ||
2815 | memcpy(pos, beacon->tail, beacon->tail_len); | ||
2816 | pos += beacon->tail_len; | ||
2817 | } | ||
2818 | if (beacon->beacon_ies_len) { | ||
2819 | new_beacon->beacon_ies_len = beacon->beacon_ies_len; | ||
2820 | new_beacon->beacon_ies = pos; | ||
2821 | memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len); | ||
2822 | pos += beacon->beacon_ies_len; | ||
2823 | } | ||
2824 | if (beacon->proberesp_ies_len) { | ||
2825 | new_beacon->proberesp_ies_len = beacon->proberesp_ies_len; | ||
2826 | new_beacon->proberesp_ies = pos; | ||
2827 | memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len); | ||
2828 | pos += beacon->proberesp_ies_len; | ||
2829 | } | ||
2830 | if (beacon->assocresp_ies_len) { | ||
2831 | new_beacon->assocresp_ies_len = beacon->assocresp_ies_len; | ||
2832 | new_beacon->assocresp_ies = pos; | ||
2833 | memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len); | ||
2834 | pos += beacon->assocresp_ies_len; | ||
2835 | } | ||
2836 | if (beacon->probe_resp_len) { | ||
2837 | new_beacon->probe_resp_len = beacon->probe_resp_len; | ||
2838 | beacon->probe_resp = pos; | ||
2839 | memcpy(pos, beacon->probe_resp, beacon->probe_resp_len); | ||
2840 | pos += beacon->probe_resp_len; | ||
2841 | } | ||
2842 | |||
2843 | return new_beacon; | ||
2844 | } | ||
2845 | |||
2846 | void ieee80211_csa_finalize_work(struct work_struct *work) | ||
2847 | { | ||
2848 | struct ieee80211_sub_if_data *sdata = | ||
2849 | container_of(work, struct ieee80211_sub_if_data, | ||
2850 | csa_finalize_work); | ||
2851 | struct ieee80211_local *local = sdata->local; | ||
2852 | int err, changed; | ||
2853 | |||
2854 | if (!ieee80211_sdata_running(sdata)) | ||
2855 | return; | ||
2856 | |||
2857 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
2858 | return; | ||
2859 | |||
2860 | sdata->radar_required = sdata->csa_radar_required; | ||
2861 | err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, | ||
2862 | &changed); | ||
2863 | if (WARN_ON(err < 0)) | ||
2864 | return; | ||
2865 | |||
2866 | err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); | ||
2867 | if (err < 0) | ||
2868 | return; | ||
2869 | |||
2870 | changed |= err; | ||
2871 | kfree(sdata->u.ap.next_beacon); | ||
2872 | sdata->u.ap.next_beacon = NULL; | ||
2873 | sdata->vif.csa_active = false; | ||
2874 | |||
2875 | ieee80211_wake_queues_by_reason(&sdata->local->hw, | ||
2876 | IEEE80211_MAX_QUEUE_MAP, | ||
2877 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
2878 | |||
2879 | ieee80211_bss_info_change_notify(sdata, changed); | ||
2880 | |||
2881 | cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); | ||
2882 | } | ||
2883 | |||
2884 | static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, | ||
2885 | struct cfg80211_csa_settings *params) | ||
2886 | { | ||
2887 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
2888 | struct ieee80211_local *local = sdata->local; | ||
2889 | struct ieee80211_chanctx_conf *chanctx_conf; | ||
2890 | struct ieee80211_chanctx *chanctx; | ||
2891 | int err, num_chanctx; | ||
2892 | |||
2893 | if (!list_empty(&local->roc_list) || local->scanning) | ||
2894 | return -EBUSY; | ||
2895 | |||
2896 | if (sdata->wdev.cac_started) | ||
2897 | return -EBUSY; | ||
2898 | |||
2899 | if (cfg80211_chandef_identical(¶ms->chandef, | ||
2900 | &sdata->vif.bss_conf.chandef)) | ||
2901 | return -EINVAL; | ||
2902 | |||
2903 | rcu_read_lock(); | ||
2904 | chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); | ||
2905 | if (!chanctx_conf) { | ||
2906 | rcu_read_unlock(); | ||
2907 | return -EBUSY; | ||
2908 | } | ||
2909 | |||
2910 | /* don't handle for multi-VIF cases */ | ||
2911 | chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); | ||
2912 | if (chanctx->refcount > 1) { | ||
2913 | rcu_read_unlock(); | ||
2914 | return -EBUSY; | ||
2915 | } | ||
2916 | num_chanctx = 0; | ||
2917 | list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) | ||
2918 | num_chanctx++; | ||
2919 | rcu_read_unlock(); | ||
2920 | |||
2921 | if (num_chanctx > 1) | ||
2922 | return -EBUSY; | ||
2923 | |||
2924 | /* don't allow another channel switch if one is already active. */ | ||
2925 | if (sdata->vif.csa_active) | ||
2926 | return -EBUSY; | ||
2927 | |||
2928 | /* only handle AP for now. */ | ||
2929 | switch (sdata->vif.type) { | ||
2930 | case NL80211_IFTYPE_AP: | ||
2931 | break; | ||
2932 | default: | ||
2933 | return -EOPNOTSUPP; | ||
2934 | } | ||
2935 | |||
2936 | sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); | ||
2937 | if (!sdata->u.ap.next_beacon) | ||
2938 | return -ENOMEM; | ||
2939 | |||
2940 | sdata->csa_counter_offset_beacon = params->counter_offset_beacon; | ||
2941 | sdata->csa_counter_offset_presp = params->counter_offset_presp; | ||
2942 | sdata->csa_radar_required = params->radar_required; | ||
2943 | |||
2944 | if (params->block_tx) | ||
2945 | ieee80211_stop_queues_by_reason(&local->hw, | ||
2946 | IEEE80211_MAX_QUEUE_MAP, | ||
2947 | IEEE80211_QUEUE_STOP_REASON_CSA); | ||
2948 | |||
2949 | err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); | ||
2950 | if (err < 0) | ||
2951 | return err; | ||
2952 | |||
2953 | local->csa_chandef = params->chandef; | ||
2954 | sdata->vif.csa_active = true; | ||
2955 | |||
2956 | ieee80211_bss_info_change_notify(sdata, err); | ||
2957 | drv_channel_switch_beacon(sdata, ¶ms->chandef); | ||
2958 | |||
2959 | return 0; | ||
2960 | } | ||
2961 | |||
2780 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, | 2962 | static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, |
2781 | struct ieee80211_channel *chan, bool offchan, | 2963 | struct ieee80211_channel *chan, bool offchan, |
2782 | unsigned int wait, const u8 *buf, size_t len, | 2964 | unsigned int wait, const u8 *buf, size_t len, |
@@ -3494,4 +3676,5 @@ struct cfg80211_ops mac80211_config_ops = { | |||
3494 | .get_et_strings = ieee80211_get_et_strings, | 3676 | .get_et_strings = ieee80211_get_et_strings, |
3495 | .get_channel = ieee80211_cfg_get_channel, | 3677 | .get_channel = ieee80211_cfg_get_channel, |
3496 | .start_radar_detection = ieee80211_start_radar_detection, | 3678 | .start_radar_detection = ieee80211_start_radar_detection, |
3679 | .channel_switch = ieee80211_channel_switch, | ||
3497 | }; | 3680 | }; |
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/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 44e201d60a13..19c54a44ed47 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c | |||
@@ -455,6 +455,15 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) | |||
455 | DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count); | 455 | DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count); |
456 | DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); | 456 | DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); |
457 | 457 | ||
458 | if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) | ||
459 | debugfs_create_x32("driver_buffered_tids", 0400, | ||
460 | sta->debugfs.dir, | ||
461 | (u32 *)&sta->driver_buffered_tids); | ||
462 | else | ||
463 | debugfs_create_x64("driver_buffered_tids", 0400, | ||
464 | sta->debugfs.dir, | ||
465 | (u64 *)&sta->driver_buffered_tids); | ||
466 | |||
458 | drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); | 467 | drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); |
459 | } | 468 | } |
460 | 469 | ||
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/ibss.c b/net/mac80211/ibss.c index 5e6836c3aa4c..e08387cdc8fd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) | 31 | #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) |
32 | #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) | 32 | #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) |
33 | #define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ) | ||
33 | 34 | ||
34 | #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 | 35 | #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 |
35 | 36 | ||
@@ -740,6 +741,33 @@ static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) | |||
740 | return active; | 741 | return active; |
741 | } | 742 | } |
742 | 743 | ||
744 | static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) | ||
745 | { | ||
746 | struct ieee80211_local *local = sdata->local; | ||
747 | struct sta_info *sta, *tmp; | ||
748 | unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT; | ||
749 | unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT; | ||
750 | |||
751 | mutex_lock(&local->sta_mtx); | ||
752 | |||
753 | list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { | ||
754 | if (sdata != sta->sdata) | ||
755 | continue; | ||
756 | |||
757 | if (time_after(jiffies, sta->last_rx + exp_time) || | ||
758 | (time_after(jiffies, sta->last_rx + exp_rsn_time) && | ||
759 | sta->sta_state != IEEE80211_STA_AUTHORIZED)) { | ||
760 | sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n", | ||
761 | sta->sta_state != IEEE80211_STA_AUTHORIZED ? | ||
762 | "not authorized " : "", sta->sta.addr); | ||
763 | |||
764 | WARN_ON(__sta_info_destroy(sta)); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | mutex_unlock(&local->sta_mtx); | ||
769 | } | ||
770 | |||
743 | /* | 771 | /* |
744 | * This function is called with state == IEEE80211_IBSS_MLME_JOINED | 772 | * This function is called with state == IEEE80211_IBSS_MLME_JOINED |
745 | */ | 773 | */ |
@@ -754,7 +782,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) | |||
754 | mod_timer(&ifibss->timer, | 782 | mod_timer(&ifibss->timer, |
755 | round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); | 783 | round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL)); |
756 | 784 | ||
757 | ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT); | 785 | ieee80211_ibss_sta_expire(sdata); |
758 | 786 | ||
759 | if (time_before(jiffies, ifibss->last_scan_completed + | 787 | if (time_before(jiffies, ifibss->last_scan_completed + |
760 | IEEE80211_IBSS_MERGE_INTERVAL)) | 788 | IEEE80211_IBSS_MERGE_INTERVAL)) |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3d32df1fbc6d..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 | ||
@@ -1094,7 +1101,6 @@ struct ieee80211_local { | |||
1094 | u32 dot11TransmittedFrameCount; | 1101 | u32 dot11TransmittedFrameCount; |
1095 | 1102 | ||
1096 | #ifdef CONFIG_MAC80211_LEDS | 1103 | #ifdef CONFIG_MAC80211_LEDS |
1097 | int tx_led_counter, rx_led_counter; | ||
1098 | struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; | 1104 | struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led; |
1099 | struct tpt_led_trigger *tpt_led_trigger; | 1105 | struct tpt_led_trigger *tpt_led_trigger; |
1100 | char tx_led_name[32], rx_led_name[32], | 1106 | char tx_led_name[32], rx_led_name[32], |
@@ -1373,6 +1379,9 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free); | |||
1373 | void ieee80211_sw_roc_work(struct work_struct *work); | 1379 | void ieee80211_sw_roc_work(struct work_struct *work); |
1374 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); | 1380 | void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); |
1375 | 1381 | ||
1382 | /* channel switch handling */ | ||
1383 | void ieee80211_csa_finalize_work(struct work_struct *work); | ||
1384 | |||
1376 | /* interface handling */ | 1385 | /* interface handling */ |
1377 | int ieee80211_iface_init(void); | 1386 | int ieee80211_iface_init(void); |
1378 | void ieee80211_iface_exit(void); | 1387 | void ieee80211_iface_exit(void); |
@@ -1394,6 +1403,8 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local); | |||
1394 | 1403 | ||
1395 | bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); | 1404 | bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); |
1396 | 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); | ||
1397 | 1408 | ||
1398 | 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) |
1399 | { | 1410 | { |
@@ -1655,6 +1666,11 @@ int __must_check | |||
1655 | ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | 1666 | ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, |
1656 | const struct cfg80211_chan_def *chandef, | 1667 | const struct cfg80211_chan_def *chandef, |
1657 | 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); | ||
1658 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); | 1674 | void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); |
1659 | 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); |
1660 | 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/led.c b/net/mac80211/led.c index bcffa6903129..e2b836446af3 100644 --- a/net/mac80211/led.c +++ b/net/mac80211/led.c | |||
@@ -12,27 +12,22 @@ | |||
12 | #include <linux/export.h> | 12 | #include <linux/export.h> |
13 | #include "led.h" | 13 | #include "led.h" |
14 | 14 | ||
15 | #define MAC80211_BLINK_DELAY 50 /* ms */ | ||
16 | |||
15 | void ieee80211_led_rx(struct ieee80211_local *local) | 17 | void ieee80211_led_rx(struct ieee80211_local *local) |
16 | { | 18 | { |
19 | unsigned long led_delay = MAC80211_BLINK_DELAY; | ||
17 | if (unlikely(!local->rx_led)) | 20 | if (unlikely(!local->rx_led)) |
18 | return; | 21 | return; |
19 | if (local->rx_led_counter++ % 2 == 0) | 22 | led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0); |
20 | led_trigger_event(local->rx_led, LED_OFF); | ||
21 | else | ||
22 | led_trigger_event(local->rx_led, LED_FULL); | ||
23 | } | 23 | } |
24 | 24 | ||
25 | /* q is 1 if a packet was enqueued, 0 if it has been transmitted */ | 25 | void ieee80211_led_tx(struct ieee80211_local *local) |
26 | void ieee80211_led_tx(struct ieee80211_local *local, int q) | ||
27 | { | 26 | { |
27 | unsigned long led_delay = MAC80211_BLINK_DELAY; | ||
28 | if (unlikely(!local->tx_led)) | 28 | if (unlikely(!local->tx_led)) |
29 | return; | 29 | return; |
30 | /* not sure how this is supposed to work ... */ | 30 | led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0); |
31 | local->tx_led_counter += 2*q-1; | ||
32 | if (local->tx_led_counter % 2 == 0) | ||
33 | led_trigger_event(local->tx_led, LED_OFF); | ||
34 | else | ||
35 | led_trigger_event(local->tx_led, LED_FULL); | ||
36 | } | 31 | } |
37 | 32 | ||
38 | void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) | 33 | void ieee80211_led_assoc(struct ieee80211_local *local, bool associated) |
diff --git a/net/mac80211/led.h b/net/mac80211/led.h index e0275d9befa8..89f4344f13b9 100644 --- a/net/mac80211/led.h +++ b/net/mac80211/led.h | |||
@@ -13,7 +13,7 @@ | |||
13 | 13 | ||
14 | #ifdef CONFIG_MAC80211_LEDS | 14 | #ifdef CONFIG_MAC80211_LEDS |
15 | void ieee80211_led_rx(struct ieee80211_local *local); | 15 | void ieee80211_led_rx(struct ieee80211_local *local); |
16 | void ieee80211_led_tx(struct ieee80211_local *local, int q); | 16 | void ieee80211_led_tx(struct ieee80211_local *local); |
17 | void ieee80211_led_assoc(struct ieee80211_local *local, | 17 | void ieee80211_led_assoc(struct ieee80211_local *local, |
18 | bool associated); | 18 | bool associated); |
19 | void ieee80211_led_radio(struct ieee80211_local *local, | 19 | void ieee80211_led_radio(struct ieee80211_local *local, |
@@ -27,7 +27,7 @@ void ieee80211_mod_tpt_led_trig(struct ieee80211_local *local, | |||
27 | static inline void ieee80211_led_rx(struct ieee80211_local *local) | 27 | static inline void ieee80211_led_rx(struct ieee80211_local *local) |
28 | { | 28 | { |
29 | } | 29 | } |
30 | static inline void ieee80211_led_tx(struct ieee80211_local *local, int q) | 30 | static inline void ieee80211_led_tx(struct ieee80211_local *local) |
31 | { | 31 | { |
32 | } | 32 | } |
33 | static inline void ieee80211_led_assoc(struct ieee80211_local *local, | 33 | static inline void ieee80211_led_assoc(struct ieee80211_local *local, |
diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 6ad4c14385ef..368837fe3b80 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c | |||
@@ -235,7 +235,8 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) | |||
235 | 235 | ||
236 | /* IEEE80211_RADIOTAP_RATE rate */ | 236 | /* IEEE80211_RADIOTAP_RATE rate */ |
237 | if (info->status.rates[0].idx >= 0 && | 237 | if (info->status.rates[0].idx >= 0 && |
238 | !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) | 238 | !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | |
239 | IEEE80211_TX_RC_VHT_MCS))) | ||
239 | len += 2; | 240 | len += 2; |
240 | 241 | ||
241 | /* IEEE80211_RADIOTAP_TX_FLAGS */ | 242 | /* IEEE80211_RADIOTAP_TX_FLAGS */ |
@@ -244,16 +245,21 @@ static int ieee80211_tx_radiotap_len(struct ieee80211_tx_info *info) | |||
244 | /* IEEE80211_RADIOTAP_DATA_RETRIES */ | 245 | /* IEEE80211_RADIOTAP_DATA_RETRIES */ |
245 | len += 1; | 246 | len += 1; |
246 | 247 | ||
247 | /* IEEE80211_TX_RC_MCS */ | 248 | /* IEEE80211_RADIOTAP_MCS |
248 | if (info->status.rates[0].idx >= 0 && | 249 | * IEEE80211_RADIOTAP_VHT */ |
249 | info->status.rates[0].flags & IEEE80211_TX_RC_MCS) | 250 | if (info->status.rates[0].idx >= 0) { |
250 | len += 3; | 251 | if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) |
252 | len += 3; | ||
253 | else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) | ||
254 | len = ALIGN(len, 2) + 12; | ||
255 | } | ||
251 | 256 | ||
252 | return len; | 257 | return len; |
253 | } | 258 | } |
254 | 259 | ||
255 | static void | 260 | static void |
256 | ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband, | 261 | ieee80211_add_tx_radiotap_header(struct ieee80211_local *local, |
262 | struct ieee80211_supported_band *sband, | ||
257 | struct sk_buff *skb, int retry_count, | 263 | struct sk_buff *skb, int retry_count, |
258 | int rtap_len, int shift) | 264 | int rtap_len, int shift) |
259 | { | 265 | { |
@@ -280,7 +286,8 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband, | |||
280 | 286 | ||
281 | /* IEEE80211_RADIOTAP_RATE */ | 287 | /* IEEE80211_RADIOTAP_RATE */ |
282 | if (info->status.rates[0].idx >= 0 && | 288 | if (info->status.rates[0].idx >= 0 && |
283 | !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) { | 289 | !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS | |
290 | IEEE80211_TX_RC_VHT_MCS))) { | ||
284 | u16 rate; | 291 | u16 rate; |
285 | 292 | ||
286 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); | 293 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); |
@@ -310,9 +317,12 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband, | |||
310 | *pos = retry_count; | 317 | *pos = retry_count; |
311 | pos++; | 318 | pos++; |
312 | 319 | ||
313 | /* IEEE80211_TX_RC_MCS */ | 320 | if (info->status.rates[0].idx < 0) |
314 | if (info->status.rates[0].idx >= 0 && | 321 | return; |
315 | info->status.rates[0].flags & IEEE80211_TX_RC_MCS) { | 322 | |
323 | /* IEEE80211_RADIOTAP_MCS | ||
324 | * IEEE80211_RADIOTAP_VHT */ | ||
325 | if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) { | ||
316 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); | 326 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); |
317 | pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | | 327 | pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS | |
318 | IEEE80211_RADIOTAP_MCS_HAVE_GI | | 328 | IEEE80211_RADIOTAP_MCS_HAVE_GI | |
@@ -325,8 +335,48 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band *sband, | |||
325 | pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF; | 335 | pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF; |
326 | pos[2] = info->status.rates[0].idx; | 336 | pos[2] = info->status.rates[0].idx; |
327 | pos += 3; | 337 | pos += 3; |
328 | } | 338 | } else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) { |
339 | u16 known = local->hw.radiotap_vht_details & | ||
340 | (IEEE80211_RADIOTAP_VHT_KNOWN_GI | | ||
341 | IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH); | ||
342 | |||
343 | rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); | ||
344 | |||
345 | /* required alignment from rthdr */ | ||
346 | pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2); | ||
329 | 347 | ||
348 | /* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */ | ||
349 | put_unaligned_le16(known, pos); | ||
350 | pos += 2; | ||
351 | |||
352 | /* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */ | ||
353 | if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI) | ||
354 | *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI; | ||
355 | pos++; | ||
356 | |||
357 | /* u8 bandwidth */ | ||
358 | if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | ||
359 | *pos = 1; | ||
360 | else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH) | ||
361 | *pos = 4; | ||
362 | else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH) | ||
363 | *pos = 11; | ||
364 | else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */ | ||
365 | *pos = 0; | ||
366 | pos++; | ||
367 | |||
368 | /* u8 mcs_nss[4] */ | ||
369 | *pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) | | ||
370 | ieee80211_rate_get_vht_nss(&info->status.rates[0]); | ||
371 | pos += 4; | ||
372 | |||
373 | /* u8 coding */ | ||
374 | pos++; | ||
375 | /* u8 group_id */ | ||
376 | pos++; | ||
377 | /* u16 partial_aid */ | ||
378 | pos += 2; | ||
379 | } | ||
330 | } | 380 | } |
331 | 381 | ||
332 | static void ieee80211_report_used_skb(struct ieee80211_local *local, | 382 | static void ieee80211_report_used_skb(struct ieee80211_local *local, |
@@ -564,7 +614,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
564 | 614 | ||
565 | rcu_read_unlock(); | 615 | rcu_read_unlock(); |
566 | 616 | ||
567 | ieee80211_led_tx(local, 0); | 617 | ieee80211_led_tx(local); |
568 | 618 | ||
569 | /* SNMP counters | 619 | /* SNMP counters |
570 | * Fragments are passed to low-level drivers as separate skbs, so these | 620 | * Fragments are passed to low-level drivers as separate skbs, so these |
@@ -631,8 +681,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) | |||
631 | dev_kfree_skb(skb); | 681 | dev_kfree_skb(skb); |
632 | return; | 682 | return; |
633 | } | 683 | } |
634 | ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len, | 684 | ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count, |
635 | shift); | 685 | rtap_len, shift); |
636 | 686 | ||
637 | /* XXX: is this sufficient for BPF? */ | 687 | /* XXX: is this sufficient for BPF? */ |
638 | skb_set_mac_header(skb, 0); | 688 | skb_set_mac_header(skb, 0); |
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 be4d3caf4879..0e42322aa6b1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c | |||
@@ -1300,7 +1300,6 @@ static bool __ieee80211_tx(struct ieee80211_local *local, | |||
1300 | txpending); | 1300 | txpending); |
1301 | 1301 | ||
1302 | ieee80211_tpt_led_trig_tx(local, fc, led_len); | 1302 | ieee80211_tpt_led_trig_tx(local, fc, led_len); |
1303 | ieee80211_led_tx(local, 1); | ||
1304 | 1303 | ||
1305 | WARN_ON_ONCE(!skb_queue_empty(skbs)); | 1304 | WARN_ON_ONCE(!skb_queue_empty(skbs)); |
1306 | 1305 | ||
@@ -2339,6 +2338,81 @@ static int ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata, | |||
2339 | return 0; | 2338 | return 0; |
2340 | } | 2339 | } |
2341 | 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 | |||
2342 | struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | 2416 | struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, |
2343 | struct ieee80211_vif *vif, | 2417 | struct ieee80211_vif *vif, |
2344 | u16 *tim_offset, u16 *tim_length) | 2418 | u16 *tim_offset, u16 *tim_length) |
@@ -2369,6 +2443,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, | |||
2369 | struct beacon_data *beacon = rcu_dereference(ap->beacon); | 2443 | struct beacon_data *beacon = rcu_dereference(ap->beacon); |
2370 | 2444 | ||
2371 | if (beacon) { | 2445 | if (beacon) { |
2446 | if (sdata->vif.csa_active) | ||
2447 | ieee80211_update_csa(sdata, beacon); | ||
2448 | |||
2372 | /* | 2449 | /* |
2373 | * headroom, head length, | 2450 | * headroom, head length, |
2374 | * tail length and maximum TIM length | 2451 | * tail length and maximum TIM length |