diff options
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 216 |
1 files changed, 189 insertions, 27 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 7b8309840d4e..b586d0dcb09e 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -109,7 +109,7 @@ static struct regulatory_request core_request_world = { * protected by RTNL (and can be accessed with RCU protection) */ static struct regulatory_request __rcu *last_request = - (void __rcu *)&core_request_world; + (void __force __rcu *)&core_request_world; /* To trigger userspace events */ static struct platform_device *reg_pdev; @@ -142,7 +142,7 @@ static const struct ieee80211_regdomain *get_cfg80211_regdom(void) return rtnl_dereference(cfg80211_regdomain); } -static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) +const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) { return rtnl_dereference(wiphy->regd); } @@ -1307,6 +1307,9 @@ static bool ignore_reg_update(struct wiphy *wiphy, { struct regulatory_request *lr = get_last_request(); + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + return true; + if (!lr) { REG_DBG_PRINT("Ignoring regulatory request set by %s " "since last_request is not set\n", @@ -1530,45 +1533,40 @@ static void reg_call_notifier(struct wiphy *wiphy, static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) { - struct ieee80211_channel *ch; struct cfg80211_chan_def chandef; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); - bool ret = true; + enum nl80211_iftype iftype; wdev_lock(wdev); + iftype = wdev->iftype; + /* make sure the interface is active */ if (!wdev->netdev || !netif_running(wdev->netdev)) - goto out; + goto wdev_inactive_unlock; - switch (wdev->iftype) { + switch (iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: if (!wdev->beacon_interval) - goto out; - - ret = cfg80211_reg_can_beacon(wiphy, - &wdev->chandef, wdev->iftype); + goto wdev_inactive_unlock; + chandef = wdev->chandef; break; case NL80211_IFTYPE_ADHOC: if (!wdev->ssid_len) - goto out; - - ret = cfg80211_reg_can_beacon(wiphy, - &wdev->chandef, wdev->iftype); + goto wdev_inactive_unlock; + chandef = wdev->chandef; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: if (!wdev->current_bss || !wdev->current_bss->pub.channel) - goto out; + goto wdev_inactive_unlock; - ch = wdev->current_bss->pub.channel; - if (rdev->ops->get_channel && - !rdev_get_channel(rdev, wdev, &chandef)) - ret = cfg80211_chandef_usable(wiphy, &chandef, - IEEE80211_CHAN_DISABLED); - else - ret = !(ch->flags & IEEE80211_CHAN_DISABLED); + if (!rdev->ops->get_channel || + rdev_get_channel(rdev, wdev, &chandef)) + cfg80211_chandef_create(&chandef, + wdev->current_bss->pub.channel, + NL80211_CHAN_NO_HT); break; case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: @@ -1581,9 +1579,26 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) break; } -out: wdev_unlock(wdev); - return ret; + + switch (iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_ADHOC: + return cfg80211_reg_can_beacon(wiphy, &chandef, iftype); + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + return cfg80211_chandef_usable(wiphy, &chandef, + IEEE80211_CHAN_DISABLED); + default: + break; + } + + return true; + +wdev_inactive_unlock: + wdev_unlock(wdev); + return true; } static void reg_leave_invalid_chans(struct wiphy *wiphy) @@ -1683,8 +1698,12 @@ static void handle_channel_custom(struct wiphy *wiphy, if (IS_ERR(reg_rule)) { REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n", chan->center_freq); - chan->orig_flags |= IEEE80211_CHAN_DISABLED; - chan->flags = chan->orig_flags; + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) { + chan->flags |= IEEE80211_CHAN_DISABLED; + } else { + chan->orig_flags |= IEEE80211_CHAN_DISABLED; + chan->flags = chan->orig_flags; + } return; } @@ -1709,7 +1728,13 @@ static void handle_channel_custom(struct wiphy *wiphy, chan->dfs_state = NL80211_DFS_USABLE; chan->beacon_found = false; - chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + chan->flags = chan->orig_flags | bw_flags | + map_regdom_flags(reg_rule->flags); + else + chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags; + chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); chan->max_reg_power = chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); @@ -2095,6 +2120,26 @@ out_free: reg_free_request(reg_request); } +static bool reg_only_self_managed_wiphys(void) +{ + 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; + } + + /* make sure at least one self-managed wiphy exists */ + return self_managed_found; +} + /* * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* * Regulatory hints come on a first come first serve basis and we @@ -2126,6 +2171,11 @@ static void reg_process_pending_hints(void) spin_unlock(®_requests_lock); + if (reg_only_self_managed_wiphys()) { + reg_free_request(reg_request); + return; + } + reg_process_hint(reg_request); } @@ -2153,11 +2203,52 @@ static void reg_process_pending_beacon_hints(void) spin_unlock_bh(®_pending_beacons_lock); } +static void reg_process_self_managed_hints(void) +{ + struct cfg80211_registered_device *rdev; + struct wiphy *wiphy; + const struct ieee80211_regdomain *tmp; + const struct ieee80211_regdomain *regd; + enum ieee80211_band band; + struct regulatory_request request = {}; + + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + wiphy = &rdev->wiphy; + + spin_lock(®_requests_lock); + regd = rdev->requested_regd; + rdev->requested_regd = NULL; + spin_unlock(®_requests_lock); + + if (regd == NULL) + continue; + + tmp = get_wiphy_regdom(wiphy); + rcu_assign_pointer(wiphy->regd, regd); + rcu_free_regdom(tmp); + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) + handle_band_custom(wiphy, wiphy->bands[band], regd); + + reg_process_ht_flags(wiphy); + + request.wiphy_idx = get_wiphy_idx(wiphy); + request.alpha2[0] = regd->alpha2[0]; + request.alpha2[1] = regd->alpha2[1]; + request.initiator = NL80211_REGDOM_SET_BY_DRIVER; + + nl80211_send_wiphy_reg_change_event(&request); + } + + reg_check_channels(); +} + static void reg_todo(struct work_struct *work) { rtnl_lock(); reg_process_pending_hints(); reg_process_pending_beacon_hints(); + reg_process_self_managed_hints(); rtnl_unlock(); } @@ -2438,6 +2529,8 @@ static void restore_regulatory_settings(bool reset_user) world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + continue; if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) restore_custom_reg_settings(&rdev->wiphy); } @@ -2841,10 +2934,79 @@ int set_regdom(const struct ieee80211_regdomain *rd) return 0; } +static int __regulatory_set_wiphy_regd(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ + const struct ieee80211_regdomain *regd; + const struct ieee80211_regdomain *prev_regd; + struct cfg80211_registered_device *rdev; + + if (WARN_ON(!wiphy || !rd)) + return -EINVAL; + + if (WARN(!(wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED), + "wiphy should have REGULATORY_WIPHY_SELF_MANAGED\n")) + return -EPERM; + + if (WARN(!is_valid_rd(rd), "Invalid regulatory domain detected\n")) { + print_regdomain_info(rd); + return -EINVAL; + } + + regd = reg_copy_regd(rd); + if (IS_ERR(regd)) + return PTR_ERR(regd); + + rdev = wiphy_to_rdev(wiphy); + + spin_lock(®_requests_lock); + prev_regd = rdev->requested_regd; + rdev->requested_regd = regd; + spin_unlock(®_requests_lock); + + kfree(prev_regd); + return 0; +} + +int regulatory_set_wiphy_regd(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ + int ret = __regulatory_set_wiphy_regd(wiphy, rd); + + if (ret) + return ret; + + schedule_work(®_work); + return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd); + +int regulatory_set_wiphy_regd_sync_rtnl(struct wiphy *wiphy, + struct ieee80211_regdomain *rd) +{ + int ret; + + ASSERT_RTNL(); + + ret = __regulatory_set_wiphy_regd(wiphy, rd); + if (ret) + return ret; + + /* process the request immediately */ + reg_process_self_managed_hints(); + return 0; +} +EXPORT_SYMBOL(regulatory_set_wiphy_regd_sync_rtnl); + void wiphy_regulatory_register(struct wiphy *wiphy) { struct regulatory_request *lr; + /* self-managed devices ignore external hints */ + if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + if (!reg_dev_ignore_cell_hint(wiphy)) reg_num_devs_support_basehint++; |