|
@@ -65,6 +65,13 @@
|
|
|
#define REG_DBG_PRINT(args...)
|
|
|
#endif
|
|
|
|
|
|
+enum reg_request_treatment {
|
|
|
+ REG_REQ_OK,
|
|
|
+ REG_REQ_IGNORE,
|
|
|
+ REG_REQ_INTERSECT,
|
|
|
+ REG_REQ_ALREADY_SET,
|
|
|
+};
|
|
|
+
|
|
|
static struct regulatory_request core_request_world = {
|
|
|
.initiator = NL80211_REGDOM_SET_BY_CORE,
|
|
|
.alpha2[0] = '0',
|
|
@@ -925,16 +932,17 @@ bool reg_last_request_cell_base(void)
|
|
|
|
|
|
#ifdef CONFIG_CFG80211_CERTIFICATION_ONUS
|
|
|
/* Core specific check */
|
|
|
-static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
|
|
|
+static enum reg_request_treatment
|
|
|
+reg_ignore_cell_hint(struct regulatory_request *pending_request)
|
|
|
{
|
|
|
if (!reg_num_devs_support_basehint)
|
|
|
- return -EOPNOTSUPP;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
|
|
|
if (reg_request_cell_base(last_request) &&
|
|
|
!regdom_changes(pending_request->alpha2))
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
|
|
|
- return 0;
|
|
|
+ return REG_REQ_OK;
|
|
|
}
|
|
|
|
|
|
/* Device specific check */
|
|
@@ -945,7 +953,7 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
|
|
|
#else
|
|
|
static int reg_ignore_cell_hint(struct regulatory_request *pending_request)
|
|
|
{
|
|
|
- return -EOPNOTSUPP;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
}
|
|
|
|
|
|
static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
|
|
@@ -1308,15 +1316,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
|
|
|
}
|
|
|
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
|
|
|
|
|
|
-/*
|
|
|
- * Return value which can be used by ignore_request() to indicate
|
|
|
- * it has been determined we should intersect two regulatory domains
|
|
|
- */
|
|
|
-#define REG_INTERSECT 1
|
|
|
-
|
|
|
/* This has the logic which determines when a new request
|
|
|
* should be ignored. */
|
|
|
-static int ignore_request(struct wiphy *wiphy,
|
|
|
+static enum reg_request_treatment
|
|
|
+get_reg_request_treatment(struct wiphy *wiphy,
|
|
|
struct regulatory_request *pending_request)
|
|
|
{
|
|
|
struct wiphy *last_wiphy = NULL;
|
|
@@ -1325,17 +1328,17 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
|
|
|
/* All initial requests are respected */
|
|
|
if (!last_request)
|
|
|
- return 0;
|
|
|
+ return REG_REQ_OK;
|
|
|
|
|
|
switch (pending_request->initiator) {
|
|
|
case NL80211_REGDOM_SET_BY_CORE:
|
|
|
- return 0;
|
|
|
+ return REG_REQ_OK;
|
|
|
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;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
}
|
|
|
|
|
|
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
|
|
@@ -1352,23 +1355,23 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
* to be correct. Reject second one for now.
|
|
|
*/
|
|
|
if (regdom_changes(pending_request->alpha2))
|
|
|
- return -EOPNOTSUPP;
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
}
|
|
|
/*
|
|
|
* Two consecutive Country IE hints on the same wiphy.
|
|
|
* This should be picked up early by the driver/stack
|
|
|
*/
|
|
|
if (WARN_ON(regdom_changes(pending_request->alpha2)))
|
|
|
- return 0;
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_OK;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
}
|
|
|
return 0;
|
|
|
case NL80211_REGDOM_SET_BY_DRIVER:
|
|
|
if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) {
|
|
|
if (regdom_changes(pending_request->alpha2))
|
|
|
- return 0;
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_OK;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1378,25 +1381,25 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
*/
|
|
|
if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
|
|
|
!regdom_changes(pending_request->alpha2))
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
|
|
|
- return REG_INTERSECT;
|
|
|
+ return REG_REQ_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;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
|
|
|
if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
|
|
|
- return REG_INTERSECT;
|
|
|
+ return REG_REQ_INTERSECT;
|
|
|
/*
|
|
|
* If the user knows better the user should set the regdom
|
|
|
* to their country before the IE is picked up
|
|
|
*/
|
|
|
if (last_request->initiator == NL80211_REGDOM_SET_BY_USER &&
|
|
|
last_request->intersect)
|
|
|
- return -EOPNOTSUPP;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
/*
|
|
|
* Process user requests only after previous user/driver/core
|
|
|
* requests have been processed
|
|
@@ -1405,15 +1408,15 @@ static int ignore_request(struct wiphy *wiphy,
|
|
|
last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
|
|
|
last_request->initiator == NL80211_REGDOM_SET_BY_USER) &&
|
|
|
regdom_changes(last_request->alpha2))
|
|
|
- return -EAGAIN;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
|
|
|
if (!regdom_changes(pending_request->alpha2))
|
|
|
- return -EALREADY;
|
|
|
+ return REG_REQ_ALREADY_SET;
|
|
|
|
|
|
- return 0;
|
|
|
+ return REG_REQ_OK;
|
|
|
}
|
|
|
|
|
|
- return -EINVAL;
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
}
|
|
|
|
|
|
static void reg_set_request_processed(void)
|
|
@@ -1443,23 +1446,24 @@ static void reg_set_request_processed(void)
|
|
|
* The Wireless subsystem can use this function to hint to the wireless core
|
|
|
* what it believes should be the current regulatory domain.
|
|
|
*
|
|
|
- * Returns zero if all went fine, %-EALREADY if a regulatory domain had
|
|
|
- * already been set or other standard error codes.
|
|
|
+ * Returns one of the different reg request treatment values.
|
|
|
*
|
|
|
* Caller must hold &cfg80211_mutex and ®_mutex
|
|
|
*/
|
|
|
-static int __regulatory_hint(struct wiphy *wiphy,
|
|
|
- struct regulatory_request *pending_request)
|
|
|
+static enum reg_request_treatment
|
|
|
+__regulatory_hint(struct wiphy *wiphy,
|
|
|
+ struct regulatory_request *pending_request)
|
|
|
{
|
|
|
const struct ieee80211_regdomain *regd;
|
|
|
bool intersect = false;
|
|
|
- int r = 0;
|
|
|
+ enum reg_request_treatment treatment;
|
|
|
|
|
|
assert_cfg80211_lock();
|
|
|
|
|
|
- r = ignore_request(wiphy, pending_request);
|
|
|
+ treatment = get_reg_request_treatment(wiphy, pending_request);
|
|
|
|
|
|
- if (r == REG_INTERSECT) {
|
|
|
+ switch (treatment) {
|
|
|
+ case REG_REQ_INTERSECT:
|
|
|
if (pending_request->initiator ==
|
|
|
NL80211_REGDOM_SET_BY_DRIVER) {
|
|
|
regd = reg_copy_regd(cfg80211_regdomain);
|
|
@@ -1470,26 +1474,28 @@ static int __regulatory_hint(struct wiphy *wiphy,
|
|
|
wiphy->regd = regd;
|
|
|
}
|
|
|
intersect = true;
|
|
|
- } else if (r) {
|
|
|
+ break;
|
|
|
+ case REG_REQ_OK:
|
|
|
+ break;
|
|
|
+ default:
|
|
|
/*
|
|
|
* If the regulatory domain being requested by the
|
|
|
* driver has already been set just copy it to the
|
|
|
* wiphy
|
|
|
*/
|
|
|
- if (r == -EALREADY &&
|
|
|
- pending_request->initiator ==
|
|
|
- NL80211_REGDOM_SET_BY_DRIVER) {
|
|
|
+ if (treatment == REG_REQ_ALREADY_SET &&
|
|
|
+ pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
|
|
|
regd = reg_copy_regd(cfg80211_regdomain);
|
|
|
if (IS_ERR(regd)) {
|
|
|
kfree(pending_request);
|
|
|
- return PTR_ERR(regd);
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
}
|
|
|
- r = -EALREADY;
|
|
|
+ treatment = REG_REQ_ALREADY_SET;
|
|
|
wiphy->regd = regd;
|
|
|
goto new_request;
|
|
|
}
|
|
|
kfree(pending_request);
|
|
|
- return r;
|
|
|
+ return treatment;
|
|
|
}
|
|
|
|
|
|
new_request:
|
|
@@ -1506,28 +1512,29 @@ new_request:
|
|
|
user_alpha2[1] = last_request->alpha2[1];
|
|
|
}
|
|
|
|
|
|
- /* When r == REG_INTERSECT we do need to call CRDA */
|
|
|
- if (r < 0) {
|
|
|
+ /* When r == REG_REQ_INTERSECT we do need to call CRDA */
|
|
|
+ if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
|
|
|
/*
|
|
|
* Since CRDA will not be called in this case as we already
|
|
|
* have applied the requested regulatory domain before we just
|
|
|
* inform userspace we have processed the request
|
|
|
*/
|
|
|
- if (r == -EALREADY) {
|
|
|
+ if (treatment == REG_REQ_ALREADY_SET) {
|
|
|
nl80211_send_reg_change_event(last_request);
|
|
|
reg_set_request_processed();
|
|
|
}
|
|
|
- return r;
|
|
|
+ return treatment;
|
|
|
}
|
|
|
|
|
|
- return call_crda(last_request->alpha2);
|
|
|
+ if (call_crda(last_request->alpha2))
|
|
|
+ return REG_REQ_IGNORE;
|
|
|
+ return REG_REQ_OK;
|
|
|
}
|
|
|
|
|
|
/* This processes *all* regulatory hints */
|
|
|
static void reg_process_hint(struct regulatory_request *reg_request,
|
|
|
enum nl80211_reg_initiator reg_initiator)
|
|
|
{
|
|
|
- int r = 0;
|
|
|
struct wiphy *wiphy = NULL;
|
|
|
|
|
|
BUG_ON(!reg_request->alpha2);
|
|
@@ -1540,20 +1547,18 @@ static void reg_process_hint(struct regulatory_request *reg_request,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- r = __regulatory_hint(wiphy, reg_request);
|
|
|
- /* This is required so that the orig_* parameters are saved */
|
|
|
- if (r == -EALREADY && wiphy &&
|
|
|
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
|
|
|
- wiphy_update_regulatory(wiphy, reg_initiator);
|
|
|
- return;
|
|
|
+ switch (__regulatory_hint(wiphy, reg_request)) {
|
|
|
+ case REG_REQ_ALREADY_SET:
|
|
|
+ /* This is required so that the orig_* parameters are saved */
|
|
|
+ if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
|
|
|
+ wiphy_update_regulatory(wiphy, reg_initiator);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
|
|
|
+ schedule_delayed_work(®_timeout,
|
|
|
+ msecs_to_jiffies(3142));
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- * We only time out user hints, given that they should be the only
|
|
|
- * source of bogus requests.
|
|
|
- */
|
|
|
- if (r != -EALREADY && reg_initiator == NL80211_REGDOM_SET_BY_USER)
|
|
|
- schedule_delayed_work(®_timeout, msecs_to_jiffies(3142));
|
|
|
}
|
|
|
|
|
|
/*
|