diff options
author | Jonathan Doron <jond@wizery.com> | 2014-12-15 12:26:00 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-12-17 05:49:55 -0500 |
commit | b0d7aa59592b4270531de5ce65dcf18338a2d98c (patch) | |
tree | 85d54dba5117fedd4d5b605be70ddbf8451d7838 /net/wireless | |
parent | ad30ca2c03cecfb1b0749874bdceead269542de6 (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.c | 8 | ||||
-rw-r--r-- | net/wireless/core.h | 7 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 49 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 16 | ||||
-rw-r--r-- | net/wireless/reg.c | 88 |
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 | /* | 11045 | static 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 | */ | ||
11049 | void 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 | |||
11083 | nla_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 | */ | ||
11091 | void 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); |
18 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, | 18 | void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev, |
19 | struct net_device *netdev); | 19 | struct net_device *netdev); |
20 | void nl80211_send_reg_change_event(struct regulatory_request *request); | 20 | void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, |
21 | struct regulatory_request *request); | ||
22 | |||
23 | static inline void | ||
24 | nl80211_send_reg_change_event(struct regulatory_request *request) | ||
25 | { | ||
26 | nl80211_common_reg_change_event(NL80211_CMD_REG_CHANGE, request); | ||
27 | } | ||
28 | |||
29 | static inline void | ||
30 | nl80211_send_wiphy_reg_change_event(struct regulatory_request *request) | ||
31 | { | ||
32 | nl80211_common_reg_change_event(NL80211_CMD_WIPHY_REG_CHANGE, request); | ||
33 | } | ||
34 | |||
21 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | 35 | void 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(®_pending_beacons_lock); | 2150 | spin_unlock_bh(®_pending_beacons_lock); |
2148 | } | 2151 | } |
2149 | 2152 | ||
2153 | static 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(®_requests_lock); | ||
2166 | regd = rdev->requested_regd; | ||
2167 | rdev->requested_regd = NULL; | ||
2168 | spin_unlock(®_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 | |||
2150 | static void reg_todo(struct work_struct *work) | 2193 | static 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 | ||
2884 | int 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(®_requests_lock); | ||
2910 | prev_regd = rdev->requested_regd; | ||
2911 | rdev->requested_regd = regd; | ||
2912 | spin_unlock(®_requests_lock); | ||
2913 | |||
2914 | kfree(prev_regd); | ||
2915 | |||
2916 | schedule_work(®_work); | ||
2917 | return 0; | ||
2918 | } | ||
2919 | EXPORT_SYMBOL(regulatory_set_wiphy_regd); | ||
2920 | |||
2838 | void wiphy_regulatory_register(struct wiphy *wiphy) | 2921 | void 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 | ||