|
@@ -2232,38 +2232,19 @@ static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/* Takes ownership of rd only if it doesn't fail */
|
|
|
-static int __set_regdom(const struct ieee80211_regdomain *rd,
|
|
|
- struct regulatory_request *lr)
|
|
|
+static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
|
|
|
+ struct regulatory_request *driver_request)
|
|
|
{
|
|
|
const struct ieee80211_regdomain *regd;
|
|
|
const struct ieee80211_regdomain *intersected_rd = NULL;
|
|
|
+ const struct ieee80211_regdomain *tmp;
|
|
|
struct wiphy *request_wiphy;
|
|
|
|
|
|
- if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
|
|
|
- !is_unknown_alpha2(rd->alpha2))
|
|
|
+ if (is_world_regdom(rd->alpha2))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- /*
|
|
|
- * Lets only bother proceeding on the same alpha2 if the current
|
|
|
- * rd is non static (it means CRDA was present and was used last)
|
|
|
- * and the pending request came in from a country IE
|
|
|
- */
|
|
|
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
|
|
|
- /*
|
|
|
- * If someone else asked us to change the rd lets only bother
|
|
|
- * checking if the alpha2 changes if CRDA was already called
|
|
|
- */
|
|
|
- if (!regdom_changes(rd->alpha2))
|
|
|
- return -EALREADY;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * Now lets set the regulatory domain, update all driver channels
|
|
|
- * and finally inform them of what we have done, in case they want
|
|
|
- * to review or adjust their own settings based on their own
|
|
|
- * internal EEPROM data
|
|
|
- */
|
|
|
+ if (!regdom_changes(rd->alpha2))
|
|
|
+ return -EALREADY;
|
|
|
|
|
|
if (!is_valid_rd(rd)) {
|
|
|
pr_err("Invalid regulatory domain detected:\n");
|
|
@@ -2271,29 +2252,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd,
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
|
|
|
- if (!request_wiphy &&
|
|
|
- (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
|
|
|
- lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
|
|
|
+ request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
|
|
|
+ if (!request_wiphy) {
|
|
|
schedule_delayed_work(®_timeout, 0);
|
|
|
return -ENODEV;
|
|
|
}
|
|
|
|
|
|
- if (!lr->intersect) {
|
|
|
- if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
|
|
|
- reset_regdomains(false, rd);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * For a driver hint, lets copy the regulatory domain the
|
|
|
- * driver wanted to the wiphy to deal with conflicts
|
|
|
- */
|
|
|
-
|
|
|
- /*
|
|
|
- * Userspace could have sent two replies with only
|
|
|
- * one kernel request.
|
|
|
- */
|
|
|
+ if (!driver_request->intersect) {
|
|
|
if (request_wiphy->regd)
|
|
|
return -EALREADY;
|
|
|
|
|
@@ -2306,38 +2271,60 @@ static int __set_regdom(const struct ieee80211_regdomain *rd,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- /* Intersection requires a bit more work */
|
|
|
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
|
|
|
+ if (!intersected_rd)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
|
|
|
- intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
|
|
|
- if (!intersected_rd)
|
|
|
- return -EINVAL;
|
|
|
+ /*
|
|
|
+ * We can trash what CRDA provided now.
|
|
|
+ * However if a driver requested this specific regulatory
|
|
|
+ * domain we keep it for its private use
|
|
|
+ */
|
|
|
+ tmp = get_wiphy_regdom(request_wiphy);
|
|
|
+ rcu_assign_pointer(request_wiphy->regd, rd);
|
|
|
+ rcu_free_regdom(tmp);
|
|
|
|
|
|
- /*
|
|
|
- * We can trash what CRDA provided now.
|
|
|
- * However if a driver requested this specific regulatory
|
|
|
- * domain we keep it for its private use
|
|
|
- */
|
|
|
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
|
|
|
- const struct ieee80211_regdomain *tmp;
|
|
|
+ rd = NULL;
|
|
|
|
|
|
- tmp = get_wiphy_regdom(request_wiphy);
|
|
|
- rcu_assign_pointer(request_wiphy->regd, rd);
|
|
|
- rcu_free_regdom(tmp);
|
|
|
- } else {
|
|
|
- kfree(rd);
|
|
|
- }
|
|
|
+ reset_regdomains(false, intersected_rd);
|
|
|
|
|
|
- rd = NULL;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Takes ownership of rd only if it doesn't fail */
|
|
|
+static int __set_regdom(const struct ieee80211_regdomain *rd,
|
|
|
+ struct regulatory_request *lr)
|
|
|
+{
|
|
|
+ struct wiphy *request_wiphy;
|
|
|
|
|
|
- reset_regdomains(false, intersected_rd);
|
|
|
+ if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
|
|
|
+ !is_unknown_alpha2(rd->alpha2))
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- return 0;
|
|
|
+ /*
|
|
|
+ * Lets only bother proceeding on the same alpha2 if the current
|
|
|
+ * rd is non static (it means CRDA was present and was used last)
|
|
|
+ * and the pending request came in from a country IE
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!is_valid_rd(rd)) {
|
|
|
+ pr_err("Invalid regulatory domain detected:\n");
|
|
|
+ print_regdomain_info(rd);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- return -EINVAL;
|
|
|
-}
|
|
|
+ request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
|
|
|
+ if (!request_wiphy) {
|
|
|
+ schedule_delayed_work(®_timeout, 0);
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
+ if (lr->intersect)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ reset_regdomains(false, rd);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
* Use this call to set the current regulatory domain. Conflicts with
|
|
@@ -2365,6 +2352,8 @@ int set_regdom(const struct ieee80211_regdomain *rd)
|
|
|
r = reg_set_rd_user(rd, lr);
|
|
|
break;
|
|
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
|
|
+ r = reg_set_rd_driver(rd, lr);
|
|
|
+ break;
|
|
|
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
|
|
|
r = __set_regdom(rd, lr);
|
|
|
break;
|