diff mbox

[3/7] cfg80211: allow drivers to provide regulatory settings

Message ID 1399798250-20987-4-git-send-email-emmanuel.grumbach@intel.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Emmanuel Grumbach May 11, 2014, 8:50 a.m. UTC
From: Arik Nemtsov <arik@wizery.com>

Define a new wiphy callback allowing drivers to provide regulatory
settings.

Only The first wiphy registered with this callback will be able to provide
regulatory domain info. If such a wiphy exists, it takes precedence over
other data sources.

Change-Id: I7c7e368e200c1414b53e3a86e131de24adc62b93
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
---
 include/net/cfg80211.h | 17 +++++++++++++
 net/wireless/reg.c     | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 76 insertions(+), 6 deletions(-)

Comments

Luis R. Rodriguez May 20, 2014, 8:44 a.m. UTC | #1
On Sun, May 11, 2014 at 1:50 AM, Emmanuel Grumbach
<emmanuel.grumbach@intel.com> wrote:
> From: Arik Nemtsov <arik@wizery.com>
>
> Define a new wiphy callback allowing drivers to provide regulatory
> settings.
>
> Only The first wiphy registered with this callback will be able to provide
> regulatory domain info. If such a wiphy exists, it takes precedence over
> other data sources.
>
> Change-Id: I7c7e368e200c1414b53e3a86e131de24adc62b93
> Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
> ---
>  include/net/cfg80211.h | 17 +++++++++++++
>  net/wireless/reg.c     | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
>  2 files changed, 76 insertions(+), 6 deletions(-)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index f2c3186..3c96b62 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -3008,6 +3008,23 @@ struct wiphy {
>         void (*reg_notifier)(struct wiphy *wiphy,
>                              struct regulatory_request *request);
>
> +       /*
> +        * Indicates this wiphy can provide regulatory information.
> +        * Must be set before the wiphy is registered. Only the first
> +        * wiphy with this callback will be called to provide a regdomain
> +        * on country-code changes. The alpha2 in the returned regdomain
> +        * information can be different from the one given via argument,
> +        * if the argument contains the "99" alpha2, meaning unknown.
> +        * If an ERR_PTR is returned the regulatory core will consult other
> +        * sources for the regdomain info (internal regdb and CRDA).
> +        * Returning NULL will cause the regdomain to remain the same.
> +        * The callee will return a struct allocated with kmalloc(). After
> +        * the struct is returned, the regulatory core is responsible
> +        * for freeing it.
> +        */

Use kdoc instead of this long documentation blob.

> +       struct ieee80211_regdomain * (*get_regd)(struct wiphy *wiphy,
> +                                                const char *alpha2);
> +
>         /* fields below are read-only, assigned by cfg80211 */
>
>         const struct ieee80211_regdomain __rcu *regd;

The driver should be able to dump a regdomain it needs when it needs
it and use the internal callbacks wiphy_apply_custom_regulatory()
which tons of drivers already use when it needs to upon
initialization. As for country IE data you can ignore country IEs,
there's a flag for this, and do what you want on firmware just as that
crappy non-upstream vendor qualcomm driver does. Apart from that its
unclear in this patch why instead any delta observed on wireless-regdb
is addressed publicly I'd like for this to be considered as a sane
alternative. Additionally keeping regulatory data in firmware is very
bug prone and it also doesn't let us grow mature the architecture on
cfg80211, Intel's firmware has historically only had a few world
regulatory domains, and historically this is why not much
contributions from Intel have gone into wireless-regdb, if there is a
need to support specific countries now on firmware I'd encourage the
firmware bloat strategy to be seriously reconsidered in light of the
issues that could arise.

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arik Nemtsov May 20, 2014, 9:12 a.m. UTC | #2
On Tue, May 20, 2014 at 11:44 AM, Luis R. Rodriguez
<mcgrof@do-not-panic.com> wrote:
> On Sun, May 11, 2014 at 1:50 AM, Emmanuel Grumbach
> <emmanuel.grumbach@intel.com> wrote:
>> From: Arik Nemtsov <arik@wizery.com>
>>
>> Define a new wiphy callback allowing drivers to provide regulatory
>> settings.
>>
>> Only The first wiphy registered with this callback will be able to provide
>> regulatory domain info. If such a wiphy exists, it takes precedence over
>> other data sources.
>>
>> Change-Id: I7c7e368e200c1414b53e3a86e131de24adc62b93
>> Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
>> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
>> ---
>>  include/net/cfg80211.h | 17 +++++++++++++
>>  net/wireless/reg.c     | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
>>  2 files changed, 76 insertions(+), 6 deletions(-)
>>
>> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
>> index f2c3186..3c96b62 100644
>> --- a/include/net/cfg80211.h
>> +++ b/include/net/cfg80211.h
>> @@ -3008,6 +3008,23 @@ struct wiphy {
>>         void (*reg_notifier)(struct wiphy *wiphy,
>>                              struct regulatory_request *request);
>>
>> +       /*
>> +        * Indicates this wiphy can provide regulatory information.
>> +        * Must be set before the wiphy is registered. Only the first
>> +        * wiphy with this callback will be called to provide a regdomain
>> +        * on country-code changes. The alpha2 in the returned regdomain
>> +        * information can be different from the one given via argument,
>> +        * if the argument contains the "99" alpha2, meaning unknown.
>> +        * If an ERR_PTR is returned the regulatory core will consult other
>> +        * sources for the regdomain info (internal regdb and CRDA).
>> +        * Returning NULL will cause the regdomain to remain the same.
>> +        * The callee will return a struct allocated with kmalloc(). After
>> +        * the struct is returned, the regulatory core is responsible
>> +        * for freeing it.
>> +        */
>
> Use kdoc instead of this long documentation blob.

Sure.

>
>> +       struct ieee80211_regdomain * (*get_regd)(struct wiphy *wiphy,
>> +                                                const char *alpha2);
>> +
>>         /* fields below are read-only, assigned by cfg80211 */
>>
>>         const struct ieee80211_regdomain __rcu *regd;
>
> The driver should be able to dump a regdomain it needs when it needs
> it and use the internal callbacks wiphy_apply_custom_regulatory()
> which tons of drivers already use when it needs to upon
> initialization. As for country IE data you can ignore country IEs,
> there's a flag for this, and do what you want on firmware just as that
> crappy non-upstream vendor qualcomm driver does. Apart from that its
> unclear in this patch why instead any delta observed on wireless-regdb
> is addressed publicly I'd like for this to be considered as a sane
> alternative. Additionally keeping regulatory data in firmware is very
> bug prone and it also doesn't let us grow mature the architecture on
> cfg80211, Intel's firmware has historically only had a few world
> regulatory domains, and historically this is why not much
> contributions from Intel have gone into wireless-regdb, if there is a
> need to support specific countries now on firmware I'd encourage the
> firmware bloat strategy to be seriously reconsidered in light of the
> issues that could arise.

The wiphy_apply_custom_regulatory() option is to be used before
registering the wiphy. We want to be able to accept country code
changes at runtime, with the driver supplying the regdomain.

As for why this was chosen - I think you're barking up the wrong tree :)
The regulatory folks at Intel decided to store the data in FW, I don't
have any say here. I think this is more legal than technology reasons.

Would this patch be acceptable with the documentation changed to kdoc format?
Again, this is a different way of doing things, but it's largely
compatible to the way reg.c already works.

Arik
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Luis R. Rodriguez May 20, 2014, 9:53 a.m. UTC | #3
On Tue, May 20, 2014 at 2:12 AM, Arik Nemtsov <arik@wizery.com> wrote:
> On Tue, May 20, 2014 at 11:44 AM, Luis R. Rodriguez
> <mcgrof@do-not-panic.com> wrote:
>> On Sun, May 11, 2014 at 1:50 AM, Emmanuel Grumbach
>> <emmanuel.grumbach@intel.com> wrote:
>>> From: Arik Nemtsov <arik@wizery.com>
>>> +       struct ieee80211_regdomain * (*get_regd)(struct wiphy *wiphy,
>>> +                                                const char *alpha2);
>>> +
>>>         /* fields below are read-only, assigned by cfg80211 */
>>>
>>>         const struct ieee80211_regdomain __rcu *regd;
>>
>> The driver should be able to dump a regdomain it needs when it needs
>> it and use the internal callbacks wiphy_apply_custom_regulatory()
>> which tons of drivers already use when it needs to upon
>> initialization. As for country IE data you can ignore country IEs,
>> there's a flag for this, and do what you want on firmware just as that
>> crappy non-upstream vendor qualcomm driver does. Apart from that its
>> unclear in this patch why instead any delta observed on wireless-regdb
>> is addressed publicly I'd like for this to be considered as a sane
>> alternative. Additionally keeping regulatory data in firmware is very
>> bug prone and it also doesn't let us grow mature the architecture on
>> cfg80211, Intel's firmware has historically only had a few world
>> regulatory domains, and historically this is why not much
>> contributions from Intel have gone into wireless-regdb, if there is a
>> need to support specific countries now on firmware I'd encourage the
>> firmware bloat strategy to be seriously reconsidered in light of the
>> issues that could arise.
>
> The wiphy_apply_custom_regulatory() option is to be used before
> registering the wiphy. We want to be able to accept country code
> changes at runtime, with the driver supplying the regdomain.

For which cases exactly at run time? This is already being handled on
other drivers without changing APIs, so its unclear why you need this
and to expose this to cfg80211. To be clear, a few drivers other than
Intel already had this strategy and they managed to just use the
reg_notifier() and do what it needs by using the flag that ignores
country IEs, and doing everything else on the driver side. Please
explore this avenue.

> As for why this was chosen - I think you're barking up the wrong tree :)
> The regulatory folks at Intel decided to store the data in FW,

This has been done for a long time but the main reason why this was
done that way was that Intel had no need to have tons of regulatory
domains, and instead had only 4 world regulatory domains, that's all,
if things have changed it'd be good to understand this and also the
reasons why things are being done.

> I don't  have any say here. I think this is more legal than technology reasons.

Asking these questions, understanding them, and addressing concerns
are the questions that need to be asked to help advance wireless on
Linux, it was not asking these questions that got us into trouble in
the first place, we don't want to go back to that. So even if it is
non technical and purely regulatory we obviously should ask why.

> Would this patch be acceptable with the documentation changed to kdoc format?
> Again, this is a different way of doing things, but it's largely
> compatible to the way reg.c already works.

Not yet, you have to explain if you've explored all other avenues
which others have before you have with similar architecture.

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arik Nemtsov May 20, 2014, noon UTC | #4
>> The wiphy_apply_custom_regulatory() option is to be used before
>> registering the wiphy. We want to be able to accept country code
>> changes at runtime, with the driver supplying the regdomain.
>
> For which cases exactly at run time? This is already being handled on
> other drivers without changing APIs, so its unclear why you need this
> and to expose this to cfg80211. To be clear, a few drivers other than
> Intel already had this strategy and they managed to just use the
> reg_notifier() and do what it needs by using the flag that ignores
> country IEs, and doing everything else on the driver side. Please
> explore this avenue.

The problem is not propagating the country setting the FW. I agree
this can be done via the notifier. The problem is propagating the
regulatory data from FW to userspace.
For instance we want P2P to be able to use 5Ghz channels in the US.
For this, wpa_s must have up to date information from the regulatory
DB for country=US. For Intel, the regulatory DB is in FW.

>
>> As for why this was chosen - I think you're barking up the wrong tree :)
>> The regulatory folks at Intel decided to store the data in FW,
>
> This has been done for a long time but the main reason why this was
> done that way was that Intel had no need to have tons of regulatory
> domains, and instead had only 4 world regulatory domains, that's all,
> if things have changed it'd be good to understand this and also the
> reasons why things are being done.

This is no longer true. Some variants will now contain settings per county.

>
>> I don't  have any say here. I think this is more legal than technology reasons.
>
> Asking these questions, understanding them, and addressing concerns
> are the questions that need to be asked to help advance wireless on
> Linux, it was not asking these questions that got us into trouble in
> the first place, we don't want to go back to that. So even if it is
> non technical and purely regulatory we obviously should ask why.
>

I'm actually an advocate of the CRDA/regdb approach. Less work for us. :)
We presented it but ultimately the decision was theirs, not mine.

I believe the main motivations were security and uniformity across
different OSes. For instance, one might replace the regdb and break
regulatory. So the FW has a regulatory checker that verifies correct
settings. Which in turn means it will hold the regulatory DB anyway.
The practical thing to do is use the same settings as the actual
regdb.

Regards,
Arik
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Luis R. Rodriguez June 10, 2014, 9:43 p.m. UTC | #5
On Tue, May 20, 2014 at 5:00 AM, Arik Nemtsov <arik@wizery.com> wrote:
>>> The wiphy_apply_custom_regulatory() option is to be used before
>>> registering the wiphy. We want to be able to accept country code
>>> changes at runtime, with the driver supplying the regdomain.
>>
>> For which cases exactly at run time? This is already being handled on
>> other drivers without changing APIs, so its unclear why you need this
>> and to expose this to cfg80211. To be clear, a few drivers other than
>> Intel already had this strategy and they managed to just use the
>> reg_notifier() and do what it needs by using the flag that ignores
>> country IEs, and doing everything else on the driver side. Please
>> explore this avenue.
>
> The problem is not propagating the country setting the FW. I agree
> this can be done via the notifier. The problem is propagating the
> regulatory data from FW to userspace.
> For instance we want P2P to be able to use 5Ghz channels in the US.
> For this, wpa_s must have up to date information from the regulatory
> DB for country=US. For Intel, the regulatory DB is in FW.

Doesn't the custom regulatory flag allow you to do what you want
already? Is the issue that the custom flag does not let dynamic run
time updates and that's what you need?

>>> As for why this was chosen - I think you're barking up the wrong tree :)
>>> The regulatory folks at Intel decided to store the data in FW,
>>
>> This has been done for a long time but the main reason why this was
>> done that way was that Intel had no need to have tons of regulatory
>> domains, and instead had only 4 world regulatory domains, that's all,
>> if things have changed it'd be good to understand this and also the
>> reasons why things are being done.
>
> This is no longer true. Some variants will now contain settings per county.

OK thanks, I suppose there is more need for regulatory folks at
companies to be talking.

>>> I don't  have any say here. I think this is more legal than technology reasons.
>>
>> Asking these questions, understanding them, and addressing concerns
>> are the questions that need to be asked to help advance wireless on
>> Linux, it was not asking these questions that got us into trouble in
>> the first place, we don't want to go back to that. So even if it is
>> non technical and purely regulatory we obviously should ask why.
>>
>
> I'm actually an advocate of the CRDA/regdb approach. Less work for us. :)
> We presented it but ultimately the decision was theirs, not mine.

I'm starting to think that having an organized group that does this
for the community would be good, having different companies do this
and come up with different conclusions seems rather a waste of time,
energy and resources.

> I believe the main motivations were security and uniformity across
> different OSes.

Having loose interpretations over what is generally accepted is
understood, specially for corner cases of new breeds of technology
like P2P and dealing with nagging customers who insist on things or
are pushing the envelope on what regulatory bodies have or have not
made explicit. However making it the norm seems rather counter
productive in the long run. I'm starting to think that having a clean
API to extract the regulatory data from FW to allow even dynamic
changes at run time might be good given that we can at least extract
that information and deduce updates from companies for the general
public wireless-regdb. That is, the mathematics on wireless-regdb /
CRDA can be used to create the intersections of what is accepted for
the case where no regulatory information is available. The technical
assumption however that one should be stuffing firmware with
regulatory data is brain dead given that it doesn't scale, prone to
regulatory issues, and bugs. The flexibility of having these updates
generalized and only using firmware for out of norm situations should
be strongly encouraged.

> For instance, one might replace the regdb and break
> regulatory. So the FW has a regulatory checker that verifies correct
> settings. Which in turn means it will hold the regulatory DB anyway.

That's just brain dead, we're reverse engineered firmware before,
firmware is no safe haven from modifications, what we should be
striving for, and what I do need your help with is arguing back up to
PHBs on the architectural issues with the firmware design, the fact
that its proven over and over to have issues, and that it doesn't
fucking scale.

That said, if your PHBs want to shoot themselves on the foot we can
let them, but we should at the very least be able to extract *all* the
regulatory db from the damn firmware so that we can then properly
implement a common solution upstream for all 802.11 drivers as we have
been doing.

  Luis
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Arik Nemtsov June 11, 2014, 5:51 a.m. UTC | #6
On Wed, Jun 11, 2014 at 12:43 AM, Luis R. Rodriguez
<mcgrof@do-not-panic.com> wrote:
> On Tue, May 20, 2014 at 5:00 AM, Arik Nemtsov <arik@wizery.com> wrote:
>>>> The wiphy_apply_custom_regulatory() option is to be used before
>>>> registering the wiphy. We want to be able to accept country code
>>>> changes at runtime, with the driver supplying the regdomain.
>>>
>>> For which cases exactly at run time? This is already being handled on
>>> other drivers without changing APIs, so its unclear why you need this
>>> and to expose this to cfg80211. To be clear, a few drivers other than
>>> Intel already had this strategy and they managed to just use the
>>> reg_notifier() and do what it needs by using the flag that ignores
>>> country IEs, and doing everything else on the driver side. Please
>>> explore this avenue.
>>
>> The problem is not propagating the country setting the FW. I agree
>> this can be done via the notifier. The problem is propagating the
>> regulatory data from FW to userspace.
>> For instance we want P2P to be able to use 5Ghz channels in the US.
>> For this, wpa_s must have up to date information from the regulatory
>> DB for country=US. For Intel, the regulatory DB is in FW.
>
> Doesn't the custom regulatory flag allow you to do what you want
> already? Is the issue that the custom flag does not let dynamic run
> time updates and that's what you need?

Yes - we need runtime updates for platforms that can roam to different
countries.

The reg.c code explicitly assumes there will be no regd change if the
custom-regulatory flags is set..

The proposed patch seemed like a clean way to have regulatory data
come from the FW - register the wiphy normally, just replace crda with
a driver callback.


>>
>> This is no longer true. Some variants will now contain settings per county.
>
> OK thanks, I suppose there is more need for regulatory folks at
> companies to be talking.

I'm all for preventing duplicated labor.

>
>>>> I don't  have any say here. I think this is more legal than technology reasons.
>>>
>>> Asking these questions, understanding them, and addressing concerns
>>> are the questions that need to be asked to help advance wireless on
>>> Linux, it was not asking these questions that got us into trouble in
>>> the first place, we don't want to go back to that. So even if it is
>>> non technical and purely regulatory we obviously should ask why.
>>>
>>
>> I'm actually an advocate of the CRDA/regdb approach. Less work for us. :)
>> We presented it but ultimately the decision was theirs, not mine.
>
> I'm starting to think that having an organized group that does this
> for the community would be good, having different companies do this
> and come up with different conclusions seems rather a waste of time,
> energy and resources.

Agree. This would need to a be group of legal/regulatory folks, in
addition to developers.

>
>> For instance, one might replace the regdb and break
>> regulatory. So the FW has a regulatory checker that verifies correct
>> settings. Which in turn means it will hold the regulatory DB anyway.
>
> That's just brain dead, we're reverse engineered firmware before,
> firmware is no safe haven from modifications, what we should be
> striving for, and what I do need your help with is arguing back up to
> PHBs on the architectural issues with the firmware design, the fact
> that its proven over and over to have issues, and that it doesn't
> fucking scale.

Reverse-engineering the FW is one thing, changing the regulatory
settings inside it and signing it with an unknown private key is
entirely different. The verification of FW integrity can be done by
HW.
Of course we could verify the driver/kernel etc using TPM, but that's
mostly unfeasible. Therefore I tend to accept the argument the FW
regulatory data is more secure.

I've tried to make the counter-argument that CRDA/wireless-regdb is
good enough for platforms where the user doesn't have root access, but
it was rejected on the grounds that the solution needs to work on
everything.

>
> That said, if your PHBs want to shoot themselves on the foot we can
> let them, but we should at the very least be able to extract *all* the
> regulatory db from the damn firmware so that we can then properly
> implement a common solution upstream for all 802.11 drivers as we have
> been doing.

I guess the code I've already implemented should be used to extract
the data. Just a simple script iterating over countries vs. a live
card.
Asking for the data in other forms would likely bring the wrath of the
Intel legal folks upon us. :)

Arik
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index f2c3186..3c96b62 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3008,6 +3008,23 @@  struct wiphy {
 	void (*reg_notifier)(struct wiphy *wiphy,
 			     struct regulatory_request *request);
 
+	/*
+	 * Indicates this wiphy can provide regulatory information.
+	 * Must be set before the wiphy is registered. Only the first
+	 * wiphy with this callback will be called to provide a regdomain
+	 * on country-code changes. The alpha2 in the returned regdomain
+	 * information can be different from the one given via argument,
+	 * if the argument contains the "99" alpha2, meaning unknown.
+	 * If an ERR_PTR is returned the regulatory core will consult other
+	 * sources for the regdomain info (internal regdb and CRDA).
+	 * Returning NULL will cause the regdomain to remain the same.
+	 * The callee will return a struct allocated with kmalloc(). After
+	 * the struct is returned, the regulatory core is responsible
+	 * for freeing it.
+	 */
+	struct ieee80211_regdomain * (*get_regd)(struct wiphy *wiphy,
+						 const char *alpha2);
+
 	/* fields below are read-only, assigned by cfg80211 */
 
 	const struct ieee80211_regdomain __rcu *regd;
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index efd6d0d..e2f33d7 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -78,6 +78,8 @@ 
  *	further processing is required, i.e., not need to update last_request
  *	etc. This should be used for user hints that do not provide an alpha2
  *	but some other type of regulatory hint, i.e., indoor operation.
+ * @REG_REQ_HANDLED: a request was handled synchronously. No need to set
+ *	timeouts and potentially revert to the default settings.
  */
 enum reg_request_treatment {
 	REG_REQ_OK,
@@ -85,6 +87,7 @@  enum reg_request_treatment {
 	REG_REQ_INTERSECT,
 	REG_REQ_ALREADY_SET,
 	REG_REQ_USER_HINT_HANDLED,
+	REG_REQ_HANDLED,
 };
 
 static struct regulatory_request core_request_world = {
@@ -129,6 +132,15 @@  static int reg_num_devs_support_basehint;
  */
 static bool reg_is_indoor;
 
+/*
+ * Wiphy with a get_regd() callback that can provide regulatory information
+ * when the country code changes. Only the first wiphy registered with the
+ * get_regd callback will be called to provide a regdomain on country-code
+ * changes.
+ * (protected by RTNL)
+ */
+static struct wiphy *regd_info_wiphy;
+
 static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
 {
 	return rtnl_dereference(cfg80211_regdomain);
@@ -538,9 +550,39 @@  static int call_crda(const char *alpha2)
 	return kobject_uevent_env(&reg_pdev->dev.kobj, KOBJ_CHANGE, env);
 }
 
+static int call_wiphy_regd_info(const char *alpha2)
+{
+	struct ieee80211_regdomain *regd;
+
+	if (!regd_info_wiphy)
+		return -ENOENT;
+
+	/* can happen if the driver removes the callback at runtime */
+	if (WARN_ON(!regd_info_wiphy->get_regd))
+		return -EINVAL;
+
+	regd = regd_info_wiphy->get_regd(regd_info_wiphy, alpha2);
+	if (IS_ERR(regd))
+		return -EIO;
+
+	if (regd)
+		set_regdom(regd);
+
+	return 0;
+}
+
 static enum reg_request_treatment
-reg_call_crda(struct regulatory_request *request)
+reg_get_regdom_data(struct regulatory_request *request)
 {
+	ASSERT_RTNL();
+
+	/*
+	 * A wiphy wishing to set the regdomain takes precedence. Note the
+	 * regdomain setting happens synchronously inside.
+	 */
+	if (!call_wiphy_regd_info(request->alpha2))
+		return REG_REQ_HANDLED;
+
 	if (call_crda(request->alpha2))
 		return REG_REQ_IGNORE;
 	return REG_REQ_OK;
@@ -1641,7 +1683,7 @@  reg_process_hint_core(struct regulatory_request *core_request)
 
 	reg_update_last_request(core_request);
 
-	return reg_call_crda(core_request);
+	return reg_get_regdom_data(core_request);
 }
 
 static enum reg_request_treatment
@@ -1715,7 +1757,7 @@  reg_process_hint_user(struct regulatory_request *user_request)
 	user_alpha2[0] = user_request->alpha2[0];
 	user_alpha2[1] = user_request->alpha2[1];
 
-	return reg_call_crda(user_request);
+	return reg_get_regdom_data(user_request);
 }
 
 static enum reg_request_treatment
@@ -1764,6 +1806,7 @@  reg_process_hint_driver(struct wiphy *wiphy,
 		break;
 	case REG_REQ_IGNORE:
 	case REG_REQ_USER_HINT_HANDLED:
+	case REG_REQ_HANDLED:
 		reg_free_request(driver_request);
 		return treatment;
 	case REG_REQ_INTERSECT:
@@ -1794,7 +1837,7 @@  reg_process_hint_driver(struct wiphy *wiphy,
 		return treatment;
 	}
 
-	return reg_call_crda(driver_request);
+	return reg_get_regdom_data(driver_request);
 }
 
 static enum reg_request_treatment
@@ -1864,6 +1907,7 @@  reg_process_hint_country_ie(struct wiphy *wiphy,
 		break;
 	case REG_REQ_IGNORE:
 	case REG_REQ_USER_HINT_HANDLED:
+	case REG_REQ_HANDLED:
 		/* fall through */
 	case REG_REQ_ALREADY_SET:
 		reg_free_request(country_ie_request);
@@ -1883,7 +1927,7 @@  reg_process_hint_country_ie(struct wiphy *wiphy,
 
 	reg_update_last_request(country_ie_request);
 
-	return reg_call_crda(country_ie_request);
+	return reg_get_regdom_data(country_ie_request);
 }
 
 /* This processes *all* regulatory hints */
@@ -1903,7 +1947,8 @@  static void reg_process_hint(struct regulatory_request *reg_request)
 		treatment = reg_process_hint_user(reg_request);
 		if (treatment == REG_REQ_IGNORE ||
 		    treatment == REG_REQ_ALREADY_SET ||
-		    treatment == REG_REQ_USER_HINT_HANDLED)
+		    treatment == REG_REQ_USER_HINT_HANDLED ||
+		    treatment == REG_REQ_HANDLED)
 			return;
 		queue_delayed_work(system_power_efficient_wq,
 				   &reg_timeout, msecs_to_jiffies(3142));
@@ -2684,6 +2729,9 @@  void wiphy_regulatory_register(struct wiphy *wiphy)
 {
 	struct regulatory_request *lr;
 
+	if (wiphy->get_regd && !regd_info_wiphy)
+		regd_info_wiphy = wiphy;
+
 	if (!reg_dev_ignore_cell_hint(wiphy))
 		reg_num_devs_support_basehint++;
 
@@ -2696,6 +2744,8 @@  void wiphy_regulatory_deregister(struct wiphy *wiphy)
 	struct wiphy *request_wiphy = NULL;
 	struct regulatory_request *lr;
 
+	ASSERT_RTNL();
+
 	lr = get_last_request();
 
 	if (!reg_dev_ignore_cell_hint(wiphy))
@@ -2704,6 +2754,9 @@  void wiphy_regulatory_deregister(struct wiphy *wiphy)
 	rcu_free_regdom(get_wiphy_regdom(wiphy));
 	RCU_INIT_POINTER(wiphy->regd, NULL);
 
+	if (wiphy == regd_info_wiphy)
+		regd_info_wiphy = NULL;
+
 	if (lr)
 		request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);