Commit b23e7a9e authored by Luis R. Rodriguez's avatar Luis R. Rodriguez Committed by Johannes Berg

cfg80211: process country IE regulatory requests on their own

This is the last split up of the old unified __regultory_hint()
processing set of functionality, it moves the country IE processing
all on its own. This makes it easier to follow and read what exactly
is going on for the case of processing country IEs.
Signed-off-by: default avatarLuis R. Rodriguez <mcgrof@do-not-panic.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 21636c7f
...@@ -1322,62 +1322,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, ...@@ -1322,62 +1322,6 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
} }
EXPORT_SYMBOL(wiphy_apply_custom_regulatory); EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
/* This has the logic which determines when a new request
* should be ignored. */
static enum reg_request_treatment
get_reg_request_treatment(struct wiphy *wiphy,
struct regulatory_request *pending_request)
{
struct wiphy *last_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
/* All initial requests are respected */
if (!lr)
return REG_REQ_OK;
switch (pending_request->initiator) {
case NL80211_REGDOM_SET_BY_CORE:
case NL80211_REGDOM_SET_BY_USER:
case NL80211_REGDOM_SET_BY_DRIVER:
return REG_REQ_IGNORE;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
if (reg_request_cell_base(lr)) {
/* Trust a Cell base station over the AP's country IE */
if (regdom_changes(pending_request->alpha2))
return REG_REQ_IGNORE;
return REG_REQ_ALREADY_SET;
}
last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (unlikely(!is_an_alpha2(pending_request->alpha2)))
return -EINVAL;
if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
if (last_wiphy != wiphy) {
/*
* Two cards with two APs claiming different
* Country IE alpha2s. We could
* intersect them, but that seems unlikely
* to be correct. Reject second one for now.
*/
if (regdom_changes(pending_request->alpha2))
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 REG_REQ_OK;
return REG_REQ_ALREADY_SET;
}
return REG_REQ_OK;
}
return REG_REQ_IGNORE;
}
static void reg_set_request_processed(void) static void reg_set_request_processed(void)
{ {
bool need_more_processing = false; bool need_more_processing = false;
...@@ -1581,96 +1525,92 @@ reg_process_hint_driver(struct wiphy *wiphy, ...@@ -1581,96 +1525,92 @@ reg_process_hint_driver(struct wiphy *wiphy,
return REG_REQ_OK; return REG_REQ_OK;
} }
static enum reg_request_treatment
__reg_process_hint_country_ie(struct wiphy *wiphy,
struct regulatory_request *country_ie_request)
{
struct wiphy *last_wiphy = NULL;
struct regulatory_request *lr = get_last_request();
if (reg_request_cell_base(lr)) {
/* Trust a Cell base station over the AP's country IE */
if (regdom_changes(country_ie_request->alpha2))
return REG_REQ_IGNORE;
return REG_REQ_ALREADY_SET;
}
last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
return -EINVAL;
if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
if (last_wiphy != wiphy) {
/*
* Two cards with two APs claiming different
* Country IE alpha2s. We could
* intersect them, but that seems unlikely
* to be correct. Reject second one for now.
*/
if (regdom_changes(country_ie_request->alpha2))
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(country_ie_request->alpha2)))
return REG_REQ_OK;
return REG_REQ_ALREADY_SET;
}
return REG_REQ_OK;
}
/** /**
* __regulatory_hint - hint to the wireless core a regulatory domain * reg_process_hint_country_ie - process regulatory requests from country IEs
* @wiphy: if the hint comes from country information from an AP, this * @country_ie_request: a regulatory request from a country IE
* is required to be set to the wiphy that received the information
* @pending_request: the regulatory request currently being processed
* *
* The Wireless subsystem can use this function to hint to the wireless core * The wireless subsystem can use this function to process
* what it believes should be the current regulatory domain. * a regulatory request issued by a country Information Element.
* *
* Returns one of the different reg request treatment values. * Returns one of the different reg request treatment values.
*/ */
static enum reg_request_treatment static enum reg_request_treatment
__regulatory_hint(struct wiphy *wiphy, reg_process_hint_country_ie(struct wiphy *wiphy,
struct regulatory_request *pending_request) struct regulatory_request *country_ie_request)
{ {
const struct ieee80211_regdomain *regd;
bool intersect = false;
enum reg_request_treatment treatment; enum reg_request_treatment treatment;
struct regulatory_request *lr; struct regulatory_request *lr;
treatment = get_reg_request_treatment(wiphy, pending_request); treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
switch (treatment) { switch (treatment) {
case REG_REQ_INTERSECT:
if (pending_request->initiator ==
NL80211_REGDOM_SET_BY_DRIVER) {
regd = reg_copy_regd(get_cfg80211_regdom());
if (IS_ERR(regd)) {
kfree(pending_request);
return PTR_ERR(regd);
}
rcu_assign_pointer(wiphy->regd, regd);
}
intersect = true;
break;
case REG_REQ_OK: case REG_REQ_OK:
break; break;
default: case REG_REQ_IGNORE:
/* fall through */
case REG_REQ_ALREADY_SET:
kfree(country_ie_request);
return treatment;
case REG_REQ_INTERSECT:
kfree(country_ie_request);
/* /*
* If the regulatory domain being requested by the * This doesn't happen yet, not sure we
* driver has already been set just copy it to the * ever want to support it for this case.
* wiphy
*/ */
if (treatment == REG_REQ_ALREADY_SET && WARN_ONCE(1, "Unexpected intersection for country IEs");
pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { return REG_REQ_IGNORE;
regd = reg_copy_regd(get_cfg80211_regdom());
if (IS_ERR(regd)) {
kfree(pending_request);
return REG_REQ_IGNORE;
}
treatment = REG_REQ_ALREADY_SET;
rcu_assign_pointer(wiphy->regd, regd);
goto new_request;
}
kfree(pending_request);
return treatment;
} }
new_request:
lr = get_last_request(); lr = get_last_request();
if (lr != &core_request_world && lr) if (lr != &core_request_world && lr)
kfree_rcu(lr, rcu_head); kfree_rcu(lr, rcu_head);
pending_request->intersect = intersect; country_ie_request->intersect = false;
pending_request->processed = false; country_ie_request->processed = false;
rcu_assign_pointer(last_request, pending_request); rcu_assign_pointer(last_request, country_ie_request);
lr = pending_request;
pending_request = NULL;
if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { if (call_crda(country_ie_request->alpha2))
user_alpha2[0] = lr->alpha2[0];
user_alpha2[1] = lr->alpha2[1];
}
/* 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 (treatment == REG_REQ_ALREADY_SET) {
nl80211_send_reg_change_event(lr);
reg_set_request_processed();
}
return treatment;
}
if (call_crda(lr->alpha2))
return REG_REQ_IGNORE; return REG_REQ_IGNORE;
return REG_REQ_OK; return REG_REQ_OK;
} }
...@@ -1707,22 +1647,17 @@ static void reg_process_hint(struct regulatory_request *reg_request) ...@@ -1707,22 +1647,17 @@ static void reg_process_hint(struct regulatory_request *reg_request)
treatment = reg_process_hint_driver(wiphy, reg_request); treatment = reg_process_hint_driver(wiphy, reg_request);
break; break;
case NL80211_REGDOM_SET_BY_COUNTRY_IE: case NL80211_REGDOM_SET_BY_COUNTRY_IE:
treatment = __regulatory_hint(wiphy, reg_request); treatment = reg_process_hint_country_ie(wiphy, reg_request);
break; break;
default: default:
WARN(1, "invalid initiator %d\n", reg_request->initiator); WARN(1, "invalid initiator %d\n", reg_request->initiator);
return; return;
} }
switch (treatment) { /* This is required so that the orig_* parameters are saved */
case REG_REQ_ALREADY_SET: if (treatment == REG_REQ_ALREADY_SET && wiphy &&
/* This is required so that the orig_* parameters are saved */ wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) wiphy_update_regulatory(wiphy, reg_request->initiator);
wiphy_update_regulatory(wiphy, reg_request->initiator);
break;
default:
break;
}
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment