|
@@ -97,9 +97,16 @@ const struct ieee80211_regdomain *cfg80211_regdomain;
|
|
|
* - cfg80211_world_regdom
|
|
|
* - cfg80211_regdom
|
|
|
* - last_request
|
|
|
+ * - reg_num_devs_support_basehint
|
|
|
*/
|
|
|
static DEFINE_MUTEX(reg_mutex);
|
|
|
|
|
|
+/*
|
|
|
+ * Number of devices that registered to the core
|
|
|
+ * that support cellular base station regulatory hints
|
|
|
+ */
|
|
|
+static int reg_num_devs_support_basehint;
|
|
|
+
|
|
|
static inline void assert_reg_lock(void)
|
|
|
{
|
|
|
lockdep_assert_held(®_mutex);
|
|
@@ -911,6 +918,59 @@ static void handle_band(struct wiphy *wiphy,
|
|
|
handle_channel(wiphy, initiator, band, i);
|
|
|
}
|
|
|
|
|
|
+static bool reg_request_cell_base(struct regulatory_request *request)
|
|
|
+{
|
|
|
+ if (request->initiator != NL80211_REGDOM_SET_BY_USER)
|
|
|
+ return false;
|
|
|
+ if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE)
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool reg_last_request_cell_base(void)
|
|
|
+{
|
|
|
+ assert_cfg80211_lock();
|
|
|
+
|
|
|
+ mutex_lock(®_mutex);
|
|
|
+ return reg_request_cell_base(last_request);
|
|
|
+ mutex_unlock(®_mutex);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
|
|
|
+
|
|
|
+/* Core specific check */
|
|
|
+static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
|
|
|
+{
|
|
|
+ if (!reg_num_devs_support_basehint)
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
+ if (reg_request_cell_base(last_request)) {
|
|
|
+ if (!regdom_changes(pending_request->alpha2))
|
|
|
+ return -EALREADY;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* Device specific check */
|
|
|
+static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
|
|
|
+{
|
|
|
+ if (!(wiphy->features & NL80211_FEATURE_CELL_BASE_REG_HINTS))
|
|
|
+ return true;
|
|
|
+ return false;
|
|
|
+}
|
|
|
+#else
|
|
|
+static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
|
|
|
+{
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+}
|
|
|
+static int reg_dev_ignore_cell_hint(struct wiphy *wiphy)
|
|
|
+{
|
|
|
+ return true;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
static bool ignore_reg_update(struct wiphy *wiphy,
|
|
|
enum nl80211_reg_initiator initiator)
|
|
|
{
|
|
@@ -944,6 +1004,9 @@ static bool ignore_reg_update(struct wiphy *wiphy,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ if (reg_request_cell_base(last_request))
|
|
|
+ return reg_dev_ignore_cell_hint(wiphy);
|
|
|
+
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1307,6 +1370,13 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
return 0;
|
|
|
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
|
|
|
|
|
|
+ if (reg_request_cell_base(last_request)) {
|
|
|
+ /* Trust a Cell base station over the AP's country IE */
|
|
|
+ if (regdom_changes(pending_request->alpha2))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ return -EALREADY;
|
|
|
+ }
|
|
|
+
|
|
|
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
|
|
|
|
|
|
if (unlikely(!is_an_alpha2(pending_request->alpha2)))
|
|
@@ -1351,6 +1421,12 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
|
|
|
return REG_INTERSECT;
|
|
|
case NL80211_REGDOM_SET_BY_USER:
|
|
|
+ if (reg_request_cell_base(pending_request))
|
|
|
+ return reg_ignore_cell_hint(pending_request);
|
|
|
+
|
|
|
+ if (reg_request_cell_base(last_request))
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+
|
|
|
if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
|
|
|
return REG_INTERSECT;
|
|
|
/*
|
|
@@ -1640,7 +1716,8 @@ static int regulatory_hint_core(const char *alpha2)
|
|
|
}
|
|
|
|
|
|
/* User hints */
|
|
|
-int regulatory_hint_user(const char *alpha2)
|
|
|
+int regulatory_hint_user(const char *alpha2,
|
|
|
+ enum nl80211_user_reg_hint_type user_reg_hint_type)
|
|
|
{
|
|
|
struct regulatory_request *request;
|
|
|
|
|
@@ -1654,6 +1731,7 @@ int regulatory_hint_user(const char *alpha2)
|
|
|
request->alpha2[0] = alpha2[0];
|
|
|
request->alpha2[1] = alpha2[1];
|
|
|
request->initiator = NL80211_REGDOM_SET_BY_USER;
|
|
|
+ request->user_reg_hint_type = user_reg_hint_type;
|
|
|
|
|
|
queue_regulatory_request(request);
|
|
|
|
|
@@ -1906,7 +1984,7 @@ static void restore_regulatory_settings(bool reset_user)
|
|
|
* settings, user regulatory settings takes precedence.
|
|
|
*/
|
|
|
if (is_an_alpha2(alpha2))
|
|
|
- regulatory_hint_user(user_alpha2);
|
|
|
+ regulatory_hint_user(user_alpha2, NL80211_USER_REG_HINT_USER);
|
|
|
|
|
|
if (list_empty(&tmp_reg_req_list))
|
|
|
return;
|
|
@@ -2081,9 +2159,16 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
|
|
|
else {
|
|
|
if (is_unknown_alpha2(rd->alpha2))
|
|
|
pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n");
|
|
|
- else
|
|
|
- pr_info("Regulatory domain changed to country: %c%c\n",
|
|
|
- rd->alpha2[0], rd->alpha2[1]);
|
|
|
+ else {
|
|
|
+ if (reg_request_cell_base(last_request))
|
|
|
+ pr_info("Regulatory domain changed "
|
|
|
+ "to country: %c%c by Cell Station\n",
|
|
|
+ rd->alpha2[0], rd->alpha2[1]);
|
|
|
+ else
|
|
|
+ pr_info("Regulatory domain changed "
|
|
|
+ "to country: %c%c\n",
|
|
|
+ rd->alpha2[0], rd->alpha2[1]);
|
|
|
+ }
|
|
|
}
|
|
|
print_dfs_region(rd->dfs_region);
|
|
|
print_rd_rules(rd);
|
|
@@ -2293,6 +2378,18 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|
|
}
|
|
|
#endif /* CONFIG_HOTPLUG */
|
|
|
|
|
|
+void wiphy_regulatory_register(struct wiphy *wiphy)
|
|
|
+{
|
|
|
+ assert_cfg80211_lock();
|
|
|
+
|
|
|
+ mutex_lock(®_mutex);
|
|
|
+
|
|
|
+ if (!reg_dev_ignore_cell_hint(wiphy))
|
|
|
+ reg_num_devs_support_basehint++;
|
|
|
+
|
|
|
+ mutex_unlock(®_mutex);
|
|
|
+}
|
|
|
+
|
|
|
/* Caller must hold cfg80211_mutex */
|
|
|
void reg_device_remove(struct wiphy *wiphy)
|
|
|
{
|
|
@@ -2302,6 +2399,9 @@ void reg_device_remove(struct wiphy *wiphy)
|
|
|
|
|
|
mutex_lock(®_mutex);
|
|
|
|
|
|
+ if (!reg_dev_ignore_cell_hint(wiphy))
|
|
|
+ reg_num_devs_support_basehint--;
|
|
|
+
|
|
|
kfree(wiphy->regd);
|
|
|
|
|
|
if (last_request)
|
|
@@ -2367,7 +2467,8 @@ int __init regulatory_init(void)
|
|
|
* as a user hint.
|
|
|
*/
|
|
|
if (!is_world_regdom(ieee80211_regdom))
|
|
|
- regulatory_hint_user(ieee80211_regdom);
|
|
|
+ regulatory_hint_user(ieee80211_regdom,
|
|
|
+ NL80211_USER_REG_HINT_USER);
|
|
|
|
|
|
return 0;
|
|
|
}
|