summaryrefslogtreecommitdiff
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c132
1 files changed, 105 insertions, 27 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 50dffd183cc6..b66db3476d3b 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -425,6 +425,11 @@ static bool is_user_regdom_saved(void)
return true;
}
+static bool is_cfg80211_regdom_intersected(void)
+{
+ return is_intersected_alpha2(get_cfg80211_regdom()->alpha2);
+}
+
static const struct ieee80211_regdomain *
reg_copy_regd(const struct ieee80211_regdomain *src_regd)
{
@@ -1676,12 +1681,48 @@ static void reg_leave_invalid_chans(struct wiphy *wiphy)
{
struct wireless_dev *wdev;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
-
+ struct net_device *dev;
+ struct cfg80211_sched_scan_request *sched_scan_req;
ASSERT_RTNL();
- list_for_each_entry(wdev, &rdev->wdev_list, list)
- if (!reg_wdev_chan_valid(wiphy, wdev))
- cfg80211_leave(rdev, wdev);
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list)
+ if (!reg_wdev_chan_valid(wiphy, wdev)) {
+ dev = wdev->netdev;
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_ADHOC:
+ cfg80211_leave_ibss(rdev, dev, true);
+ break;
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ ASSERT_RTNL();
+ sched_scan_req = rtnl_dereference(rdev->sched_scan_req);
+ if (sched_scan_req && dev == sched_scan_req->dev)
+ __cfg80211_stop_sched_scan(rdev, false);
+
+ wdev_lock(wdev);
+#ifdef CONFIG_CFG80211_WEXT
+ kfree(wdev->wext.ie);
+ wdev->wext.ie = NULL;
+ wdev->wext.ie_len = 0;
+ wdev->wext.connect.auth_type =
+ NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+ cfg80211_disconnect(rdev, dev,
+ WLAN_REASON_DEAUTH_LEAVING, true);
+ cfg80211_mlme_down(rdev, dev);
+ wdev_unlock(wdev);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ cfg80211_leave_mesh(rdev, dev);
+ break;
+ case NL80211_IFTYPE_AP:
+ cfg80211_stop_ap(rdev, dev, false);
+ break;
+ default:
+ break;
+ }
+ wdev->beacon_interval = 0;
+ }
}
static void reg_check_chans_work(struct work_struct *work)
@@ -1723,7 +1764,9 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
* as some drivers used this to restore its orig_* reg domain.
*/
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG &&
+ !(wiphy->regulatory_flags &
+ REGULATORY_WIPHY_SELF_MANAGED))
reg_call_notifier(wiphy, lr);
return;
}
@@ -1941,9 +1984,14 @@ __reg_process_hint_user(struct regulatory_request *user_request)
*/
if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
- regdom_changes(lr->alpha2))
- return REG_REQ_IGNORE;
+ lr->initiator == NL80211_REGDOM_SET_BY_USER)) {
+ if (lr->intersect) {
+ if (!is_cfg80211_regdom_intersected())
+ return REG_REQ_IGNORE;
+ } else if (regdom_changes(lr->alpha2)) {
+ return REG_REQ_IGNORE;
+ }
+ }
if (!regdom_changes(user_request->alpha2))
return REG_REQ_ALREADY_SET;
@@ -2201,24 +2249,19 @@ out_free:
reg_free_request(reg_request);
}
-static bool reg_only_self_managed_wiphys(void)
+static void notify_self_managed_wiphys(struct regulatory_request *request)
{
struct cfg80211_registered_device *rdev;
struct wiphy *wiphy;
- bool self_managed_found = false;
-
- ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
- if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
- self_managed_found = true;
- else
- return false;
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
+ request->initiator == NL80211_REGDOM_SET_BY_USER &&
+ request->user_reg_hint_type ==
+ NL80211_USER_REG_HINT_CELL_BASE)
+ reg_call_notifier(wiphy, request);
}
-
- /* make sure at least one self-managed wiphy exists */
- return self_managed_found;
}
/*
@@ -2252,10 +2295,7 @@ static void reg_process_pending_hints(void)
spin_unlock(&reg_requests_lock);
- if (reg_only_self_managed_wiphys()) {
- reg_free_request(reg_request);
- return;
- }
+ notify_self_managed_wiphys(reg_request);
reg_process_hint(reg_request);
@@ -2400,6 +2440,7 @@ int regulatory_hint_user(const char *alpha2,
return 0;
}
+EXPORT_SYMBOL(regulatory_hint_user);
int regulatory_hint_indoor(bool is_indoor, u32 portid)
{
@@ -3130,17 +3171,26 @@ EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl);
void wiphy_regulatory_register(struct wiphy *wiphy)
{
- struct regulatory_request *lr;
+ struct regulatory_request *lr = get_last_request();
- /* self-managed devices ignore external hints */
- if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
+ /* self-managed devices ignore beacon hints and country IE */
+ if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) {
wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS |
REGULATORY_COUNTRY_IE_IGNORE;
+ /*
+ * The last request may have been received before this
+ * registration call. Call the driver notifier if
+ * initiator is USER and user type is CELL_BASE.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->user_reg_hint_type == NL80211_USER_REG_HINT_CELL_BASE)
+ reg_call_notifier(wiphy, lr);
+ }
+
if (!reg_dev_ignore_cell_hint(wiphy))
reg_num_devs_support_basehint++;
- lr = get_last_request();
wiphy_update_regulatory(wiphy, lr->initiator);
}
@@ -3201,6 +3251,34 @@ bool regulatory_indoor_allowed(void)
return reg_is_indoor;
}
+bool regulatory_pre_cac_allowed(struct wiphy *wiphy)
+{
+ const struct ieee80211_regdomain *regd = NULL;
+ const struct ieee80211_regdomain *wiphy_regd = NULL;
+ bool pre_cac_allowed = false;
+
+ rcu_read_lock();
+
+ regd = rcu_dereference(cfg80211_regdomain);
+ wiphy_regd = rcu_dereference(wiphy->regd);
+ if (!wiphy_regd) {
+ if (regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+ }
+
+ if (regd->dfs_region == wiphy_regd->dfs_region &&
+ wiphy_regd->dfs_region == NL80211_DFS_ETSI)
+ pre_cac_allowed = true;
+
+ rcu_read_unlock();
+
+ return pre_cac_allowed;
+}
+
int __init regulatory_init(void)
{
int err = 0;