diff options
author | John W. Linville <linville@tuxdriver.com> | 2013-01-28 14:43:00 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-01-28 14:43:00 -0500 |
commit | 4205e6ef4ee747aa81930537b6035086ba5f1e28 (patch) | |
tree | b2ebe2b4621f5f531f283cb9bf0005cd3c04ca7b /net/wireless | |
parent | cef401de7be8c4e155c6746bfccf721a4fa5fab9 (diff) | |
parent | 9ebea3829fac7505e0cd2642fbd13cfa9c038831 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/ap.c | 62 | ||||
-rw-r--r-- | net/wireless/chan.c | 4 | ||||
-rw-r--r-- | net/wireless/core.c | 26 | ||||
-rw-r--r-- | net/wireless/core.h | 25 | ||||
-rw-r--r-- | net/wireless/mesh.c | 8 | ||||
-rw-r--r-- | net/wireless/mlme.c | 62 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 268 | ||||
-rw-r--r-- | net/wireless/reg.c | 1124 | ||||
-rw-r--r-- | net/wireless/reg.h | 7 | ||||
-rw-r--r-- | net/wireless/sme.c | 9 | ||||
-rw-r--r-- | net/wireless/util.c | 41 |
11 files changed, 817 insertions, 819 deletions
diff --git a/net/wireless/ap.c b/net/wireless/ap.c index 324e8d851dc4..a4a14e8f55cc 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c | |||
@@ -46,3 +46,65 @@ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, | |||
46 | 46 | ||
47 | return err; | 47 | return err; |
48 | } | 48 | } |
49 | |||
50 | void cfg80211_ch_switch_notify(struct net_device *dev, | ||
51 | struct cfg80211_chan_def *chandef) | ||
52 | { | ||
53 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
54 | struct wiphy *wiphy = wdev->wiphy; | ||
55 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
56 | |||
57 | trace_cfg80211_ch_switch_notify(dev, chandef); | ||
58 | |||
59 | wdev_lock(wdev); | ||
60 | |||
61 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
62 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) | ||
63 | goto out; | ||
64 | |||
65 | wdev->channel = chandef->chan; | ||
66 | nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); | ||
67 | out: | ||
68 | wdev_unlock(wdev); | ||
69 | return; | ||
70 | } | ||
71 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); | ||
72 | |||
73 | bool cfg80211_rx_spurious_frame(struct net_device *dev, | ||
74 | const u8 *addr, gfp_t gfp) | ||
75 | { | ||
76 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
77 | bool ret; | ||
78 | |||
79 | trace_cfg80211_rx_spurious_frame(dev, addr); | ||
80 | |||
81 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
82 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) { | ||
83 | trace_cfg80211_return_bool(false); | ||
84 | return false; | ||
85 | } | ||
86 | ret = nl80211_unexpected_frame(dev, addr, gfp); | ||
87 | trace_cfg80211_return_bool(ret); | ||
88 | return ret; | ||
89 | } | ||
90 | EXPORT_SYMBOL(cfg80211_rx_spurious_frame); | ||
91 | |||
92 | bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, | ||
93 | const u8 *addr, gfp_t gfp) | ||
94 | { | ||
95 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
96 | bool ret; | ||
97 | |||
98 | trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); | ||
99 | |||
100 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
101 | wdev->iftype != NL80211_IFTYPE_P2P_GO && | ||
102 | wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { | ||
103 | trace_cfg80211_return_bool(false); | ||
104 | return false; | ||
105 | } | ||
106 | ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); | ||
107 | trace_cfg80211_return_bool(ret); | ||
108 | return ret; | ||
109 | } | ||
110 | EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); | ||
diff --git a/net/wireless/chan.c b/net/wireless/chan.c index a7990bb16529..396373f3ec26 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c | |||
@@ -76,6 +76,10 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) | |||
76 | return false; | 76 | return false; |
77 | if (!chandef->center_freq2) | 77 | if (!chandef->center_freq2) |
78 | return false; | 78 | return false; |
79 | /* adjacent is not allowed -- that's a 160 MHz channel */ | ||
80 | if (chandef->center_freq1 - chandef->center_freq2 == 80 || | ||
81 | chandef->center_freq2 - chandef->center_freq1 == 80) | ||
82 | return false; | ||
79 | break; | 83 | break; |
80 | case NL80211_CHAN_WIDTH_80: | 84 | case NL80211_CHAN_WIDTH_80: |
81 | if (chandef->center_freq1 != control_freq + 30 && | 85 | if (chandef->center_freq1 != control_freq + 30 && |
diff --git a/net/wireless/core.c b/net/wireless/core.c index b677eab55b68..9245729694d2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -57,9 +57,6 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) | |||
57 | { | 57 | { |
58 | struct cfg80211_registered_device *result = NULL, *rdev; | 58 | struct cfg80211_registered_device *result = NULL, *rdev; |
59 | 59 | ||
60 | if (!wiphy_idx_valid(wiphy_idx)) | ||
61 | return NULL; | ||
62 | |||
63 | assert_cfg80211_lock(); | 60 | assert_cfg80211_lock(); |
64 | 61 | ||
65 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 62 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
@@ -74,10 +71,8 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx) | |||
74 | 71 | ||
75 | int get_wiphy_idx(struct wiphy *wiphy) | 72 | int get_wiphy_idx(struct wiphy *wiphy) |
76 | { | 73 | { |
77 | struct cfg80211_registered_device *rdev; | 74 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
78 | if (!wiphy) | 75 | |
79 | return WIPHY_IDX_STALE; | ||
80 | rdev = wiphy_to_dev(wiphy); | ||
81 | return rdev->wiphy_idx; | 76 | return rdev->wiphy_idx; |
82 | } | 77 | } |
83 | 78 | ||
@@ -86,9 +81,6 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx) | |||
86 | { | 81 | { |
87 | struct cfg80211_registered_device *rdev; | 82 | struct cfg80211_registered_device *rdev; |
88 | 83 | ||
89 | if (!wiphy_idx_valid(wiphy_idx)) | ||
90 | return NULL; | ||
91 | |||
92 | assert_cfg80211_lock(); | 84 | assert_cfg80211_lock(); |
93 | 85 | ||
94 | rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); | 86 | rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx); |
@@ -309,7 +301,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
309 | 301 | ||
310 | rdev->wiphy_idx = wiphy_counter++; | 302 | rdev->wiphy_idx = wiphy_counter++; |
311 | 303 | ||
312 | if (unlikely(!wiphy_idx_valid(rdev->wiphy_idx))) { | 304 | if (unlikely(rdev->wiphy_idx < 0)) { |
313 | wiphy_counter--; | 305 | wiphy_counter--; |
314 | mutex_unlock(&cfg80211_mutex); | 306 | mutex_unlock(&cfg80211_mutex); |
315 | /* ugh, wrapped! */ | 307 | /* ugh, wrapped! */ |
@@ -390,8 +382,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) | |||
390 | 382 | ||
391 | c = &wiphy->iface_combinations[i]; | 383 | c = &wiphy->iface_combinations[i]; |
392 | 384 | ||
393 | /* Combinations with just one interface aren't real */ | 385 | /* |
394 | if (WARN_ON(c->max_interfaces < 2)) | 386 | * Combinations with just one interface aren't real, |
387 | * however we make an exception for DFS. | ||
388 | */ | ||
389 | if (WARN_ON((c->max_interfaces < 2) && !c->radar_detect_widths)) | ||
395 | return -EINVAL; | 390 | return -EINVAL; |
396 | 391 | ||
397 | /* Need at least one channel */ | 392 | /* Need at least one channel */ |
@@ -406,6 +401,11 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) | |||
406 | CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) | 401 | CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) |
407 | return -EINVAL; | 402 | return -EINVAL; |
408 | 403 | ||
404 | /* DFS only works on one channel. */ | ||
405 | if (WARN_ON(c->radar_detect_widths && | ||
406 | (c->num_different_channels > 1))) | ||
407 | return -EINVAL; | ||
408 | |||
409 | if (WARN_ON(!c->n_limits)) | 409 | if (WARN_ON(!c->n_limits)) |
410 | return -EINVAL; | 410 | return -EINVAL; |
411 | 411 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index 3563097169cb..8396f7671c8d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -18,6 +18,9 @@ | |||
18 | #include <net/cfg80211.h> | 18 | #include <net/cfg80211.h> |
19 | #include "reg.h" | 19 | #include "reg.h" |
20 | 20 | ||
21 | |||
22 | #define WIPHY_IDX_INVALID -1 | ||
23 | |||
21 | struct cfg80211_registered_device { | 24 | struct cfg80211_registered_device { |
22 | const struct cfg80211_ops *ops; | 25 | const struct cfg80211_ops *ops; |
23 | struct list_head list; | 26 | struct list_head list; |
@@ -86,7 +89,7 @@ struct cfg80211_registered_device { | |||
86 | 89 | ||
87 | /* must be last because of the way we do wiphy_priv(), | 90 | /* must be last because of the way we do wiphy_priv(), |
88 | * and it should at least be aligned to NETDEV_ALIGN */ | 91 | * and it should at least be aligned to NETDEV_ALIGN */ |
89 | struct wiphy wiphy __attribute__((__aligned__(NETDEV_ALIGN))); | 92 | struct wiphy wiphy __aligned(NETDEV_ALIGN); |
90 | }; | 93 | }; |
91 | 94 | ||
92 | static inline | 95 | static inline |
@@ -96,13 +99,6 @@ struct cfg80211_registered_device *wiphy_to_dev(struct wiphy *wiphy) | |||
96 | return container_of(wiphy, struct cfg80211_registered_device, wiphy); | 99 | return container_of(wiphy, struct cfg80211_registered_device, wiphy); |
97 | } | 100 | } |
98 | 101 | ||
99 | /* Note 0 is valid, hence phy0 */ | ||
100 | static inline | ||
101 | bool wiphy_idx_valid(int wiphy_idx) | ||
102 | { | ||
103 | return wiphy_idx >= 0; | ||
104 | } | ||
105 | |||
106 | static inline void | 102 | static inline void |
107 | cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) | 103 | cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev) |
108 | { | 104 | { |
@@ -126,12 +122,6 @@ static inline void assert_cfg80211_lock(void) | |||
126 | lockdep_assert_held(&cfg80211_mutex); | 122 | lockdep_assert_held(&cfg80211_mutex); |
127 | } | 123 | } |
128 | 124 | ||
129 | /* | ||
130 | * You can use this to mark a wiphy_idx as not having an associated wiphy. | ||
131 | * It guarantees cfg80211_rdev_by_wiphy_idx(wiphy_idx) will return NULL | ||
132 | */ | ||
133 | #define WIPHY_IDX_STALE -1 | ||
134 | |||
135 | struct cfg80211_internal_bss { | 125 | struct cfg80211_internal_bss { |
136 | struct list_head list; | 126 | struct list_head list; |
137 | struct rb_node rbn; | 127 | struct rb_node rbn; |
@@ -435,7 +425,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
435 | struct wireless_dev *wdev, | 425 | struct wireless_dev *wdev, |
436 | enum nl80211_iftype iftype, | 426 | enum nl80211_iftype iftype, |
437 | struct ieee80211_channel *chan, | 427 | struct ieee80211_channel *chan, |
438 | enum cfg80211_chan_mode chanmode); | 428 | enum cfg80211_chan_mode chanmode, |
429 | u8 radar_detect); | ||
439 | 430 | ||
440 | static inline int | 431 | static inline int |
441 | cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | 432 | cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, |
@@ -443,7 +434,7 @@ cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | |||
443 | enum nl80211_iftype iftype) | 434 | enum nl80211_iftype iftype) |
444 | { | 435 | { |
445 | return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, | 436 | return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL, |
446 | CHAN_MODE_UNDEFINED); | 437 | CHAN_MODE_UNDEFINED, 0); |
447 | } | 438 | } |
448 | 439 | ||
449 | static inline int | 440 | static inline int |
@@ -460,7 +451,7 @@ cfg80211_can_use_chan(struct cfg80211_registered_device *rdev, | |||
460 | enum cfg80211_chan_mode chanmode) | 451 | enum cfg80211_chan_mode chanmode) |
461 | { | 452 | { |
462 | return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, | 453 | return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, |
463 | chan, chanmode); | 454 | chan, chanmode, 0); |
464 | } | 455 | } |
465 | 456 | ||
466 | void | 457 | void |
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index f9d6ce5cfabb..55957a284f6c 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c | |||
@@ -44,6 +44,10 @@ | |||
44 | 44 | ||
45 | #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 | 45 | #define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50 |
46 | 46 | ||
47 | #define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units (=TUs) */ | ||
48 | #define MESH_DEFAULT_DTIM_PERIOD 2 | ||
49 | #define MESH_DEFAULT_AWAKE_WINDOW 10 /* in 1024 us units (=TUs) */ | ||
50 | |||
47 | const struct mesh_config default_mesh_config = { | 51 | const struct mesh_config default_mesh_config = { |
48 | .dot11MeshRetryTimeout = MESH_RET_T, | 52 | .dot11MeshRetryTimeout = MESH_RET_T, |
49 | .dot11MeshConfirmTimeout = MESH_CONF_T, | 53 | .dot11MeshConfirmTimeout = MESH_CONF_T, |
@@ -69,6 +73,8 @@ const struct mesh_config default_mesh_config = { | |||
69 | .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, | 73 | .dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT, |
70 | .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, | 74 | .dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL, |
71 | .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, | 75 | .dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL, |
76 | .power_mode = NL80211_MESH_POWER_ACTIVE, | ||
77 | .dot11MeshAwakeWindowDuration = MESH_DEFAULT_AWAKE_WINDOW, | ||
72 | }; | 78 | }; |
73 | 79 | ||
74 | const struct mesh_setup default_mesh_setup = { | 80 | const struct mesh_setup default_mesh_setup = { |
@@ -79,6 +85,8 @@ const struct mesh_setup default_mesh_setup = { | |||
79 | .ie = NULL, | 85 | .ie = NULL, |
80 | .ie_len = 0, | 86 | .ie_len = 0, |
81 | .is_secure = false, | 87 | .is_secure = false, |
88 | .beacon_interval = MESH_DEFAULT_BEACON_INTERVAL, | ||
89 | .dtim_period = MESH_DEFAULT_DTIM_PERIOD, | ||
82 | }; | 90 | }; |
83 | 91 | ||
84 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, | 92 | int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 5e8123ee63fd..461e692cdfec 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -987,65 +987,3 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, | |||
987 | nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); | 987 | nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); |
988 | } | 988 | } |
989 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); | 989 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); |
990 | |||
991 | void cfg80211_ch_switch_notify(struct net_device *dev, | ||
992 | struct cfg80211_chan_def *chandef) | ||
993 | { | ||
994 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
995 | struct wiphy *wiphy = wdev->wiphy; | ||
996 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
997 | |||
998 | trace_cfg80211_ch_switch_notify(dev, chandef); | ||
999 | |||
1000 | wdev_lock(wdev); | ||
1001 | |||
1002 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
1003 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) | ||
1004 | goto out; | ||
1005 | |||
1006 | wdev->channel = chandef->chan; | ||
1007 | nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); | ||
1008 | out: | ||
1009 | wdev_unlock(wdev); | ||
1010 | return; | ||
1011 | } | ||
1012 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); | ||
1013 | |||
1014 | bool cfg80211_rx_spurious_frame(struct net_device *dev, | ||
1015 | const u8 *addr, gfp_t gfp) | ||
1016 | { | ||
1017 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1018 | bool ret; | ||
1019 | |||
1020 | trace_cfg80211_rx_spurious_frame(dev, addr); | ||
1021 | |||
1022 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
1023 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) { | ||
1024 | trace_cfg80211_return_bool(false); | ||
1025 | return false; | ||
1026 | } | ||
1027 | ret = nl80211_unexpected_frame(dev, addr, gfp); | ||
1028 | trace_cfg80211_return_bool(ret); | ||
1029 | return ret; | ||
1030 | } | ||
1031 | EXPORT_SYMBOL(cfg80211_rx_spurious_frame); | ||
1032 | |||
1033 | bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, | ||
1034 | const u8 *addr, gfp_t gfp) | ||
1035 | { | ||
1036 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
1037 | bool ret; | ||
1038 | |||
1039 | trace_cfg80211_rx_unexpected_4addr_frame(dev, addr); | ||
1040 | |||
1041 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | ||
1042 | wdev->iftype != NL80211_IFTYPE_P2P_GO && | ||
1043 | wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { | ||
1044 | trace_cfg80211_return_bool(false); | ||
1045 | return false; | ||
1046 | } | ||
1047 | ret = nl80211_unexpected_4addr_frame(dev, addr, gfp); | ||
1048 | trace_cfg80211_return_bool(ret); | ||
1049 | return ret; | ||
1050 | } | ||
1051 | EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f45706adaf34..33de80364c5c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -856,6 +856,9 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, | |||
856 | nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM, | 856 | nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM, |
857 | c->max_interfaces)) | 857 | c->max_interfaces)) |
858 | goto nla_put_failure; | 858 | goto nla_put_failure; |
859 | if (nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, | ||
860 | c->radar_detect_widths)) | ||
861 | goto nla_put_failure; | ||
859 | 862 | ||
860 | nla_nest_end(msg, nl_combi); | 863 | nla_nest_end(msg, nl_combi); |
861 | } | 864 | } |
@@ -2079,6 +2082,13 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
2079 | !(rdev->wiphy.interface_modes & (1 << type))) | 2082 | !(rdev->wiphy.interface_modes & (1 << type))) |
2080 | return -EOPNOTSUPP; | 2083 | return -EOPNOTSUPP; |
2081 | 2084 | ||
2085 | if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) { | ||
2086 | nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC], | ||
2087 | ETH_ALEN); | ||
2088 | if (!is_valid_ether_addr(params.macaddr)) | ||
2089 | return -EADDRNOTAVAIL; | ||
2090 | } | ||
2091 | |||
2082 | if (info->attrs[NL80211_ATTR_4ADDR]) { | 2092 | if (info->attrs[NL80211_ATTR_4ADDR]) { |
2083 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); | 2093 | params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); |
2084 | err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); | 2094 | err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); |
@@ -3001,6 +3011,18 @@ static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, | |||
3001 | nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, | 3011 | nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS, |
3002 | sinfo->beacon_loss_count)) | 3012 | sinfo->beacon_loss_count)) |
3003 | goto nla_put_failure; | 3013 | goto nla_put_failure; |
3014 | if ((sinfo->filled & STATION_INFO_LOCAL_PM) && | ||
3015 | nla_put_u32(msg, NL80211_STA_INFO_LOCAL_PM, | ||
3016 | sinfo->local_pm)) | ||
3017 | goto nla_put_failure; | ||
3018 | if ((sinfo->filled & STATION_INFO_PEER_PM) && | ||
3019 | nla_put_u32(msg, NL80211_STA_INFO_PEER_PM, | ||
3020 | sinfo->peer_pm)) | ||
3021 | goto nla_put_failure; | ||
3022 | if ((sinfo->filled & STATION_INFO_NONPEER_PM) && | ||
3023 | nla_put_u32(msg, NL80211_STA_INFO_NONPEER_PM, | ||
3024 | sinfo->nonpeer_pm)) | ||
3025 | goto nla_put_failure; | ||
3004 | if (sinfo->filled & STATION_INFO_BSS_PARAM) { | 3026 | if (sinfo->filled & STATION_INFO_BSS_PARAM) { |
3005 | bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); | 3027 | bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM); |
3006 | if (!bss_param) | 3028 | if (!bss_param) |
@@ -3188,13 +3210,9 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3188 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 3210 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
3189 | } | 3211 | } |
3190 | 3212 | ||
3191 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | 3213 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL] || |
3192 | params.listen_interval = | 3214 | info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
3193 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 3215 | return -EINVAL; |
3194 | |||
3195 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | ||
3196 | params.ht_capa = | ||
3197 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); | ||
3198 | 3216 | ||
3199 | if (!rdev->ops->change_station) | 3217 | if (!rdev->ops->change_station) |
3200 | return -EOPNOTSUPP; | 3218 | return -EOPNOTSUPP; |
@@ -3210,6 +3228,17 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3210 | params.plink_state = | 3228 | params.plink_state = |
3211 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); | 3229 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]); |
3212 | 3230 | ||
3231 | if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) { | ||
3232 | enum nl80211_mesh_power_mode pm = nla_get_u32( | ||
3233 | info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); | ||
3234 | |||
3235 | if (pm <= NL80211_MESH_POWER_UNKNOWN || | ||
3236 | pm > NL80211_MESH_POWER_MAX) | ||
3237 | return -EINVAL; | ||
3238 | |||
3239 | params.local_pm = pm; | ||
3240 | } | ||
3241 | |||
3213 | switch (dev->ieee80211_ptr->iftype) { | 3242 | switch (dev->ieee80211_ptr->iftype) { |
3214 | case NL80211_IFTYPE_AP: | 3243 | case NL80211_IFTYPE_AP: |
3215 | case NL80211_IFTYPE_AP_VLAN: | 3244 | case NL80211_IFTYPE_AP_VLAN: |
@@ -3217,6 +3246,8 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3217 | /* disallow mesh-specific things */ | 3246 | /* disallow mesh-specific things */ |
3218 | if (params.plink_action) | 3247 | if (params.plink_action) |
3219 | return -EINVAL; | 3248 | return -EINVAL; |
3249 | if (params.local_pm) | ||
3250 | return -EINVAL; | ||
3220 | 3251 | ||
3221 | /* TDLS can't be set, ... */ | 3252 | /* TDLS can't be set, ... */ |
3222 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) | 3253 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) |
@@ -3231,11 +3262,25 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3231 | /* accept only the listed bits */ | 3262 | /* accept only the listed bits */ |
3232 | if (params.sta_flags_mask & | 3263 | if (params.sta_flags_mask & |
3233 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | | 3264 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | |
3265 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3266 | BIT(NL80211_STA_FLAG_ASSOCIATED) | | ||
3234 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | | 3267 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | |
3235 | BIT(NL80211_STA_FLAG_WME) | | 3268 | BIT(NL80211_STA_FLAG_WME) | |
3236 | BIT(NL80211_STA_FLAG_MFP))) | 3269 | BIT(NL80211_STA_FLAG_MFP))) |
3237 | return -EINVAL; | 3270 | return -EINVAL; |
3238 | 3271 | ||
3272 | /* but authenticated/associated only if driver handles it */ | ||
3273 | if (!(rdev->wiphy.features & | ||
3274 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | ||
3275 | params.sta_flags_mask & | ||
3276 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3277 | BIT(NL80211_STA_FLAG_ASSOCIATED))) | ||
3278 | return -EINVAL; | ||
3279 | |||
3280 | /* reject other things that can't change */ | ||
3281 | if (params.supported_rates) | ||
3282 | return -EINVAL; | ||
3283 | |||
3239 | /* must be last in here for error handling */ | 3284 | /* must be last in here for error handling */ |
3240 | params.vlan = get_vlan(info, rdev); | 3285 | params.vlan = get_vlan(info, rdev); |
3241 | if (IS_ERR(params.vlan)) | 3286 | if (IS_ERR(params.vlan)) |
@@ -3255,9 +3300,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3255 | /* disallow things sta doesn't support */ | 3300 | /* disallow things sta doesn't support */ |
3256 | if (params.plink_action) | 3301 | if (params.plink_action) |
3257 | return -EINVAL; | 3302 | return -EINVAL; |
3258 | if (params.ht_capa) | 3303 | if (params.local_pm) |
3259 | return -EINVAL; | ||
3260 | if (params.listen_interval >= 0) | ||
3261 | return -EINVAL; | 3304 | return -EINVAL; |
3262 | /* reject any changes other than AUTHORIZED */ | 3305 | /* reject any changes other than AUTHORIZED */ |
3263 | if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) | 3306 | if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) |
@@ -3267,9 +3310,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
3267 | /* disallow things mesh doesn't support */ | 3310 | /* disallow things mesh doesn't support */ |
3268 | if (params.vlan) | 3311 | if (params.vlan) |
3269 | return -EINVAL; | 3312 | return -EINVAL; |
3270 | if (params.ht_capa) | 3313 | if (params.supported_rates) |
3271 | return -EINVAL; | ||
3272 | if (params.listen_interval >= 0) | ||
3273 | return -EINVAL; | 3314 | return -EINVAL; |
3274 | /* | 3315 | /* |
3275 | * No special handling for TDLS here -- the userspace | 3316 | * No special handling for TDLS here -- the userspace |
@@ -3393,17 +3434,31 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
3393 | /* but don't bother the driver with it */ | 3434 | /* but don't bother the driver with it */ |
3394 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 3435 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
3395 | 3436 | ||
3437 | /* allow authenticated/associated only if driver handles it */ | ||
3438 | if (!(rdev->wiphy.features & | ||
3439 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | ||
3440 | params.sta_flags_mask & | ||
3441 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | | ||
3442 | BIT(NL80211_STA_FLAG_ASSOCIATED))) | ||
3443 | return -EINVAL; | ||
3444 | |||
3396 | /* must be last in here for error handling */ | 3445 | /* must be last in here for error handling */ |
3397 | params.vlan = get_vlan(info, rdev); | 3446 | params.vlan = get_vlan(info, rdev); |
3398 | if (IS_ERR(params.vlan)) | 3447 | if (IS_ERR(params.vlan)) |
3399 | return PTR_ERR(params.vlan); | 3448 | return PTR_ERR(params.vlan); |
3400 | break; | 3449 | break; |
3401 | case NL80211_IFTYPE_MESH_POINT: | 3450 | case NL80211_IFTYPE_MESH_POINT: |
3451 | /* associated is disallowed */ | ||
3452 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) | ||
3453 | return -EINVAL; | ||
3402 | /* TDLS peers cannot be added */ | 3454 | /* TDLS peers cannot be added */ |
3403 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) | 3455 | if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) |
3404 | return -EINVAL; | 3456 | return -EINVAL; |
3405 | break; | 3457 | break; |
3406 | case NL80211_IFTYPE_STATION: | 3458 | case NL80211_IFTYPE_STATION: |
3459 | /* associated is disallowed */ | ||
3460 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) | ||
3461 | return -EINVAL; | ||
3407 | /* Only TDLS peers can be added */ | 3462 | /* Only TDLS peers can be added */ |
3408 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) | 3463 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) |
3409 | return -EINVAL; | 3464 | return -EINVAL; |
@@ -3787,12 +3842,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
3787 | * window between nl80211_init() and regulatory_init(), if that is | 3842 | * window between nl80211_init() and regulatory_init(), if that is |
3788 | * even possible. | 3843 | * even possible. |
3789 | */ | 3844 | */ |
3790 | mutex_lock(&cfg80211_mutex); | 3845 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) |
3791 | if (unlikely(!cfg80211_regdomain)) { | ||
3792 | mutex_unlock(&cfg80211_mutex); | ||
3793 | return -EINPROGRESS; | 3846 | return -EINPROGRESS; |
3794 | } | ||
3795 | mutex_unlock(&cfg80211_mutex); | ||
3796 | 3847 | ||
3797 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 3848 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
3798 | return -EINVAL; | 3849 | return -EINVAL; |
@@ -3908,7 +3959,11 @@ static int nl80211_get_mesh_config(struct sk_buff *skb, | |||
3908 | nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, | 3959 | nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, |
3909 | cur_params.dot11MeshHWMProotInterval) || | 3960 | cur_params.dot11MeshHWMProotInterval) || |
3910 | nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, | 3961 | nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, |
3911 | cur_params.dot11MeshHWMPconfirmationInterval)) | 3962 | cur_params.dot11MeshHWMPconfirmationInterval) || |
3963 | nla_put_u32(msg, NL80211_MESHCONF_POWER_MODE, | ||
3964 | cur_params.power_mode) || | ||
3965 | nla_put_u16(msg, NL80211_MESHCONF_AWAKE_WINDOW, | ||
3966 | cur_params.dot11MeshAwakeWindowDuration)) | ||
3912 | goto nla_put_failure; | 3967 | goto nla_put_failure; |
3913 | nla_nest_end(msg, pinfoattr); | 3968 | nla_nest_end(msg, pinfoattr); |
3914 | genlmsg_end(msg, hdr); | 3969 | genlmsg_end(msg, hdr); |
@@ -3947,6 +4002,8 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A | |||
3947 | [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, | 4002 | [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, |
3948 | [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, | 4003 | [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 }, |
3949 | [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, | 4004 | [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 }, |
4005 | [NL80211_MESHCONF_POWER_MODE] = { .type = NLA_U32 }, | ||
4006 | [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, | ||
3950 | }; | 4007 | }; |
3951 | 4008 | ||
3952 | static const struct nla_policy | 4009 | static const struct nla_policy |
@@ -3967,13 +4024,15 @@ static int nl80211_parse_mesh_config(struct genl_info *info, | |||
3967 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; | 4024 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; |
3968 | u32 mask = 0; | 4025 | u32 mask = 0; |
3969 | 4026 | ||
3970 | #define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ | 4027 | #define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, min, max, mask, attr, fn) \ |
3971 | do {\ | 4028 | do { \ |
3972 | if (table[attr_num]) {\ | 4029 | if (tb[attr]) { \ |
3973 | cfg->param = nla_fn(table[attr_num]); \ | 4030 | if (fn(tb[attr]) < min || fn(tb[attr]) > max) \ |
3974 | mask |= (1 << (attr_num - 1)); \ | 4031 | return -EINVAL; \ |
3975 | } \ | 4032 | cfg->param = fn(tb[attr]); \ |
3976 | } while (0);\ | 4033 | mask |= (1 << (attr - 1)); \ |
4034 | } \ | ||
4035 | } while (0) | ||
3977 | 4036 | ||
3978 | 4037 | ||
3979 | if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) | 4038 | if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) |
@@ -3988,83 +4047,98 @@ do {\ | |||
3988 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); | 4047 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); |
3989 | 4048 | ||
3990 | /* Fill in the params struct */ | 4049 | /* Fill in the params struct */ |
3991 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, | 4050 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, 1, 255, |
3992 | mask, NL80211_MESHCONF_RETRY_TIMEOUT, | 4051 | mask, NL80211_MESHCONF_RETRY_TIMEOUT, |
3993 | nla_get_u16); | 4052 | nla_get_u16); |
3994 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, | 4053 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, 1, 255, |
3995 | mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, | 4054 | mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, |
3996 | nla_get_u16); | 4055 | nla_get_u16); |
3997 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, | 4056 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, 1, 255, |
3998 | mask, NL80211_MESHCONF_HOLDING_TIMEOUT, | 4057 | mask, NL80211_MESHCONF_HOLDING_TIMEOUT, |
3999 | nla_get_u16); | 4058 | nla_get_u16); |
4000 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, | 4059 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, 0, 255, |
4001 | mask, NL80211_MESHCONF_MAX_PEER_LINKS, | 4060 | mask, NL80211_MESHCONF_MAX_PEER_LINKS, |
4002 | nla_get_u16); | 4061 | nla_get_u16); |
4003 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, | 4062 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, 0, 16, |
4004 | mask, NL80211_MESHCONF_MAX_RETRIES, | 4063 | mask, NL80211_MESHCONF_MAX_RETRIES, |
4005 | nla_get_u8); | 4064 | nla_get_u8); |
4006 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, | 4065 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, 1, 255, |
4007 | mask, NL80211_MESHCONF_TTL, nla_get_u8); | 4066 | mask, NL80211_MESHCONF_TTL, nla_get_u8); |
4008 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, | 4067 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, 1, 255, |
4009 | mask, NL80211_MESHCONF_ELEMENT_TTL, | 4068 | mask, NL80211_MESHCONF_ELEMENT_TTL, |
4010 | nla_get_u8); | 4069 | nla_get_u8); |
4011 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, | 4070 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, 0, 1, |
4012 | mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, | 4071 | mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, |
4013 | nla_get_u8); | 4072 | nla_get_u8); |
4014 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask, | 4073 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, |
4074 | 1, 255, mask, | ||
4015 | NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, | 4075 | NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, |
4016 | nla_get_u32); | 4076 | nla_get_u32); |
4017 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, | 4077 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, 0, 255, |
4018 | mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | 4078 | mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, |
4019 | nla_get_u8); | 4079 | nla_get_u8); |
4020 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, | 4080 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, 1, 65535, |
4021 | mask, NL80211_MESHCONF_PATH_REFRESH_TIME, | 4081 | mask, NL80211_MESHCONF_PATH_REFRESH_TIME, |
4022 | nla_get_u32); | 4082 | nla_get_u32); |
4023 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, | 4083 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, 1, 65535, |
4024 | mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | 4084 | mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, |
4025 | nla_get_u16); | 4085 | nla_get_u16); |
4026 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask, | 4086 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, |
4087 | 1, 65535, mask, | ||
4027 | NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | 4088 | NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, |
4028 | nla_get_u32); | 4089 | nla_get_u32); |
4029 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, | 4090 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, |
4030 | mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | 4091 | 1, 65535, mask, |
4092 | NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | ||
4031 | nla_get_u16); | 4093 | nla_get_u16); |
4032 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, | 4094 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, |
4033 | mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, | 4095 | 1, 65535, mask, |
4096 | NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, | ||
4034 | nla_get_u16); | 4097 | nla_get_u16); |
4035 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | 4098 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, |
4036 | dot11MeshHWMPnetDiameterTraversalTime, mask, | 4099 | dot11MeshHWMPnetDiameterTraversalTime, |
4100 | 1, 65535, mask, | ||
4037 | NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 4101 | NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
4038 | nla_get_u16); | 4102 | nla_get_u16); |
4039 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, | 4103 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, 0, 4, |
4040 | NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); | 4104 | mask, NL80211_MESHCONF_HWMP_ROOTMODE, |
4041 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, | 4105 | nla_get_u8); |
4042 | NL80211_MESHCONF_HWMP_RANN_INTERVAL, | 4106 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, 1, 65535, |
4107 | mask, NL80211_MESHCONF_HWMP_RANN_INTERVAL, | ||
4043 | nla_get_u16); | 4108 | nla_get_u16); |
4044 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | 4109 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, |
4045 | dot11MeshGateAnnouncementProtocol, mask, | 4110 | dot11MeshGateAnnouncementProtocol, 0, 1, |
4046 | NL80211_MESHCONF_GATE_ANNOUNCEMENTS, | 4111 | mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, |
4047 | nla_get_u8); | 4112 | nla_get_u8); |
4048 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, | 4113 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, 0, 1, |
4049 | mask, NL80211_MESHCONF_FORWARDING, | 4114 | mask, NL80211_MESHCONF_FORWARDING, |
4050 | nla_get_u8); | 4115 | nla_get_u8); |
4051 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, | 4116 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, 1, 255, |
4052 | mask, NL80211_MESHCONF_RSSI_THRESHOLD, | 4117 | mask, NL80211_MESHCONF_RSSI_THRESHOLD, |
4053 | nla_get_u32); | 4118 | nla_get_u32); |
4054 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, | 4119 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode, 0, 16, |
4055 | mask, NL80211_MESHCONF_HT_OPMODE, | 4120 | mask, NL80211_MESHCONF_HT_OPMODE, |
4056 | nla_get_u16); | 4121 | nla_get_u16); |
4057 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, | 4122 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout, |
4058 | mask, | 4123 | 1, 65535, mask, |
4059 | NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, | 4124 | NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, |
4060 | nla_get_u32); | 4125 | nla_get_u32); |
4061 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, | 4126 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, 1, 65535, |
4062 | mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, | 4127 | mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL, |
4063 | nla_get_u16); | 4128 | nla_get_u16); |
4064 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | 4129 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, |
4065 | dot11MeshHWMPconfirmationInterval, mask, | 4130 | dot11MeshHWMPconfirmationInterval, |
4131 | 1, 65535, mask, | ||
4066 | NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, | 4132 | NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, |
4067 | nla_get_u16); | 4133 | nla_get_u16); |
4134 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, | ||
4135 | NL80211_MESH_POWER_ACTIVE, | ||
4136 | NL80211_MESH_POWER_MAX, | ||
4137 | mask, NL80211_MESHCONF_POWER_MODE, | ||
4138 | nla_get_u32); | ||
4139 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, | ||
4140 | 0, 65535, mask, | ||
4141 | NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); | ||
4068 | if (mask_out) | 4142 | if (mask_out) |
4069 | *mask_out = mask; | 4143 | *mask_out = mask; |
4070 | 4144 | ||
@@ -4152,6 +4226,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, | |||
4152 | 4226 | ||
4153 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | 4227 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) |
4154 | { | 4228 | { |
4229 | const struct ieee80211_regdomain *regdom; | ||
4155 | struct sk_buff *msg; | 4230 | struct sk_buff *msg; |
4156 | void *hdr = NULL; | 4231 | void *hdr = NULL; |
4157 | struct nlattr *nl_reg_rules; | 4232 | struct nlattr *nl_reg_rules; |
@@ -4174,35 +4249,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4174 | if (!hdr) | 4249 | if (!hdr) |
4175 | goto put_failure; | 4250 | goto put_failure; |
4176 | 4251 | ||
4177 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, | ||
4178 | cfg80211_regdomain->alpha2) || | ||
4179 | (cfg80211_regdomain->dfs_region && | ||
4180 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, | ||
4181 | cfg80211_regdomain->dfs_region))) | ||
4182 | goto nla_put_failure; | ||
4183 | |||
4184 | if (reg_last_request_cell_base() && | 4252 | if (reg_last_request_cell_base() && |
4185 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, | 4253 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, |
4186 | NL80211_USER_REG_HINT_CELL_BASE)) | 4254 | NL80211_USER_REG_HINT_CELL_BASE)) |
4187 | goto nla_put_failure; | 4255 | goto nla_put_failure; |
4188 | 4256 | ||
4257 | rcu_read_lock(); | ||
4258 | regdom = rcu_dereference(cfg80211_regdomain); | ||
4259 | |||
4260 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || | ||
4261 | (regdom->dfs_region && | ||
4262 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) | ||
4263 | goto nla_put_failure_rcu; | ||
4264 | |||
4189 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); | 4265 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); |
4190 | if (!nl_reg_rules) | 4266 | if (!nl_reg_rules) |
4191 | goto nla_put_failure; | 4267 | goto nla_put_failure_rcu; |
4192 | 4268 | ||
4193 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | 4269 | for (i = 0; i < regdom->n_reg_rules; i++) { |
4194 | struct nlattr *nl_reg_rule; | 4270 | struct nlattr *nl_reg_rule; |
4195 | const struct ieee80211_reg_rule *reg_rule; | 4271 | const struct ieee80211_reg_rule *reg_rule; |
4196 | const struct ieee80211_freq_range *freq_range; | 4272 | const struct ieee80211_freq_range *freq_range; |
4197 | const struct ieee80211_power_rule *power_rule; | 4273 | const struct ieee80211_power_rule *power_rule; |
4198 | 4274 | ||
4199 | reg_rule = &cfg80211_regdomain->reg_rules[i]; | 4275 | reg_rule = ®dom->reg_rules[i]; |
4200 | freq_range = ®_rule->freq_range; | 4276 | freq_range = ®_rule->freq_range; |
4201 | power_rule = ®_rule->power_rule; | 4277 | power_rule = ®_rule->power_rule; |
4202 | 4278 | ||
4203 | nl_reg_rule = nla_nest_start(msg, i); | 4279 | nl_reg_rule = nla_nest_start(msg, i); |
4204 | if (!nl_reg_rule) | 4280 | if (!nl_reg_rule) |
4205 | goto nla_put_failure; | 4281 | goto nla_put_failure_rcu; |
4206 | 4282 | ||
4207 | if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, | 4283 | if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, |
4208 | reg_rule->flags) || | 4284 | reg_rule->flags) || |
@@ -4216,10 +4292,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4216 | power_rule->max_antenna_gain) || | 4292 | power_rule->max_antenna_gain) || |
4217 | nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, | 4293 | nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, |
4218 | power_rule->max_eirp)) | 4294 | power_rule->max_eirp)) |
4219 | goto nla_put_failure; | 4295 | goto nla_put_failure_rcu; |
4220 | 4296 | ||
4221 | nla_nest_end(msg, nl_reg_rule); | 4297 | nla_nest_end(msg, nl_reg_rule); |
4222 | } | 4298 | } |
4299 | rcu_read_unlock(); | ||
4223 | 4300 | ||
4224 | nla_nest_end(msg, nl_reg_rules); | 4301 | nla_nest_end(msg, nl_reg_rules); |
4225 | 4302 | ||
@@ -4227,6 +4304,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4227 | err = genlmsg_reply(msg, info); | 4304 | err = genlmsg_reply(msg, info); |
4228 | goto out; | 4305 | goto out; |
4229 | 4306 | ||
4307 | nla_put_failure_rcu: | ||
4308 | rcu_read_unlock(); | ||
4230 | nla_put_failure: | 4309 | nla_put_failure: |
4231 | genlmsg_cancel(msg, hdr); | 4310 | genlmsg_cancel(msg, hdr); |
4232 | put_failure: | 4311 | put_failure: |
@@ -4259,27 +4338,18 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
4259 | dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]); | 4338 | dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]); |
4260 | 4339 | ||
4261 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | 4340 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], |
4262 | rem_reg_rules) { | 4341 | rem_reg_rules) { |
4263 | num_rules++; | 4342 | num_rules++; |
4264 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | 4343 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) |
4265 | return -EINVAL; | 4344 | return -EINVAL; |
4266 | } | 4345 | } |
4267 | 4346 | ||
4268 | mutex_lock(&cfg80211_mutex); | ||
4269 | |||
4270 | if (!reg_is_valid_request(alpha2)) { | ||
4271 | r = -EINVAL; | ||
4272 | goto bad_reg; | ||
4273 | } | ||
4274 | |||
4275 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 4347 | size_of_regd = sizeof(struct ieee80211_regdomain) + |
4276 | (num_rules * sizeof(struct ieee80211_reg_rule)); | 4348 | num_rules * sizeof(struct ieee80211_reg_rule); |
4277 | 4349 | ||
4278 | rd = kzalloc(size_of_regd, GFP_KERNEL); | 4350 | rd = kzalloc(size_of_regd, GFP_KERNEL); |
4279 | if (!rd) { | 4351 | if (!rd) |
4280 | r = -ENOMEM; | 4352 | return -ENOMEM; |
4281 | goto bad_reg; | ||
4282 | } | ||
4283 | 4353 | ||
4284 | rd->n_reg_rules = num_rules; | 4354 | rd->n_reg_rules = num_rules; |
4285 | rd->alpha2[0] = alpha2[0]; | 4355 | rd->alpha2[0] = alpha2[0]; |
@@ -4293,10 +4363,10 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
4293 | rd->dfs_region = dfs_region; | 4363 | rd->dfs_region = dfs_region; |
4294 | 4364 | ||
4295 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | 4365 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], |
4296 | rem_reg_rules) { | 4366 | rem_reg_rules) { |
4297 | nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, | 4367 | nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, |
4298 | nla_data(nl_reg_rule), nla_len(nl_reg_rule), | 4368 | nla_data(nl_reg_rule), nla_len(nl_reg_rule), |
4299 | reg_rule_policy); | 4369 | reg_rule_policy); |
4300 | r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); | 4370 | r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); |
4301 | if (r) | 4371 | if (r) |
4302 | goto bad_reg; | 4372 | goto bad_reg; |
@@ -4309,16 +4379,14 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
4309 | } | 4379 | } |
4310 | } | 4380 | } |
4311 | 4381 | ||
4312 | BUG_ON(rule_idx != num_rules); | 4382 | mutex_lock(&cfg80211_mutex); |
4313 | 4383 | ||
4314 | r = set_regdom(rd); | 4384 | r = set_regdom(rd); |
4315 | 4385 | /* set_regdom took ownership */ | |
4386 | rd = NULL; | ||
4316 | mutex_unlock(&cfg80211_mutex); | 4387 | mutex_unlock(&cfg80211_mutex); |
4317 | 4388 | ||
4318 | return r; | ||
4319 | |||
4320 | bad_reg: | 4389 | bad_reg: |
4321 | mutex_unlock(&cfg80211_mutex); | ||
4322 | kfree(rd); | 4390 | kfree(rd); |
4323 | return r; | 4391 | return r; |
4324 | } | 4392 | } |
@@ -5867,6 +5935,15 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | |||
5867 | connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | 5935 | connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); |
5868 | } | 5936 | } |
5869 | 5937 | ||
5938 | if (info->attrs[NL80211_ATTR_USE_MFP]) { | ||
5939 | connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); | ||
5940 | if (connect.mfp != NL80211_MFP_REQUIRED && | ||
5941 | connect.mfp != NL80211_MFP_NO) | ||
5942 | return -EINVAL; | ||
5943 | } else { | ||
5944 | connect.mfp = NL80211_MFP_NO; | ||
5945 | } | ||
5946 | |||
5870 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 5947 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
5871 | connect.channel = | 5948 | connect.channel = |
5872 | ieee80211_get_channel(wiphy, | 5949 | ieee80211_get_channel(wiphy, |
@@ -6652,6 +6729,21 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) | |||
6652 | nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) | 6729 | nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) |
6653 | return -EINVAL; | 6730 | return -EINVAL; |
6654 | 6731 | ||
6732 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { | ||
6733 | setup.beacon_interval = | ||
6734 | nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | ||
6735 | if (setup.beacon_interval < 10 || | ||
6736 | setup.beacon_interval > 10000) | ||
6737 | return -EINVAL; | ||
6738 | } | ||
6739 | |||
6740 | if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { | ||
6741 | setup.dtim_period = | ||
6742 | nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); | ||
6743 | if (setup.dtim_period < 1 || setup.dtim_period > 100) | ||
6744 | return -EINVAL; | ||
6745 | } | ||
6746 | |||
6655 | if (info->attrs[NL80211_ATTR_MESH_SETUP]) { | 6747 | if (info->attrs[NL80211_ATTR_MESH_SETUP]) { |
6656 | /* parse additional setup parameters if given */ | 6748 | /* parse additional setup parameters if given */ |
6657 | err = nl80211_parse_mesh_setup(info, &setup); | 6749 | err = nl80211_parse_mesh_setup(info, &setup); |
@@ -8051,7 +8143,7 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) | |||
8051 | goto nla_put_failure; | 8143 | goto nla_put_failure; |
8052 | } | 8144 | } |
8053 | 8145 | ||
8054 | if (wiphy_idx_valid(request->wiphy_idx) && | 8146 | if (request->wiphy_idx != WIPHY_IDX_INVALID && |
8055 | nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) | 8147 | nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx)) |
8056 | goto nla_put_failure; | 8148 | goto nla_put_failure; |
8057 | 8149 | ||
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 82c4fc7c994c..de02d633c212 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -48,7 +48,6 @@ | |||
48 | #include <linux/export.h> | 48 | #include <linux/export.h> |
49 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
50 | #include <linux/list.h> | 50 | #include <linux/list.h> |
51 | #include <linux/random.h> | ||
52 | #include <linux/ctype.h> | 51 | #include <linux/ctype.h> |
53 | #include <linux/nl80211.h> | 52 | #include <linux/nl80211.h> |
54 | #include <linux/platform_device.h> | 53 | #include <linux/platform_device.h> |
@@ -66,6 +65,13 @@ | |||
66 | #define REG_DBG_PRINT(args...) | 65 | #define REG_DBG_PRINT(args...) |
67 | #endif | 66 | #endif |
68 | 67 | ||
68 | enum reg_request_treatment { | ||
69 | REG_REQ_OK, | ||
70 | REG_REQ_IGNORE, | ||
71 | REG_REQ_INTERSECT, | ||
72 | REG_REQ_ALREADY_SET, | ||
73 | }; | ||
74 | |||
69 | static struct regulatory_request core_request_world = { | 75 | static struct regulatory_request core_request_world = { |
70 | .initiator = NL80211_REGDOM_SET_BY_CORE, | 76 | .initiator = NL80211_REGDOM_SET_BY_CORE, |
71 | .alpha2[0] = '0', | 77 | .alpha2[0] = '0', |
@@ -76,7 +82,8 @@ static struct regulatory_request core_request_world = { | |||
76 | }; | 82 | }; |
77 | 83 | ||
78 | /* Receipt of information from last regulatory request */ | 84 | /* Receipt of information from last regulatory request */ |
79 | static struct regulatory_request *last_request = &core_request_world; | 85 | static struct regulatory_request __rcu *last_request = |
86 | (void __rcu *)&core_request_world; | ||
80 | 87 | ||
81 | /* To trigger userspace events */ | 88 | /* To trigger userspace events */ |
82 | static struct platform_device *reg_pdev; | 89 | static struct platform_device *reg_pdev; |
@@ -88,16 +95,16 @@ static struct device_type reg_device_type = { | |||
88 | /* | 95 | /* |
89 | * Central wireless core regulatory domains, we only need two, | 96 | * Central wireless core regulatory domains, we only need two, |
90 | * the current one and a world regulatory domain in case we have no | 97 | * the current one and a world regulatory domain in case we have no |
91 | * information to give us an alpha2 | 98 | * information to give us an alpha2. |
92 | */ | 99 | */ |
93 | const struct ieee80211_regdomain *cfg80211_regdomain; | 100 | const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
94 | 101 | ||
95 | /* | 102 | /* |
96 | * Protects static reg.c components: | 103 | * Protects static reg.c components: |
97 | * - cfg80211_world_regdom | 104 | * - cfg80211_regdomain (if not used with RCU) |
98 | * - cfg80211_regdom | 105 | * - cfg80211_world_regdom |
99 | * - last_request | 106 | * - last_request (if not used with RCU) |
100 | * - reg_num_devs_support_basehint | 107 | * - reg_num_devs_support_basehint |
101 | */ | 108 | */ |
102 | static DEFINE_MUTEX(reg_mutex); | 109 | static DEFINE_MUTEX(reg_mutex); |
103 | 110 | ||
@@ -112,6 +119,31 @@ static inline void assert_reg_lock(void) | |||
112 | lockdep_assert_held(®_mutex); | 119 | lockdep_assert_held(®_mutex); |
113 | } | 120 | } |
114 | 121 | ||
122 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | ||
123 | { | ||
124 | return rcu_dereference_protected(cfg80211_regdomain, | ||
125 | lockdep_is_held(®_mutex)); | ||
126 | } | ||
127 | |||
128 | static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) | ||
129 | { | ||
130 | return rcu_dereference_protected(wiphy->regd, | ||
131 | lockdep_is_held(®_mutex)); | ||
132 | } | ||
133 | |||
134 | static void rcu_free_regdom(const struct ieee80211_regdomain *r) | ||
135 | { | ||
136 | if (!r) | ||
137 | return; | ||
138 | kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); | ||
139 | } | ||
140 | |||
141 | static struct regulatory_request *get_last_request(void) | ||
142 | { | ||
143 | return rcu_dereference_check(last_request, | ||
144 | lockdep_is_held(®_mutex)); | ||
145 | } | ||
146 | |||
115 | /* Used to queue up regulatory hints */ | 147 | /* Used to queue up regulatory hints */ |
116 | static LIST_HEAD(reg_requests_list); | 148 | static LIST_HEAD(reg_requests_list); |
117 | static spinlock_t reg_requests_lock; | 149 | static spinlock_t reg_requests_lock; |
@@ -177,28 +209,37 @@ static char user_alpha2[2]; | |||
177 | module_param(ieee80211_regdom, charp, 0444); | 209 | module_param(ieee80211_regdom, charp, 0444); |
178 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | 210 | MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); |
179 | 211 | ||
180 | static void reset_regdomains(bool full_reset) | 212 | static void reset_regdomains(bool full_reset, |
213 | const struct ieee80211_regdomain *new_regdom) | ||
181 | { | 214 | { |
215 | const struct ieee80211_regdomain *r; | ||
216 | struct regulatory_request *lr; | ||
217 | |||
218 | assert_reg_lock(); | ||
219 | |||
220 | r = get_cfg80211_regdom(); | ||
221 | |||
182 | /* avoid freeing static information or freeing something twice */ | 222 | /* avoid freeing static information or freeing something twice */ |
183 | if (cfg80211_regdomain == cfg80211_world_regdom) | 223 | if (r == cfg80211_world_regdom) |
184 | cfg80211_regdomain = NULL; | 224 | r = NULL; |
185 | if (cfg80211_world_regdom == &world_regdom) | 225 | if (cfg80211_world_regdom == &world_regdom) |
186 | cfg80211_world_regdom = NULL; | 226 | cfg80211_world_regdom = NULL; |
187 | if (cfg80211_regdomain == &world_regdom) | 227 | if (r == &world_regdom) |
188 | cfg80211_regdomain = NULL; | 228 | r = NULL; |
189 | 229 | ||
190 | kfree(cfg80211_regdomain); | 230 | rcu_free_regdom(r); |
191 | kfree(cfg80211_world_regdom); | 231 | rcu_free_regdom(cfg80211_world_regdom); |
192 | 232 | ||
193 | cfg80211_world_regdom = &world_regdom; | 233 | cfg80211_world_regdom = &world_regdom; |
194 | cfg80211_regdomain = NULL; | 234 | rcu_assign_pointer(cfg80211_regdomain, new_regdom); |
195 | 235 | ||
196 | if (!full_reset) | 236 | if (!full_reset) |
197 | return; | 237 | return; |
198 | 238 | ||
199 | if (last_request != &core_request_world) | 239 | lr = get_last_request(); |
200 | kfree(last_request); | 240 | if (lr != &core_request_world && lr) |
201 | last_request = &core_request_world; | 241 | kfree_rcu(lr, rcu_head); |
242 | rcu_assign_pointer(last_request, &core_request_world); | ||
202 | } | 243 | } |
203 | 244 | ||
204 | /* | 245 | /* |
@@ -207,30 +248,29 @@ static void reset_regdomains(bool full_reset) | |||
207 | */ | 248 | */ |
208 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 249 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
209 | { | 250 | { |
210 | BUG_ON(!last_request); | 251 | struct regulatory_request *lr; |
252 | |||
253 | lr = get_last_request(); | ||
254 | |||
255 | WARN_ON(!lr); | ||
211 | 256 | ||
212 | reset_regdomains(false); | 257 | reset_regdomains(false, rd); |
213 | 258 | ||
214 | cfg80211_world_regdom = rd; | 259 | cfg80211_world_regdom = rd; |
215 | cfg80211_regdomain = rd; | ||
216 | } | 260 | } |
217 | 261 | ||
218 | bool is_world_regdom(const char *alpha2) | 262 | bool is_world_regdom(const char *alpha2) |
219 | { | 263 | { |
220 | if (!alpha2) | 264 | if (!alpha2) |
221 | return false; | 265 | return false; |
222 | if (alpha2[0] == '0' && alpha2[1] == '0') | 266 | return alpha2[0] == '0' && alpha2[1] == '0'; |
223 | return true; | ||
224 | return false; | ||
225 | } | 267 | } |
226 | 268 | ||
227 | static bool is_alpha2_set(const char *alpha2) | 269 | static bool is_alpha2_set(const char *alpha2) |
228 | { | 270 | { |
229 | if (!alpha2) | 271 | if (!alpha2) |
230 | return false; | 272 | return false; |
231 | if (alpha2[0] != 0 && alpha2[1] != 0) | 273 | return alpha2[0] && alpha2[1]; |
232 | return true; | ||
233 | return false; | ||
234 | } | 274 | } |
235 | 275 | ||
236 | static bool is_unknown_alpha2(const char *alpha2) | 276 | static bool is_unknown_alpha2(const char *alpha2) |
@@ -241,9 +281,7 @@ static bool is_unknown_alpha2(const char *alpha2) | |||
241 | * Special case where regulatory domain was built by driver | 281 | * Special case where regulatory domain was built by driver |
242 | * but a specific alpha2 cannot be determined | 282 | * but a specific alpha2 cannot be determined |
243 | */ | 283 | */ |
244 | if (alpha2[0] == '9' && alpha2[1] == '9') | 284 | return alpha2[0] == '9' && alpha2[1] == '9'; |
245 | return true; | ||
246 | return false; | ||
247 | } | 285 | } |
248 | 286 | ||
249 | static bool is_intersected_alpha2(const char *alpha2) | 287 | static bool is_intersected_alpha2(const char *alpha2) |
@@ -255,39 +293,30 @@ static bool is_intersected_alpha2(const char *alpha2) | |||
255 | * result of an intersection between two regulatory domain | 293 | * result of an intersection between two regulatory domain |
256 | * structures | 294 | * structures |
257 | */ | 295 | */ |
258 | if (alpha2[0] == '9' && alpha2[1] == '8') | 296 | return alpha2[0] == '9' && alpha2[1] == '8'; |
259 | return true; | ||
260 | return false; | ||
261 | } | 297 | } |
262 | 298 | ||
263 | static bool is_an_alpha2(const char *alpha2) | 299 | static bool is_an_alpha2(const char *alpha2) |
264 | { | 300 | { |
265 | if (!alpha2) | 301 | if (!alpha2) |
266 | return false; | 302 | return false; |
267 | if (isalpha(alpha2[0]) && isalpha(alpha2[1])) | 303 | return isalpha(alpha2[0]) && isalpha(alpha2[1]); |
268 | return true; | ||
269 | return false; | ||
270 | } | 304 | } |
271 | 305 | ||
272 | static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) | 306 | static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) |
273 | { | 307 | { |
274 | if (!alpha2_x || !alpha2_y) | 308 | if (!alpha2_x || !alpha2_y) |
275 | return false; | 309 | return false; |
276 | if (alpha2_x[0] == alpha2_y[0] && | 310 | return alpha2_x[0] == alpha2_y[0] && alpha2_x[1] == alpha2_y[1]; |
277 | alpha2_x[1] == alpha2_y[1]) | ||
278 | return true; | ||
279 | return false; | ||
280 | } | 311 | } |
281 | 312 | ||
282 | static bool regdom_changes(const char *alpha2) | 313 | static bool regdom_changes(const char *alpha2) |
283 | { | 314 | { |
284 | assert_cfg80211_lock(); | 315 | const struct ieee80211_regdomain *r = get_cfg80211_regdom(); |
285 | 316 | ||
286 | if (!cfg80211_regdomain) | 317 | if (!r) |
287 | return true; | 318 | return true; |
288 | if (alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | 319 | return !alpha2_equal(r->alpha2, alpha2); |
289 | return false; | ||
290 | return true; | ||
291 | } | 320 | } |
292 | 321 | ||
293 | /* | 322 | /* |
@@ -301,38 +330,36 @@ static bool is_user_regdom_saved(void) | |||
301 | return false; | 330 | return false; |
302 | 331 | ||
303 | /* This would indicate a mistake on the design */ | 332 | /* This would indicate a mistake on the design */ |
304 | if (WARN((!is_world_regdom(user_alpha2) && | 333 | if (WARN(!is_world_regdom(user_alpha2) && !is_an_alpha2(user_alpha2), |
305 | !is_an_alpha2(user_alpha2)), | ||
306 | "Unexpected user alpha2: %c%c\n", | 334 | "Unexpected user alpha2: %c%c\n", |
307 | user_alpha2[0], | 335 | user_alpha2[0], user_alpha2[1])) |
308 | user_alpha2[1])) | ||
309 | return false; | 336 | return false; |
310 | 337 | ||
311 | return true; | 338 | return true; |
312 | } | 339 | } |
313 | 340 | ||
314 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | 341 | static const struct ieee80211_regdomain * |
315 | const struct ieee80211_regdomain *src_regd) | 342 | reg_copy_regd(const struct ieee80211_regdomain *src_regd) |
316 | { | 343 | { |
317 | struct ieee80211_regdomain *regd; | 344 | struct ieee80211_regdomain *regd; |
318 | int size_of_regd = 0; | 345 | int size_of_regd; |
319 | unsigned int i; | 346 | unsigned int i; |
320 | 347 | ||
321 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 348 | size_of_regd = |
322 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | 349 | sizeof(struct ieee80211_regdomain) + |
350 | src_regd->n_reg_rules * sizeof(struct ieee80211_reg_rule); | ||
323 | 351 | ||
324 | regd = kzalloc(size_of_regd, GFP_KERNEL); | 352 | regd = kzalloc(size_of_regd, GFP_KERNEL); |
325 | if (!regd) | 353 | if (!regd) |
326 | return -ENOMEM; | 354 | return ERR_PTR(-ENOMEM); |
327 | 355 | ||
328 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | 356 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); |
329 | 357 | ||
330 | for (i = 0; i < src_regd->n_reg_rules; i++) | 358 | for (i = 0; i < src_regd->n_reg_rules; i++) |
331 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | 359 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], |
332 | sizeof(struct ieee80211_reg_rule)); | 360 | sizeof(struct ieee80211_reg_rule)); |
333 | 361 | ||
334 | *dst_regd = regd; | 362 | return regd; |
335 | return 0; | ||
336 | } | 363 | } |
337 | 364 | ||
338 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB | 365 | #ifdef CONFIG_CFG80211_INTERNAL_REGDB |
@@ -347,9 +374,8 @@ static DEFINE_MUTEX(reg_regdb_search_mutex); | |||
347 | static void reg_regdb_search(struct work_struct *work) | 374 | static void reg_regdb_search(struct work_struct *work) |
348 | { | 375 | { |
349 | struct reg_regdb_search_request *request; | 376 | struct reg_regdb_search_request *request; |
350 | const struct ieee80211_regdomain *curdom, *regdom; | 377 | const struct ieee80211_regdomain *curdom, *regdom = NULL; |
351 | int i, r; | 378 | int i; |
352 | bool set_reg = false; | ||
353 | 379 | ||
354 | mutex_lock(&cfg80211_mutex); | 380 | mutex_lock(&cfg80211_mutex); |
355 | 381 | ||
@@ -360,14 +386,11 @@ static void reg_regdb_search(struct work_struct *work) | |||
360 | list); | 386 | list); |
361 | list_del(&request->list); | 387 | list_del(&request->list); |
362 | 388 | ||
363 | for (i=0; i<reg_regdb_size; i++) { | 389 | for (i = 0; i < reg_regdb_size; i++) { |
364 | curdom = reg_regdb[i]; | 390 | curdom = reg_regdb[i]; |
365 | 391 | ||
366 | if (!memcmp(request->alpha2, curdom->alpha2, 2)) { | 392 | if (alpha2_equal(request->alpha2, curdom->alpha2)) { |
367 | r = reg_copy_regd(®dom, curdom); | 393 | regdom = reg_copy_regd(curdom); |
368 | if (r) | ||
369 | break; | ||
370 | set_reg = true; | ||
371 | break; | 394 | break; |
372 | } | 395 | } |
373 | } | 396 | } |
@@ -376,7 +399,7 @@ static void reg_regdb_search(struct work_struct *work) | |||
376 | } | 399 | } |
377 | mutex_unlock(®_regdb_search_mutex); | 400 | mutex_unlock(®_regdb_search_mutex); |
378 | 401 | ||
379 | if (set_reg) | 402 | if (!IS_ERR_OR_NULL(regdom)) |
380 | set_regdom(regdom); | 403 | set_regdom(regdom); |
381 | 404 | ||
382 | mutex_unlock(&cfg80211_mutex); | 405 | mutex_unlock(&cfg80211_mutex); |
@@ -434,15 +457,14 @@ static int call_crda(const char *alpha2) | |||
434 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); | 457 | return kobject_uevent(®_pdev->dev.kobj, KOBJ_CHANGE); |
435 | } | 458 | } |
436 | 459 | ||
437 | /* Used by nl80211 before kmalloc'ing our regulatory domain */ | 460 | static bool reg_is_valid_request(const char *alpha2) |
438 | bool reg_is_valid_request(const char *alpha2) | ||
439 | { | 461 | { |
440 | assert_cfg80211_lock(); | 462 | struct regulatory_request *lr = get_last_request(); |
441 | 463 | ||
442 | if (!last_request) | 464 | if (!lr || lr->processed) |
443 | return false; | 465 | return false; |
444 | 466 | ||
445 | return alpha2_equal(last_request->alpha2, alpha2); | 467 | return alpha2_equal(lr->alpha2, alpha2); |
446 | } | 468 | } |
447 | 469 | ||
448 | /* Sanity check on a regulatory rule */ | 470 | /* Sanity check on a regulatory rule */ |
@@ -460,7 +482,7 @@ static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule) | |||
460 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 482 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
461 | 483 | ||
462 | if (freq_range->end_freq_khz <= freq_range->start_freq_khz || | 484 | if (freq_range->end_freq_khz <= freq_range->start_freq_khz || |
463 | freq_range->max_bandwidth_khz > freq_diff) | 485 | freq_range->max_bandwidth_khz > freq_diff) |
464 | return false; | 486 | return false; |
465 | 487 | ||
466 | return true; | 488 | return true; |
@@ -487,8 +509,7 @@ static bool is_valid_rd(const struct ieee80211_regdomain *rd) | |||
487 | } | 509 | } |
488 | 510 | ||
489 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, | 511 | static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, |
490 | u32 center_freq_khz, | 512 | u32 center_freq_khz, u32 bw_khz) |
491 | u32 bw_khz) | ||
492 | { | 513 | { |
493 | u32 start_freq_khz, end_freq_khz; | 514 | u32 start_freq_khz, end_freq_khz; |
494 | 515 | ||
@@ -518,7 +539,7 @@ static bool reg_does_bw_fit(const struct ieee80211_freq_range *freq_range, | |||
518 | * regulatory rule support for other "bands". | 539 | * regulatory rule support for other "bands". |
519 | **/ | 540 | **/ |
520 | static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | 541 | static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, |
521 | u32 freq_khz) | 542 | u32 freq_khz) |
522 | { | 543 | { |
523 | #define ONE_GHZ_IN_KHZ 1000000 | 544 | #define ONE_GHZ_IN_KHZ 1000000 |
524 | /* | 545 | /* |
@@ -540,10 +561,9 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, | |||
540 | * Helper for regdom_intersect(), this does the real | 561 | * Helper for regdom_intersect(), this does the real |
541 | * mathematical intersection fun | 562 | * mathematical intersection fun |
542 | */ | 563 | */ |
543 | static int reg_rules_intersect( | 564 | static int reg_rules_intersect(const struct ieee80211_reg_rule *rule1, |
544 | const struct ieee80211_reg_rule *rule1, | 565 | const struct ieee80211_reg_rule *rule2, |
545 | const struct ieee80211_reg_rule *rule2, | 566 | struct ieee80211_reg_rule *intersected_rule) |
546 | struct ieee80211_reg_rule *intersected_rule) | ||
547 | { | 567 | { |
548 | const struct ieee80211_freq_range *freq_range1, *freq_range2; | 568 | const struct ieee80211_freq_range *freq_range1, *freq_range2; |
549 | struct ieee80211_freq_range *freq_range; | 569 | struct ieee80211_freq_range *freq_range; |
@@ -560,11 +580,11 @@ static int reg_rules_intersect( | |||
560 | power_rule = &intersected_rule->power_rule; | 580 | power_rule = &intersected_rule->power_rule; |
561 | 581 | ||
562 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, | 582 | freq_range->start_freq_khz = max(freq_range1->start_freq_khz, |
563 | freq_range2->start_freq_khz); | 583 | freq_range2->start_freq_khz); |
564 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, | 584 | freq_range->end_freq_khz = min(freq_range1->end_freq_khz, |
565 | freq_range2->end_freq_khz); | 585 | freq_range2->end_freq_khz); |
566 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, | 586 | freq_range->max_bandwidth_khz = min(freq_range1->max_bandwidth_khz, |
567 | freq_range2->max_bandwidth_khz); | 587 | freq_range2->max_bandwidth_khz); |
568 | 588 | ||
569 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; | 589 | freq_diff = freq_range->end_freq_khz - freq_range->start_freq_khz; |
570 | if (freq_range->max_bandwidth_khz > freq_diff) | 590 | if (freq_range->max_bandwidth_khz > freq_diff) |
@@ -575,7 +595,7 @@ static int reg_rules_intersect( | |||
575 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, | 595 | power_rule->max_antenna_gain = min(power_rule1->max_antenna_gain, |
576 | power_rule2->max_antenna_gain); | 596 | power_rule2->max_antenna_gain); |
577 | 597 | ||
578 | intersected_rule->flags = (rule1->flags | rule2->flags); | 598 | intersected_rule->flags = rule1->flags | rule2->flags; |
579 | 599 | ||
580 | if (!is_valid_reg_rule(intersected_rule)) | 600 | if (!is_valid_reg_rule(intersected_rule)) |
581 | return -EINVAL; | 601 | return -EINVAL; |
@@ -596,9 +616,9 @@ static int reg_rules_intersect( | |||
596 | * resulting intersection of rules between rd1 and rd2. We will | 616 | * resulting intersection of rules between rd1 and rd2. We will |
597 | * kzalloc() this structure for you. | 617 | * kzalloc() this structure for you. |
598 | */ | 618 | */ |
599 | static struct ieee80211_regdomain *regdom_intersect( | 619 | static struct ieee80211_regdomain * |
600 | const struct ieee80211_regdomain *rd1, | 620 | regdom_intersect(const struct ieee80211_regdomain *rd1, |
601 | const struct ieee80211_regdomain *rd2) | 621 | const struct ieee80211_regdomain *rd2) |
602 | { | 622 | { |
603 | int r, size_of_regd; | 623 | int r, size_of_regd; |
604 | unsigned int x, y; | 624 | unsigned int x, y; |
@@ -607,12 +627,7 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
607 | struct ieee80211_reg_rule *intersected_rule; | 627 | struct ieee80211_reg_rule *intersected_rule; |
608 | struct ieee80211_regdomain *rd; | 628 | struct ieee80211_regdomain *rd; |
609 | /* This is just a dummy holder to help us count */ | 629 | /* This is just a dummy holder to help us count */ |
610 | struct ieee80211_reg_rule irule; | 630 | struct ieee80211_reg_rule dummy_rule; |
611 | |||
612 | /* Uses the stack temporarily for counter arithmetic */ | ||
613 | intersected_rule = &irule; | ||
614 | |||
615 | memset(intersected_rule, 0, sizeof(struct ieee80211_reg_rule)); | ||
616 | 631 | ||
617 | if (!rd1 || !rd2) | 632 | if (!rd1 || !rd2) |
618 | return NULL; | 633 | return NULL; |
@@ -629,11 +644,8 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
629 | rule1 = &rd1->reg_rules[x]; | 644 | rule1 = &rd1->reg_rules[x]; |
630 | for (y = 0; y < rd2->n_reg_rules; y++) { | 645 | for (y = 0; y < rd2->n_reg_rules; y++) { |
631 | rule2 = &rd2->reg_rules[y]; | 646 | rule2 = &rd2->reg_rules[y]; |
632 | if (!reg_rules_intersect(rule1, rule2, | 647 | if (!reg_rules_intersect(rule1, rule2, &dummy_rule)) |
633 | intersected_rule)) | ||
634 | num_rules++; | 648 | num_rules++; |
635 | memset(intersected_rule, 0, | ||
636 | sizeof(struct ieee80211_reg_rule)); | ||
637 | } | 649 | } |
638 | } | 650 | } |
639 | 651 | ||
@@ -641,15 +653,15 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
641 | return NULL; | 653 | return NULL; |
642 | 654 | ||
643 | size_of_regd = sizeof(struct ieee80211_regdomain) + | 655 | size_of_regd = sizeof(struct ieee80211_regdomain) + |
644 | ((num_rules + 1) * sizeof(struct ieee80211_reg_rule)); | 656 | num_rules * sizeof(struct ieee80211_reg_rule); |
645 | 657 | ||
646 | rd = kzalloc(size_of_regd, GFP_KERNEL); | 658 | rd = kzalloc(size_of_regd, GFP_KERNEL); |
647 | if (!rd) | 659 | if (!rd) |
648 | return NULL; | 660 | return NULL; |
649 | 661 | ||
650 | for (x = 0; x < rd1->n_reg_rules; x++) { | 662 | for (x = 0; x < rd1->n_reg_rules && rule_idx < num_rules; x++) { |
651 | rule1 = &rd1->reg_rules[x]; | 663 | rule1 = &rd1->reg_rules[x]; |
652 | for (y = 0; y < rd2->n_reg_rules; y++) { | 664 | for (y = 0; y < rd2->n_reg_rules && rule_idx < num_rules; y++) { |
653 | rule2 = &rd2->reg_rules[y]; | 665 | rule2 = &rd2->reg_rules[y]; |
654 | /* | 666 | /* |
655 | * This time around instead of using the stack lets | 667 | * This time around instead of using the stack lets |
@@ -657,8 +669,7 @@ static struct ieee80211_regdomain *regdom_intersect( | |||
657 | * a memcpy() | 669 | * a memcpy() |
658 | */ | 670 | */ |
659 | intersected_rule = &rd->reg_rules[rule_idx]; | 671 | intersected_rule = &rd->reg_rules[rule_idx]; |
660 | r = reg_rules_intersect(rule1, rule2, | 672 | r = reg_rules_intersect(rule1, rule2, intersected_rule); |
661 | intersected_rule); | ||
662 | /* | 673 | /* |
663 | * No need to memset here the intersected rule here as | 674 | * No need to memset here the intersected rule here as |
664 | * we're not using the stack anymore | 675 | * we're not using the stack anymore |
@@ -699,34 +710,16 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
699 | return channel_flags; | 710 | return channel_flags; |
700 | } | 711 | } |
701 | 712 | ||
702 | static int freq_reg_info_regd(struct wiphy *wiphy, | 713 | static const struct ieee80211_reg_rule * |
703 | u32 center_freq, | 714 | freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, |
704 | u32 desired_bw_khz, | 715 | const struct ieee80211_regdomain *regd) |
705 | const struct ieee80211_reg_rule **reg_rule, | ||
706 | const struct ieee80211_regdomain *custom_regd) | ||
707 | { | 716 | { |
708 | int i; | 717 | int i; |
709 | bool band_rule_found = false; | 718 | bool band_rule_found = false; |
710 | const struct ieee80211_regdomain *regd; | ||
711 | bool bw_fits = false; | 719 | bool bw_fits = false; |
712 | 720 | ||
713 | if (!desired_bw_khz) | ||
714 | desired_bw_khz = MHZ_TO_KHZ(20); | ||
715 | |||
716 | regd = custom_regd ? custom_regd : cfg80211_regdomain; | ||
717 | |||
718 | /* | ||
719 | * Follow the driver's regulatory domain, if present, unless a country | ||
720 | * IE has been processed or a user wants to help complaince further | ||
721 | */ | ||
722 | if (!custom_regd && | ||
723 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
724 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && | ||
725 | wiphy->regd) | ||
726 | regd = wiphy->regd; | ||
727 | |||
728 | if (!regd) | 721 | if (!regd) |
729 | return -EINVAL; | 722 | return ERR_PTR(-EINVAL); |
730 | 723 | ||
731 | for (i = 0; i < regd->n_reg_rules; i++) { | 724 | for (i = 0; i < regd->n_reg_rules; i++) { |
732 | const struct ieee80211_reg_rule *rr; | 725 | const struct ieee80211_reg_rule *rr; |
@@ -743,33 +736,36 @@ static int freq_reg_info_regd(struct wiphy *wiphy, | |||
743 | if (!band_rule_found) | 736 | if (!band_rule_found) |
744 | band_rule_found = freq_in_rule_band(fr, center_freq); | 737 | band_rule_found = freq_in_rule_band(fr, center_freq); |
745 | 738 | ||
746 | bw_fits = reg_does_bw_fit(fr, | 739 | bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); |
747 | center_freq, | ||
748 | desired_bw_khz); | ||
749 | 740 | ||
750 | if (band_rule_found && bw_fits) { | 741 | if (band_rule_found && bw_fits) |
751 | *reg_rule = rr; | 742 | return rr; |
752 | return 0; | ||
753 | } | ||
754 | } | 743 | } |
755 | 744 | ||
756 | if (!band_rule_found) | 745 | if (!band_rule_found) |
757 | return -ERANGE; | 746 | return ERR_PTR(-ERANGE); |
758 | 747 | ||
759 | return -EINVAL; | 748 | return ERR_PTR(-EINVAL); |
760 | } | 749 | } |
761 | 750 | ||
762 | int freq_reg_info(struct wiphy *wiphy, | 751 | const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, |
763 | u32 center_freq, | 752 | u32 center_freq) |
764 | u32 desired_bw_khz, | ||
765 | const struct ieee80211_reg_rule **reg_rule) | ||
766 | { | 753 | { |
767 | assert_cfg80211_lock(); | 754 | const struct ieee80211_regdomain *regd; |
768 | return freq_reg_info_regd(wiphy, | 755 | struct regulatory_request *lr = get_last_request(); |
769 | center_freq, | 756 | |
770 | desired_bw_khz, | 757 | /* |
771 | reg_rule, | 758 | * Follow the driver's regulatory domain, if present, unless a country |
772 | NULL); | 759 | * IE has been processed or a user wants to help complaince further |
760 | */ | ||
761 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
762 | lr->initiator != NL80211_REGDOM_SET_BY_USER && | ||
763 | wiphy->regd) | ||
764 | regd = get_wiphy_regdom(wiphy); | ||
765 | else | ||
766 | regd = get_cfg80211_regdom(); | ||
767 | |||
768 | return freq_reg_info_regd(wiphy, center_freq, regd); | ||
773 | } | 769 | } |
774 | EXPORT_SYMBOL(freq_reg_info); | 770 | EXPORT_SYMBOL(freq_reg_info); |
775 | 771 | ||
@@ -792,7 +788,6 @@ static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) | |||
792 | } | 788 | } |
793 | 789 | ||
794 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | 790 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, |
795 | u32 desired_bw_khz, | ||
796 | const struct ieee80211_reg_rule *reg_rule) | 791 | const struct ieee80211_reg_rule *reg_rule) |
797 | { | 792 | { |
798 | const struct ieee80211_power_rule *power_rule; | 793 | const struct ieee80211_power_rule *power_rule; |
@@ -807,21 +802,16 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | |||
807 | else | 802 | else |
808 | snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); | 803 | snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); |
809 | 804 | ||
810 | REG_DBG_PRINT("Updating information on frequency %d MHz " | 805 | REG_DBG_PRINT("Updating information on frequency %d MHz with regulatory rule:\n", |
811 | "for a %d MHz width channel with regulatory rule:\n", | 806 | chan->center_freq); |
812 | chan->center_freq, | ||
813 | KHZ_TO_MHZ(desired_bw_khz)); | ||
814 | 807 | ||
815 | REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", | 808 | REG_DBG_PRINT("%d KHz - %d KHz @ %d KHz), (%s mBi, %d mBm)\n", |
816 | freq_range->start_freq_khz, | 809 | freq_range->start_freq_khz, freq_range->end_freq_khz, |
817 | freq_range->end_freq_khz, | 810 | freq_range->max_bandwidth_khz, max_antenna_gain, |
818 | freq_range->max_bandwidth_khz, | ||
819 | max_antenna_gain, | ||
820 | power_rule->max_eirp); | 811 | power_rule->max_eirp); |
821 | } | 812 | } |
822 | #else | 813 | #else |
823 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | 814 | static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, |
824 | u32 desired_bw_khz, | ||
825 | const struct ieee80211_reg_rule *reg_rule) | 815 | const struct ieee80211_reg_rule *reg_rule) |
826 | { | 816 | { |
827 | return; | 817 | return; |
@@ -831,43 +821,25 @@ static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, | |||
831 | /* | 821 | /* |
832 | * Note that right now we assume the desired channel bandwidth | 822 | * Note that right now we assume the desired channel bandwidth |
833 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz | 823 | * is always 20 MHz for each individual channel (HT40 uses 20 MHz |
834 | * per channel, the primary and the extension channel). To support | 824 | * per channel, the primary and the extension channel). |
835 | * smaller custom bandwidths such as 5 MHz or 10 MHz we'll need a | ||
836 | * new ieee80211_channel.target_bw and re run the regulatory check | ||
837 | * on the wiphy with the target_bw specified. Then we can simply use | ||
838 | * that below for the desired_bw_khz below. | ||
839 | */ | 825 | */ |
840 | static void handle_channel(struct wiphy *wiphy, | 826 | static void handle_channel(struct wiphy *wiphy, |
841 | enum nl80211_reg_initiator initiator, | 827 | enum nl80211_reg_initiator initiator, |
842 | enum ieee80211_band band, | 828 | struct ieee80211_channel *chan) |
843 | unsigned int chan_idx) | ||
844 | { | 829 | { |
845 | int r; | ||
846 | u32 flags, bw_flags = 0; | 830 | u32 flags, bw_flags = 0; |
847 | u32 desired_bw_khz = MHZ_TO_KHZ(20); | ||
848 | const struct ieee80211_reg_rule *reg_rule = NULL; | 831 | const struct ieee80211_reg_rule *reg_rule = NULL; |
849 | const struct ieee80211_power_rule *power_rule = NULL; | 832 | const struct ieee80211_power_rule *power_rule = NULL; |
850 | const struct ieee80211_freq_range *freq_range = NULL; | 833 | const struct ieee80211_freq_range *freq_range = NULL; |
851 | struct ieee80211_supported_band *sband; | ||
852 | struct ieee80211_channel *chan; | ||
853 | struct wiphy *request_wiphy = NULL; | 834 | struct wiphy *request_wiphy = NULL; |
835 | struct regulatory_request *lr = get_last_request(); | ||
854 | 836 | ||
855 | assert_cfg80211_lock(); | 837 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
856 | |||
857 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | ||
858 | |||
859 | sband = wiphy->bands[band]; | ||
860 | BUG_ON(chan_idx >= sband->n_channels); | ||
861 | chan = &sband->channels[chan_idx]; | ||
862 | 838 | ||
863 | flags = chan->orig_flags; | 839 | flags = chan->orig_flags; |
864 | 840 | ||
865 | r = freq_reg_info(wiphy, | 841 | reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); |
866 | MHZ_TO_KHZ(chan->center_freq), | 842 | if (IS_ERR(reg_rule)) { |
867 | desired_bw_khz, | ||
868 | ®_rule); | ||
869 | |||
870 | if (r) { | ||
871 | /* | 843 | /* |
872 | * We will disable all channels that do not match our | 844 | * We will disable all channels that do not match our |
873 | * received regulatory rule unless the hint is coming | 845 | * received regulatory rule unless the hint is coming |
@@ -879,7 +851,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
879 | * while 5 GHz is still supported. | 851 | * while 5 GHz is still supported. |
880 | */ | 852 | */ |
881 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && | 853 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
882 | r == -ERANGE) | 854 | PTR_ERR(reg_rule) == -ERANGE) |
883 | return; | 855 | return; |
884 | 856 | ||
885 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); | 857 | REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); |
@@ -887,7 +859,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
887 | return; | 859 | return; |
888 | } | 860 | } |
889 | 861 | ||
890 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | 862 | chan_reg_rule_print_dbg(chan, reg_rule); |
891 | 863 | ||
892 | power_rule = ®_rule->power_rule; | 864 | power_rule = ®_rule->power_rule; |
893 | freq_range = ®_rule->freq_range; | 865 | freq_range = ®_rule->freq_range; |
@@ -895,7 +867,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
895 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | 867 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) |
896 | bw_flags = IEEE80211_CHAN_NO_HT40; | 868 | bw_flags = IEEE80211_CHAN_NO_HT40; |
897 | 869 | ||
898 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 870 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
899 | request_wiphy && request_wiphy == wiphy && | 871 | request_wiphy && request_wiphy == wiphy && |
900 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 872 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
901 | /* | 873 | /* |
@@ -914,8 +886,9 @@ static void handle_channel(struct wiphy *wiphy, | |||
914 | 886 | ||
915 | chan->beacon_found = false; | 887 | chan->beacon_found = false; |
916 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); | 888 | chan->flags = flags | bw_flags | map_regdom_flags(reg_rule->flags); |
917 | chan->max_antenna_gain = min(chan->orig_mag, | 889 | chan->max_antenna_gain = |
918 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 890 | min_t(int, chan->orig_mag, |
891 | MBI_TO_DBI(power_rule->max_antenna_gain)); | ||
919 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); | 892 | chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp); |
920 | if (chan->orig_mpwr) { | 893 | if (chan->orig_mpwr) { |
921 | /* | 894 | /* |
@@ -935,68 +908,65 @@ static void handle_channel(struct wiphy *wiphy, | |||
935 | } | 908 | } |
936 | 909 | ||
937 | static void handle_band(struct wiphy *wiphy, | 910 | static void handle_band(struct wiphy *wiphy, |
938 | enum ieee80211_band band, | 911 | enum nl80211_reg_initiator initiator, |
939 | enum nl80211_reg_initiator initiator) | 912 | struct ieee80211_supported_band *sband) |
940 | { | 913 | { |
941 | unsigned int i; | 914 | unsigned int i; |
942 | struct ieee80211_supported_band *sband; | ||
943 | 915 | ||
944 | BUG_ON(!wiphy->bands[band]); | 916 | if (!sband) |
945 | sband = wiphy->bands[band]; | 917 | return; |
946 | 918 | ||
947 | for (i = 0; i < sband->n_channels; i++) | 919 | for (i = 0; i < sband->n_channels; i++) |
948 | handle_channel(wiphy, initiator, band, i); | 920 | handle_channel(wiphy, initiator, &sband->channels[i]); |
949 | } | 921 | } |
950 | 922 | ||
951 | static bool reg_request_cell_base(struct regulatory_request *request) | 923 | static bool reg_request_cell_base(struct regulatory_request *request) |
952 | { | 924 | { |
953 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) | 925 | if (request->initiator != NL80211_REGDOM_SET_BY_USER) |
954 | return false; | 926 | return false; |
955 | if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) | 927 | return request->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE; |
956 | return false; | ||
957 | return true; | ||
958 | } | 928 | } |
959 | 929 | ||
960 | bool reg_last_request_cell_base(void) | 930 | bool reg_last_request_cell_base(void) |
961 | { | 931 | { |
962 | bool val; | 932 | bool val; |
963 | assert_cfg80211_lock(); | ||
964 | 933 | ||
965 | mutex_lock(®_mutex); | 934 | mutex_lock(®_mutex); |
966 | val = reg_request_cell_base(last_request); | 935 | val = reg_request_cell_base(get_last_request()); |
967 | mutex_unlock(®_mutex); | 936 | mutex_unlock(®_mutex); |
937 | |||
968 | return val; | 938 | return val; |
969 | } | 939 | } |
970 | 940 | ||
971 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS | 941 | #ifdef CONFIG_CFG80211_CERTIFICATION_ONUS |
972 | |||
973 | /* Core specific check */ | 942 | /* Core specific check */ |
974 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 943 | static enum reg_request_treatment |
944 | reg_ignore_cell_hint(struct regulatory_request *pending_request) | ||
975 | { | 945 | { |
946 | struct regulatory_request *lr = get_last_request(); | ||
947 | |||
976 | if (!reg_num_devs_support_basehint) | 948 | if (!reg_num_devs_support_basehint) |
977 | return -EOPNOTSUPP; | 949 | return REG_REQ_IGNORE; |
978 | 950 | ||
979 | if (reg_request_cell_base(last_request)) { | 951 | if (reg_request_cell_base(lr) && |
980 | if (!regdom_changes(pending_request->alpha2)) | 952 | !regdom_changes(pending_request->alpha2)) |
981 | return -EALREADY; | 953 | return REG_REQ_ALREADY_SET; |
982 | return 0; | 954 | |
983 | } | 955 | return REG_REQ_OK; |
984 | return 0; | ||
985 | } | 956 | } |
986 | 957 | ||
987 | /* Device specific check */ | 958 | /* Device specific check */ |
988 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | 959 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) |
989 | { | 960 | { |
990 | if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS)) | 961 | return !(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS); |
991 | return true; | ||
992 | return false; | ||
993 | } | 962 | } |
994 | #else | 963 | #else |
995 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) | 964 | static int reg_ignore_cell_hint(struct regulatory_request *pending_request) |
996 | { | 965 | { |
997 | return -EOPNOTSUPP; | 966 | return REG_REQ_IGNORE; |
998 | } | 967 | } |
999 | static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) | 968 | |
969 | static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | ||
1000 | { | 970 | { |
1001 | return true; | 971 | return true; |
1002 | } | 972 | } |
@@ -1006,18 +976,17 @@ static int reg_dev_ignore_cell_hint(struct wiphy *wiphy) | |||
1006 | static bool ignore_reg_update(struct wiphy *wiphy, | 976 | static bool ignore_reg_update(struct wiphy *wiphy, |
1007 | enum nl80211_reg_initiator initiator) | 977 | enum nl80211_reg_initiator initiator) |
1008 | { | 978 | { |
1009 | if (!last_request) { | 979 | struct regulatory_request *lr = get_last_request(); |
1010 | REG_DBG_PRINT("Ignoring regulatory request %s since " | 980 | |
1011 | "last_request is not set\n", | 981 | if (!lr) { |
982 | REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", | ||
1012 | reg_initiator_name(initiator)); | 983 | reg_initiator_name(initiator)); |
1013 | return true; | 984 | return true; |
1014 | } | 985 | } |
1015 | 986 | ||
1016 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 987 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1017 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { | 988 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { |
1018 | REG_DBG_PRINT("Ignoring regulatory request %s " | 989 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", |
1019 | "since the driver uses its own custom " | ||
1020 | "regulatory domain\n", | ||
1021 | reg_initiator_name(initiator)); | 990 | reg_initiator_name(initiator)); |
1022 | return true; | 991 | return true; |
1023 | } | 992 | } |
@@ -1028,22 +997,35 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
1028 | */ | 997 | */ |
1029 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && | 998 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
1030 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 999 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1031 | !is_world_regdom(last_request->alpha2)) { | 1000 | !is_world_regdom(lr->alpha2)) { |
1032 | REG_DBG_PRINT("Ignoring regulatory request %s " | 1001 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", |
1033 | "since the driver requires its own regulatory " | ||
1034 | "domain to be set first\n", | ||
1035 | reg_initiator_name(initiator)); | 1002 | reg_initiator_name(initiator)); |
1036 | return true; | 1003 | return true; |
1037 | } | 1004 | } |
1038 | 1005 | ||
1039 | if (reg_request_cell_base(last_request)) | 1006 | if (reg_request_cell_base(lr)) |
1040 | return reg_dev_ignore_cell_hint(wiphy); | 1007 | return reg_dev_ignore_cell_hint(wiphy); |
1041 | 1008 | ||
1042 | return false; | 1009 | return false; |
1043 | } | 1010 | } |
1044 | 1011 | ||
1045 | static void handle_reg_beacon(struct wiphy *wiphy, | 1012 | static bool reg_is_world_roaming(struct wiphy *wiphy) |
1046 | unsigned int chan_idx, | 1013 | { |
1014 | const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); | ||
1015 | const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); | ||
1016 | struct regulatory_request *lr = get_last_request(); | ||
1017 | |||
1018 | if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) | ||
1019 | return true; | ||
1020 | |||
1021 | if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
1022 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | ||
1023 | return true; | ||
1024 | |||
1025 | return false; | ||
1026 | } | ||
1027 | |||
1028 | static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx, | ||
1047 | struct reg_beacon *reg_beacon) | 1029 | struct reg_beacon *reg_beacon) |
1048 | { | 1030 | { |
1049 | struct ieee80211_supported_band *sband; | 1031 | struct ieee80211_supported_band *sband; |
@@ -1051,8 +1033,6 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1051 | bool channel_changed = false; | 1033 | bool channel_changed = false; |
1052 | struct ieee80211_channel chan_before; | 1034 | struct ieee80211_channel chan_before; |
1053 | 1035 | ||
1054 | assert_cfg80211_lock(); | ||
1055 | |||
1056 | sband = wiphy->bands[reg_beacon->chan.band]; | 1036 | sband = wiphy->bands[reg_beacon->chan.band]; |
1057 | chan = &sband->channels[chan_idx]; | 1037 | chan = &sband->channels[chan_idx]; |
1058 | 1038 | ||
@@ -1064,6 +1044,9 @@ static void handle_reg_beacon(struct wiphy *wiphy, | |||
1064 | 1044 | ||
1065 | chan->beacon_found = true; | 1045 | chan->beacon_found = true; |
1066 | 1046 | ||
1047 | if (!reg_is_world_roaming(wiphy)) | ||
1048 | return; | ||
1049 | |||
1067 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) | 1050 | if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS) |
1068 | return; | 1051 | return; |
1069 | 1052 | ||
@@ -1094,8 +1077,6 @@ static void wiphy_update_new_beacon(struct wiphy *wiphy, | |||
1094 | unsigned int i; | 1077 | unsigned int i; |
1095 | struct ieee80211_supported_band *sband; | 1078 | struct ieee80211_supported_band *sband; |
1096 | 1079 | ||
1097 | assert_cfg80211_lock(); | ||
1098 | |||
1099 | if (!wiphy->bands[reg_beacon->chan.band]) | 1080 | if (!wiphy->bands[reg_beacon->chan.band]) |
1100 | return; | 1081 | return; |
1101 | 1082 | ||
@@ -1114,11 +1095,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) | |||
1114 | struct ieee80211_supported_band *sband; | 1095 | struct ieee80211_supported_band *sband; |
1115 | struct reg_beacon *reg_beacon; | 1096 | struct reg_beacon *reg_beacon; |
1116 | 1097 | ||
1117 | assert_cfg80211_lock(); | ||
1118 | |||
1119 | if (list_empty(®_beacon_list)) | ||
1120 | return; | ||
1121 | |||
1122 | list_for_each_entry(reg_beacon, ®_beacon_list, list) { | 1098 | list_for_each_entry(reg_beacon, ®_beacon_list, list) { |
1123 | if (!wiphy->bands[reg_beacon->chan.band]) | 1099 | if (!wiphy->bands[reg_beacon->chan.band]) |
1124 | continue; | 1100 | continue; |
@@ -1128,18 +1104,6 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) | |||
1128 | } | 1104 | } |
1129 | } | 1105 | } |
1130 | 1106 | ||
1131 | static bool reg_is_world_roaming(struct wiphy *wiphy) | ||
1132 | { | ||
1133 | if (is_world_regdom(cfg80211_regdomain->alpha2) || | ||
1134 | (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) | ||
1135 | return true; | ||
1136 | if (last_request && | ||
1137 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
1138 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | ||
1139 | return true; | ||
1140 | return false; | ||
1141 | } | ||
1142 | |||
1143 | /* Reap the advantages of previously found beacons */ | 1107 | /* Reap the advantages of previously found beacons */ |
1144 | static void reg_process_beacons(struct wiphy *wiphy) | 1108 | static void reg_process_beacons(struct wiphy *wiphy) |
1145 | { | 1109 | { |
@@ -1149,39 +1113,29 @@ static void reg_process_beacons(struct wiphy *wiphy) | |||
1149 | */ | 1113 | */ |
1150 | if (!last_request) | 1114 | if (!last_request) |
1151 | return; | 1115 | return; |
1152 | if (!reg_is_world_roaming(wiphy)) | ||
1153 | return; | ||
1154 | wiphy_update_beacon_reg(wiphy); | 1116 | wiphy_update_beacon_reg(wiphy); |
1155 | } | 1117 | } |
1156 | 1118 | ||
1157 | static bool is_ht40_not_allowed(struct ieee80211_channel *chan) | 1119 | static bool is_ht40_allowed(struct ieee80211_channel *chan) |
1158 | { | 1120 | { |
1159 | if (!chan) | 1121 | if (!chan) |
1160 | return true; | 1122 | return false; |
1161 | if (chan->flags & IEEE80211_CHAN_DISABLED) | 1123 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
1162 | return true; | 1124 | return false; |
1163 | /* This would happen when regulatory rules disallow HT40 completely */ | 1125 | /* This would happen when regulatory rules disallow HT40 completely */ |
1164 | if (IEEE80211_CHAN_NO_HT40 == (chan->flags & (IEEE80211_CHAN_NO_HT40))) | 1126 | if ((chan->flags & IEEE80211_CHAN_NO_HT40) == IEEE80211_CHAN_NO_HT40) |
1165 | return true; | 1127 | return false; |
1166 | return false; | 1128 | return true; |
1167 | } | 1129 | } |
1168 | 1130 | ||
1169 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, | 1131 | static void reg_process_ht_flags_channel(struct wiphy *wiphy, |
1170 | enum ieee80211_band band, | 1132 | struct ieee80211_channel *channel) |
1171 | unsigned int chan_idx) | ||
1172 | { | 1133 | { |
1173 | struct ieee80211_supported_band *sband; | 1134 | struct ieee80211_supported_band *sband = wiphy->bands[channel->band]; |
1174 | struct ieee80211_channel *channel; | ||
1175 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; | 1135 | struct ieee80211_channel *channel_before = NULL, *channel_after = NULL; |
1176 | unsigned int i; | 1136 | unsigned int i; |
1177 | 1137 | ||
1178 | assert_cfg80211_lock(); | 1138 | if (!is_ht40_allowed(channel)) { |
1179 | |||
1180 | sband = wiphy->bands[band]; | ||
1181 | BUG_ON(chan_idx >= sband->n_channels); | ||
1182 | channel = &sband->channels[chan_idx]; | ||
1183 | |||
1184 | if (is_ht40_not_allowed(channel)) { | ||
1185 | channel->flags |= IEEE80211_CHAN_NO_HT40; | 1139 | channel->flags |= IEEE80211_CHAN_NO_HT40; |
1186 | return; | 1140 | return; |
1187 | } | 1141 | } |
@@ -1192,6 +1146,7 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, | |||
1192 | */ | 1146 | */ |
1193 | for (i = 0; i < sband->n_channels; i++) { | 1147 | for (i = 0; i < sband->n_channels; i++) { |
1194 | struct ieee80211_channel *c = &sband->channels[i]; | 1148 | struct ieee80211_channel *c = &sband->channels[i]; |
1149 | |||
1195 | if (c->center_freq == (channel->center_freq - 20)) | 1150 | if (c->center_freq == (channel->center_freq - 20)) |
1196 | channel_before = c; | 1151 | channel_before = c; |
1197 | if (c->center_freq == (channel->center_freq + 20)) | 1152 | if (c->center_freq == (channel->center_freq + 20)) |
@@ -1203,28 +1158,27 @@ static void reg_process_ht_flags_channel(struct wiphy *wiphy, | |||
1203 | * if that ever changes we also need to change the below logic | 1158 | * if that ever changes we also need to change the below logic |
1204 | * to include that as well. | 1159 | * to include that as well. |
1205 | */ | 1160 | */ |
1206 | if (is_ht40_not_allowed(channel_before)) | 1161 | if (!is_ht40_allowed(channel_before)) |
1207 | channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; | 1162 | channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; |
1208 | else | 1163 | else |
1209 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; | 1164 | channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; |
1210 | 1165 | ||
1211 | if (is_ht40_not_allowed(channel_after)) | 1166 | if (!is_ht40_allowed(channel_after)) |
1212 | channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; | 1167 | channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; |
1213 | else | 1168 | else |
1214 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; | 1169 | channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; |
1215 | } | 1170 | } |
1216 | 1171 | ||
1217 | static void reg_process_ht_flags_band(struct wiphy *wiphy, | 1172 | static void reg_process_ht_flags_band(struct wiphy *wiphy, |
1218 | enum ieee80211_band band) | 1173 | struct ieee80211_supported_band *sband) |
1219 | { | 1174 | { |
1220 | unsigned int i; | 1175 | unsigned int i; |
1221 | struct ieee80211_supported_band *sband; | ||
1222 | 1176 | ||
1223 | BUG_ON(!wiphy->bands[band]); | 1177 | if (!sband) |
1224 | sband = wiphy->bands[band]; | 1178 | return; |
1225 | 1179 | ||
1226 | for (i = 0; i < sband->n_channels; i++) | 1180 | for (i = 0; i < sband->n_channels; i++) |
1227 | reg_process_ht_flags_channel(wiphy, band, i); | 1181 | reg_process_ht_flags_channel(wiphy, &sband->channels[i]); |
1228 | } | 1182 | } |
1229 | 1183 | ||
1230 | static void reg_process_ht_flags(struct wiphy *wiphy) | 1184 | static void reg_process_ht_flags(struct wiphy *wiphy) |
@@ -1234,34 +1188,29 @@ static void reg_process_ht_flags(struct wiphy *wiphy) | |||
1234 | if (!wiphy) | 1188 | if (!wiphy) |
1235 | return; | 1189 | return; |
1236 | 1190 | ||
1237 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1191 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1238 | if (wiphy->bands[band]) | 1192 | reg_process_ht_flags_band(wiphy, wiphy->bands[band]); |
1239 | reg_process_ht_flags_band(wiphy, band); | ||
1240 | } | ||
1241 | |||
1242 | } | 1193 | } |
1243 | 1194 | ||
1244 | static void wiphy_update_regulatory(struct wiphy *wiphy, | 1195 | static void wiphy_update_regulatory(struct wiphy *wiphy, |
1245 | enum nl80211_reg_initiator initiator) | 1196 | enum nl80211_reg_initiator initiator) |
1246 | { | 1197 | { |
1247 | enum ieee80211_band band; | 1198 | enum ieee80211_band band; |
1248 | 1199 | struct regulatory_request *lr = get_last_request(); | |
1249 | assert_reg_lock(); | ||
1250 | 1200 | ||
1251 | if (ignore_reg_update(wiphy, initiator)) | 1201 | if (ignore_reg_update(wiphy, initiator)) |
1252 | return; | 1202 | return; |
1253 | 1203 | ||
1254 | last_request->dfs_region = cfg80211_regdomain->dfs_region; | 1204 | lr->dfs_region = get_cfg80211_regdom()->dfs_region; |
1255 | 1205 | ||
1256 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1206 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1257 | if (wiphy->bands[band]) | 1207 | handle_band(wiphy, initiator, wiphy->bands[band]); |
1258 | handle_band(wiphy, band, initiator); | ||
1259 | } | ||
1260 | 1208 | ||
1261 | reg_process_beacons(wiphy); | 1209 | reg_process_beacons(wiphy); |
1262 | reg_process_ht_flags(wiphy); | 1210 | reg_process_ht_flags(wiphy); |
1211 | |||
1263 | if (wiphy->reg_notifier) | 1212 | if (wiphy->reg_notifier) |
1264 | wiphy->reg_notifier(wiphy, last_request); | 1213 | wiphy->reg_notifier(wiphy, lr); |
1265 | } | 1214 | } |
1266 | 1215 | ||
1267 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | 1216 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) |
@@ -1269,6 +1218,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1269 | struct cfg80211_registered_device *rdev; | 1218 | struct cfg80211_registered_device *rdev; |
1270 | struct wiphy *wiphy; | 1219 | struct wiphy *wiphy; |
1271 | 1220 | ||
1221 | assert_cfg80211_lock(); | ||
1222 | |||
1272 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1223 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1273 | wiphy = &rdev->wiphy; | 1224 | wiphy = &rdev->wiphy; |
1274 | wiphy_update_regulatory(wiphy, initiator); | 1225 | wiphy_update_regulatory(wiphy, initiator); |
@@ -1280,47 +1231,30 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1280 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 1231 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1281 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && | 1232 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && |
1282 | wiphy->reg_notifier) | 1233 | wiphy->reg_notifier) |
1283 | wiphy->reg_notifier(wiphy, last_request); | 1234 | wiphy->reg_notifier(wiphy, get_last_request()); |
1284 | } | 1235 | } |
1285 | } | 1236 | } |
1286 | 1237 | ||
1287 | static void handle_channel_custom(struct wiphy *wiphy, | 1238 | static void handle_channel_custom(struct wiphy *wiphy, |
1288 | enum ieee80211_band band, | 1239 | struct ieee80211_channel *chan, |
1289 | unsigned int chan_idx, | ||
1290 | const struct ieee80211_regdomain *regd) | 1240 | const struct ieee80211_regdomain *regd) |
1291 | { | 1241 | { |
1292 | int r; | ||
1293 | u32 desired_bw_khz = MHZ_TO_KHZ(20); | ||
1294 | u32 bw_flags = 0; | 1242 | u32 bw_flags = 0; |
1295 | const struct ieee80211_reg_rule *reg_rule = NULL; | 1243 | const struct ieee80211_reg_rule *reg_rule = NULL; |
1296 | const struct ieee80211_power_rule *power_rule = NULL; | 1244 | const struct ieee80211_power_rule *power_rule = NULL; |
1297 | const struct ieee80211_freq_range *freq_range = NULL; | 1245 | const struct ieee80211_freq_range *freq_range = NULL; |
1298 | struct ieee80211_supported_band *sband; | ||
1299 | struct ieee80211_channel *chan; | ||
1300 | |||
1301 | assert_reg_lock(); | ||
1302 | 1246 | ||
1303 | sband = wiphy->bands[band]; | 1247 | reg_rule = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), |
1304 | BUG_ON(chan_idx >= sband->n_channels); | 1248 | regd); |
1305 | chan = &sband->channels[chan_idx]; | ||
1306 | |||
1307 | r = freq_reg_info_regd(wiphy, | ||
1308 | MHZ_TO_KHZ(chan->center_freq), | ||
1309 | desired_bw_khz, | ||
1310 | ®_rule, | ||
1311 | regd); | ||
1312 | 1249 | ||
1313 | if (r) { | 1250 | if (IS_ERR(reg_rule)) { |
1314 | REG_DBG_PRINT("Disabling freq %d MHz as custom " | 1251 | REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", |
1315 | "regd has no rule that fits a %d MHz " | 1252 | chan->center_freq); |
1316 | "wide channel\n", | ||
1317 | chan->center_freq, | ||
1318 | KHZ_TO_MHZ(desired_bw_khz)); | ||
1319 | chan->flags = IEEE80211_CHAN_DISABLED; | 1253 | chan->flags = IEEE80211_CHAN_DISABLED; |
1320 | return; | 1254 | return; |
1321 | } | 1255 | } |
1322 | 1256 | ||
1323 | chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); | 1257 | chan_reg_rule_print_dbg(chan, reg_rule); |
1324 | 1258 | ||
1325 | power_rule = ®_rule->power_rule; | 1259 | power_rule = ®_rule->power_rule; |
1326 | freq_range = ®_rule->freq_range; | 1260 | freq_range = ®_rule->freq_range; |
@@ -1334,17 +1268,17 @@ static void handle_channel_custom(struct wiphy *wiphy, | |||
1334 | (int) MBM_TO_DBM(power_rule->max_eirp); | 1268 | (int) MBM_TO_DBM(power_rule->max_eirp); |
1335 | } | 1269 | } |
1336 | 1270 | ||
1337 | static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, | 1271 | static void handle_band_custom(struct wiphy *wiphy, |
1272 | struct ieee80211_supported_band *sband, | ||
1338 | const struct ieee80211_regdomain *regd) | 1273 | const struct ieee80211_regdomain *regd) |
1339 | { | 1274 | { |
1340 | unsigned int i; | 1275 | unsigned int i; |
1341 | struct ieee80211_supported_band *sband; | ||
1342 | 1276 | ||
1343 | BUG_ON(!wiphy->bands[band]); | 1277 | if (!sband) |
1344 | sband = wiphy->bands[band]; | 1278 | return; |
1345 | 1279 | ||
1346 | for (i = 0; i < sband->n_channels; i++) | 1280 | for (i = 0; i < sband->n_channels; i++) |
1347 | handle_channel_custom(wiphy, band, i, regd); | 1281 | handle_channel_custom(wiphy, &sband->channels[i], regd); |
1348 | } | 1282 | } |
1349 | 1283 | ||
1350 | /* Used by drivers prior to wiphy registration */ | 1284 | /* Used by drivers prior to wiphy registration */ |
@@ -1354,60 +1288,50 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | |||
1354 | enum ieee80211_band band; | 1288 | enum ieee80211_band band; |
1355 | unsigned int bands_set = 0; | 1289 | unsigned int bands_set = 0; |
1356 | 1290 | ||
1357 | mutex_lock(®_mutex); | ||
1358 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 1291 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
1359 | if (!wiphy->bands[band]) | 1292 | if (!wiphy->bands[band]) |
1360 | continue; | 1293 | continue; |
1361 | handle_band_custom(wiphy, band, regd); | 1294 | handle_band_custom(wiphy, wiphy->bands[band], regd); |
1362 | bands_set++; | 1295 | bands_set++; |
1363 | } | 1296 | } |
1364 | mutex_unlock(®_mutex); | ||
1365 | 1297 | ||
1366 | /* | 1298 | /* |
1367 | * no point in calling this if it won't have any effect | 1299 | * no point in calling this if it won't have any effect |
1368 | * on your device's supportd bands. | 1300 | * on your device's supported bands. |
1369 | */ | 1301 | */ |
1370 | WARN_ON(!bands_set); | 1302 | WARN_ON(!bands_set); |
1371 | } | 1303 | } |
1372 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | 1304 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); |
1373 | 1305 | ||
1374 | /* | ||
1375 | * Return value which can be used by ignore_request() to indicate | ||
1376 | * it has been determined we should intersect two regulatory domains | ||
1377 | */ | ||
1378 | #define REG_INTERSECT 1 | ||
1379 | |||
1380 | /* This has the logic which determines when a new request | 1306 | /* This has the logic which determines when a new request |
1381 | * should be ignored. */ | 1307 | * should be ignored. */ |
1382 | static int ignore_request(struct wiphy *wiphy, | 1308 | static enum reg_request_treatment |
1309 | get_reg_request_treatment(struct wiphy *wiphy, | ||
1383 | struct regulatory_request *pending_request) | 1310 | struct regulatory_request *pending_request) |
1384 | { | 1311 | { |
1385 | struct wiphy *last_wiphy = NULL; | 1312 | struct wiphy *last_wiphy = NULL; |
1386 | 1313 | struct regulatory_request *lr = get_last_request(); | |
1387 | assert_cfg80211_lock(); | ||
1388 | 1314 | ||
1389 | /* All initial requests are respected */ | 1315 | /* All initial requests are respected */ |
1390 | if (!last_request) | 1316 | if (!lr) |
1391 | return 0; | 1317 | return REG_REQ_OK; |
1392 | 1318 | ||
1393 | switch (pending_request->initiator) { | 1319 | switch (pending_request->initiator) { |
1394 | case NL80211_REGDOM_SET_BY_CORE: | 1320 | case NL80211_REGDOM_SET_BY_CORE: |
1395 | return 0; | 1321 | return REG_REQ_OK; |
1396 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1322 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1397 | 1323 | if (reg_request_cell_base(lr)) { | |
1398 | if (reg_request_cell_base(last_request)) { | ||
1399 | /* Trust a Cell base station over the AP's country IE */ | 1324 | /* Trust a Cell base station over the AP's country IE */ |
1400 | if (regdom_changes(pending_request->alpha2)) | 1325 | if (regdom_changes(pending_request->alpha2)) |
1401 | return -EOPNOTSUPP; | 1326 | return REG_REQ_IGNORE; |
1402 | return -EALREADY; | 1327 | return REG_REQ_ALREADY_SET; |
1403 | } | 1328 | } |
1404 | 1329 | ||
1405 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1330 | last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
1406 | 1331 | ||
1407 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) | 1332 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) |
1408 | return -EINVAL; | 1333 | return -EINVAL; |
1409 | if (last_request->initiator == | 1334 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
1410 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
1411 | if (last_wiphy != wiphy) { | 1335 | if (last_wiphy != wiphy) { |
1412 | /* | 1336 | /* |
1413 | * Two cards with two APs claiming different | 1337 | * Two cards with two APs claiming different |
@@ -1416,23 +1340,23 @@ static int ignore_request(struct wiphy *wiphy, | |||
1416 | * to be correct. Reject second one for now. | 1340 | * to be correct. Reject second one for now. |
1417 | */ | 1341 | */ |
1418 | if (regdom_changes(pending_request->alpha2)) | 1342 | if (regdom_changes(pending_request->alpha2)) |
1419 | return -EOPNOTSUPP; | 1343 | return REG_REQ_IGNORE; |
1420 | return -EALREADY; | 1344 | return REG_REQ_ALREADY_SET; |
1421 | } | 1345 | } |
1422 | /* | 1346 | /* |
1423 | * Two consecutive Country IE hints on the same wiphy. | 1347 | * Two consecutive Country IE hints on the same wiphy. |
1424 | * This should be picked up early by the driver/stack | 1348 | * This should be picked up early by the driver/stack |
1425 | */ | 1349 | */ |
1426 | if (WARN_ON(regdom_changes(pending_request->alpha2))) | 1350 | if (WARN_ON(regdom_changes(pending_request->alpha2))) |
1427 | return 0; | 1351 | return REG_REQ_OK; |
1428 | return -EALREADY; | 1352 | return REG_REQ_ALREADY_SET; |
1429 | } | 1353 | } |
1430 | return 0; | 1354 | return 0; |
1431 | case NL80211_REGDOM_SET_BY_DRIVER: | 1355 | case NL80211_REGDOM_SET_BY_DRIVER: |
1432 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1356 | if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1433 | if (regdom_changes(pending_request->alpha2)) | 1357 | if (regdom_changes(pending_request->alpha2)) |
1434 | return 0; | 1358 | return REG_REQ_OK; |
1435 | return -EALREADY; | 1359 | return REG_REQ_ALREADY_SET; |
1436 | } | 1360 | } |
1437 | 1361 | ||
1438 | /* | 1362 | /* |
@@ -1440,59 +1364,59 @@ static int ignore_request(struct wiphy *wiphy, | |||
1440 | * back in or if you add a new device for which the previously | 1364 | * back in or if you add a new device for which the previously |
1441 | * loaded card also agrees on the regulatory domain. | 1365 | * loaded card also agrees on the regulatory domain. |
1442 | */ | 1366 | */ |
1443 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1367 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1444 | !regdom_changes(pending_request->alpha2)) | 1368 | !regdom_changes(pending_request->alpha2)) |
1445 | return -EALREADY; | 1369 | return REG_REQ_ALREADY_SET; |
1446 | 1370 | ||
1447 | return REG_INTERSECT; | 1371 | return REG_REQ_INTERSECT; |
1448 | case NL80211_REGDOM_SET_BY_USER: | 1372 | case NL80211_REGDOM_SET_BY_USER: |
1449 | if (reg_request_cell_base(pending_request)) | 1373 | if (reg_request_cell_base(pending_request)) |
1450 | return reg_ignore_cell_hint(pending_request); | 1374 | return reg_ignore_cell_hint(pending_request); |
1451 | 1375 | ||
1452 | if (reg_request_cell_base(last_request)) | 1376 | if (reg_request_cell_base(lr)) |
1453 | return -EOPNOTSUPP; | 1377 | return REG_REQ_IGNORE; |
1454 | 1378 | ||
1455 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1379 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) |
1456 | return REG_INTERSECT; | 1380 | return REG_REQ_INTERSECT; |
1457 | /* | 1381 | /* |
1458 | * If the user knows better the user should set the regdom | 1382 | * If the user knows better the user should set the regdom |
1459 | * to their country before the IE is picked up | 1383 | * to their country before the IE is picked up |
1460 | */ | 1384 | */ |
1461 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && | 1385 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER && |
1462 | last_request->intersect) | 1386 | lr->intersect) |
1463 | return -EOPNOTSUPP; | 1387 | return REG_REQ_IGNORE; |
1464 | /* | 1388 | /* |
1465 | * Process user requests only after previous user/driver/core | 1389 | * Process user requests only after previous user/driver/core |
1466 | * requests have been processed | 1390 | * requests have been processed |
1467 | */ | 1391 | */ |
1468 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE || | 1392 | if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || |
1469 | last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 1393 | lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
1470 | last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | 1394 | lr->initiator == NL80211_REGDOM_SET_BY_USER) && |
1471 | if (regdom_changes(last_request->alpha2)) | 1395 | regdom_changes(lr->alpha2)) |
1472 | return -EAGAIN; | 1396 | return REG_REQ_IGNORE; |
1473 | } | ||
1474 | 1397 | ||
1475 | if (!regdom_changes(pending_request->alpha2)) | 1398 | if (!regdom_changes(pending_request->alpha2)) |
1476 | return -EALREADY; | 1399 | return REG_REQ_ALREADY_SET; |
1477 | 1400 | ||
1478 | return 0; | 1401 | return REG_REQ_OK; |
1479 | } | 1402 | } |
1480 | 1403 | ||
1481 | return -EINVAL; | 1404 | return REG_REQ_IGNORE; |
1482 | } | 1405 | } |
1483 | 1406 | ||
1484 | static void reg_set_request_processed(void) | 1407 | static void reg_set_request_processed(void) |
1485 | { | 1408 | { |
1486 | bool need_more_processing = false; | 1409 | bool need_more_processing = false; |
1410 | struct regulatory_request *lr = get_last_request(); | ||
1487 | 1411 | ||
1488 | last_request->processed = true; | 1412 | lr->processed = true; |
1489 | 1413 | ||
1490 | spin_lock(®_requests_lock); | 1414 | spin_lock(®_requests_lock); |
1491 | if (!list_empty(®_requests_list)) | 1415 | if (!list_empty(®_requests_list)) |
1492 | need_more_processing = true; | 1416 | need_more_processing = true; |
1493 | spin_unlock(®_requests_lock); | 1417 | spin_unlock(®_requests_lock); |
1494 | 1418 | ||
1495 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) | 1419 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) |
1496 | cancel_delayed_work(®_timeout); | 1420 | cancel_delayed_work(®_timeout); |
1497 | 1421 | ||
1498 | if (need_more_processing) | 1422 | if (need_more_processing) |
@@ -1508,116 +1432,122 @@ static void reg_set_request_processed(void) | |||
1508 | * The Wireless subsystem can use this function to hint to the wireless core | 1432 | * The Wireless subsystem can use this function to hint to the wireless core |
1509 | * what it believes should be the current regulatory domain. | 1433 | * what it believes should be the current regulatory domain. |
1510 | * | 1434 | * |
1511 | * Returns zero if all went fine, %-EALREADY if a regulatory domain had | 1435 | * Returns one of the different reg request treatment values. |
1512 | * already been set or other standard error codes. | ||
1513 | * | 1436 | * |
1514 | * Caller must hold &cfg80211_mutex and ®_mutex | 1437 | * Caller must hold ®_mutex |
1515 | */ | 1438 | */ |
1516 | static int __regulatory_hint(struct wiphy *wiphy, | 1439 | static enum reg_request_treatment |
1517 | struct regulatory_request *pending_request) | 1440 | __regulatory_hint(struct wiphy *wiphy, |
1441 | struct regulatory_request *pending_request) | ||
1518 | { | 1442 | { |
1443 | const struct ieee80211_regdomain *regd; | ||
1519 | bool intersect = false; | 1444 | bool intersect = false; |
1520 | int r = 0; | 1445 | enum reg_request_treatment treatment; |
1521 | 1446 | struct regulatory_request *lr; | |
1522 | assert_cfg80211_lock(); | ||
1523 | 1447 | ||
1524 | r = ignore_request(wiphy, pending_request); | 1448 | treatment = get_reg_request_treatment(wiphy, pending_request); |
1525 | 1449 | ||
1526 | if (r == REG_INTERSECT) { | 1450 | switch (treatment) { |
1451 | case REG_REQ_INTERSECT: | ||
1527 | if (pending_request->initiator == | 1452 | if (pending_request->initiator == |
1528 | NL80211_REGDOM_SET_BY_DRIVER) { | 1453 | NL80211_REGDOM_SET_BY_DRIVER) { |
1529 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | 1454 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1530 | if (r) { | 1455 | if (IS_ERR(regd)) { |
1531 | kfree(pending_request); | 1456 | kfree(pending_request); |
1532 | return r; | 1457 | return PTR_ERR(regd); |
1533 | } | 1458 | } |
1459 | rcu_assign_pointer(wiphy->regd, regd); | ||
1534 | } | 1460 | } |
1535 | intersect = true; | 1461 | intersect = true; |
1536 | } else if (r) { | 1462 | break; |
1463 | case REG_REQ_OK: | ||
1464 | break; | ||
1465 | default: | ||
1537 | /* | 1466 | /* |
1538 | * If the regulatory domain being requested by the | 1467 | * If the regulatory domain being requested by the |
1539 | * driver has already been set just copy it to the | 1468 | * driver has already been set just copy it to the |
1540 | * wiphy | 1469 | * wiphy |
1541 | */ | 1470 | */ |
1542 | if (r == -EALREADY && | 1471 | if (treatment == REG_REQ_ALREADY_SET && |
1543 | pending_request->initiator == | 1472 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
1544 | NL80211_REGDOM_SET_BY_DRIVER) { | 1473 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1545 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | 1474 | if (IS_ERR(regd)) { |
1546 | if (r) { | ||
1547 | kfree(pending_request); | 1475 | kfree(pending_request); |
1548 | return r; | 1476 | return REG_REQ_IGNORE; |
1549 | } | 1477 | } |
1550 | r = -EALREADY; | 1478 | treatment = REG_REQ_ALREADY_SET; |
1479 | rcu_assign_pointer(wiphy->regd, regd); | ||
1551 | goto new_request; | 1480 | goto new_request; |
1552 | } | 1481 | } |
1553 | kfree(pending_request); | 1482 | kfree(pending_request); |
1554 | return r; | 1483 | return treatment; |
1555 | } | 1484 | } |
1556 | 1485 | ||
1557 | new_request: | 1486 | new_request: |
1558 | if (last_request != &core_request_world) | 1487 | lr = get_last_request(); |
1559 | kfree(last_request); | 1488 | if (lr != &core_request_world && lr) |
1489 | kfree_rcu(lr, rcu_head); | ||
1560 | 1490 | ||
1561 | last_request = pending_request; | 1491 | pending_request->intersect = intersect; |
1562 | last_request->intersect = intersect; | 1492 | pending_request->processed = false; |
1493 | rcu_assign_pointer(last_request, pending_request); | ||
1494 | lr = pending_request; | ||
1563 | 1495 | ||
1564 | pending_request = NULL; | 1496 | pending_request = NULL; |
1565 | 1497 | ||
1566 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | 1498 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { |
1567 | user_alpha2[0] = last_request->alpha2[0]; | 1499 | user_alpha2[0] = lr->alpha2[0]; |
1568 | user_alpha2[1] = last_request->alpha2[1]; | 1500 | user_alpha2[1] = lr->alpha2[1]; |
1569 | } | 1501 | } |
1570 | 1502 | ||
1571 | /* When r == REG_INTERSECT we do need to call CRDA */ | 1503 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ |
1572 | if (r < 0) { | 1504 | if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) { |
1573 | /* | 1505 | /* |
1574 | * Since CRDA will not be called in this case as we already | 1506 | * Since CRDA will not be called in this case as we already |
1575 | * have applied the requested regulatory domain before we just | 1507 | * have applied the requested regulatory domain before we just |
1576 | * inform userspace we have processed the request | 1508 | * inform userspace we have processed the request |
1577 | */ | 1509 | */ |
1578 | if (r == -EALREADY) { | 1510 | if (treatment == REG_REQ_ALREADY_SET) { |
1579 | nl80211_send_reg_change_event(last_request); | 1511 | nl80211_send_reg_change_event(lr); |
1580 | reg_set_request_processed(); | 1512 | reg_set_request_processed(); |
1581 | } | 1513 | } |
1582 | return r; | 1514 | return treatment; |
1583 | } | 1515 | } |
1584 | 1516 | ||
1585 | return call_crda(last_request->alpha2); | 1517 | if (call_crda(lr->alpha2)) |
1518 | return REG_REQ_IGNORE; | ||
1519 | return REG_REQ_OK; | ||
1586 | } | 1520 | } |
1587 | 1521 | ||
1588 | /* This processes *all* regulatory hints */ | 1522 | /* This processes *all* regulatory hints */ |
1589 | static void reg_process_hint(struct regulatory_request *reg_request, | 1523 | static void reg_process_hint(struct regulatory_request *reg_request, |
1590 | enum nl80211_reg_initiator reg_initiator) | 1524 | enum nl80211_reg_initiator reg_initiator) |
1591 | { | 1525 | { |
1592 | int r = 0; | ||
1593 | struct wiphy *wiphy = NULL; | 1526 | struct wiphy *wiphy = NULL; |
1594 | 1527 | ||
1595 | BUG_ON(!reg_request->alpha2); | 1528 | if (WARN_ON(!reg_request->alpha2)) |
1529 | return; | ||
1596 | 1530 | ||
1597 | if (wiphy_idx_valid(reg_request->wiphy_idx)) | 1531 | if (reg_request->wiphy_idx != WIPHY_IDX_INVALID) |
1598 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); | 1532 | wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); |
1599 | 1533 | ||
1600 | if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1534 | if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { |
1601 | !wiphy) { | ||
1602 | kfree(reg_request); | 1535 | kfree(reg_request); |
1603 | return; | 1536 | return; |
1604 | } | 1537 | } |
1605 | 1538 | ||
1606 | r = __regulatory_hint(wiphy, reg_request); | 1539 | switch (__regulatory_hint(wiphy, reg_request)) { |
1607 | /* This is required so that the orig_* parameters are saved */ | 1540 | case REG_REQ_ALREADY_SET: |
1608 | if (r == -EALREADY && wiphy && | 1541 | /* This is required so that the orig_* parameters are saved */ |
1609 | wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 1542 | if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) |
1610 | wiphy_update_regulatory(wiphy, reg_initiator); | 1543 | wiphy_update_regulatory(wiphy, reg_initiator); |
1611 | return; | 1544 | break; |
1545 | default: | ||
1546 | if (reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1547 | schedule_delayed_work(®_timeout, | ||
1548 | msecs_to_jiffies(3142)); | ||
1549 | break; | ||
1612 | } | 1550 | } |
1613 | |||
1614 | /* | ||
1615 | * We only time out user hints, given that they should be the only | ||
1616 | * source of bogus requests. | ||
1617 | */ | ||
1618 | if (r != -EALREADY && | ||
1619 | reg_initiator == NL80211_REGDOM_SET_BY_USER) | ||
1620 | schedule_delayed_work(®_timeout, msecs_to_jiffies(3142)); | ||
1621 | } | 1551 | } |
1622 | 1552 | ||
1623 | /* | 1553 | /* |
@@ -1627,15 +1557,15 @@ static void reg_process_hint(struct regulatory_request *reg_request, | |||
1627 | */ | 1557 | */ |
1628 | static void reg_process_pending_hints(void) | 1558 | static void reg_process_pending_hints(void) |
1629 | { | 1559 | { |
1630 | struct regulatory_request *reg_request; | 1560 | struct regulatory_request *reg_request, *lr; |
1631 | 1561 | ||
1632 | mutex_lock(&cfg80211_mutex); | 1562 | mutex_lock(&cfg80211_mutex); |
1633 | mutex_lock(®_mutex); | 1563 | mutex_lock(®_mutex); |
1564 | lr = get_last_request(); | ||
1634 | 1565 | ||
1635 | /* When last_request->processed becomes true this will be rescheduled */ | 1566 | /* When last_request->processed becomes true this will be rescheduled */ |
1636 | if (last_request && !last_request->processed) { | 1567 | if (lr && !lr->processed) { |
1637 | REG_DBG_PRINT("Pending regulatory request, waiting " | 1568 | REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); |
1638 | "for it to be processed...\n"); | ||
1639 | goto out; | 1569 | goto out; |
1640 | } | 1570 | } |
1641 | 1571 | ||
@@ -1666,23 +1596,14 @@ static void reg_process_pending_beacon_hints(void) | |||
1666 | struct cfg80211_registered_device *rdev; | 1596 | struct cfg80211_registered_device *rdev; |
1667 | struct reg_beacon *pending_beacon, *tmp; | 1597 | struct reg_beacon *pending_beacon, *tmp; |
1668 | 1598 | ||
1669 | /* | ||
1670 | * No need to hold the reg_mutex here as we just touch wiphys | ||
1671 | * and do not read or access regulatory variables. | ||
1672 | */ | ||
1673 | mutex_lock(&cfg80211_mutex); | 1599 | mutex_lock(&cfg80211_mutex); |
1600 | mutex_lock(®_mutex); | ||
1674 | 1601 | ||
1675 | /* This goes through the _pending_ beacon list */ | 1602 | /* This goes through the _pending_ beacon list */ |
1676 | spin_lock_bh(®_pending_beacons_lock); | 1603 | spin_lock_bh(®_pending_beacons_lock); |
1677 | 1604 | ||
1678 | if (list_empty(®_pending_beacons)) { | ||
1679 | spin_unlock_bh(®_pending_beacons_lock); | ||
1680 | goto out; | ||
1681 | } | ||
1682 | |||
1683 | list_for_each_entry_safe(pending_beacon, tmp, | 1605 | list_for_each_entry_safe(pending_beacon, tmp, |
1684 | ®_pending_beacons, list) { | 1606 | ®_pending_beacons, list) { |
1685 | |||
1686 | list_del_init(&pending_beacon->list); | 1607 | list_del_init(&pending_beacon->list); |
1687 | 1608 | ||
1688 | /* Applies the beacon hint to current wiphys */ | 1609 | /* Applies the beacon hint to current wiphys */ |
@@ -1694,7 +1615,7 @@ static void reg_process_pending_beacon_hints(void) | |||
1694 | } | 1615 | } |
1695 | 1616 | ||
1696 | spin_unlock_bh(®_pending_beacons_lock); | 1617 | spin_unlock_bh(®_pending_beacons_lock); |
1697 | out: | 1618 | mutex_unlock(®_mutex); |
1698 | mutex_unlock(&cfg80211_mutex); | 1619 | mutex_unlock(&cfg80211_mutex); |
1699 | } | 1620 | } |
1700 | 1621 | ||
@@ -1706,10 +1627,8 @@ static void reg_todo(struct work_struct *work) | |||
1706 | 1627 | ||
1707 | static void queue_regulatory_request(struct regulatory_request *request) | 1628 | static void queue_regulatory_request(struct regulatory_request *request) |
1708 | { | 1629 | { |
1709 | if (isalpha(request->alpha2[0])) | 1630 | request->alpha2[0] = toupper(request->alpha2[0]); |
1710 | request->alpha2[0] = toupper(request->alpha2[0]); | 1631 | request->alpha2[1] = toupper(request->alpha2[1]); |
1711 | if (isalpha(request->alpha2[1])) | ||
1712 | request->alpha2[1] = toupper(request->alpha2[1]); | ||
1713 | 1632 | ||
1714 | spin_lock(®_requests_lock); | 1633 | spin_lock(®_requests_lock); |
1715 | list_add_tail(&request->list, ®_requests_list); | 1634 | list_add_tail(&request->list, ®_requests_list); |
@@ -1726,8 +1645,7 @@ static int regulatory_hint_core(const char *alpha2) | |||
1726 | { | 1645 | { |
1727 | struct regulatory_request *request; | 1646 | struct regulatory_request *request; |
1728 | 1647 | ||
1729 | request = kzalloc(sizeof(struct regulatory_request), | 1648 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1730 | GFP_KERNEL); | ||
1731 | if (!request) | 1649 | if (!request) |
1732 | return -ENOMEM; | 1650 | return -ENOMEM; |
1733 | 1651 | ||
@@ -1746,13 +1664,14 @@ int regulatory_hint_user(const char *alpha2, | |||
1746 | { | 1664 | { |
1747 | struct regulatory_request *request; | 1665 | struct regulatory_request *request; |
1748 | 1666 | ||
1749 | BUG_ON(!alpha2); | 1667 | if (WARN_ON(!alpha2)) |
1668 | return -EINVAL; | ||
1750 | 1669 | ||
1751 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1670 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1752 | if (!request) | 1671 | if (!request) |
1753 | return -ENOMEM; | 1672 | return -ENOMEM; |
1754 | 1673 | ||
1755 | request->wiphy_idx = WIPHY_IDX_STALE; | 1674 | request->wiphy_idx = WIPHY_IDX_INVALID; |
1756 | request->alpha2[0] = alpha2[0]; | 1675 | request->alpha2[0] = alpha2[0]; |
1757 | request->alpha2[1] = alpha2[1]; | 1676 | request->alpha2[1] = alpha2[1]; |
1758 | request->initiator = NL80211_REGDOM_SET_BY_USER; | 1677 | request->initiator = NL80211_REGDOM_SET_BY_USER; |
@@ -1768,8 +1687,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
1768 | { | 1687 | { |
1769 | struct regulatory_request *request; | 1688 | struct regulatory_request *request; |
1770 | 1689 | ||
1771 | BUG_ON(!alpha2); | 1690 | if (WARN_ON(!alpha2 || !wiphy)) |
1772 | BUG_ON(!wiphy); | 1691 | return -EINVAL; |
1773 | 1692 | ||
1774 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1693 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
1775 | if (!request) | 1694 | if (!request) |
@@ -1777,9 +1696,6 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) | |||
1777 | 1696 | ||
1778 | request->wiphy_idx = get_wiphy_idx(wiphy); | 1697 | request->wiphy_idx = get_wiphy_idx(wiphy); |
1779 | 1698 | ||
1780 | /* Must have registered wiphy first */ | ||
1781 | BUG_ON(!wiphy_idx_valid(request->wiphy_idx)); | ||
1782 | |||
1783 | request->alpha2[0] = alpha2[0]; | 1699 | request->alpha2[0] = alpha2[0]; |
1784 | request->alpha2[1] = alpha2[1]; | 1700 | request->alpha2[1] = alpha2[1]; |
1785 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; | 1701 | request->initiator = NL80211_REGDOM_SET_BY_DRIVER; |
@@ -1794,18 +1710,17 @@ EXPORT_SYMBOL(regulatory_hint); | |||
1794 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and | 1710 | * We hold wdev_lock() here so we cannot hold cfg80211_mutex() and |
1795 | * therefore cannot iterate over the rdev list here. | 1711 | * therefore cannot iterate over the rdev list here. |
1796 | */ | 1712 | */ |
1797 | void regulatory_hint_11d(struct wiphy *wiphy, | 1713 | void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, |
1798 | enum ieee80211_band band, | 1714 | const u8 *country_ie, u8 country_ie_len) |
1799 | const u8 *country_ie, | ||
1800 | u8 country_ie_len) | ||
1801 | { | 1715 | { |
1802 | char alpha2[2]; | 1716 | char alpha2[2]; |
1803 | enum environment_cap env = ENVIRON_ANY; | 1717 | enum environment_cap env = ENVIRON_ANY; |
1804 | struct regulatory_request *request; | 1718 | struct regulatory_request *request, *lr; |
1805 | 1719 | ||
1806 | mutex_lock(®_mutex); | 1720 | mutex_lock(®_mutex); |
1721 | lr = get_last_request(); | ||
1807 | 1722 | ||
1808 | if (unlikely(!last_request)) | 1723 | if (unlikely(!lr)) |
1809 | goto out; | 1724 | goto out; |
1810 | 1725 | ||
1811 | /* IE len must be evenly divisible by 2 */ | 1726 | /* IE len must be evenly divisible by 2 */ |
@@ -1828,9 +1743,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1828 | * We leave conflict resolution to the workqueue, where can hold | 1743 | * We leave conflict resolution to the workqueue, where can hold |
1829 | * cfg80211_mutex. | 1744 | * cfg80211_mutex. |
1830 | */ | 1745 | */ |
1831 | if (likely(last_request->initiator == | 1746 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1832 | NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1747 | lr->wiphy_idx != WIPHY_IDX_INVALID) |
1833 | wiphy_idx_valid(last_request->wiphy_idx))) | ||
1834 | goto out; | 1748 | goto out; |
1835 | 1749 | ||
1836 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1750 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
@@ -1843,12 +1757,7 @@ void regulatory_hint_11d(struct wiphy *wiphy, | |||
1843 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; | 1757 | request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE; |
1844 | request->country_ie_env = env; | 1758 | request->country_ie_env = env; |
1845 | 1759 | ||
1846 | mutex_unlock(®_mutex); | ||
1847 | |||
1848 | queue_regulatory_request(request); | 1760 | queue_regulatory_request(request); |
1849 | |||
1850 | return; | ||
1851 | |||
1852 | out: | 1761 | out: |
1853 | mutex_unlock(®_mutex); | 1762 | mutex_unlock(®_mutex); |
1854 | } | 1763 | } |
@@ -1863,8 +1772,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1863 | if (is_user_regdom_saved()) { | 1772 | if (is_user_regdom_saved()) { |
1864 | /* Unless we're asked to ignore it and reset it */ | 1773 | /* Unless we're asked to ignore it and reset it */ |
1865 | if (reset_user) { | 1774 | if (reset_user) { |
1866 | REG_DBG_PRINT("Restoring regulatory settings " | 1775 | REG_DBG_PRINT("Restoring regulatory settings including user preference\n"); |
1867 | "including user preference\n"); | ||
1868 | user_alpha2[0] = '9'; | 1776 | user_alpha2[0] = '9'; |
1869 | user_alpha2[1] = '7'; | 1777 | user_alpha2[1] = '7'; |
1870 | 1778 | ||
@@ -1874,26 +1782,20 @@ static void restore_alpha2(char *alpha2, bool reset_user) | |||
1874 | * back as they were for a full restore. | 1782 | * back as they were for a full restore. |
1875 | */ | 1783 | */ |
1876 | if (!is_world_regdom(ieee80211_regdom)) { | 1784 | if (!is_world_regdom(ieee80211_regdom)) { |
1877 | REG_DBG_PRINT("Keeping preference on " | 1785 | REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", |
1878 | "module parameter ieee80211_regdom: %c%c\n", | 1786 | ieee80211_regdom[0], ieee80211_regdom[1]); |
1879 | ieee80211_regdom[0], | ||
1880 | ieee80211_regdom[1]); | ||
1881 | alpha2[0] = ieee80211_regdom[0]; | 1787 | alpha2[0] = ieee80211_regdom[0]; |
1882 | alpha2[1] = ieee80211_regdom[1]; | 1788 | alpha2[1] = ieee80211_regdom[1]; |
1883 | } | 1789 | } |
1884 | } else { | 1790 | } else { |
1885 | REG_DBG_PRINT("Restoring regulatory settings " | 1791 | REG_DBG_PRINT("Restoring regulatory settings while preserving user preference for: %c%c\n", |
1886 | "while preserving user preference for: %c%c\n", | 1792 | user_alpha2[0], user_alpha2[1]); |
1887 | user_alpha2[0], | ||
1888 | user_alpha2[1]); | ||
1889 | alpha2[0] = user_alpha2[0]; | 1793 | alpha2[0] = user_alpha2[0]; |
1890 | alpha2[1] = user_alpha2[1]; | 1794 | alpha2[1] = user_alpha2[1]; |
1891 | } | 1795 | } |
1892 | } else if (!is_world_regdom(ieee80211_regdom)) { | 1796 | } else if (!is_world_regdom(ieee80211_regdom)) { |
1893 | REG_DBG_PRINT("Keeping preference on " | 1797 | REG_DBG_PRINT("Keeping preference on module parameter ieee80211_regdom: %c%c\n", |
1894 | "module parameter ieee80211_regdom: %c%c\n", | 1798 | ieee80211_regdom[0], ieee80211_regdom[1]); |
1895 | ieee80211_regdom[0], | ||
1896 | ieee80211_regdom[1]); | ||
1897 | alpha2[0] = ieee80211_regdom[0]; | 1799 | alpha2[0] = ieee80211_regdom[0]; |
1898 | alpha2[1] = ieee80211_regdom[1]; | 1800 | alpha2[1] = ieee80211_regdom[1]; |
1899 | } else | 1801 | } else |
@@ -1948,7 +1850,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
1948 | mutex_lock(&cfg80211_mutex); | 1850 | mutex_lock(&cfg80211_mutex); |
1949 | mutex_lock(®_mutex); | 1851 | mutex_lock(®_mutex); |
1950 | 1852 | ||
1951 | reset_regdomains(true); | 1853 | reset_regdomains(true, &world_regdom); |
1952 | restore_alpha2(alpha2, reset_user); | 1854 | restore_alpha2(alpha2, reset_user); |
1953 | 1855 | ||
1954 | /* | 1856 | /* |
@@ -1958,49 +1860,35 @@ static void restore_regulatory_settings(bool reset_user) | |||
1958 | * settings. | 1860 | * settings. |
1959 | */ | 1861 | */ |
1960 | spin_lock(®_requests_lock); | 1862 | spin_lock(®_requests_lock); |
1961 | if (!list_empty(®_requests_list)) { | 1863 | list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { |
1962 | list_for_each_entry_safe(reg_request, tmp, | 1864 | if (reg_request->initiator != NL80211_REGDOM_SET_BY_USER) |
1963 | ®_requests_list, list) { | 1865 | continue; |
1964 | if (reg_request->initiator != | 1866 | list_move_tail(®_request->list, &tmp_reg_req_list); |
1965 | NL80211_REGDOM_SET_BY_USER) | ||
1966 | continue; | ||
1967 | list_move_tail(®_request->list, &tmp_reg_req_list); | ||
1968 | } | ||
1969 | } | 1867 | } |
1970 | spin_unlock(®_requests_lock); | 1868 | spin_unlock(®_requests_lock); |
1971 | 1869 | ||
1972 | /* Clear beacon hints */ | 1870 | /* Clear beacon hints */ |
1973 | spin_lock_bh(®_pending_beacons_lock); | 1871 | spin_lock_bh(®_pending_beacons_lock); |
1974 | if (!list_empty(®_pending_beacons)) { | 1872 | list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { |
1975 | list_for_each_entry_safe(reg_beacon, btmp, | 1873 | list_del(®_beacon->list); |
1976 | ®_pending_beacons, list) { | 1874 | kfree(reg_beacon); |
1977 | list_del(®_beacon->list); | ||
1978 | kfree(reg_beacon); | ||
1979 | } | ||
1980 | } | 1875 | } |
1981 | spin_unlock_bh(®_pending_beacons_lock); | 1876 | spin_unlock_bh(®_pending_beacons_lock); |
1982 | 1877 | ||
1983 | if (!list_empty(®_beacon_list)) { | 1878 | list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { |
1984 | list_for_each_entry_safe(reg_beacon, btmp, | 1879 | list_del(®_beacon->list); |
1985 | ®_beacon_list, list) { | 1880 | kfree(reg_beacon); |
1986 | list_del(®_beacon->list); | ||
1987 | kfree(reg_beacon); | ||
1988 | } | ||
1989 | } | 1881 | } |
1990 | 1882 | ||
1991 | /* First restore to the basic regulatory settings */ | 1883 | /* First restore to the basic regulatory settings */ |
1992 | cfg80211_regdomain = cfg80211_world_regdom; | 1884 | world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; |
1993 | world_alpha2[0] = cfg80211_regdomain->alpha2[0]; | 1885 | world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; |
1994 | world_alpha2[1] = cfg80211_regdomain->alpha2[1]; | ||
1995 | 1886 | ||
1996 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1887 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1997 | if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 1888 | if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1998 | restore_custom_reg_settings(&rdev->wiphy); | 1889 | restore_custom_reg_settings(&rdev->wiphy); |
1999 | } | 1890 | } |
2000 | 1891 | ||
2001 | mutex_unlock(®_mutex); | ||
2002 | mutex_unlock(&cfg80211_mutex); | ||
2003 | |||
2004 | regulatory_hint_core(world_alpha2); | 1892 | regulatory_hint_core(world_alpha2); |
2005 | 1893 | ||
2006 | /* | 1894 | /* |
@@ -2011,20 +1899,8 @@ static void restore_regulatory_settings(bool reset_user) | |||
2011 | if (is_an_alpha2(alpha2)) | 1899 | if (is_an_alpha2(alpha2)) |
2012 | regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); | 1900 | regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER); |
2013 | 1901 | ||
2014 | if (list_empty(&tmp_reg_req_list)) | ||
2015 | return; | ||
2016 | |||
2017 | mutex_lock(&cfg80211_mutex); | ||
2018 | mutex_lock(®_mutex); | ||
2019 | |||
2020 | spin_lock(®_requests_lock); | 1902 | spin_lock(®_requests_lock); |
2021 | list_for_each_entry_safe(reg_request, tmp, &tmp_reg_req_list, list) { | 1903 | list_splice_tail_init(&tmp_reg_req_list, ®_requests_list); |
2022 | REG_DBG_PRINT("Adding request for country %c%c back " | ||
2023 | "into the queue\n", | ||
2024 | reg_request->alpha2[0], | ||
2025 | reg_request->alpha2[1]); | ||
2026 | list_move_tail(®_request->list, ®_requests_list); | ||
2027 | } | ||
2028 | spin_unlock(®_requests_lock); | 1904 | spin_unlock(®_requests_lock); |
2029 | 1905 | ||
2030 | mutex_unlock(®_mutex); | 1906 | mutex_unlock(®_mutex); |
@@ -2037,8 +1913,7 @@ static void restore_regulatory_settings(bool reset_user) | |||
2037 | 1913 | ||
2038 | void regulatory_hint_disconnect(void) | 1914 | void regulatory_hint_disconnect(void) |
2039 | { | 1915 | { |
2040 | REG_DBG_PRINT("All devices are disconnected, going to " | 1916 | REG_DBG_PRINT("All devices are disconnected, going to restore regulatory settings\n"); |
2041 | "restore regulatory settings\n"); | ||
2042 | restore_regulatory_settings(false); | 1917 | restore_regulatory_settings(false); |
2043 | } | 1918 | } |
2044 | 1919 | ||
@@ -2051,31 +1926,48 @@ static bool freq_is_chan_12_13_14(u16 freq) | |||
2051 | return false; | 1926 | return false; |
2052 | } | 1927 | } |
2053 | 1928 | ||
1929 | static bool pending_reg_beacon(struct ieee80211_channel *beacon_chan) | ||
1930 | { | ||
1931 | struct reg_beacon *pending_beacon; | ||
1932 | |||
1933 | list_for_each_entry(pending_beacon, ®_pending_beacons, list) | ||
1934 | if (beacon_chan->center_freq == | ||
1935 | pending_beacon->chan.center_freq) | ||
1936 | return true; | ||
1937 | return false; | ||
1938 | } | ||
1939 | |||
2054 | int regulatory_hint_found_beacon(struct wiphy *wiphy, | 1940 | int regulatory_hint_found_beacon(struct wiphy *wiphy, |
2055 | struct ieee80211_channel *beacon_chan, | 1941 | struct ieee80211_channel *beacon_chan, |
2056 | gfp_t gfp) | 1942 | gfp_t gfp) |
2057 | { | 1943 | { |
2058 | struct reg_beacon *reg_beacon; | 1944 | struct reg_beacon *reg_beacon; |
1945 | bool processing; | ||
2059 | 1946 | ||
2060 | if (likely((beacon_chan->beacon_found || | 1947 | if (beacon_chan->beacon_found || |
2061 | (beacon_chan->flags & IEEE80211_CHAN_RADAR) || | 1948 | beacon_chan->flags & IEEE80211_CHAN_RADAR || |
2062 | (beacon_chan->band == IEEE80211_BAND_2GHZ && | 1949 | (beacon_chan->band == IEEE80211_BAND_2GHZ && |
2063 | !freq_is_chan_12_13_14(beacon_chan->center_freq))))) | 1950 | !freq_is_chan_12_13_14(beacon_chan->center_freq))) |
1951 | return 0; | ||
1952 | |||
1953 | spin_lock_bh(®_pending_beacons_lock); | ||
1954 | processing = pending_reg_beacon(beacon_chan); | ||
1955 | spin_unlock_bh(®_pending_beacons_lock); | ||
1956 | |||
1957 | if (processing) | ||
2064 | return 0; | 1958 | return 0; |
2065 | 1959 | ||
2066 | reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); | 1960 | reg_beacon = kzalloc(sizeof(struct reg_beacon), gfp); |
2067 | if (!reg_beacon) | 1961 | if (!reg_beacon) |
2068 | return -ENOMEM; | 1962 | return -ENOMEM; |
2069 | 1963 | ||
2070 | REG_DBG_PRINT("Found new beacon on " | 1964 | REG_DBG_PRINT("Found new beacon on frequency: %d MHz (Ch %d) on %s\n", |
2071 | "frequency: %d MHz (Ch %d) on %s\n", | ||
2072 | beacon_chan->center_freq, | 1965 | beacon_chan->center_freq, |
2073 | ieee80211_frequency_to_channel(beacon_chan->center_freq), | 1966 | ieee80211_frequency_to_channel(beacon_chan->center_freq), |
2074 | wiphy_name(wiphy)); | 1967 | wiphy_name(wiphy)); |
2075 | 1968 | ||
2076 | memcpy(®_beacon->chan, beacon_chan, | 1969 | memcpy(®_beacon->chan, beacon_chan, |
2077 | sizeof(struct ieee80211_channel)); | 1970 | sizeof(struct ieee80211_channel)); |
2078 | |||
2079 | 1971 | ||
2080 | /* | 1972 | /* |
2081 | * Since we can be called from BH or and non-BH context | 1973 | * Since we can be called from BH or and non-BH context |
@@ -2155,21 +2047,19 @@ static void print_dfs_region(u8 dfs_region) | |||
2155 | pr_info(" DFS Master region JP"); | 2047 | pr_info(" DFS Master region JP"); |
2156 | break; | 2048 | break; |
2157 | default: | 2049 | default: |
2158 | pr_info(" DFS Master region Uknown"); | 2050 | pr_info(" DFS Master region Unknown"); |
2159 | break; | 2051 | break; |
2160 | } | 2052 | } |
2161 | } | 2053 | } |
2162 | 2054 | ||
2163 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 2055 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
2164 | { | 2056 | { |
2057 | struct regulatory_request *lr = get_last_request(); | ||
2165 | 2058 | ||
2166 | if (is_intersected_alpha2(rd->alpha2)) { | 2059 | if (is_intersected_alpha2(rd->alpha2)) { |
2167 | 2060 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { | |
2168 | if (last_request->initiator == | ||
2169 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
2170 | struct cfg80211_registered_device *rdev; | 2061 | struct cfg80211_registered_device *rdev; |
2171 | rdev = cfg80211_rdev_by_wiphy_idx( | 2062 | rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); |
2172 | last_request->wiphy_idx); | ||
2173 | if (rdev) { | 2063 | if (rdev) { |
2174 | pr_info("Current regulatory domain updated by AP to: %c%c\n", | 2064 | pr_info("Current regulatory domain updated by AP to: %c%c\n", |
2175 | rdev->country_ie_alpha2[0], | 2065 | rdev->country_ie_alpha2[0], |
@@ -2178,22 +2068,21 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
2178 | pr_info("Current regulatory domain intersected:\n"); | 2068 | pr_info("Current regulatory domain intersected:\n"); |
2179 | } else | 2069 | } else |
2180 | pr_info("Current regulatory domain intersected:\n"); | 2070 | pr_info("Current regulatory domain intersected:\n"); |
2181 | } else if (is_world_regdom(rd->alpha2)) | 2071 | } else if (is_world_regdom(rd->alpha2)) { |
2182 | pr_info("World regulatory domain updated:\n"); | 2072 | pr_info("World regulatory domain updated:\n"); |
2183 | else { | 2073 | } else { |
2184 | if (is_unknown_alpha2(rd->alpha2)) | 2074 | if (is_unknown_alpha2(rd->alpha2)) |
2185 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); | 2075 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); |
2186 | else { | 2076 | else { |
2187 | if (reg_request_cell_base(last_request)) | 2077 | if (reg_request_cell_base(lr)) |
2188 | pr_info("Regulatory domain changed " | 2078 | pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", |
2189 | "to country: %c%c by Cell Station\n", | ||
2190 | rd->alpha2[0], rd->alpha2[1]); | 2079 | rd->alpha2[0], rd->alpha2[1]); |
2191 | else | 2080 | else |
2192 | pr_info("Regulatory domain changed " | 2081 | pr_info("Regulatory domain changed to country: %c%c\n", |
2193 | "to country: %c%c\n", | ||
2194 | rd->alpha2[0], rd->alpha2[1]); | 2082 | rd->alpha2[0], rd->alpha2[1]); |
2195 | } | 2083 | } |
2196 | } | 2084 | } |
2085 | |||
2197 | print_dfs_region(rd->dfs_region); | 2086 | print_dfs_region(rd->dfs_region); |
2198 | print_rd_rules(rd); | 2087 | print_rd_rules(rd); |
2199 | } | 2088 | } |
@@ -2207,22 +2096,23 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd) | |||
2207 | /* Takes ownership of rd only if it doesn't fail */ | 2096 | /* Takes ownership of rd only if it doesn't fail */ |
2208 | static int __set_regdom(const struct ieee80211_regdomain *rd) | 2097 | static int __set_regdom(const struct ieee80211_regdomain *rd) |
2209 | { | 2098 | { |
2099 | const struct ieee80211_regdomain *regd; | ||
2210 | const struct ieee80211_regdomain *intersected_rd = NULL; | 2100 | const struct ieee80211_regdomain *intersected_rd = NULL; |
2211 | struct wiphy *request_wiphy; | 2101 | struct wiphy *request_wiphy; |
2102 | struct regulatory_request *lr = get_last_request(); | ||
2103 | |||
2212 | /* Some basic sanity checks first */ | 2104 | /* Some basic sanity checks first */ |
2213 | 2105 | ||
2106 | if (!reg_is_valid_request(rd->alpha2)) | ||
2107 | return -EINVAL; | ||
2108 | |||
2214 | if (is_world_regdom(rd->alpha2)) { | 2109 | if (is_world_regdom(rd->alpha2)) { |
2215 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) | ||
2216 | return -EINVAL; | ||
2217 | update_world_regdomain(rd); | 2110 | update_world_regdomain(rd); |
2218 | return 0; | 2111 | return 0; |
2219 | } | 2112 | } |
2220 | 2113 | ||
2221 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && | 2114 | if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) && |
2222 | !is_unknown_alpha2(rd->alpha2)) | 2115 | !is_unknown_alpha2(rd->alpha2)) |
2223 | return -EINVAL; | ||
2224 | |||
2225 | if (!last_request) | ||
2226 | return -EINVAL; | 2116 | return -EINVAL; |
2227 | 2117 | ||
2228 | /* | 2118 | /* |
@@ -2230,7 +2120,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2230 | * rd is non static (it means CRDA was present and was used last) | 2120 | * rd is non static (it means CRDA was present and was used last) |
2231 | * and the pending request came in from a country IE | 2121 | * and the pending request came in from a country IE |
2232 | */ | 2122 | */ |
2233 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2123 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2234 | /* | 2124 | /* |
2235 | * If someone else asked us to change the rd lets only bother | 2125 | * If someone else asked us to change the rd lets only bother |
2236 | * checking if the alpha2 changes if CRDA was already called | 2126 | * checking if the alpha2 changes if CRDA was already called |
@@ -2246,29 +2136,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2246 | * internal EEPROM data | 2136 | * internal EEPROM data |
2247 | */ | 2137 | */ |
2248 | 2138 | ||
2249 | if (WARN_ON(!reg_is_valid_request(rd->alpha2))) | ||
2250 | return -EINVAL; | ||
2251 | |||
2252 | if (!is_valid_rd(rd)) { | 2139 | if (!is_valid_rd(rd)) { |
2253 | pr_err("Invalid regulatory domain detected:\n"); | 2140 | pr_err("Invalid regulatory domain detected:\n"); |
2254 | print_regdomain_info(rd); | 2141 | print_regdomain_info(rd); |
2255 | return -EINVAL; | 2142 | return -EINVAL; |
2256 | } | 2143 | } |
2257 | 2144 | ||
2258 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2145 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2259 | if (!request_wiphy && | 2146 | if (!request_wiphy && |
2260 | (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 2147 | (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
2261 | last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { | 2148 | lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { |
2262 | schedule_delayed_work(®_timeout, 0); | 2149 | schedule_delayed_work(®_timeout, 0); |
2263 | return -ENODEV; | 2150 | return -ENODEV; |
2264 | } | 2151 | } |
2265 | 2152 | ||
2266 | if (!last_request->intersect) { | 2153 | if (!lr->intersect) { |
2267 | int r; | 2154 | if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { |
2268 | 2155 | reset_regdomains(false, rd); | |
2269 | if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { | ||
2270 | reset_regdomains(false); | ||
2271 | cfg80211_regdomain = rd; | ||
2272 | return 0; | 2156 | return 0; |
2273 | } | 2157 | } |
2274 | 2158 | ||
@@ -2284,20 +2168,19 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2284 | if (request_wiphy->regd) | 2168 | if (request_wiphy->regd) |
2285 | return -EALREADY; | 2169 | return -EALREADY; |
2286 | 2170 | ||
2287 | r = reg_copy_regd(&request_wiphy->regd, rd); | 2171 | regd = reg_copy_regd(rd); |
2288 | if (r) | 2172 | if (IS_ERR(regd)) |
2289 | return r; | 2173 | return PTR_ERR(regd); |
2290 | 2174 | ||
2291 | reset_regdomains(false); | 2175 | rcu_assign_pointer(request_wiphy->regd, regd); |
2292 | cfg80211_regdomain = rd; | 2176 | reset_regdomains(false, rd); |
2293 | return 0; | 2177 | return 0; |
2294 | } | 2178 | } |
2295 | 2179 | ||
2296 | /* Intersection requires a bit more work */ | 2180 | /* Intersection requires a bit more work */ |
2297 | 2181 | ||
2298 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2182 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2299 | 2183 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); | |
2300 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | ||
2301 | if (!intersected_rd) | 2184 | if (!intersected_rd) |
2302 | return -EINVAL; | 2185 | return -EINVAL; |
2303 | 2186 | ||
@@ -2306,15 +2189,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2306 | * However if a driver requested this specific regulatory | 2189 | * However if a driver requested this specific regulatory |
2307 | * domain we keep it for its private use | 2190 | * domain we keep it for its private use |
2308 | */ | 2191 | */ |
2309 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) | 2192 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) |
2310 | request_wiphy->regd = rd; | 2193 | rcu_assign_pointer(request_wiphy->regd, rd); |
2311 | else | 2194 | else |
2312 | kfree(rd); | 2195 | kfree(rd); |
2313 | 2196 | ||
2314 | rd = NULL; | 2197 | rd = NULL; |
2315 | 2198 | ||
2316 | reset_regdomains(false); | 2199 | reset_regdomains(false, intersected_rd); |
2317 | cfg80211_regdomain = intersected_rd; | ||
2318 | 2200 | ||
2319 | return 0; | 2201 | return 0; |
2320 | } | 2202 | } |
@@ -2326,15 +2208,15 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2326 | /* | 2208 | /* |
2327 | * Use this call to set the current regulatory domain. Conflicts with | 2209 | * Use this call to set the current regulatory domain. Conflicts with |
2328 | * multiple drivers can be ironed out later. Caller must've already | 2210 | * multiple drivers can be ironed out later. Caller must've already |
2329 | * kmalloc'd the rd structure. Caller must hold cfg80211_mutex | 2211 | * kmalloc'd the rd structure. |
2330 | */ | 2212 | */ |
2331 | int set_regdom(const struct ieee80211_regdomain *rd) | 2213 | int set_regdom(const struct ieee80211_regdomain *rd) |
2332 | { | 2214 | { |
2215 | struct regulatory_request *lr; | ||
2333 | int r; | 2216 | int r; |
2334 | 2217 | ||
2335 | assert_cfg80211_lock(); | ||
2336 | |||
2337 | mutex_lock(®_mutex); | 2218 | mutex_lock(®_mutex); |
2219 | lr = get_last_request(); | ||
2338 | 2220 | ||
2339 | /* Note that this doesn't update the wiphys, this is done below */ | 2221 | /* Note that this doesn't update the wiphys, this is done below */ |
2340 | r = __set_regdom(rd); | 2222 | r = __set_regdom(rd); |
@@ -2343,23 +2225,25 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2343 | reg_set_request_processed(); | 2225 | reg_set_request_processed(); |
2344 | 2226 | ||
2345 | kfree(rd); | 2227 | kfree(rd); |
2346 | mutex_unlock(®_mutex); | 2228 | goto out; |
2347 | return r; | ||
2348 | } | 2229 | } |
2349 | 2230 | ||
2350 | /* This would make this whole thing pointless */ | 2231 | /* This would make this whole thing pointless */ |
2351 | if (!last_request->intersect) | 2232 | if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { |
2352 | BUG_ON(rd != cfg80211_regdomain); | 2233 | r = -EINVAL; |
2234 | goto out; | ||
2235 | } | ||
2353 | 2236 | ||
2354 | /* update all wiphys now with the new established regulatory domain */ | 2237 | /* update all wiphys now with the new established regulatory domain */ |
2355 | update_all_wiphy_regulatory(last_request->initiator); | 2238 | update_all_wiphy_regulatory(lr->initiator); |
2356 | 2239 | ||
2357 | print_regdomain(cfg80211_regdomain); | 2240 | print_regdomain(get_cfg80211_regdom()); |
2358 | 2241 | ||
2359 | nl80211_send_reg_change_event(last_request); | 2242 | nl80211_send_reg_change_event(lr); |
2360 | 2243 | ||
2361 | reg_set_request_processed(); | 2244 | reg_set_request_processed(); |
2362 | 2245 | ||
2246 | out: | ||
2363 | mutex_unlock(®_mutex); | 2247 | mutex_unlock(®_mutex); |
2364 | 2248 | ||
2365 | return r; | 2249 | return r; |
@@ -2367,20 +2251,26 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2367 | 2251 | ||
2368 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 2252 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
2369 | { | 2253 | { |
2370 | if (last_request && !last_request->processed) { | 2254 | struct regulatory_request *lr; |
2371 | if (add_uevent_var(env, "COUNTRY=%c%c", | 2255 | u8 alpha2[2]; |
2372 | last_request->alpha2[0], | 2256 | bool add = false; |
2373 | last_request->alpha2[1])) | 2257 | |
2374 | return -ENOMEM; | 2258 | rcu_read_lock(); |
2259 | lr = get_last_request(); | ||
2260 | if (lr && !lr->processed) { | ||
2261 | memcpy(alpha2, lr->alpha2, 2); | ||
2262 | add = true; | ||
2375 | } | 2263 | } |
2264 | rcu_read_unlock(); | ||
2376 | 2265 | ||
2266 | if (add) | ||
2267 | return add_uevent_var(env, "COUNTRY=%c%c", | ||
2268 | alpha2[0], alpha2[1]); | ||
2377 | return 0; | 2269 | return 0; |
2378 | } | 2270 | } |
2379 | 2271 | ||
2380 | void wiphy_regulatory_register(struct wiphy *wiphy) | 2272 | void wiphy_regulatory_register(struct wiphy *wiphy) |
2381 | { | 2273 | { |
2382 | assert_cfg80211_lock(); | ||
2383 | |||
2384 | mutex_lock(®_mutex); | 2274 | mutex_lock(®_mutex); |
2385 | 2275 | ||
2386 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2276 | if (!reg_dev_ignore_cell_hint(wiphy)) |
@@ -2395,32 +2285,32 @@ void wiphy_regulatory_register(struct wiphy *wiphy) | |||
2395 | void wiphy_regulatory_deregister(struct wiphy *wiphy) | 2285 | void wiphy_regulatory_deregister(struct wiphy *wiphy) |
2396 | { | 2286 | { |
2397 | struct wiphy *request_wiphy = NULL; | 2287 | struct wiphy *request_wiphy = NULL; |
2398 | 2288 | struct regulatory_request *lr; | |
2399 | assert_cfg80211_lock(); | ||
2400 | 2289 | ||
2401 | mutex_lock(®_mutex); | 2290 | mutex_lock(®_mutex); |
2291 | lr = get_last_request(); | ||
2402 | 2292 | ||
2403 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2293 | if (!reg_dev_ignore_cell_hint(wiphy)) |
2404 | reg_num_devs_support_basehint--; | 2294 | reg_num_devs_support_basehint--; |
2405 | 2295 | ||
2406 | kfree(wiphy->regd); | 2296 | rcu_free_regdom(get_wiphy_regdom(wiphy)); |
2297 | rcu_assign_pointer(wiphy->regd, NULL); | ||
2407 | 2298 | ||
2408 | if (last_request) | 2299 | if (lr) |
2409 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2300 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2410 | 2301 | ||
2411 | if (!request_wiphy || request_wiphy != wiphy) | 2302 | if (!request_wiphy || request_wiphy != wiphy) |
2412 | goto out; | 2303 | goto out; |
2413 | 2304 | ||
2414 | last_request->wiphy_idx = WIPHY_IDX_STALE; | 2305 | lr->wiphy_idx = WIPHY_IDX_INVALID; |
2415 | last_request->country_ie_env = ENVIRON_ANY; | 2306 | lr->country_ie_env = ENVIRON_ANY; |
2416 | out: | 2307 | out: |
2417 | mutex_unlock(®_mutex); | 2308 | mutex_unlock(®_mutex); |
2418 | } | 2309 | } |
2419 | 2310 | ||
2420 | static void reg_timeout_work(struct work_struct *work) | 2311 | static void reg_timeout_work(struct work_struct *work) |
2421 | { | 2312 | { |
2422 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, " | 2313 | REG_DBG_PRINT("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); |
2423 | "restoring regulatory settings\n"); | ||
2424 | restore_regulatory_settings(true); | 2314 | restore_regulatory_settings(true); |
2425 | } | 2315 | } |
2426 | 2316 | ||
@@ -2439,13 +2329,13 @@ int __init regulatory_init(void) | |||
2439 | 2329 | ||
2440 | reg_regdb_size_check(); | 2330 | reg_regdb_size_check(); |
2441 | 2331 | ||
2442 | cfg80211_regdomain = cfg80211_world_regdom; | 2332 | rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); |
2443 | 2333 | ||
2444 | user_alpha2[0] = '9'; | 2334 | user_alpha2[0] = '9'; |
2445 | user_alpha2[1] = '7'; | 2335 | user_alpha2[1] = '7'; |
2446 | 2336 | ||
2447 | /* We always try to get an update for the static regdomain */ | 2337 | /* We always try to get an update for the static regdomain */ |
2448 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2338 | err = regulatory_hint_core(cfg80211_world_regdom->alpha2); |
2449 | if (err) { | 2339 | if (err) { |
2450 | if (err == -ENOMEM) | 2340 | if (err == -ENOMEM) |
2451 | return err; | 2341 | return err; |
@@ -2457,10 +2347,6 @@ int __init regulatory_init(void) | |||
2457 | * errors as non-fatal. | 2347 | * errors as non-fatal. |
2458 | */ | 2348 | */ |
2459 | pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); | 2349 | pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); |
2460 | #ifdef CONFIG_CFG80211_REG_DEBUG | ||
2461 | /* We want to find out exactly why when debugging */ | ||
2462 | WARN_ON(err); | ||
2463 | #endif | ||
2464 | } | 2350 | } |
2465 | 2351 | ||
2466 | /* | 2352 | /* |
@@ -2474,7 +2360,7 @@ int __init regulatory_init(void) | |||
2474 | return 0; | 2360 | return 0; |
2475 | } | 2361 | } |
2476 | 2362 | ||
2477 | void /* __init_or_exit */ regulatory_exit(void) | 2363 | void regulatory_exit(void) |
2478 | { | 2364 | { |
2479 | struct regulatory_request *reg_request, *tmp; | 2365 | struct regulatory_request *reg_request, *tmp; |
2480 | struct reg_beacon *reg_beacon, *btmp; | 2366 | struct reg_beacon *reg_beacon, *btmp; |
@@ -2482,43 +2368,27 @@ void /* __init_or_exit */ regulatory_exit(void) | |||
2482 | cancel_work_sync(®_work); | 2368 | cancel_work_sync(®_work); |
2483 | cancel_delayed_work_sync(®_timeout); | 2369 | cancel_delayed_work_sync(®_timeout); |
2484 | 2370 | ||
2485 | mutex_lock(&cfg80211_mutex); | 2371 | /* Lock to suppress warnings */ |
2486 | mutex_lock(®_mutex); | 2372 | mutex_lock(®_mutex); |
2487 | 2373 | reset_regdomains(true, NULL); | |
2488 | reset_regdomains(true); | 2374 | mutex_unlock(®_mutex); |
2489 | 2375 | ||
2490 | dev_set_uevent_suppress(®_pdev->dev, true); | 2376 | dev_set_uevent_suppress(®_pdev->dev, true); |
2491 | 2377 | ||
2492 | platform_device_unregister(reg_pdev); | 2378 | platform_device_unregister(reg_pdev); |
2493 | 2379 | ||
2494 | spin_lock_bh(®_pending_beacons_lock); | 2380 | list_for_each_entry_safe(reg_beacon, btmp, ®_pending_beacons, list) { |
2495 | if (!list_empty(®_pending_beacons)) { | 2381 | list_del(®_beacon->list); |
2496 | list_for_each_entry_safe(reg_beacon, btmp, | 2382 | kfree(reg_beacon); |
2497 | ®_pending_beacons, list) { | ||
2498 | list_del(®_beacon->list); | ||
2499 | kfree(reg_beacon); | ||
2500 | } | ||
2501 | } | 2383 | } |
2502 | spin_unlock_bh(®_pending_beacons_lock); | ||
2503 | 2384 | ||
2504 | if (!list_empty(®_beacon_list)) { | 2385 | list_for_each_entry_safe(reg_beacon, btmp, ®_beacon_list, list) { |
2505 | list_for_each_entry_safe(reg_beacon, btmp, | 2386 | list_del(®_beacon->list); |
2506 | ®_beacon_list, list) { | 2387 | kfree(reg_beacon); |
2507 | list_del(®_beacon->list); | ||
2508 | kfree(reg_beacon); | ||
2509 | } | ||
2510 | } | 2388 | } |
2511 | 2389 | ||
2512 | spin_lock(®_requests_lock); | 2390 | list_for_each_entry_safe(reg_request, tmp, ®_requests_list, list) { |
2513 | if (!list_empty(®_requests_list)) { | 2391 | list_del(®_request->list); |
2514 | list_for_each_entry_safe(reg_request, tmp, | 2392 | kfree(reg_request); |
2515 | ®_requests_list, list) { | ||
2516 | list_del(®_request->list); | ||
2517 | kfree(reg_request); | ||
2518 | } | ||
2519 | } | 2393 | } |
2520 | spin_unlock(®_requests_lock); | ||
2521 | |||
2522 | mutex_unlock(®_mutex); | ||
2523 | mutex_unlock(&cfg80211_mutex); | ||
2524 | } | 2394 | } |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 4c0a32ffd530..af2d5f8a5d82 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -16,10 +16,9 @@ | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | extern const struct ieee80211_regdomain *cfg80211_regdomain; | 19 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
20 | 20 | ||
21 | bool is_world_regdom(const char *alpha2); | 21 | bool is_world_regdom(const char *alpha2); |
22 | bool reg_is_valid_request(const char *alpha2); | ||
23 | bool reg_supported_dfs_region(u8 dfs_region); | 22 | bool reg_supported_dfs_region(u8 dfs_region); |
24 | 23 | ||
25 | int regulatory_hint_user(const char *alpha2, | 24 | int regulatory_hint_user(const char *alpha2, |
@@ -55,8 +54,8 @@ bool reg_last_request_cell_base(void); | |||
55 | * set the wiphy->disable_beacon_hints to true. | 54 | * set the wiphy->disable_beacon_hints to true. |
56 | */ | 55 | */ |
57 | int regulatory_hint_found_beacon(struct wiphy *wiphy, | 56 | int regulatory_hint_found_beacon(struct wiphy *wiphy, |
58 | struct ieee80211_channel *beacon_chan, | 57 | struct ieee80211_channel *beacon_chan, |
59 | gfp_t gfp); | 58 | gfp_t gfp); |
60 | 59 | ||
61 | /** | 60 | /** |
62 | * regulatory_hint_11d - hints a country IE as a regulatory domain | 61 | * regulatory_hint_11d - hints a country IE as a regulatory domain |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index f2431e41a373..a825dfe12cf7 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -192,7 +192,8 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev) | |||
192 | prev_bssid, | 192 | prev_bssid, |
193 | params->ssid, params->ssid_len, | 193 | params->ssid, params->ssid_len, |
194 | params->ie, params->ie_len, | 194 | params->ie, params->ie_len, |
195 | false, ¶ms->crypto, | 195 | params->mfp != NL80211_MFP_NO, |
196 | ¶ms->crypto, | ||
196 | params->flags, ¶ms->ht_capa, | 197 | params->flags, ¶ms->ht_capa, |
197 | ¶ms->ht_capa_mask); | 198 | ¶ms->ht_capa_mask); |
198 | if (err) | 199 | if (err) |
@@ -519,10 +520,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
519 | * - country_ie + 2, the start of the country ie data, and | 520 | * - country_ie + 2, the start of the country ie data, and |
520 | * - and country_ie[1] which is the IE length | 521 | * - and country_ie[1] which is the IE length |
521 | */ | 522 | */ |
522 | regulatory_hint_11d(wdev->wiphy, | 523 | regulatory_hint_11d(wdev->wiphy, bss->channel->band, |
523 | bss->channel->band, | 524 | country_ie + 2, country_ie[1]); |
524 | country_ie + 2, | ||
525 | country_ie[1]); | ||
526 | kfree(country_ie); | 525 | kfree(country_ie); |
527 | } | 526 | } |
528 | 527 | ||
diff --git a/net/wireless/util.c b/net/wireless/util.c index 16d76a807c2f..1c2795d52db0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -1184,7 +1184,8 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
1184 | struct wireless_dev *wdev, | 1184 | struct wireless_dev *wdev, |
1185 | enum nl80211_iftype iftype, | 1185 | enum nl80211_iftype iftype, |
1186 | struct ieee80211_channel *chan, | 1186 | struct ieee80211_channel *chan, |
1187 | enum cfg80211_chan_mode chanmode) | 1187 | enum cfg80211_chan_mode chanmode, |
1188 | u8 radar_detect) | ||
1188 | { | 1189 | { |
1189 | struct wireless_dev *wdev_iter; | 1190 | struct wireless_dev *wdev_iter; |
1190 | u32 used_iftypes = BIT(iftype); | 1191 | u32 used_iftypes = BIT(iftype); |
@@ -1195,14 +1196,45 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
1195 | enum cfg80211_chan_mode chmode; | 1196 | enum cfg80211_chan_mode chmode; |
1196 | int num_different_channels = 0; | 1197 | int num_different_channels = 0; |
1197 | int total = 1; | 1198 | int total = 1; |
1199 | bool radar_required; | ||
1198 | int i, j; | 1200 | int i, j; |
1199 | 1201 | ||
1200 | ASSERT_RTNL(); | 1202 | ASSERT_RTNL(); |
1201 | lockdep_assert_held(&rdev->devlist_mtx); | 1203 | lockdep_assert_held(&rdev->devlist_mtx); |
1202 | 1204 | ||
1205 | if (WARN_ON(hweight32(radar_detect) > 1)) | ||
1206 | return -EINVAL; | ||
1207 | |||
1208 | switch (iftype) { | ||
1209 | case NL80211_IFTYPE_ADHOC: | ||
1210 | case NL80211_IFTYPE_AP: | ||
1211 | case NL80211_IFTYPE_AP_VLAN: | ||
1212 | case NL80211_IFTYPE_MESH_POINT: | ||
1213 | case NL80211_IFTYPE_P2P_GO: | ||
1214 | case NL80211_IFTYPE_WDS: | ||
1215 | radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); | ||
1216 | break; | ||
1217 | case NL80211_IFTYPE_P2P_CLIENT: | ||
1218 | case NL80211_IFTYPE_STATION: | ||
1219 | case NL80211_IFTYPE_MONITOR: | ||
1220 | radar_required = false; | ||
1221 | break; | ||
1222 | case NL80211_IFTYPE_P2P_DEVICE: | ||
1223 | case NUM_NL80211_IFTYPES: | ||
1224 | case NL80211_IFTYPE_UNSPECIFIED: | ||
1225 | default: | ||
1226 | return -EINVAL; | ||
1227 | } | ||
1228 | |||
1229 | if (radar_required && !radar_detect) | ||
1230 | return -EINVAL; | ||
1231 | |||
1203 | /* Always allow software iftypes */ | 1232 | /* Always allow software iftypes */ |
1204 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | 1233 | if (rdev->wiphy.software_iftypes & BIT(iftype)) { |
1234 | if (radar_detect) | ||
1235 | return -EINVAL; | ||
1205 | return 0; | 1236 | return 0; |
1237 | } | ||
1206 | 1238 | ||
1207 | memset(num, 0, sizeof(num)); | 1239 | memset(num, 0, sizeof(num)); |
1208 | memset(used_channels, 0, sizeof(used_channels)); | 1240 | memset(used_channels, 0, sizeof(used_channels)); |
@@ -1275,7 +1307,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
1275 | used_iftypes |= BIT(wdev_iter->iftype); | 1307 | used_iftypes |= BIT(wdev_iter->iftype); |
1276 | } | 1308 | } |
1277 | 1309 | ||
1278 | if (total == 1) | 1310 | if (total == 1 && !radar_detect) |
1279 | return 0; | 1311 | return 0; |
1280 | 1312 | ||
1281 | for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { | 1313 | for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { |
@@ -1308,6 +1340,9 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, | |||
1308 | } | 1340 | } |
1309 | } | 1341 | } |
1310 | 1342 | ||
1343 | if (radar_detect && !(c->radar_detect_widths & radar_detect)) | ||
1344 | goto cont; | ||
1345 | |||
1311 | /* | 1346 | /* |
1312 | * Finally check that all iftypes that we're currently | 1347 | * Finally check that all iftypes that we're currently |
1313 | * using are actually part of this combination. If they | 1348 | * using are actually part of this combination. If they |