diff mbox series

[V7,4/9] wifi: mac80211: Add support for ACPI WBRF

Message ID 20230719090020.2716892-5-evan.quan@amd.com
State Superseded
Headers show
Series Enable Wifi RFI interference mitigation feature support | expand

Commit Message

Evan Quan July 19, 2023, 9 a.m. UTC
To support AMD's WBRF interference mitigation mechanism, Wifi adapters
utilized in the system must register the frequencies in use(or unregister
those frequencies no longer used) via the dedicated APCI calls. So that,
other drivers responding to the frequencies can take proper actions to
mitigate possible interference.

Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <evan.quan@amd.com>
Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v1->v2:
  - place the new added member(`wbrf_supported`) in
    ieee80211_local(Johannes)
  - handle chandefs change scenario properly(Johannes)
  - some minor fixes around code sharing and possible invalid input
    checks(Johannes)
v2->v3:
  - drop unnecessary input checks and intermediate APIs(Mario)
  - Separate some mac80211 common code(Mario, Johannes)
v3->v4:
  - some minor fixes around return values(Johannes)
---
 include/linux/ieee80211.h  |   1 +
 net/mac80211/Makefile      |   2 +
 net/mac80211/chan.c        |   9 ++++
 net/mac80211/ieee80211_i.h |  19 +++++++
 net/mac80211/main.c        |   2 +
 net/mac80211/wbrf.c        | 103 +++++++++++++++++++++++++++++++++++++
 6 files changed, 136 insertions(+)
 create mode 100644 net/mac80211/wbrf.c

Comments

Andrew Lunn July 24, 2023, 9:22 a.m. UTC | #1
> @@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>  	debugfs_hw_add(local);
>  	rate_control_add_debugfs(local);
>  
> +	ieee80211_check_wbrf_support(local);
> +
>  	rtnl_lock();
>  	wiphy_lock(hw->wiphy);
>  

> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
> +{
> +	struct wiphy *wiphy = local->hw.wiphy;
> +	struct device *dev;
> +
> +	if (!wiphy)
> +		return;
> +
> +	dev = wiphy->dev.parent;
> +	if (!dev)
> +		return;
> +
> +	local->wbrf_supported = wbrf_supported_producer(dev);
> +	dev_dbg(dev, "WBRF is %s supported\n",
> +		local->wbrf_supported ? "" : "not");
> +}

This seems wrong. wbrf_supported_producer() is about "Should this
device report the frequencies it is using?" The answer to that depends
on a combination of: Are there consumers registered with the core, and
is the policy set so WBRF should take actions.

The problem here is, you have no idea of the probe order. It could be
this device probes before others, so wbrf_supported_producer() reports
false, but a few second later would report true, once other devices
have probed.

It should be an inexpensive call into the core, so can be made every
time the channel changes. All the core needs to do is check if the
list of consumers is empty, and if not, check a Boolean policy value.

     Andrew
Mario Limonciello July 24, 2023, 1:40 p.m. UTC | #2
On 7/24/2023 04:22, Andrew Lunn wrote:
>> @@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>>   	debugfs_hw_add(local);
>>   	rate_control_add_debugfs(local);
>>   
>> +	ieee80211_check_wbrf_support(local);
>> +
>>   	rtnl_lock();
>>   	wiphy_lock(hw->wiphy);
>>   
> 
>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local)
>> +{
>> +	struct wiphy *wiphy = local->hw.wiphy;
>> +	struct device *dev;
>> +
>> +	if (!wiphy)
>> +		return;
>> +
>> +	dev = wiphy->dev.parent;
>> +	if (!dev)
>> +		return;
>> +
>> +	local->wbrf_supported = wbrf_supported_producer(dev);
>> +	dev_dbg(dev, "WBRF is %s supported\n",
>> +		local->wbrf_supported ? "" : "not");
>> +}
> 
> This seems wrong. wbrf_supported_producer() is about "Should this
> device report the frequencies it is using?" The answer to that depends
> on a combination of: Are there consumers registered with the core, and
> is the policy set so WBRF should take actions. >
> The problem here is, you have no idea of the probe order. It could be
> this device probes before others, so wbrf_supported_producer() reports
> false, but a few second later would report true, once other devices
> have probed.
> 
> It should be an inexpensive call into the core, so can be made every
> time the channel changes. All the core needs to do is check if the
> list of consumers is empty, and if not, check a Boolean policy value.
> 
>       Andrew

No, it's not a combination of whether consumers are registered with the 
core.  If a consumer probes later it needs to know the current in use 
frequencies too.

The reason is because of this sequence of events:
1) Producer probes.
2) Producer selects a frequency.
3) Consumer probes.
4) Producer stays at same frequency.

If the producer doesn't notify the frequency because a consumer isn't 
yet loaded then the consumer won't be able to get the current frequency.
Evan Quan July 25, 2023, 10:38 a.m. UTC | #3
[AMD Official Use Only - General]

> -----Original Message-----
> From: Limonciello, Mario <Mario.Limonciello@amd.com>
> Sent: Monday, July 24, 2023 9:41 PM
> To: Andrew Lunn <andrew@lunn.ch>; Quan, Evan <Evan.Quan@amd.com>
> Cc: rafael@kernel.org; lenb@kernel.org; Deucher, Alexander
> <Alexander.Deucher@amd.com>; Koenig, Christian
> <Christian.Koenig@amd.com>; Pan, Xinhui <Xinhui.Pan@amd.com>;
> airlied@gmail.com; daniel@ffwll.ch; johannes@sipsolutions.net;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; mdaenzer@redhat.com;
> maarten.lankhorst@linux.intel.com; tzimmermann@suse.de;
> hdegoede@redhat.com; jingyuwang_vip@163.com; Lazar, Lijo
> <Lijo.Lazar@amd.com>; jim.cromie@gmail.com; bellosilicio@gmail.com;
> andrealmeid@igalia.com; trix@redhat.com; jsg@jsg.id.au; arnd@arndb.de;
> linux-kernel@vger.kernel.org; linux-acpi@vger.kernel.org; amd-
> gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org; linux-
> wireless@vger.kernel.org; netdev@vger.kernel.org
> Subject: Re: [PATCH V7 4/9] wifi: mac80211: Add support for ACPI WBRF
>
> On 7/24/2023 04:22, Andrew Lunn wrote:
> >> @@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct
> ieee80211_hw *hw)
> >>    debugfs_hw_add(local);
> >>    rate_control_add_debugfs(local);
> >>
> >> +  ieee80211_check_wbrf_support(local);
> >> +
> >>    rtnl_lock();
> >>    wiphy_lock(hw->wiphy);
> >>
> >
> >> +void ieee80211_check_wbrf_support(struct ieee80211_local *local) {
> >> +  struct wiphy *wiphy = local->hw.wiphy;
> >> +  struct device *dev;
> >> +
> >> +  if (!wiphy)
> >> +          return;
> >> +
> >> +  dev = wiphy->dev.parent;
> >> +  if (!dev)
> >> +          return;
> >> +
> >> +  local->wbrf_supported = wbrf_supported_producer(dev);
> >> +  dev_dbg(dev, "WBRF is %s supported\n",
> >> +          local->wbrf_supported ? "" : "not"); }
> >
> > This seems wrong. wbrf_supported_producer() is about "Should this
> > device report the frequencies it is using?" The answer to that depends
> > on a combination of: Are there consumers registered with the core, and
> > is the policy set so WBRF should take actions. > The problem here is,
> > you have no idea of the probe order. It could be this device probes
> > before others, so wbrf_supported_producer() reports false, but a few
> > second later would report true, once other devices have probed.
> >
> > It should be an inexpensive call into the core, so can be made every
> > time the channel changes. All the core needs to do is check if the
> > list of consumers is empty, and if not, check a Boolean policy value.
> >
> >       Andrew
>
> No, it's not a combination of whether consumers are registered with the core.
> If a consumer probes later it needs to know the current in use frequencies too.
>
> The reason is because of this sequence of events:
> 1) Producer probes.
> 2) Producer selects a frequency.
> 3) Consumer probes.
> 4) Producer stays at same frequency.
>
> If the producer doesn't notify the frequency because a consumer isn't yet
> loaded then the consumer won't be able to get the current frequency.
Yes, exactly.
Andrew Lunn July 25, 2023, 6:57 p.m. UTC | #4
> > >> @@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct
> > ieee80211_hw *hw)
> > >>    debugfs_hw_add(local);
> > >>    rate_control_add_debugfs(local);
> > >>
> > >> +  ieee80211_check_wbrf_support(local);
> > >> +
> > >>    rtnl_lock();
> > >>    wiphy_lock(hw->wiphy);
> > >>
> > >
> > >> +void ieee80211_check_wbrf_support(struct ieee80211_local *local) {
> > >> +  struct wiphy *wiphy = local->hw.wiphy;
> > >> +  struct device *dev;
> > >> +
> > >> +  if (!wiphy)
> > >> +          return;
> > >> +
> > >> +  dev = wiphy->dev.parent;
> > >> +  if (!dev)
> > >> +          return;
> > >> +
> > >> +  local->wbrf_supported = wbrf_supported_producer(dev);
> > >> +  dev_dbg(dev, "WBRF is %s supported\n",
> > >> +          local->wbrf_supported ? "" : "not"); }
> > >
> > > This seems wrong. wbrf_supported_producer() is about "Should this
> > > device report the frequencies it is using?" The answer to that depends
> > > on a combination of: Are there consumers registered with the core, and
> > > is the policy set so WBRF should take actions. > The problem here is,
> > > you have no idea of the probe order. It could be this device probes
> > > before others, so wbrf_supported_producer() reports false, but a few
> > > second later would report true, once other devices have probed.
> > >
> > > It should be an inexpensive call into the core, so can be made every
> > > time the channel changes. All the core needs to do is check if the
> > > list of consumers is empty, and if not, check a Boolean policy value.
> > >
> > >       Andrew
> >
> > No, it's not a combination of whether consumers are registered with the core.
> > If a consumer probes later it needs to know the current in use frequencies too.
> >
> > The reason is because of this sequence of events:
> > 1) Producer probes.
> > 2) Producer selects a frequency.
> > 3) Consumer probes.
> > 4) Producer stays at same frequency.
> >
> > If the producer doesn't notify the frequency because a consumer isn't yet
> > loaded then the consumer won't be able to get the current frequency.
> Yes, exactly.

So now we are back to, what is the point of wbrf_supported_producer()?

I'm talking general case here, not your ACPI implementation. All i'm
really interested in is the generic API, which is what an Intel CPU,
combined with a Radieon GPU and a Qualcomm WiFi device will use. Or an
AMD CPU combined with an nvidia GPU and a Mediatek Wifi, etc. The wbrf
core should support an combination of produces and consumers in a
generic way.

If you assume devices can probe in any order, and come and go, it
seems like the producers need to always report what frequencies they
are using. Otherwise when a noise generator pops into existence, as
you say, it has no idea what frequencies the producers are using.

The exception is when policy says there is no need to actually do
anything. If we can assume the policy is fixed, then
wbrf_supported_producer() could just report the policy which the wbrf
core should know about.

    Andrew
Mario Limonciello July 25, 2023, 7:15 p.m. UTC | #5
On 7/25/2023 13:57, Andrew Lunn wrote:
>>>>> @@ -1395,6 +1395,8 @@ int ieee80211_register_hw(struct
>>> ieee80211_hw *hw)
>>>>>     debugfs_hw_add(local);
>>>>>     rate_control_add_debugfs(local);
>>>>>
>>>>> +  ieee80211_check_wbrf_support(local);
>>>>> +
>>>>>     rtnl_lock();
>>>>>     wiphy_lock(hw->wiphy);
>>>>>
>>>>
>>>>> +void ieee80211_check_wbrf_support(struct ieee80211_local *local) {
>>>>> +  struct wiphy *wiphy = local->hw.wiphy;
>>>>> +  struct device *dev;
>>>>> +
>>>>> +  if (!wiphy)
>>>>> +          return;
>>>>> +
>>>>> +  dev = wiphy->dev.parent;
>>>>> +  if (!dev)
>>>>> +          return;
>>>>> +
>>>>> +  local->wbrf_supported = wbrf_supported_producer(dev);
>>>>> +  dev_dbg(dev, "WBRF is %s supported\n",
>>>>> +          local->wbrf_supported ? "" : "not"); }
>>>>
>>>> This seems wrong. wbrf_supported_producer() is about "Should this
>>>> device report the frequencies it is using?" The answer to that depends
>>>> on a combination of: Are there consumers registered with the core, and
>>>> is the policy set so WBRF should take actions. > The problem here is,
>>>> you have no idea of the probe order. It could be this device probes
>>>> before others, so wbrf_supported_producer() reports false, but a few
>>>> second later would report true, once other devices have probed.
>>>>
>>>> It should be an inexpensive call into the core, so can be made every
>>>> time the channel changes. All the core needs to do is check if the
>>>> list of consumers is empty, and if not, check a Boolean policy value.
>>>>
>>>>        Andrew
>>>
>>> No, it's not a combination of whether consumers are registered with the core.
>>> If a consumer probes later it needs to know the current in use frequencies too.
>>>
>>> The reason is because of this sequence of events:
>>> 1) Producer probes.
>>> 2) Producer selects a frequency.
>>> 3) Consumer probes.
>>> 4) Producer stays at same frequency.
>>>
>>> If the producer doesn't notify the frequency because a consumer isn't yet
>>> loaded then the consumer won't be able to get the current frequency.
>> Yes, exactly.
> 
> So now we are back to, what is the point of wbrf_supported_producer()?
> 
> I'm talking general case here, not your ACPI implementation. All i'm
> really interested in is the generic API, which is what an Intel CPU,
> combined with a Radieon GPU and a Qualcomm WiFi device will use. Or an
> AMD CPU combined with an nvidia GPU and a Mediatek Wifi, etc. The wbrf
> core should support an combination of produces and consumers in a
> generic way.
> 
> If you assume devices can probe in any order, and come and go, it
> seems like the producers need to always report what frequencies they
> are using. Otherwise when a noise generator pops into existence, as
> you say, it has no idea what frequencies the producers are using.
As the series stands today if the probe order is reversed everything 
works fine.

1) Consumer probes
2) Producer probes
3) Producer selects a frequency
4) Consumer reacts to frequency.

> 
> The exception is when policy says there is no need to actually do
> anything. If we can assume the policy is fixed, then
> wbrf_supported_producer() could just report the policy which the wbrf
> core should know about.
> 
>      Andrew
> 

This comes back to the point that was mentioned by Johannes - you need 
to have deep design understanding of the hardware to know whether or not 
you will have producers that a consumer need to react to.

For example the physical location GDDR6 memory and proximity to the 
hinge where the antenna was routed might play a big factor in whether 
you need something like this.

If all producers indicate their frequency and all consumers react to it 
you may have activated mitigations that are unnecessary. The hardware 
designer may have added extra shielding or done the layout such that 
they're not needed.

So I don't think we're ever going to be in a situation that the generic 
implementation should be turned on by default.  It's a "developer knob".

As mentioned in the Kconfig it's intended use is for identifying 
situations that may benefit from mitigation before support was 
introduced into the firmware.

If needed these can then be enabled using the AMD ACPI interface, a DT 
one if one is developed or maybe even an allow-list of SMBIOS strings.
Andrew Lunn July 25, 2023, 8:09 p.m. UTC | #6
> This comes back to the point that was mentioned by Johannes - you need to
> have deep design understanding of the hardware to know whether or not you
> will have producers that a consumer need to react to.

Yes, this is the policy is keep referring to. I would expect that
there is something somewhere in ACPI which says for this machine, the
policy is Yes/No.

It could well be that AMD based machine has a different ACPI extension
to indicate this policy to what Intel machine has. As far as i
understand it, you have not submitted this yet for formal approval,
this is all vendor specific, so Intel could do it completely
differently. Hence i would expect a generic API to tell the core what
the policy is, and your glue code can call into ACPI to find out that
information, and then tell the core.

> If all producers indicate their frequency and all consumers react to it you
> may have activated mitigations that are unnecessary. The hardware designer
> may have added extra shielding or done the layout such that they're not
> needed.

And the policy will indicate No, nothing needs to be done. The core
can then tell produces and consumes not to bother telling the core
anything.

> So I don't think we're ever going to be in a situation that the generic
> implementation should be turned on by default.  It's a "developer knob".

Wrong. You should have a generic core, which your AMD CPU DDR device
plugs into. The Intel CPU DDR device can plug into, the nvidea GPU can
plug into, your Radeon GPU can plug into, the intel ARC can plug into,
the generic WiFi core plugs into, etc.

> If needed these can then be enabled using the AMD ACPI interface, a DT one
> if one is developed or maybe even an allow-list of SMBIOS strings.

Notice i've not mentioned DT for a while. I just want a generic core,
which AMD, Intel, nvidea, Ampare, Graviton, Qualcomm, Marvell, ...,
etc can use. We should be solving this problem once, for everybody,
not adding a solution for just one vendor.

      Andrew
Mario Limonciello July 25, 2023, 8:44 p.m. UTC | #7
On 7/25/23 15:09, Andrew Lunn wrote:
>> This comes back to the point that was mentioned by Johannes - you need to
>> have deep design understanding of the hardware to know whether or not you
>> will have producers that a consumer need to react to.
> Yes, this is the policy is keep referring to. I would expect that
> there is something somewhere in ACPI which says for this machine, the
> policy is Yes/No.
It's not yes/no for a "model" or "machine".  It's yes/no for a given 
*device*
within a machine.
>
> It could well be that AMD based machine has a different ACPI extension
> to indicate this policy to what Intel machine has. As far as i
> understand it, you have not submitted this yet for formal approval,
> this is all vendor specific, so Intel could do it completely
> differently. Hence i would expect a generic API to tell the core what
> the policy is, and your glue code can call into ACPI to find out that
> information, and then tell the core.
Which is exactly what wbrf_supported_producer() and 
wbrf_supported_consumer() do.
If there is another vendor's implementation introduced they can make 
those functions
return TRUE for their implementations.
>> If all producers indicate their frequency and all consumers react to it you
>> may have activated mitigations that are unnecessary. The hardware designer
>> may have added extra shielding or done the layout such that they're not
>> needed.
> And the policy will indicate No, nothing needs to be done. The core
> can then tell produces and consumes not to bother telling the core
> anything.
>
>> So I don't think we're ever going to be in a situation that the generic
>> implementation should be turned on by default.  It's a "developer knob".
> Wrong. You should have a generic core, which your AMD CPU DDR device
> plugs into. The Intel CPU DDR device can plug into, the nvidea GPU can
> plug into, your Radeon GPU can plug into, the intel ARC can plug into,
> the generic WiFi core plugs into, etc.
It's not a function of "device" though, it's "device within machine".
>
>> If needed these can then be enabled using the AMD ACPI interface, a DT one
>> if one is developed or maybe even an allow-list of SMBIOS strings.
> Notice i've not mentioned DT for a while. I just want a generic core,
> which AMD, Intel, nvidea, Ampare, Graviton, Qualcomm, Marvell, ...,
> etc can use. We should be solving this problem once, for everybody,
> not adding a solution for just one vendor.
>
>        Andrew
I don't see why other implementations can't just come up with other
platform specific ways to respond affirmatively to
wbrf_supported_producer() or
wbrf_supported_consumer().
Evan Quan Aug. 14, 2023, 9:50 a.m. UTC | #8
[AMD Official Use Only - General]

Hi Andrew,

I sent out a new V8 series last week.
A kernel parameter `wbrf` was introduced there to decide the policy.
Please help to check whether that makes sense to you.
Please share your insights there.

BR,
Evan
> -----Original Message-----
> From: Andrew Lunn <andrew@lunn.ch>
> Sent: Wednesday, July 26, 2023 4:10 AM
> To: Limonciello, Mario <Mario.Limonciello@amd.com>
> Cc: Quan, Evan <Evan.Quan@amd.com>; rafael@kernel.org; lenb@kernel.org;
> Deucher, Alexander <Alexander.Deucher@amd.com>; Koenig, Christian
> <Christian.Koenig@amd.com>; Pan, Xinhui <Xinhui.Pan@amd.com>;
> airlied@gmail.com; daniel@ffwll.ch; johannes@sipsolutions.net;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com; mdaenzer@redhat.com;
> maarten.lankhorst@linux.intel.com; tzimmermann@suse.de;
> hdegoede@redhat.com; jingyuwang_vip@163.com; Lazar, Lijo
> <Lijo.Lazar@amd.com>; jim.cromie@gmail.com; bellosilicio@gmail.com;
> andrealmeid@igalia.com; trix@redhat.com; jsg@jsg.id.au; arnd@arndb.de;
> linux-kernel@vger.kernel.org; linux-acpi@vger.kernel.org; amd-
> gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org; linux-
> wireless@vger.kernel.org; netdev@vger.kernel.org
> Subject: Re: [PATCH V7 4/9] wifi: mac80211: Add support for ACPI WBRF
> 
> > This comes back to the point that was mentioned by Johannes - you need
> > to have deep design understanding of the hardware to know whether or
> > not you will have producers that a consumer need to react to.
> 
> Yes, this is the policy is keep referring to. I would expect that there is something
> somewhere in ACPI which says for this machine, the policy is Yes/No.
> 
> It could well be that AMD based machine has a different ACPI extension to
> indicate this policy to what Intel machine has. As far as i understand it, you
> have not submitted this yet for formal approval, this is all vendor specific, so
> Intel could do it completely differently. Hence i would expect a generic API to
> tell the core what the policy is, and your glue code can call into ACPI to find out
> that information, and then tell the core.
> 
> > If all producers indicate their frequency and all consumers react to
> > it you may have activated mitigations that are unnecessary. The
> > hardware designer may have added extra shielding or done the layout
> > such that they're not needed.
> 
> And the policy will indicate No, nothing needs to be done. The core can then
> tell produces and consumes not to bother telling the core anything.
> 
> > So I don't think we're ever going to be in a situation that the
> > generic implementation should be turned on by default.  It's a "developer
> knob".
> 
> Wrong. You should have a generic core, which your AMD CPU DDR device
> plugs into. The Intel CPU DDR device can plug into, the nvidea GPU can plug
> into, your Radeon GPU can plug into, the intel ARC can plug into, the generic
> WiFi core plugs into, etc.
> 
> > If needed these can then be enabled using the AMD ACPI interface, a DT
> > one if one is developed or maybe even an allow-list of SMBIOS strings.
> 
> Notice i've not mentioned DT for a while. I just want a generic core, which
> AMD, Intel, nvidea, Ampare, Graviton, Qualcomm, Marvell, ..., etc can use. We
> should be solving this problem once, for everybody, not adding a solution for
> just one vendor.
> 
>       Andrew
Johannes Berg Aug. 14, 2023, 10:02 a.m. UTC | #9
On Tue, 2023-07-25 at 22:09 +0200, Andrew Lunn wrote:
> 
> 
> It could well be that AMD based machine has a different ACPI extension
> to indicate this policy to what Intel machine has. As far as i
> understand it, you have not submitted this yet for formal approval,
> this is all vendor specific, so Intel could do it completely
> differently.

Already do, without the host software being involved in the same way.
There, I believe the ACPI tables just indicate what's needed and the
WiFi firmware sorts out the rest.

johannes
Andrew Lunn Aug. 14, 2023, 2:31 p.m. UTC | #10
On Mon, Aug 14, 2023 at 09:50:49AM +0000, Quan, Evan wrote:
> [AMD Official Use Only - General]
> 
> Hi Andrew,
> 
> I sent out a new V8 series last week.
> A kernel parameter `wbrf` was introduced there to decide the policy.
> Please help to check whether that makes sense to you.
> Please share your insights there.

netdev has a pretty strong policy of not adding new kernel
parameters. It is a really painful interface to use, and there are
generally better configuration interfaces within netdev.

However, as far as i can see, it is outside of netdev, so this policy
does not necessarily apply.

     Andrew
diff mbox series

Patch

diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index c4cf296e7eaf..0703921547f5 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -4319,6 +4319,7 @@  static inline int ieee80211_get_tdls_action(struct sk_buff *skb, u32 hdr_size)
 /* convert frequencies */
 #define MHZ_TO_KHZ(freq) ((freq) * 1000)
 #define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define KHZ_TO_HZ(freq)  ((freq) * 1000)
 #define PR_KHZ(f) KHZ_TO_MHZ(f), f % 1000
 #define KHZ_F "%d.%03d"
 
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index b8de44da1fb8..8f8ac567e7c8 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -65,4 +65,6 @@  rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += \
 
 mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
 
+mac80211-$(CONFIG_WBRF) += wbrf.o
+
 ccflags-y += -DDEBUG
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 77c90ed8f5d7..9887471028dc 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -506,11 +506,16 @@  static void _ieee80211_change_chanctx(struct ieee80211_local *local,
 
 	WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
 
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
+
 	ctx->conf.def = *chandef;
 
 	/* check if min chanctx also changed */
 	changed = IEEE80211_CHANCTX_CHANGE_WIDTH |
 		  _ieee80211_recalc_chanctx_min_def(local, ctx, rsvd_for);
+
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	drv_change_chanctx(local, ctx, changed);
 
 	if (!local->use_chanctx) {
@@ -668,6 +673,8 @@  static int ieee80211_add_chanctx(struct ieee80211_local *local,
 	lockdep_assert_held(&local->mtx);
 	lockdep_assert_held(&local->chanctx_mtx);
 
+	ieee80211_add_wbrf(local, &ctx->conf.def);
+
 	if (!local->use_chanctx)
 		local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
 
@@ -748,6 +755,8 @@  static void ieee80211_del_chanctx(struct ieee80211_local *local,
 	}
 
 	ieee80211_recalc_idle(local);
+
+	ieee80211_remove_wbrf(local, &ctx->conf.def);
 }
 
 static void ieee80211_free_chanctx(struct ieee80211_local *local,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4159fb65038b..fb984ce7038c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1591,6 +1591,10 @@  struct ieee80211_local {
 
 	/* extended capabilities provided by mac80211 */
 	u8 ext_capa[8];
+
+#if IS_ENABLED(CONFIG_WBRF)
+	bool wbrf_supported;
+#endif
 };
 
 static inline struct ieee80211_sub_if_data *
@@ -2615,4 +2619,19 @@  ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
 				    const struct ieee80211_eht_cap_elem *eht_cap_ie_elem,
 				    u8 eht_cap_len,
 				    struct link_sta_info *link_sta);
+
+#if IS_ENABLED(CONFIG_WBRF)
+void ieee80211_check_wbrf_support(struct ieee80211_local *local);
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef);
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef);
+#else
+static inline void ieee80211_check_wbrf_support(struct ieee80211_local *local) { }
+static inline void ieee80211_add_wbrf(struct ieee80211_local *local,
+				      struct cfg80211_chan_def *chandef) { }
+static inline void ieee80211_remove_wbrf(struct ieee80211_local *local,
+					 struct cfg80211_chan_def *chandef) { }
+#endif /* CONFIG_WBRF */
+
 #endif /* IEEE80211_I_H */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 55cdfaef0f5d..0a55626b1546 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1395,6 +1395,8 @@  int ieee80211_register_hw(struct ieee80211_hw *hw)
 	debugfs_hw_add(local);
 	rate_control_add_debugfs(local);
 
+	ieee80211_check_wbrf_support(local);
+
 	rtnl_lock();
 	wiphy_lock(hw->wiphy);
 
diff --git a/net/mac80211/wbrf.c b/net/mac80211/wbrf.c
new file mode 100644
index 000000000000..7ddb29d128b1
--- /dev/null
+++ b/net/mac80211/wbrf.c
@@ -0,0 +1,103 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface for WWAN
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/wbrf.h>
+#include <net/cfg80211.h>
+#include "ieee80211_i.h"
+
+void ieee80211_check_wbrf_support(struct ieee80211_local *local)
+{
+	struct wiphy *wiphy = local->hw.wiphy;
+	struct device *dev;
+
+	if (!wiphy)
+		return;
+
+	dev = wiphy->dev.parent;
+	if (!dev)
+		return;
+
+	local->wbrf_supported = wbrf_supported_producer(dev);
+	dev_dbg(dev, "WBRF is %s supported\n",
+		local->wbrf_supported ? "" : "not");
+}
+
+static void get_chan_freq_boundary(u32 center_freq,
+				   u32 bandwidth,
+				   u64 *start,
+				   u64 *end)
+{
+	bandwidth = MHZ_TO_KHZ(bandwidth);
+	center_freq = MHZ_TO_KHZ(center_freq);
+
+	*start = center_freq - bandwidth / 2;
+	*end = center_freq + bandwidth / 2;
+
+	/* Frequency in HZ is expected */
+	*start = KHZ_TO_HZ(*start);
+	*end = KHZ_TO_HZ(*end);
+}
+
+static void wbrf_get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
+					 struct wbrf_ranges_in *ranges_in)
+{
+	u64 start_freq1, end_freq1;
+	u64 start_freq2, end_freq2;
+	int bandwidth;
+
+	bandwidth = nl80211_chan_width_to_mhz(chandef->width);
+
+	get_chan_freq_boundary(chandef->center_freq1,
+			       bandwidth,
+			       &start_freq1,
+			       &end_freq1);
+
+	ranges_in->band_list[0].start = start_freq1;
+	ranges_in->band_list[0].end = end_freq1;
+
+	if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
+		get_chan_freq_boundary(chandef->center_freq2,
+				       bandwidth,
+				       &start_freq2,
+				       &end_freq2);
+
+		ranges_in->band_list[1].start = start_freq2;
+		ranges_in->band_list[1].end = end_freq2;
+	}
+}
+
+void ieee80211_add_wbrf(struct ieee80211_local *local,
+			struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+
+	wbrf_add_exclusion(dev, &ranges_in);
+}
+
+void ieee80211_remove_wbrf(struct ieee80211_local *local,
+			   struct cfg80211_chan_def *chandef)
+{
+	struct wbrf_ranges_in ranges_in = {0};
+	struct device *dev;
+
+	if (!local->wbrf_supported)
+		return;
+
+	dev = local->hw.wiphy->dev.parent;
+
+	wbrf_get_ranges_from_chandef(chandef, &ranges_in);
+
+	wbrf_remove_exclusion(dev, &ranges_in);
+}