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.c162
1 files changed, 120 insertions, 42 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index dcbf5cd44bb3..98a5959b52a5 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)
{
@@ -1586,12 +1591,12 @@ static void reg_process_ht_flags_band(struct wiphy *wiphy,
static void reg_process_ht_flags(struct wiphy *wiphy)
{
- enum ieee80211_band band;
+ enum nl80211_band band;
if (!wiphy)
return;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
}
@@ -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)
@@ -1713,7 +1754,7 @@ static void reg_check_channels(void)
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
- enum ieee80211_band band;
+ enum nl80211_band band;
struct regulatory_request *lr = get_last_request();
if (ignore_reg_update(wiphy, initiator)) {
@@ -1723,14 +1764,16 @@ 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;
}
lr->dfs_region = get_cfg80211_regdom()->dfs_region;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
handle_band(wiphy, initiator, wiphy->bands[band]);
reg_process_beacons(wiphy);
@@ -1855,14 +1898,14 @@ static void handle_band_custom(struct wiphy *wiphy,
void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
const struct ieee80211_regdomain *regd)
{
- enum ieee80211_band band;
+ enum nl80211_band band;
unsigned int bands_set = 0;
WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
"wiphy should have REGULATORY_CUSTOM_REG\n");
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
if (!wiphy->bands[band])
continue;
handle_band_custom(wiphy, wiphy->bands[band], regd);
@@ -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);
@@ -2297,7 +2337,7 @@ static void reg_process_self_managed_hints(void)
struct wiphy *wiphy;
const struct ieee80211_regdomain *tmp;
const struct ieee80211_regdomain *regd;
- enum ieee80211_band band;
+ enum nl80211_band band;
struct regulatory_request request = {};
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
@@ -2315,7 +2355,7 @@ static void reg_process_self_managed_hints(void)
rcu_assign_pointer(wiphy->regd, regd);
rcu_free_regdom(tmp);
- for (band = 0; band < IEEE80211_NUM_BANDS; band++)
+ for (band = 0; band < NUM_NL80211_BANDS; band++)
handle_band_custom(wiphy, wiphy->bands[band], regd);
reg_process_ht_flags(wiphy);
@@ -2403,6 +2443,7 @@ int regulatory_hint_user(const char *alpha2,
return 0;
}
+EXPORT_SYMBOL(regulatory_hint_user);
int regulatory_hint_indoor(bool is_indoor, u32 portid)
{
@@ -2477,7 +2518,7 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2)
}
EXPORT_SYMBOL(regulatory_hint);
-void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band,
+void regulatory_hint_country_ie(struct wiphy *wiphy, enum nl80211_band band,
const u8 *country_ie, u8 country_ie_len)
{
char alpha2[2];
@@ -2577,11 +2618,11 @@ static void restore_alpha2(char *alpha2, bool reset_user)
static void restore_custom_reg_settings(struct wiphy *wiphy)
{
struct ieee80211_supported_band *sband;
- enum ieee80211_band band;
+ enum nl80211_band band;
struct ieee80211_channel *chan;
int i;
- for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
sband = wiphy->bands[band];
if (!sband)
continue;
@@ -2696,9 +2737,9 @@ void regulatory_hint_disconnect(void)
static bool freq_is_chan_12_13_14(u16 freq)
{
- if (freq == ieee80211_channel_to_frequency(12, IEEE80211_BAND_2GHZ) ||
- freq == ieee80211_channel_to_frequency(13, IEEE80211_BAND_2GHZ) ||
- freq == ieee80211_channel_to_frequency(14, IEEE80211_BAND_2GHZ))
+ if (freq == ieee80211_channel_to_frequency(12, NL80211_BAND_2GHZ) ||
+ freq == ieee80211_channel_to_frequency(13, NL80211_BAND_2GHZ) ||
+ freq == ieee80211_channel_to_frequency(14, NL80211_BAND_2GHZ))
return true;
return false;
}
@@ -2723,7 +2764,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
if (beacon_chan->beacon_found ||
beacon_chan->flags & IEEE80211_CHAN_RADAR ||
- (beacon_chan->band == IEEE80211_BAND_2GHZ &&
+ (beacon_chan->band == NL80211_BAND_2GHZ &&
!freq_is_chan_12_13_14(beacon_chan->center_freq)))
return 0;
@@ -3133,17 +3174,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);
}
@@ -3204,6 +3254,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;