aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJonathan Doron <jond@wizery.com>2014-12-15 12:26:00 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-12-17 05:49:55 -0500
commitb0d7aa59592b4270531de5ce65dcf18338a2d98c (patch)
tree85d54dba5117fedd4d5b605be70ddbf8451d7838 /net/wireless
parentad30ca2c03cecfb1b0749874bdceead269542de6 (diff)
cfg80211: allow wiphy specific regdomain management
Add a new regulatory flag that allows a driver to manage regdomain changes/updates for its own wiphy. A self-managed wiphys only employs regulatory information obtained from the FW and driver and does not use other cfg80211 sources like beacon-hints, country-code IEs and hints from other devices on the same system. Conversely, a self-managed wiphy does not share its regulatory hints with other devices in the system. If a system contains several devices, one or more of which are self-managed, there might be contradictory regulatory settings between them. Usage of flag is generally discouraged. Only use it if the FW/driver is incompatible with non-locally originated hints. A new API lets the driver send a complete regdomain, to be applied on its wiphy only. After a wiphy-specific regdomain change takes place, usermode will get a new type of change notification. The regulatory core also takes care enforce regulatory restrictions, in case some interfaces are on forbidden channels. Signed-off-by: Jonathan Doron <jonathanx.doron@intel.com> Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com> Reviewed-by: Luis R. Rodriguez <mcgrof@suse.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/core.c8
-rw-r--r--net/wireless/core.h7
-rw-r--r--net/wireless/nl80211.c49
-rw-r--r--net/wireless/nl80211.h16
-rw-r--r--net/wireless/reg.c88
5 files changed, 149 insertions, 19 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 4910758baab1..b661fcce7e3c 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -561,6 +561,14 @@ int wiphy_register(struct wiphy *wiphy)
561 BIT(NL80211_IFTYPE_MONITOR))) 561 BIT(NL80211_IFTYPE_MONITOR)))
562 wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF; 562 wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
563 563
564 if (WARN_ON((wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) &&
565 (wiphy->regulatory_flags &
566 (REGULATORY_CUSTOM_REG |
567 REGULATORY_STRICT_REG |
568 REGULATORY_COUNTRY_IE_FOLLOW_POWER |
569 REGULATORY_COUNTRY_IE_IGNORE))))
570 return -EINVAL;
571
564 if (WARN_ON(wiphy->coalesce && 572 if (WARN_ON(wiphy->coalesce &&
565 (!wiphy->coalesce->n_rules || 573 (!wiphy->coalesce->n_rules ||
566 !wiphy->coalesce->n_patterns) && 574 !wiphy->coalesce->n_patterns) &&
diff --git a/net/wireless/core.h b/net/wireless/core.h
index faa5b1609aae..e87cae57a83b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -36,6 +36,13 @@ struct cfg80211_registered_device {
36 * the country on the country IE changed. */ 36 * the country on the country IE changed. */
37 char country_ie_alpha2[2]; 37 char country_ie_alpha2[2];
38 38
39 /*
40 * the driver requests the regulatory core to set this regulatory
41 * domain as the wiphy's. Only used for %REGULATORY_WIPHY_SELF_MANAGED
42 * devices using the regulatory_set_wiphy_regd() API
43 */
44 const struct ieee80211_regdomain *requested_regd;
45
39 /* If a Country IE has been received this tells us the environment 46 /* If a Country IE has been received this tells us the environment
40 * which its telling us its in. This defaults to ENVIRON_ANY */ 47 * which its telling us its in. This defaults to ENVIRON_ANY */
41 enum environment_cap env; 48 enum environment_cap env;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 2d5dc428c5ab..eebb7e422989 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -11042,25 +11042,9 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
11042 NL80211_MCGRP_SCAN, GFP_KERNEL); 11042 NL80211_MCGRP_SCAN, GFP_KERNEL);
11043} 11043}
11044 11044
11045/* 11045static bool nl80211_reg_change_event_fill(struct sk_buff *msg,
11046 * This can happen on global regulatory changes or device specific settings 11046 struct regulatory_request *request)
11047 * based on custom world regulatory domains.
11048 */
11049void nl80211_send_reg_change_event(struct regulatory_request *request)
11050{ 11047{
11051 struct sk_buff *msg;
11052 void *hdr;
11053
11054 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
11055 if (!msg)
11056 return;
11057
11058 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
11059 if (!hdr) {
11060 nlmsg_free(msg);
11061 return;
11062 }
11063
11064 /* Userspace can always count this one always being set */ 11048 /* Userspace can always count this one always being set */
11065 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator)) 11049 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
11066 goto nla_put_failure; 11050 goto nla_put_failure;
@@ -11094,6 +11078,35 @@ void nl80211_send_reg_change_event(struct regulatory_request *request)
11094 goto nla_put_failure; 11078 goto nla_put_failure;
11095 } 11079 }
11096 11080
11081 return true;
11082
11083nla_put_failure:
11084 return false;
11085}
11086
11087/*
11088 * This can happen on global regulatory changes or device specific settings
11089 * based on custom regulatory domains.
11090 */
11091void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
11092 struct regulatory_request *request)
11093{
11094 struct sk_buff *msg;
11095 void *hdr;
11096
11097 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
11098 if (!msg)
11099 return;
11100
11101 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd_id);
11102 if (!hdr) {
11103 nlmsg_free(msg);
11104 return;
11105 }
11106
11107 if (nl80211_reg_change_event_fill(msg, request) == false)
11108 goto nla_put_failure;
11109
11097 genlmsg_end(msg, hdr); 11110 genlmsg_end(msg, hdr);
11098 11111
11099 rcu_read_lock(); 11112 rcu_read_lock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 7ad70d6f0cc6..84d4edf1d545 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -17,7 +17,21 @@ void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
17 struct net_device *netdev, u32 cmd); 17 struct net_device *netdev, u32 cmd);
18void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, 18void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
19 struct net_device *netdev); 19 struct net_device *netdev);
20void nl80211_send_reg_change_event(struct regulatory_request *request); 20void nl80211_common_reg_change_event(enum nl80211_commands cmd_id,
21 struct regulatory_request *request);
22
23static inline void
24nl80211_send_reg_change_event(struct regulatory_request *request)
25{
26 nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request);
27}
28
29static inline void
30nl80211_send_wiphy_reg_change_event(struct regulatory_request *request)
31{
32 nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request);
33}
34
21void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, 35void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
22 struct net_device *netdev, 36 struct net_device *netdev,
23 const u8 *buf, size_t len, gfp_t gfp); 37 const u8 *buf, size_t len, gfp_t gfp);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2d9760084b74..c040f8a0f1ed 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
1307{ 1307{
1308 struct regulatory_request *lr = get_last_request(); 1308 struct regulatory_request *lr = get_last_request();
1309 1309
1310 if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
1311 return true;
1312
1310 if (!lr) { 1313 if (!lr) {
1311 REG_DBG_PRINT("Ignoring regulatory request set by %s " 1314 REG_DBG_PRINT("Ignoring regulatory request set by %s "
1312 "since last_request is not set\n", 1315 "since last_request is not set\n",
@@ -2147,11 +2150,52 @@ static void reg_process_pending_beacon_hints(void)
2147 spin_unlock_bh(&reg_pending_beacons_lock); 2150 spin_unlock_bh(&reg_pending_beacons_lock);
2148} 2151}
2149 2152
2153static void reg_process_self_managed_hints(void)
2154{
2155 struct cfg80211_registered_device *rdev;
2156 struct wiphy *wiphy;
2157 const struct ieee80211_regdomain *tmp;
2158 const struct ieee80211_regdomain *regd;
2159 enum ieee80211_band band;
2160 struct regulatory_request request = {};
2161
2162 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
2163 wiphy = &rdev->wiphy;
2164
2165 spin_lock(&reg_requests_lock);
2166 regd = rdev->requested_regd;
2167 rdev->requested_regd = NULL;
2168 spin_unlock(&reg_requests_lock);
2169
2170 if (regd == NULL)
2171 continue;
2172
2173 tmp = get_wiphy_regdom(wiphy);
2174 rcu_assign_pointer(wiphy->regd, regd);
2175 rcu_free_regdom(tmp);
2176
2177 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
2178 handle_band_custom(wiphy, wiphy->bands[band], regd);
2179
2180 reg_process_ht_flags(wiphy);
2181
2182 request.wiphy_idx = get_wiphy_idx(wiphy);
2183 request.alpha2[0] = regd->alpha2[0];
2184 request.alpha2[1] = regd->alpha2[1];
2185 request.initiator = NL80211_REGDOM_SET_BY_DRIVER;
2186
2187 nl80211_send_wiphy_reg_change_event(&request);
2188 }
2189
2190 reg_check_channels();
2191}
2192
2150static void reg_todo(struct work_struct *work) 2193static void reg_todo(struct work_struct *work)
2151{ 2194{
2152 rtnl_lock(); 2195 rtnl_lock();
2153 reg_process_pending_hints(); 2196 reg_process_pending_hints();
2154 reg_process_pending_beacon_hints(); 2197 reg_process_pending_beacon_hints();
2198 reg_process_self_managed_hints();
2155 rtnl_unlock(); 2199 rtnl_unlock();
2156} 2200}
2157 2201
@@ -2432,6 +2476,8 @@ static void restore_regulatory_settings(bool reset_user)
2432 world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; 2476 world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
2433 2477
2434 list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 2478 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
2479 if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
2480 continue;
2435 if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) 2481 if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
2436 restore_custom_reg_settings(&rdev->wiphy); 2482 restore_custom_reg_settings(&rdev->wiphy);
2437 } 2483 }
@@ -2835,10 +2881,52 @@ int set_regdom(const struct ieee80211_regdomain *rd)
2835 return 0; 2881 return 0;
2836} 2882}
2837 2883
2884int regulatory_set_wiphy_regd(struct wiphy *wiphy,
2885 struct ieee80211_regdomain *rd)
2886{
2887 const struct ieee80211_regdomain *regd;
2888 const struct ieee80211_regdomain *prev_regd;
2889 struct cfg80211_registered_device *rdev;
2890
2891 if (WARN_ON(!wiphy || !rd))
2892 return -EINVAL;
2893
2894 if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED),
2895 "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n"))
2896 return -EPERM;
2897
2898 if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) {
2899 print_regdomain_info(rd);
2900 return -EINVAL;
2901 }
2902
2903 regd = reg_copy_regd(rd);
2904 if (IS_ERR(regd))
2905 return PTR_ERR(regd);
2906
2907 rdev = wiphy_to_rdev(wiphy);
2908
2909 spin_lock(&reg_requests_lock);
2910 prev_regd = rdev->requested_regd;
2911 rdev->requested_regd = regd;
2912 spin_unlock(&reg_requests_lock);
2913
2914 kfree(prev_regd);
2915
2916 schedule_work(&reg_work);
2917 return 0;
2918}
2919EXPORT_SYMBOL(regulatory_set_wiphy_regd);
2920
2838void wiphy_regulatory_register(struct wiphy *wiphy) 2921void wiphy_regulatory_register(struct wiphy *wiphy)
2839{ 2922{
2840 struct regulatory_request *lr; 2923 struct regulatory_request *lr;
2841 2924
2925 /* self-managed devices ignore external hints */
2926 if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
2927 wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
2928 REGULATORY_COUNTRY_IE_IGNORE;
2929
2842 if (!reg_dev_ignore_cell_hint(wiphy)) 2930 if (!reg_dev_ignore_cell_hint(wiphy))
2843 reg_num_devs_support_basehint++; 2931 reg_num_devs_support_basehint++;
2844 2932