Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH ath-current v2] wifi: ath12k: fix wrong TID passed when stopping AMPDU session
From: Jeff Johnson @ 2026-01-27 23:39 UTC (permalink / raw)
  To: Pablo Martin-Gomez, ath12k; +Cc: linux-wireless
In-Reply-To: <20260126173655.1369715-1-pmartin-gomez@freebox.fr>

On 1/26/2026 9:36 AM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath12k_dp_rx_ampdu_stop() calls
> ath12k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
> 
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
> 
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
> 
> Fix this by passing the correct TID entry:
>   &peer->rx_tid[params->tid]
> 
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
> ---
>  drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
> index d28d8ffec0f8..f2327c82953b 100644
> --- a/drivers/net/wireless/ath/ath12k/dp_rx.c
> +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
> @@ -1289,7 +1289,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
>  		return 0;
>  	}
>  
> -	ret = ath12k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
> +	ret = ath12k_peer_rx_tid_reo_update(ar, peer, &peer->rx_tid[params->tid], 1, 0, false);

like the ath11k change, this also triggers ath12k-check:
drivers/net/wireless/ath/ath12k/dp_rx.c:1292: line length of 95 exceeds 90 columns

I'll break this line when I process the patch

>  	spin_unlock_bh(&ab->base_lock);
>  	if (ret) {
>  		ath12k_warn(ab, "failed to update reo for rx tid %d: %d\n",


^ permalink raw reply

* Re: [PATCH] wifi: rtw89: usb: fix TX flow control by tracking in-flight URBs
From: Lucid Duck @ 2026-01-27 20:01 UTC (permalink / raw)
  To: Bitterblue Smith; +Cc: linux-wireless, Ping-Ke Shih, Mh_chen
In-Reply-To: <471ce67a-8633-46f3-882e-6051ab022a7e@gmail.com>

On Mon, 26 Jan 2026, Bitterblue Smith wrote:
> The CH12 checks in the completion handler are unnecessary since there
> is one in the resource checking function.

You're right. I'll remove the redundant check in v2.

> Is there a reason to add a new counter instead of just using the
> length of each tx_queue?

Good question. The tx_queue length tracks SKBs queued for submission,
but doesn't account for URBs that have been submitted to USB core and
are in-flight (between submit and completion callback). An SKB is
dequeued before usb_submit_urb(), so skb_queue_len() would undercount
actual resource usage.

The atomic counter tracks the full lifecycle: incremented at submit,
decremented at completion. This gives mac80211 accurate backpressure
even when URBs are pending in the USB subsystem.

Happy to hear if I'm missing something that would make skb_queue_len()
work here.

--
Lucid Duck

^ permalink raw reply

* Clarification on the usage of NL80211_SCAN_FLAG_FLUSH
From: Matthew Schwartz @ 2026-01-27 19:40 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

Hello,

I had a quick question about the proper usage of NL80211_SCAN_FLAG_FLUSH. I'm looking into a bug while using iwd where rapidly switching between a 2.4GHz network and a 5GHz network can have spurious connection failures. After debugging the issue, it looks like iwd is flushing all BSS entries by using NL80211_SCAN_FLAG_FLUSH for both full scans and partial-channel scans (i.e. only 2.4GHz, only 5GHz), and if this happens during the network connection process it causes the failures.

The observed behavior is:

1. iwd issues a 2.4GHz-only quick scan with FLUSH
2. User initiates connection to a 5GHz BSS from a previous scan
3. Scan completes, kernel flushes all BSS entries
4. Connection fails because the target BSS is no longer in cache

Is this intended behavior of NL80211_SCAN_FLAG_FLUSH where it clears all BSS entries regardless of which channels were scanned, or should NL80211_SCAN_FLAG_FLUSH be limited to channels that were scanned in the request which triggered them? The uAPI definition for it is simply "flush cache before scanning", but this seems open to interpretation as to whether it means the entire cache or just the cache on scanned channels.

If this is working as designed then I will look at submitting a fix to iwd, but if limiting the flush during partial scans is a viable option I could look at fixing that on the kernel side.

Thanks,
Matt

^ permalink raw reply

* Re: [PATCH v2 1/2] wifi: ath12k: Remove frequency range filtering for single-phy devices
From: Saikiran B @ 2026-01-27 19:06 UTC (permalink / raw)
  To: Baochen Qiang; +Cc: ath12k, linux-wireless, kvalo
In-Reply-To: <d6910fe4-cd35-4696-9a0e-13904f512ff8@oss.qualcomm.com>

Hi Baochen,

You were absolutely right.

I manually updated board-2.bin to the latest upstream version (MD5:
74878...), and 5GHz is now working perfectly without any driver
changes.

It seems the linux-firmware package provided by my distro was just old
enough to miss that specific fix.

Next Steps:

I will drop the "Remove frequency range filtering" patch (Patch 1/2)
since the correct firmware resolves the root cause.

Regarding the "Firmware stats leak" (Patch 2/2): I am currently
testing the new firmware without this patch to see if the -71 errors
persist. If the driver still leaks memory on error paths, I will send
this as a standalone patch.

Thanks for pointing out the firmware mismatch!

Thanks & Regards,
Saikiran

On Tue, Jan 27, 2026 at 3:52 PM Baochen Qiang
<baochen.qiang@oss.qualcomm.com> wrote:
>
>
>
> On 1/27/2026 4:58 PM, Saikiran B wrote:
> > Hi Baochen,
> >
> > 1. Here are the MD5 sums from my /lib/firmware/ath12k/WCN7850/hw2.0/
> > (sourced from linux-firmware 20250901):
>
> this is a bit old. Please try the latest board-2.bin from linux-firmware repo:
>
>
> commit 1d588f106ceea113c4c650b43d948da5746e704c
> Author: Jeff Johnson <jjohnson@qti.qualcomm.com>
> Date:   Thu Sep 18 11:03:58 2025 -0700
>
>     ath12k: WCN7850 hw2.0: update board-2.bin
>
>     Signed-off-by: Jeff Johnson <jjohnson@qti.qualcomm.com>
>
>
> $ md5sum board-2.bin
> 74878eeb4ea01b8ea6bd19b57c99d702  board-2.bin
>
>
> >
> > 2fa9a691f199b25aafce48967d93c85b  amss.bin.zst
> > 7cd6eb50e9a6ad98e658a53033214c9e  board-2.bin.zst
> > 62c117046e52cb78c922635bea177afe  m3.bin.zst
> > 953ba9719c55664a5d91d99b412caee1  Notice.txt.zst
> >
> > 2. Logs: I realized CONFIG_ATH12K_DEBUG was disabled in my recent
> > build. I am rebuilding the kernel now with the debug mask enabled and
> > will provide the verbose logs in a follow-up email once tested.
> >
> > 3. Regarding the Frequency Logic: While I gather the logs, I'd like to
> > highlight that the issue likely persists regardless of the firmware
> > behavior.
> >
> > The freq_range filter assumes ar has a single contiguous range (e.g.,
> > start=5150, end=5850). For a Single-PHY device like WCN7850 that
> > supports disjoint bands (2.4GHz + 5GHz), a single start/end pair
> > cannot correctly represent the capabilities.
> >
> > If we set it to cover 2.4GHz, it filters 5GHz.
> >
> > If we set it to cover both (e.g., 2.4GHz to 7GHz), the filter allows
> > everything and becomes redundant.
> >
> > I will get back to you shortly with the logs to confirm the firmware state.
> >
> > Thanks & Regards,
> > Saikiran
> >
> > On Tue, Jan 27, 2026 at 1:11 PM Baochen Qiang
> > <baochen.qiang@oss.qualcomm.com> wrote:
> >>
> >>
> >>
> >> On 1/27/2026 12:04 PM, Saikiran B wrote:
> >>> Hi Baochen,
> >>>
> >>> I checked the logs again.
> >>>
> >>> The issue seems to be coming from how
> >>> [ath12k_regd_update()](drivers/net/wireless/ath/ath12k/reg.c) handles
> >>> frequency range updates for this device. WCN7850 is a single-phy
> >>> device (single [ar](drivers/net/wireless/ath/ath12k/mac.c) that
> >>> supports both 2.4GHz and 5GHz/6GHz.
> >>>
> >>> In ath12k_regd_update():
> >>>
> >>> 1. It updates the range for 2GHz capabilities (sets range to ~2.4GHz).
> >>> 2. It then attempts to update for 5GHz capabilities.
> >>> 3. Because `ar->supports_6ghz` is true, it uses the 6GHz path logic.
> >>> 4. However, `ab->reg_freq_6ghz` appears to be uninitialized/invalid at
> >>
> >> Hmm, this seems like a known firmware bug. Are you using the latest board-2.bin? please
> >> share md5sum of your firmware binaries under /lib/firmware/ath12k/WCN7850/hw2.0
> >>
> >> If you are using the latest, please help collect verbose ath12k dmesg log:
> >>
> >> modprobe ath12k debug_mask=0xffffffff
> >> modprobe ath12k_wifi7
> >>
> >>> this stage (possibly due to regulatory init timing or country code
> >>> issues, On Lenovo Yoga Slim 7x, SMBIOS does not seem to provide a
> >>> valid country code. The driver attempts to set an invalid country code
> >>> which the firmware rejects. This inturn triggers this issue. Fixing
> >>> SMBIOS parsing/defaults is a separate, complex issue that varies by
> >>> vendor.), causing the 5GHz range update to be skipped or invalid.
> >>>
> >>> [ 11.699027] ath12k_pci 0004:01:00.0: Failed to set the requested
> >>> Country regulatory setting
> >>> [ 31.111995] ath12k_pci 0004:01:00.0: Failed to set the requested
> >>> Country regulatory setting
> >>>
> >>> As a result, `ar->freq_range` remains effectively set for only the 2.4GHz band.
> >>>
> >>> When ath12k_reg_update_chan_list() runs:
> >>>
> >>> It compares 5GHz channels (e.g., 5180 MHz) against a
> >>> [freq_range](drivers/net/wireless/ath/ath12k/reg.c) that only covers
> >>> 2.4GHz.
> >>> Result: `center_freq > end_freq` is TRUE -> Channel is filtered out.
> >>>
> >>> The filtering logic added in acc152f9be20 works for split-phy topology
> >>> where each host ar covers a specific range. For WCN7850, where a
> >>> single host ar covers disjoint bands (2.4G + 5G), [freq_range] but
> >>> [freq_range](drivers/net/wireless/ath/ath12k/reg.c) (start/end) cannot
> >>> represent multiple disjoint bands at the same time.
> >>>
> >>> Removing this driver-level filter allows the firmware (which knows the
> >>> true capabilities) to handle the channel list correctly.
> >>>
> >>> Thanks & Regards,
> >>> Saikiran
> >>>
> >>> On Tue, Jan 27, 2026 at 8:52 AM Baochen Qiang
> >>> <baochen.qiang@oss.qualcomm.com> wrote:
> >>>>
> >>>>
> >>>>
> >>>> On 1/26/2026 5:52 PM, Saikiran wrote:
> >>>>> The frequency range filtering added in commit acc152f9be20 was designed
> >>>>> for split-phy devices where multiple radios with overlapping frequency
> >>>>> ranges within the same band are combined into a single wiphy. Each radio
> >>>>> in such setups handles only a subset of channels within a band (e.g., two
> >>>>> 5GHz radios covering 5GHz-low and 5GHz-high separately).
> >>>>>
> >>>>> However, this filtering breaks single-phy devices like WCN7850 that use
> >>>>> a single radio to handle both 2.4GHz and 5GHz bands. On these devices,
> >>>>
> >>>> To be accurate, WCN7850 still gets two phys in hardware, it is just in host that we treat
> >>>> it as single.
> >>>>
> >>>>> the freq_range is set to cover the entire supported spectrum, but the
> >>>>
> >>>> exactly
> >>>>
> >>>>> filtering logic incorrectly restricts channels, causing 5GHz to become
> >>>>> completely unusable.
> >>>>
> >>>> how? what are the actual values of freq_range when you hit the issue and how are they
> >>>> calculated?
> >>>>
> >>>>>
> >>>>> The issue manifests as:
> >>>>> - All 5GHz channels filtered out during channel list updates
> >>>>> - No 5GHz SSIDs visible in scans
> >>>>> - Only 2.4GHz networks functional
> >>>>>
> >>>>> Remove the frequency range filtering entirely and rely on the firmware
> >>>>> to handle frequency restrictions based on actual hardware capabilities.
> >>>>> This approach works correctly for both split-phy and single-phy devices,
> >>>>> as the firmware has complete knowledge of what the hardware supports.
> >>>>>
> >>>>> Fixes: acc152f9be20 ("wifi: ath12k: combine channel list for split-phy devices in single-wiphy")
> >>>>> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302 (Lenovo Yoga Slim 7x)
> >>>>> Signed-off-by: Saikiran <bjsaikiran@gmail.com>
> >>>>> ---
> >>>>>  drivers/net/wireless/ath/ath12k/reg.c | 13 -------------
> >>>>>  1 file changed, 13 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
> >>>>> index 7898f6981e5a..48c362a86524 100644
> >>>>> --- a/drivers/net/wireless/ath/ath12k/reg.c
> >>>>> +++ b/drivers/net/wireless/ath/ath12k/reg.c
> >>>>> @@ -153,12 +153,6 @@ int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
> >>>>>                       if (bands[band]->channels[i].flags &
> >>>>>                           IEEE80211_CHAN_DISABLED)
> >>>>>                               continue;
> >>>>> -                     /* Skip Channels that are not in current radio's range */
> >>>>> -                     if (bands[band]->channels[i].center_freq <
> >>>>> -                         KHZ_TO_MHZ(ar->freq_range.start_freq) ||
> >>>>> -                         bands[band]->channels[i].center_freq >
> >>>>> -                         KHZ_TO_MHZ(ar->freq_range.end_freq))
> >>>>> -                             continue;
> >>>>>
> >>>>>                       num_channels++;
> >>>>>               }
> >>>>> @@ -190,13 +184,6 @@ int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait)
> >>>>>                       if (channel->flags & IEEE80211_CHAN_DISABLED)
> >>>>>                               continue;
> >>>>>
> >>>>> -                     /* Skip Channels that are not in current radio's range */
> >>>>> -                     if (bands[band]->channels[i].center_freq <
> >>>>> -                         KHZ_TO_MHZ(ar->freq_range.start_freq) ||
> >>>>> -                         bands[band]->channels[i].center_freq >
> >>>>> -                         KHZ_TO_MHZ(ar->freq_range.end_freq))
> >>>>> -                             continue;
> >>>>> -
> >>>>>                       /* TODO: Set to true/false based on some condition? */
> >>>>>                       ch->allow_ht = true;
> >>>>>                       ch->allow_vht = true;
> >>>>
> >>
>

^ permalink raw reply

* Re: [PATCH ath-current v2] wifi: ath11k: fix wrong TID passed when stopping AMPDU session
From: Jeff Johnson @ 2026-01-27 18:37 UTC (permalink / raw)
  To: Pablo Martin-Gomez, ath11k; +Cc: linux-wireless
In-Reply-To: <20260126174049.1370659-1-pmartin-gomez@freebox.fr>

On 1/26/2026 9:40 AM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath11k_dp_rx_ampdu_stop() calls
> ath11k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
> 
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
> 
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
> 
> Fix this by passing the correct TID entry:
>   &peer->rx_tid[params->tid]
> 
> Fixes: fe201947f8bd ("ath11k: update bawindow size in delba process")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
> ---
>  drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
> index b9e976ddcbbf..8db04c38dfba 100644
> --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
> +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
> @@ -1132,7 +1132,7 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
>  		return 0;
>  	}
>  
> -	ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
> +	ret = ath11k_peer_rx_tid_reo_update(ar, peer, &peer->rx_tid[params->tid], 1, 0, false);

Our ath11k-check scrip reports:
drivers/net/wireless/ath/ath11k/dp_rx.c:1135: line length of 95 exceeds 90 columns

I can split this for you when I apply the patch

>  	spin_unlock_bh(&ab->base_lock);
>  	if (ret) {
>  		ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",


^ permalink raw reply

* Re: [PULL linux-firmware] ath10k, ath11k and ath12k firmware ath-20260127
From: Dmitry Baryshkov @ 2026-01-27 18:36 UTC (permalink / raw)
  To: Jeff Johnson
  Cc: linux-firmware, linux-wireless, ath10k, ath11k, ath12k, jjohnson
In-Reply-To: <6f1fa3be-e28e-4653-bbf6-b0b64b7e65b2@oss.qualcomm.com>

On Tue, Jan 27, 2026 at 10:04:46AM -0800, Jeff Johnson wrote:
> Hi,
> Here's a new pull request for ath10k, ath11k and ath12k.
> 
> No updates for ath10k.
> 
> For ath11k update board files for QCA6698AQ/hw2.1 and WCN6855/hw2.0 to support:
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=255

Don't we need the variant for this one? Otherwise it's too easy to clash
with another unprogrammed board.

> bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618,variant=QC_QCS615_Ride
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268,variant=QC_Hamoa_IOT_EVK
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618
> bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618,variant=QC_QCS615_Ride
> 
> For ath12k update firmware for QCN9274/hw2.0.
> 
> Please let me know if there are any problems.
> 
> Thanks,
> /jeff
-- 
With best wishes
Dmitry

^ permalink raw reply

* [PULL linux-firmware] ath10k, ath11k and ath12k firmware ath-20260127
From: Jeff Johnson @ 2026-01-27 18:04 UTC (permalink / raw)
  To: linux-firmware; +Cc: linux-wireless, ath10k, ath11k, ath12k, jjohnson

Hi,
Here's a new pull request for ath10k, ath11k and ath12k.

No updates for ath10k.

For ath11k update board files for QCA6698AQ/hw2.1 and WCN6855/hw2.0 to support:
bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268
bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=255
bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618,variant=QC_QCS615_Ride
bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268
bus=pci,vendor=17cb,device=1103,subsystem-vendor=105b,subsystem-device=e105,qmi-chip-id=2,qmi-board-id=268,variant=QC_Hamoa_IOT_EVK
bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618
bus=pci,vendor=17cb,device=1103,subsystem-vendor=17cb,subsystem-device=0108,qmi-chip-id=2,qmi-board-id=34618,variant=QC_QCS615_Ride

For ath12k update firmware for QCN9274/hw2.0.

Please let me know if there are any problems.

Thanks,
/jeff
---

The following changes since commit 0882248d05045565bfefe1f07cbfc22276c9f058:

  Merge branch 'lenovo-ish-uspstream-clean' into 'main' (2026-01-23 19:26:31 +0000)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git ath-20260127

for you to fetch changes up to 6bbca7605494e4a72b219c2d85a8026e8de3eef2:

  ath12k: QCN9274 hw2.0: update to WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1 (2026-01-27 09:50:03 -0800)

----------------------------------------------------------------
Jeff Johnson (3):
      ath11k: QCA6698AQ hw2.1: update board-2.bin
      ath11k: WCN6855 hw2.0: update board-2.bin
      ath12k: QCN9274 hw2.0: update to WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1

 WHENCE                              |   2 +-
 ath11k/QCA6698AQ/hw2.1/board-2.bin  | Bin 986384 -> 1046764 bytes
 ath11k/WCN6855/hw2.0/board-2.bin    | Bin 7056532 -> 7116936 bytes
 ath12k/QCN9274/hw2.0/firmware-2.bin | Bin 15183928 -> 16035896 bytes
 4 files changed, 1 insertion(+), 1 deletion(-)

^ permalink raw reply

* Re: [PATCH v4 ath-current 0/2] wifi: ath11k: add usecase firmware handling based on device compatible
From: Jeff Johnson @ 2026-01-27 17:31 UTC (permalink / raw)
  To: Miaoqing Pan, jjohnson, johannes, robh, krzk+dt, conor+dt, krzk
  Cc: ath11k, linux-wireless, linux-kernel, devicetree, jonas.gorski
In-Reply-To: <20260121095055.3683957-1-miaoqing.pan@oss.qualcomm.com>

On 1/21/2026 1:50 AM, Miaoqing Pan wrote:
> The 'firmware-name' property was introduced to allow end-users and
> integrators to select use-case-specific firmware for the WCN6855.
> But for M.2 WLAN chips, there is no suitable DTS node to specify
> the 'firmware-name' property. In addition, assigning firmware for
> the M.2 PCIe interface causes chips that do not use use-case-specific
> firmware to fail. Therefore, abandoning the approach of specifying
> firmware in DTS. As an alternative, propose a static lookup table
> mapping device compatible to firmware names.
> 
> ---
> v2:
> - Drops `firmware-name` from completely.
> - Updates the commit message to clearly state that the property is
>   obsolete and the change is ABI-breaking but safe for upstream.
> v3:
>  - Deprecate 'firmware-name' property instead of obsolete.
>  - Keep the ABI backwards compatible.
> v4:
>  - Use of_machine_is_compatible() to simplify the code.
>  - Add back Acked-by tag. 
> ---
> 
> Miaoqing Pan (2):
>   wifi: ath11k: add usecase firmware handling based on device compatible
>   dt-bindings: net: wireless: ath11k-pci: deprecate 'firmware-name'
>     property
> 
>  .../net/wireless/qcom,ath11k-pci.yaml         |  1 +
>  drivers/net/wireless/ath/ath11k/core.c        | 27 +++++++++++++++++++
>  drivers/net/wireless/ath/ath11k/core.h        |  4 +++
>  3 files changed, 32 insertions(+)
> 
> 
> base-commit: d8e1f4a193101a72235416f189b01131a57e26e9

Krzysztof,
Since you previously NAKed this series, can you confirm that your review
comments have been addressed?

Thanks!
/jeff


^ permalink raw reply

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
From: Felix Fietkau @ 2026-01-27 17:00 UTC (permalink / raw)
  To: Bitterblue Smith, Lucid Duck, linux-wireless; +Cc: Lorenzo Bianconi, Ryder Lee
In-Reply-To: <f677b9d5-235c-4ff4-be36-c71c43b6988a@gmail.com>

On 27.01.26 17:17, Bitterblue Smith wrote:
> On 27/01/2026 14:21, Felix Fietkau wrote:
>> On 25.01.26 23:22, Lucid Duck wrote:
>>> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
>>> to the firmware but never updates phy->txpower_cur. This causes
>>> mt76_get_txpower() to return stale or incorrect values (typically
>>> showing 3 dBm regardless of actual transmit power) when userspace
>>> queries TX power via nl80211.
>>>
>>> This affects MT7921 and other connac-based devices. Users observe:
>>>    $ iw dev wlan0 info
>>>    ...
>>>    txpower 3.00 dBm
>>>
>>> The firmware receives and applies the correct power level, but the
>>> reported value is wrong because txpower_cur is never set.
>>>
>>> Fix by updating phy->txpower_cur after successfully configuring TX
>>> power, matching the behavior of other mt76 drivers like mt7915.
>>>
>>> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
>>> ---
>>>   drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> index 045771228..7cd357419 100644
>>> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>>> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>>>     int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>>   {
>>> -    int err;
>>> +    int err, tx_power;
>>>         if (phy->cap.has_2ghz) {
>>>           err = mt76_connac_mcu_rate_txpower_band(phy,
>>> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>>               return err;
>>>       }
>>>   +    /* Update txpower_cur for accurate reporting via nl80211 */
>>> +    tx_power = 2 * phy->hw->conf.power_level;
>>> +    if (!tx_power)
>>> +        tx_power = 127;
>>> +    phy->txpower_cur = tx_power;
>> 
>> phy->hw->conf.power_level is the user configured power level, not what the hardware is capable of transmitting.
>> 
>> To fix it properly, I think you should determine the maximum rate power used in the loop within mt76_connac_mcu_rate_txpower_band (updated with each call).
>> 
>> - Felix
>> 
> 
> What about these older patches?
> 
> https://patchwork.kernel.org/project/linux-wireless/list/?series=932665&submitter=&state=*&q=&archive=&delegate=

If I remember correctly, I found some issues in those patches and asked 
the Author about them and he mentioned that he only tested them on some 
old 5.15 vendor tree and didn't actually use anything recent.
So I dropped them from my tree again.

- Felix

^ permalink raw reply

* Re: [PATCH wireless-next v3 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-27 16:27 UTC (permalink / raw)
  To: Pablo MARTIN-GOMEZ, linux-wireless
In-Reply-To: <55abccdc-3752-4bb6-b022-42901a8b303f@freebox.fr>

On Tue, 2026-01-27 at 16:08 +0100, Pablo MARTIN-GOMEZ wrote:
> 
> > +#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN		0x000000FF
> So I understand you've changed the NPCA channel to be 8bits instead of 
> 4. I see it's 8bits in D0.3 but it has been updated to 4bits in both 
> D1.0 and D1.2, so I would believe 4bits is the correct value

Hmm. Four bits are clearly not enough to hold a "channel number" as
described in the text in D1.2, and had been told that it's going to be
changed to 8 bits. It's also inconsistent between this and 9.4.2.360.3
where it says it's 8 bits, but the interpretation is the same as here.
Clearly not possible.

However, now asking around again it seems that there's a chance it'll
just remain four bits places, defining this as the location of the NPCA
primary channel within the overall BSS bandwidth. But then the text
needs to be updated, the field should be renamed to "... Location", and
the 8 bit field in the update needs to be changed to four bits ...


Also, yes, I haven't updated this to D1.2 yet, not sure there's much
value in doing that at this point. Mostly we need this for the code and
the netlink attributes etc. at this point so Qualcomm can also base
their other changes on _something_.

> > +#define IEEE80211_UHR_MAC_CAP0_ML_PM_SUPP		0x08
> FYI this has been converted in Reserved for D1.2

Noted.

> > +#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV	0x1C
> I would choose between shrinking `Interval` into either INTV or INTVL 
> but not use both
> > +#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTVL_LOW	0xE0

Heh, fair.

> > +
> > +#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTVL_LOW	0x03
> IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTVL_HIGH
> > +#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE		0x04
> You are missing the bit for Bounded ESS (B34) [BTM Assurance is B35]
> > +
> > +struct ieee80211_uhr_capa_mac {
> > +	u8 mac_cap[5];
> > +} __packed;
> DBE Capability Parameters is not handled yet? [it's defined in figure 
> 9-aa8, even if the title of the figure is wrong in D1.0]

Seems plausible that the title of the figure confused me, or at least I
didn't trust it :)

> > +
> > +struct ieee80211_uhr_capa {
> > +	struct ieee80211_uhr_capa_mac mac;
> > +	u8 variable[];
> > +} __packed;
> > +
> > +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_LE80		0x01
> This is specifically for sounding NDP, I would name them 
> IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_XXX instead

Yeah, good point.

> > +	WLAN_EID_EXT_BSS_TRANS_PARAMS = 155,
> This is specific for SMD, I feel like WLAN_EID_EXT_SMD_BSS_TRANS_PARAMS 
> is more appropriate

fair

johannes

^ permalink raw reply

* Re: [PATCH] wifi: mt76: connac: fix txpower_cur not being updated
From: Bitterblue Smith @ 2026-01-27 16:17 UTC (permalink / raw)
  To: Felix Fietkau, Lucid Duck, linux-wireless; +Cc: Lorenzo Bianconi, Ryder Lee
In-Reply-To: <4ddb4417-d623-44ae-878d-5ee5939f2826@nbd.name>

On 27/01/2026 14:21, Felix Fietkau wrote:
> On 25.01.26 23:22, Lucid Duck wrote:
>> The mt76_connac_mcu_set_rate_txpower() function sends TX power settings
>> to the firmware but never updates phy->txpower_cur. This causes
>> mt76_get_txpower() to return stale or incorrect values (typically
>> showing 3 dBm regardless of actual transmit power) when userspace
>> queries TX power via nl80211.
>>
>> This affects MT7921 and other connac-based devices. Users observe:
>>    $ iw dev wlan0 info
>>    ...
>>    txpower 3.00 dBm
>>
>> The firmware receives and applies the correct power level, but the
>> reported value is wrong because txpower_cur is never set.
>>
>> Fix by updating phy->txpower_cur after successfully configuring TX
>> power, matching the behavior of other mt76 drivers like mt7915.
>>
>> Signed-off-by: Lucid Duck <lucid_duck@justthetip.ca>
>> ---
>>   drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 8 +++++++-
>>   1 file changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> index 045771228..7cd357419 100644
>> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
>> @@ -2251,7 +2251,7 @@ mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy,
>>     int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>   {
>> -    int err;
>> +    int err, tx_power;
>>         if (phy->cap.has_2ghz) {
>>           err = mt76_connac_mcu_rate_txpower_band(phy,
>> @@ -2272,6 +2272,12 @@ int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy)
>>               return err;
>>       }
>>   +    /* Update txpower_cur for accurate reporting via nl80211 */
>> +    tx_power = 2 * phy->hw->conf.power_level;
>> +    if (!tx_power)
>> +        tx_power = 127;
>> +    phy->txpower_cur = tx_power;
> 
> phy->hw->conf.power_level is the user configured power level, not what the hardware is capable of transmitting.
> 
> To fix it properly, I think you should determine the maximum rate power used in the loop within mt76_connac_mcu_rate_txpower_band (updated with each call).
> 
> - Felix
> 

What about these older patches?

https://patchwork.kernel.org/project/linux-wireless/list/?series=932665&submitter=&state=*&q=&archive=&delegate=

^ permalink raw reply

* Re: [PATCH wireless-next v3 3/3] wifi: mac80211: add initial UHR support
From: Pablo MARTIN-GOMEZ @ 2026-01-27 16:12 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127151005.751175e8035e.Ieec940b58dbf8115dab7e1e24cb5513f52c8cb2f@changeid>

Hello,

My comment below:

On 27/01/2026 15:09, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> Add support for making UHR connections and accepting AP
> stations with UHR support.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> v3:
>   - use uhr_oper instead of removed uhr_capa
>   - fix indentation (Jeff Johnson)
> ---
>   include/net/mac80211.h     |  33 +++++++++-
>   net/mac80211/Makefile      |   2 +-
>   net/mac80211/cfg.c         |  14 ++++
>   net/mac80211/ieee80211_i.h |  17 ++++-
>   net/mac80211/main.c        |  13 +++-
>   net/mac80211/mlme.c        | 131 ++++++++++++++++++++++++++++++++++---
>   net/mac80211/parse.c       |  20 ++++++
>   net/mac80211/rx.c          |  21 ++++++
>   net/mac80211/sta_info.c    |  11 ++++
>   net/mac80211/sta_info.h    |  80 ++++++++++++++++------
>   net/mac80211/uhr.c         |  29 ++++++++
>   net/mac80211/util.c        |  34 ++++++++++
>   12 files changed, 373 insertions(+), 32 deletions(-)
>   create mode 100644 net/mac80211/uhr.c
[...]
> @@ -1040,8 +1064,15 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
>   
>   	r = STA_STATS_FIELD(BW, s->bw);
>   
> -	if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
> -		r |= STA_STATS_FIELD(SGI, 1);
> +	switch (s->encoding) {
> +	case RX_ENC_HT:
> +	case RX_ENC_VHT:
> +		if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
> +			r |= STA_STATS_FIELD(SGI, 1);
> +		break;
> +	default:
> +		break;
> +	}
Not sure this change should be in this commit

Best regards,

Pablo MG


^ permalink raw reply

* Re: [PATCH wireless-next v3 2/3] wifi: cfg80211: add initial UHR support
From: Pablo MARTIN-GOMEZ @ 2026-01-27 15:43 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127151005.dea4882ccc8e.I26126bebd83c7ab17e99827489f946ceabb3521f@changeid>

Hello,

One comment below:

On 27/01/2026 15:09, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> Add initial support for making UHR connections (or suppressing
> that), adding UHR capable stations on the AP side, encoding
> and decoding UHR MCSes (except rate calculation for the new
> MCSes 17, 19, 20 and 23) as well as regulatory support.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> v3:
>   - remove UHR capa pointer from AP settings, it's not in
>     the beacon anyway
>   - fix kernel-doc (Jeff Johnson)
> ---
>   include/net/cfg80211.h       |  56 ++++++++++++++++++-
>   include/uapi/linux/nl80211.h |  30 +++++++++++
>   net/wireless/nl80211.c       | 102 +++++++++++++++++++++++++++++++++--
>   net/wireless/reg.c           |   2 +
>   net/wireless/util.c          |  96 +++++++++++++++++++++++++--------
>   5 files changed, 259 insertions(+), 27 deletions(-)
[...]
>   
> -static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
> +static u32 _cfg80211_calculate_bitrate_eht_uhr(struct rate_info *rate)
>   {
>   #define SCALE 6144
> -	static const u32 mcs_divisors[16] = {
> -		102399, /* 16.666666... */
> -		 51201, /*  8.333333... */
> -		 34134, /*  5.555555... */
> -		 25599, /*  4.166666... */
> -		 17067, /*  2.777777... */
> -		 12801, /*  2.083333... */
> -		 11377, /*  1.851725... */
> -		 10239, /*  1.666666... */
> -		  8532, /*  1.388888... */
> -		  7680, /*  1.250000... */
> -		  6828, /*  1.111111... */
> -		  6144, /*  1.000000... */
> -		  5690, /*  0.926106... */
> -		  5120, /*  0.833333... */
> -		409600, /* 66.666666... */
> -		204800, /* 33.333333... */
> +	static const u32 mcs_divisors[] = {
> +		[ 0] = 102399, /* 16.666666... */
> +		[ 1] =  51201, /*  8.333333... */
> +		[ 2] =  34134, /*  5.555555... */
> +		[ 3] =  25599, /*  4.166666... */
> +		[ 4] =  17067, /*  2.777777... */
> +		[ 5] =  12801, /*  2.083333... */
> +		[ 6] =  11377, /*  1.851725... */
> +		[ 7] =  10239, /*  1.666666... */
> +		[ 8] =   8532, /*  1.388888... */
> +		[ 9] =   7680, /*  1.250000... */
> +		[10] =   6828, /*  1.111111... */
> +		[11] =   6144, /*  1.000000... */
> +		[12] =   5690, /*  0.926106... */
> +		[13] =   5120, /*  0.833333... */
> +		[14] = 409600, /* 66.666666... */
> +		[15] = 204800, /* 33.333333... */
> +		[17] =  38400, /*  6.250180... */
> +		[19] =  19200, /*  3.125090... */
> +		[20] =  15360, /*  2.500000... */
> +		[23] =   9600, /*  1.562545... */
>   	};
>   	static const u32 rates_996[3] =  { 480388888, 453700000, 408333333 };
>   	static const u32 rates_484[3] =  { 229411111, 216666666, 195000000 };
> @@ -1602,8 +1606,6 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
>   	u64 tmp;
>   	u32 result;
>   
> -	if (WARN_ON_ONCE(rate->mcs > 15))
> -		return 0;
>   	if (WARN_ON_ONCE(rate->eht_gi > NL80211_RATE_INFO_EHT_GI_3_2))
>   		return 0;
>   	if (WARN_ON_ONCE(rate->eht_ru_alloc >
> @@ -1684,7 +1686,7 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
>   		 rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_26)
>   		result = rates_26[rate->eht_gi];
>   	else {
> -		WARN(1, "invalid EHT MCS: bw:%d, ru:%d\n",
> +		WARN(1, "invalid EHT or UHR MCS: bw:%d, ru:%d\n",
>   		     rate->bw, rate->eht_ru_alloc);
>   		return 0;
>   	}
> @@ -1698,11 +1700,61 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
>   	tmp *= rate->nss;
>   	do_div(tmp, 8);
>   
> +	/* and handle interference mitigation - 0.9x */
> +	if (rate->flags & RATE_INFO_FLAGS_UHR_IM) {
The function cfg80211_calculate_bitrate_eht has already a bunch of 
sanity check, shouldn't we add nss == 1 and MCS != 15 for IM?
> +		tmp *= 9000;
> +		do_div(tmp, 10000);
> +	}
> +
>   	result = tmp;
>   
>   	return result / 10000;
>   }
>   
>
>   

Best regards,

Pablo MG


^ permalink raw reply

* Re: [PATCH wireless-next v3 1/3] wifi: ieee80211: add some initial UHR definitions
From: Pablo MARTIN-GOMEZ @ 2026-01-27 15:08 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127151005.7681c4f95202.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>

Hello,

See my comments below:

On 27/01/2026 15:09, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> This is based on Draft P802.11bn_D1.0, but that's still very
> incomplete, so don't handle a number of things and make some
> local decisions such as 40 bits for MAC capabilities etc.
> Also, I fixed the NPCA channel to be 8 instead of 4 bits.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
>   include/linux/ieee80211-uhr.h | 176 ++++++++++++++++++++++++++++++++++
>   include/linux/ieee80211.h     |  31 +++++-
>   2 files changed, 206 insertions(+), 1 deletion(-)
>   create mode 100644 include/linux/ieee80211-uhr.h
>
> diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
> new file mode 100644
> index 000000000000..e57181ba0182
> --- /dev/null
> +++ b/include/linux/ieee80211-uhr.h
> @@ -0,0 +1,176 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * IEEE 802.11 UHR definitions
> + *
> + * Copyright (c) 2025 Intel Corporation
> + */
> +#ifndef LINUX_IEEE80211_UHR_H
> +#define LINUX_IEEE80211_UHR_H
> +
> +#include <linux/types.h>
> +#include <linux/if_ether.h>
> +
> +#define IEEE80211_UHR_OPER_PARAMS_DPS_ENA		0x0001
> +#define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA		0x0002
> +#define IEEE80211_UHR_OPER_PARAMS_DBE_ENA		0x0004
> +#define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA		0x0008
> +
> +struct ieee80211_uhr_oper {
> +	__le16 params;
> +	u8 basic_mcs_nss_set[4];
> +	u8 variable[];
> +} __packed;
> +
> +#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN		0x000000FF
So I understand you've changed the NPCA channel to be 8bits instead of 
4. I see it's 8bits in D0.3 but it has been updated to 4bits in both 
D1.0 and D1.2, so I would believe 4bits is the correct value
> +#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH	0x00000F00
> +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY		0x0003F000
> +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY	0x00FC0000
> +#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC		0x03000000
> +#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN		0x04000000
> +#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES	0x08000000
> +
> +struct ieee80211_uhr_npca_info {
> +	__le32 params;
> +	__le16 dis_subch_bmap[];
> +} __packed;
> +
> +static inline bool ieee80211_uhr_oper_size_ok(const u8 *data, u8 len,
> +					      bool beacon)
> +{
> +	const struct ieee80211_uhr_oper *oper = (const void *)data;
> +	u8 needed = sizeof(*oper);
> +
> +	if (len < needed)
> +		return false;
> +
> +	/* nothing else present in beacons */
> +	if (beacon)
> +		return true;
> +
> +	/* FIXME: DPS, DBE, P-EDCA (consider order, also relative to NPCA) */
> +
> +	if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) {
> +		const struct ieee80211_uhr_npca_info *npca =
> +			(const void *)oper->variable;
> +
> +		needed += sizeof(*npca);
> +
> +		if (len < needed)
> +			return false;
> +
> +		if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
> +			needed += sizeof(npca->dis_subch_bmap[0]);
> +	}
> +
> +	return len >= needed;
> +}
> +
> +/*
> + * Note: cannot call this on the element coming from a beacon,
> + * must ensure ieee80211_uhr_oper_size_ok(..., false) first
> + */
> +static inline const struct ieee80211_uhr_npca_info *
> +ieee80211_uhr_npca_info(const struct ieee80211_uhr_oper *oper)
> +{
> +	if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)))
> +		return NULL;
> +
> +	/* FIXME: DPS */
> +
> +	return (const void *)oper->variable;
> +}
> +
> +static inline const __le16 *
> +ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_oper *oper)
> +{
> +	const struct ieee80211_uhr_npca_info *npca;
> +
> +	npca = ieee80211_uhr_npca_info(oper);
> +	if (!npca)
> +		return NULL;
> +	if (!(npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)))
> +		return NULL;
> +	return npca->dis_subch_bmap;
> +}
> +
> +#define IEEE80211_UHR_MAC_CAP0_DPS_SUPP			0x01
> +#define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP		0x02
> +#define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP	0x04
> +#define IEEE80211_UHR_MAC_CAP0_ML_PM_SUPP		0x08
FYI this has been converted in Reserved for D1.2
> +#define IEEE80211_UHR_MAC_CAP0_NPCA_SUPP		0x10
> +#define IEEE80211_UHR_MAC_CAP0_ENH_BSR_SUPP		0x20
> +#define IEEE80211_UHR_MAC_CAP0_ADD_MAP_TID_SUPP		0x40
> +#define IEEE80211_UHR_MAC_CAP0_EOTSP_SUPP		0x80
> +
> +#define IEEE80211_UHR_MAC_CAP1_DSO_SUPP			0x01
> +#define IEEE80211_UHR_MAC_CAP1_PEDCA_SUPP		0x02
> +#define IEEE80211_UHR_MAC_CAP1_DBE_SUPP			0x04
> +#define IEEE80211_UHR_MAC_CAP1_UL_LLI_SUPP		0x08
> +#define IEEE80211_UHR_MAC_CAP1_P2P_LLI_SUPP		0x10
> +#define IEEE80211_UHR_MAC_CAP1_PUO_SUPP			0x20
> +#define IEEE80211_UHR_MAC_CAP1_AP_PUO_SUPP		0x40
> +#define IEEE80211_UHR_MAC_CAP1_DUO_SUPP			0x80
> +
> +#define IEEE80211_UHR_MAC_CAP2_OMC_UL_MU_DIS_RX_SUPP	0x01
> +#define IEEE80211_UHR_MAC_CAP2_AOM_SUPP			0x02
> +#define IEEE80211_UHR_MAC_CAP2_IFCS_LOC_SUPP		0x04
> +#define IEEE80211_UHR_MAC_CAP2_UHR_TRS_SUPP		0x08
> +#define IEEE80211_UHR_MAC_CAP2_TXSPG_SUPP		0x10
> +#define IEEE80211_UHR_MAC_CAP2_TXOP_RET_IN_TXSPG	0x20
> +#define IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW		0xC0
> +
> +#define IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH	0x03
> +#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV	0x1C
I would choose between shrinking `Interval` into either INTV or INTVL 
but not use both
> +#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTVL_LOW	0xE0
> +
> +#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTVL_LOW	0x03
IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTVL_HIGH
> +#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE		0x04
You are missing the bit for Bounded ESS (B34) [BTM Assurance is B35]
> +
> +struct ieee80211_uhr_capa_mac {
> +	u8 mac_cap[5];
> +} __packed;
DBE Capability Parameters is not handled yet? [it's defined in figure 
9-aa8, even if the title of the figure is wrong in D1.0]
> +
> +struct ieee80211_uhr_capa {
> +	struct ieee80211_uhr_capa_mac mac;
> +	u8 variable[];
> +} __packed;
> +
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_LE80		0x01
This is specifically for sounding NDP, I would name them 
IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_XXX instead
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_LE80	0x02
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_160		0x04
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_160		0x08
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_320		0x10
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_320		0x20
> +#define IEEE80211_UHR_PHY_CAP_ELR_RX			0x40
> +#define IEEE80211_UHR_PHY_CAP_ELR_TX			0x80
> +
> +struct ieee80211_uhr_capa_phy {
> +	u8 cap;
> +} __packed;
> +
> +static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len)
> +{
> +	/* FIXME - DBE */
> +	return len >= sizeof(struct ieee80211_uhr_capa) +
> +		      sizeof(struct ieee80211_uhr_capa_phy);
> +}
> +
> +static inline const struct ieee80211_uhr_capa_phy *
> +ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
> +{
> +	/* FIXME - skip variable DBE stuff, assume not present for now */
> +	return (const void *)&cap->variable;
> +}
> +
> +#define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD		0x01
> +#define IEEE80211_SMD_INFO_CAPA_MAX_NUM_PREP		0x0E
> +#define IEEE80211_SMD_INFO_CAPA_TYPE			0x10
> +#define IEEE80211_SMD_INFO_CAPA_PTK_PER_AP_MLD		0x20
> +
> +struct ieee80211_smd_info {
> +	u8 id[ETH_ALEN];
> +	u8 capa;
> +	__le16 timeout;
> +} __packed;
> +
> +#endif /* LINUX_IEEE80211_UHR_H */
> diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
> index fbde215c25aa..3dcfc4acf7d5 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -1200,8 +1200,9 @@ struct ieee80211_mgmt {
>   #define BSS_MEMBERSHIP_SELECTOR_SAE_H2E 123
>   #define BSS_MEMBERSHIP_SELECTOR_HE_PHY	122
>   #define BSS_MEMBERSHIP_SELECTOR_EHT_PHY	121
> +#define BSS_MEMBERSHIP_SELECTOR_UHR_PHY	120
>   
> -#define BSS_MEMBERSHIP_SELECTOR_MIN	BSS_MEMBERSHIP_SELECTOR_EHT_PHY
> +#define BSS_MEMBERSHIP_SELECTOR_MIN	BSS_MEMBERSHIP_SELECTOR_UHR_PHY
>   
>   /* mgmt header + 1 byte category code */
>   #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
> @@ -1802,6 +1803,15 @@ enum ieee80211_eid_ext {
>   	WLAN_EID_EXT_BANDWIDTH_INDICATION = 135,
>   	WLAN_EID_EXT_KNOWN_STA_IDENTIFCATION = 136,
>   	WLAN_EID_EXT_NON_AP_STA_REG_CON = 137,
> +	WLAN_EID_EXT_UHR_OPER = 151,
> +	WLAN_EID_EXT_UHR_CAPA = 152,
> +	WLAN_EID_EXT_MACP = 153,
> +	WLAN_EID_EXT_SMD = 154,
> +	WLAN_EID_EXT_BSS_TRANS_PARAMS = 155,
This is specific for SMD, I feel like WLAN_EID_EXT_SMD_BSS_TRANS_PARAMS 
is more appropriate
> +	WLAN_EID_EXT_CHAN_USAGE = 156,
> +	WLAN_EID_EXT_UHR_MODE_CHG = 157,
> +	WLAN_EID_EXT_UHR_PARAM_UPD = 158,
> +	WLAN_EID_EXT_TXPI = 159,
>   };
>   
>   /* Action category code */
> @@ -2745,6 +2755,22 @@ static inline bool for_each_element_completed(const struct element *element,
>   #define WLAN_RSNX_CAPA_PROTECTED_TWT BIT(4)
>   #define WLAN_RSNX_CAPA_SAE_H2E BIT(5)
>   
> +/* EBPCC = Enhanced BSS Parameter Change Count */
> +#define IEEE80211_ENH_CRIT_UPD_EBPCC		0x0F
> +#define IEEE80211_ENH_CRIT_UPD_TYPE		0x70
> +#define IEEE80211_ENH_CRIT_UPD_TYPE_NO_UHR	0
> +#define IEEE80211_ENH_CRIT_UPD_TYPE_UHR		1
> +#define IEEE80211_ENH_CRIT_UPD_ALL		0x80
> +
> +/**
> + * struct ieee80211_enh_crit_upd - enhanced critical update (UHR)
> + * @v: value of the enhanced critical update data,
> + *	see %IEEE80211_ENH_CRIT_UPD_* to parse the bits
> + */
> +struct ieee80211_enh_crit_upd {
> +	u8 v;
> +} __packed;
> +
>   /*
>    * reduced neighbor report, based on Draft P802.11ax_D6.1,
>    * section 9.4.2.170 and accepted contributions.
> @@ -2763,6 +2789,7 @@ static inline bool for_each_element_completed(const struct element *element,
>   #define IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS			0x10
>   #define IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE			0x20
>   #define IEEE80211_RNR_TBTT_PARAMS_COLOC_AP			0x40
> +#define IEEE80211_RNR_TBTT_PARAMS_SAME_SMD			0x80
>   
>   #define IEEE80211_RNR_TBTT_PARAMS_PSD_NO_LIMIT			127
>   #define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED			-128
> @@ -2815,12 +2842,14 @@ struct ieee80211_tbtt_info_ge_11 {
>   	u8 bss_params;
>   	s8 psd_20;
>   	struct ieee80211_rnr_mld_params mld_params;
> +	struct ieee80211_enh_crit_upd enh_crit_upd;
>   } __packed;
>   
>   #include "ieee80211-ht.h"
>   #include "ieee80211-vht.h"
>   #include "ieee80211-he.h"
>   #include "ieee80211-eht.h"
> +#include "ieee80211-uhr.h"
>   #include "ieee80211-mesh.h"
>   #include "ieee80211-s1g.h"
>   #include "ieee80211-p2p.h"

Best regards,

Pablo MG


^ permalink raw reply

* [PATCH wireless-next v3 3/3] wifi: mac80211: add initial UHR support
From: Johannes Berg @ 2026-01-27 14:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127141005.583581-5-johannes@sipsolutions.net>

From: Johannes Berg <johannes.berg@intel.com>

Add support for making UHR connections and accepting AP
stations with UHR support.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v3:
 - use uhr_oper instead of removed uhr_capa
 - fix indentation (Jeff Johnson)
---
 include/net/mac80211.h     |  33 +++++++++-
 net/mac80211/Makefile      |   2 +-
 net/mac80211/cfg.c         |  14 ++++
 net/mac80211/ieee80211_i.h |  17 ++++-
 net/mac80211/main.c        |  13 +++-
 net/mac80211/mlme.c        | 131 ++++++++++++++++++++++++++++++++++---
 net/mac80211/parse.c       |  20 ++++++
 net/mac80211/rx.c          |  21 ++++++
 net/mac80211/sta_info.c    |  11 ++++
 net/mac80211/sta_info.h    |  80 ++++++++++++++++------
 net/mac80211/uhr.c         |  29 ++++++++
 net/mac80211/util.c        |  34 ++++++++++
 12 files changed, 373 insertions(+), 32 deletions(-)
 create mode 100644 net/mac80211/uhr.c

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 36ae7fe9ddf3..7bfcb2c02801 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -706,6 +706,7 @@ struct ieee80211_parsed_tpe {
  * @pwr_reduction: power constraint of BSS.
  * @eht_support: does this BSS support EHT
  * @epcs_support: does this BSS support EPCS
+ * @uhr_support: does this BSS support UHR
  * @csa_active: marks whether a channel switch is going on.
  * @mu_mimo_owner: indicates interface owns MU-MIMO capability
  * @chanctx_conf: The channel context this interface is assigned to, or %NULL
@@ -832,6 +833,8 @@ struct ieee80211_bss_conf {
 	u8 pwr_reduction;
 	bool eht_support;
 	bool epcs_support;
+	bool uhr_support;
+
 	bool csa_active;
 
 	bool mu_mimo_owner;
@@ -1598,6 +1601,7 @@ enum mac80211_rx_encoding {
 	RX_ENC_VHT,
 	RX_ENC_HE,
 	RX_ENC_EHT,
+	RX_ENC_UHR,
 };
 
 /**
@@ -1631,7 +1635,7 @@ enum mac80211_rx_encoding {
  * @antenna: antenna used
  * @rate_idx: index of data rate into band's supported rates or MCS index if
  *	HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
- * @nss: number of streams (VHT, HE and EHT only)
+ * @nss: number of streams (VHT, HE, EHT and UHR only)
  * @flag: %RX_FLAG_\*
  * @encoding: &enum mac80211_rx_encoding
  * @bw: &enum rate_info_bw
@@ -1642,6 +1646,11 @@ enum mac80211_rx_encoding {
  * @eht: EHT specific rate information
  * @eht.ru: EHT RU, from &enum nl80211_eht_ru_alloc
  * @eht.gi: EHT GI, from &enum nl80211_eht_gi
+ * @uhr: UHR specific rate information
+ * @uhr.ru: UHR RU, from &enum nl80211_eht_ru_alloc
+ * @uhr.gi: UHR GI, from &enum nl80211_eht_gi
+ * @uhr.elr: UHR ELR MCS was used
+ * @uhr.im: UHR interference mitigation was used
  * @rx_flags: internal RX flags for mac80211
  * @ampdu_reference: A-MPDU reference number, must be a different value for
  *	each A-MPDU but the same for each subframe within one A-MPDU
@@ -1673,6 +1682,12 @@ struct ieee80211_rx_status {
 			u8 ru:4;
 			u8 gi:2;
 		} eht;
+		struct {
+			u8 ru:4;
+			u8 gi:2;
+			u8 elr:1;
+			u8 im:1;
+		} uhr;
 	};
 	u8 rate_idx;
 	u8 nss;
@@ -2434,6 +2449,7 @@ struct ieee80211_sta_aggregates {
  * @he_cap: HE capabilities of this STA
  * @he_6ghz_capa: on 6 GHz, holds the HE 6 GHz band capabilities
  * @eht_cap: EHT capabilities of this STA
+ * @uhr_cap: UHR capabilities of this STA
  * @s1g_cap: S1G capabilities of this STA
  * @agg: per-link data for multi-link aggregation
  * @bandwidth: current bandwidth the station can receive with
@@ -2457,6 +2473,7 @@ struct ieee80211_link_sta {
 	struct ieee80211_sta_he_cap he_cap;
 	struct ieee80211_he_6ghz_capa he_6ghz_capa;
 	struct ieee80211_sta_eht_cap eht_cap;
+	struct ieee80211_sta_uhr_cap uhr_cap;
 	struct ieee80211_sta_s1g_cap s1g_cap;
 
 	struct ieee80211_sta_aggregates agg;
@@ -7289,6 +7306,20 @@ ieee80211_get_eht_iftype_cap_vif(const struct ieee80211_supported_band *sband,
 	return ieee80211_get_eht_iftype_cap(sband, ieee80211_vif_type_p2p(vif));
 }
 
+/**
+ * ieee80211_get_uhr_iftype_cap_vif - return UHR capabilities for sband/vif
+ * @sband: the sband to search for the iftype on
+ * @vif: the vif to get the iftype from
+ *
+ * Return: pointer to the struct ieee80211_sta_uhr_cap, or %NULL is none found
+ */
+static inline const struct ieee80211_sta_uhr_cap *
+ieee80211_get_uhr_iftype_cap_vif(const struct ieee80211_supported_band *sband,
+				 struct ieee80211_vif *vif)
+{
+	return ieee80211_get_uhr_iftype_cap(sband, ieee80211_vif_type_p2p(vif));
+}
+
 /**
  * ieee80211_update_mu_groups - set the VHT MU-MIMO groud data
  *
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a33884967f21..b0e392eb7753 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -36,7 +36,7 @@ mac80211-y := \
 	tdls.o \
 	ocb.o \
 	airtime.o \
-	eht.o
+	eht.o uhr.o
 
 mac80211-$(CONFIG_MAC80211_LEDS) += led.o
 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 964f440e31cd..5f816b4ec438 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1608,6 +1608,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
 		link_conf->eht_mu_beamformer = false;
 	}
 
+	if (params->uhr_oper) {
+		if (!link_conf->eht_support)
+			return -EOPNOTSUPP;
+
+		link_conf->uhr_support = true;
+	}
+
 	if (sdata->vif.type == NL80211_IFTYPE_AP &&
 	    params->mbssid_config.tx_wdev) {
 		err = ieee80211_set_ap_mbssid_options(sdata,
@@ -2085,6 +2092,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
 		       params->vht_capa ||
 		       params->he_capa ||
 		       params->eht_capa ||
+		       params->uhr_capa ||
 		       params->s1g_capa ||
 		       params->opmode_notif_used;
 
@@ -2163,6 +2171,12 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
 						    params->eht_capa_len,
 						    link_sta);
 
+	if (params->uhr_capa)
+		ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband,
+						    params->uhr_capa,
+						    params->uhr_capa_len,
+						    link_sta);
+
 	if (params->s1g_capa)
 		ieee80211_s1g_cap_to_sta_s1g_cap(sdata, params->s1g_capa,
 						 link_sta);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index dc757cb32974..b0a3b264d678 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -394,9 +394,10 @@ enum ieee80211_conn_mode {
 	IEEE80211_CONN_MODE_VHT,
 	IEEE80211_CONN_MODE_HE,
 	IEEE80211_CONN_MODE_EHT,
+	IEEE80211_CONN_MODE_UHR,
 };
 
-#define IEEE80211_CONN_MODE_HIGHEST	IEEE80211_CONN_MODE_EHT
+#define IEEE80211_CONN_MODE_HIGHEST	IEEE80211_CONN_MODE_UHR
 
 enum ieee80211_conn_bw_limit {
 	IEEE80211_CONN_BW_LIMIT_20,
@@ -1826,6 +1827,8 @@ struct ieee802_11_elems {
 	const struct ieee80211_multi_link_elem *ml_epcs;
 	const struct ieee80211_bandwidth_indication *bandwidth_indication;
 	const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
+	const struct ieee80211_uhr_capa *uhr_capa;
+	const struct ieee80211_uhr_oper *uhr_oper;
 
 	/* not the order in the psd values is per element, not per chandef */
 	struct ieee80211_parsed_tpe tpe;
@@ -1850,6 +1853,8 @@ struct ieee802_11_elems {
 	u8 country_elem_len;
 	u8 bssid_index_len;
 	u8 eht_cap_len;
+	u8 uhr_capa_len;
+	u8 uhr_oper_len;
 
 	/* mult-link element can be de-fragmented and thus u8 is not sufficient */
 	size_t ml_basic_len;
@@ -2693,6 +2698,9 @@ int ieee80211_put_eht_cap(struct sk_buff *skb,
 			  struct ieee80211_sub_if_data *sdata,
 			  const struct ieee80211_supported_band *sband,
 			  const struct ieee80211_conn_settings *conn);
+int ieee80211_put_uhr_cap(struct sk_buff *skb,
+			  struct ieee80211_sub_if_data *sdata,
+			  const struct ieee80211_supported_band *sband);
 int ieee80211_put_reg_conn(struct sk_buff *skb,
 			   enum ieee80211_channel_flags flags);
 
@@ -2868,6 +2876,13 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
 				      struct ieee80211_mgmt *mgmt, size_t len);
 void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata);
 
+void
+ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata,
+				    struct ieee80211_supported_band *sband,
+				    const struct ieee80211_uhr_capa *uhr_capa,
+				    u8 uhr_capa_len,
+				    struct link_sta_info *link_sta);
+
 #if IS_ENABLED(CONFIG_MAC80211_KUNIT_TEST)
 #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym)
 #define VISIBLE_IF_MAC80211_KUNIT
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b05e313c7f17..b209e865a657 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1123,7 +1123,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	int result, i;
 	enum nl80211_band band;
 	int channels, max_bitrates;
-	bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g;
+	bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g, supp_uhr;
 	struct cfg80211_chan_def dflt_chandef = {};
 
 	if (ieee80211_hw_check(hw, QUEUE_CONTROL) &&
@@ -1237,6 +1237,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	supp_he = false;
 	supp_eht = false;
 	supp_s1g = false;
+	supp_uhr = false;
 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
 		const struct ieee80211_sband_iftype_data *iftd;
 		struct ieee80211_supported_band *sband;
@@ -1293,6 +1294,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
 			supp_he = supp_he || iftd->he_cap.has_he;
 			supp_eht = supp_eht || iftd->eht_cap.has_eht;
+			supp_uhr = supp_uhr || iftd->uhr_cap.has_uhr;
 
 			if (band == NL80211_BAND_2GHZ)
 				he_40_mhz_cap =
@@ -1325,6 +1327,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 		if (WARN_ON(supp_eht && !supp_he))
 			return -EINVAL;
 
+		/* UHR requires EHT support */
+		if (WARN_ON(supp_uhr && !supp_eht))
+			return -EINVAL;
+
 		if (!sband->ht_cap.ht_supported)
 			continue;
 
@@ -1437,6 +1443,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 				IEEE80211_EHT_PPE_THRES_MAX_LEN;
 	}
 
+	if (supp_uhr)
+		local->scan_ies_len +=
+			3 + sizeof(struct ieee80211_uhr_capa) +
+			sizeof(struct ieee80211_uhr_capa_phy);
+
 	if (!local->ops->hw_scan) {
 		/* For hw_scan, driver needs to set these up. */
 		local->hw.wiphy->max_scan_ssids = 4;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 6e468c4fcda2..07f990ba1438 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -149,6 +149,28 @@ static int ecw2cw(int ecw)
 	return (1 << ecw) - 1;
 }
 
+static int ieee80211_prim_chan_index(const struct cfg80211_chan_def *chandef)
+{
+	u32 start;
+
+	switch (chandef->width) {
+	case NL80211_CHAN_WIDTH_20_NOHT:
+	case NL80211_CHAN_WIDTH_20:
+		return 0;
+	case NL80211_CHAN_WIDTH_40:
+	case NL80211_CHAN_WIDTH_80:
+	case NL80211_CHAN_WIDTH_80P80:
+	case NL80211_CHAN_WIDTH_160:
+	case NL80211_CHAN_WIDTH_320:
+		start = chandef->center_freq1 -
+			cfg80211_chandef_get_width(chandef) / 2;
+		return (chandef->chan->center_freq - start - 10) / 20;
+	default:
+		WARN_ON(1);
+		return 0;
+	}
+}
+
 static enum ieee80211_conn_mode
 ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
 			    struct ieee80211_channel *channel,
@@ -162,6 +184,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
 	const struct ieee80211_vht_operation *vht_oper = elems->vht_operation;
 	const struct ieee80211_he_operation *he_oper = elems->he_operation;
 	const struct ieee80211_eht_operation *eht_oper = elems->eht_operation;
+	const struct ieee80211_uhr_oper *uhr_oper = elems->uhr_oper;
 	struct ieee80211_supported_band *sband =
 		sdata->local->hw.wiphy->bands[channel->band];
 	struct cfg80211_chan_def vht_chandef;
@@ -192,7 +215,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
 
 	/* get special 6 GHz case out of the way */
 	if (sband->band == NL80211_BAND_6GHZ) {
-		enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_EHT;
+		enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_HIGHEST;
 
 		/* this is an error */
 		if (conn->mode < IEEE80211_CONN_MODE_HE)
@@ -215,7 +238,9 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
 			return IEEE80211_CONN_MODE_LEGACY;
 		}
 
-		return mode;
+		if (mode <= IEEE80211_CONN_MODE_EHT)
+			return mode;
+		goto check_uhr;
 	}
 
 	/* now we have the progression HT, VHT, ... */
@@ -340,7 +365,57 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
 		*chandef = eht_chandef;
 	}
 
-	return IEEE80211_CONN_MODE_EHT;
+check_uhr:
+	if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper)
+		return IEEE80211_CONN_MODE_EHT;
+
+	/*
+	 * In beacons we don't have all the data - but we know the size was OK,
+	 * so if the size is valid as a non-beacon case, we have more data and
+	 * can validate the NPCA parameters.
+	 */
+	if (ieee80211_uhr_oper_size_ok((const void *)uhr_oper,
+				       elems->uhr_oper_len,
+				       false)) {
+		struct cfg80211_chan_def npca_chandef = *chandef;
+		const struct ieee80211_uhr_npca_info *npca;
+		const __le16 *dis_subch_bmap;
+		u16 punct = chandef->punctured, npca_punct;
+
+		npca = ieee80211_uhr_npca_info(uhr_oper);
+		if (npca) {
+			u8 ch = le32_get_bits(npca->params,
+					      IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN);
+
+			if (20 * ch >= cfg80211_chandef_get_width(chandef) ||
+			    ch == ieee80211_prim_chan_index(chandef)) {
+				sdata_info(sdata,
+					   "AP UHR NPCA primary channel invalid, disabling UHR\n");
+				return IEEE80211_CONN_MODE_EHT;
+			}
+		}
+
+		dis_subch_bmap = ieee80211_uhr_npca_dis_subch_bitmap(uhr_oper);
+
+		if (dis_subch_bmap) {
+			npca_punct = get_unaligned_le16(dis_subch_bmap);
+			npca_chandef.punctured = npca_punct;
+		}
+
+		/*
+		 * must be a valid puncturing pattern for this channel as
+		 * well as puncturing all subchannels that are already in
+		 * the disabled subchannel bitmap on the primary channel
+		 */
+		if (!cfg80211_chandef_valid(&npca_chandef) ||
+		    ((punct & npca_punct) != punct)) {
+			sdata_info(sdata,
+				   "AP UHR NPCA disabled subchannel bitmap invalid, disabling UHR\n");
+			return IEEE80211_CONN_MODE_EHT;
+		}
+	}
+
+	return IEEE80211_CONN_MODE_UHR;
 }
 
 static bool
@@ -1091,6 +1166,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
 				       IEEE80211_CONN_BW_LIMIT_160);
 		break;
 	case IEEE80211_CONN_MODE_EHT:
+	case IEEE80211_CONN_MODE_UHR:
 		conn->bw_limit = min_t(enum ieee80211_conn_bw_limit,
 				       conn->bw_limit,
 				       IEEE80211_CONN_BW_LIMIT_320);
@@ -1108,6 +1184,8 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
 		set_bit(BSS_MEMBERSHIP_SELECTOR_HE_PHY, sta_selectors);
 	if (conn->mode >= IEEE80211_CONN_MODE_EHT)
 		set_bit(BSS_MEMBERSHIP_SELECTOR_EHT_PHY, sta_selectors);
+	if (conn->mode >= IEEE80211_CONN_MODE_UHR)
+		set_bit(BSS_MEMBERSHIP_SELECTOR_UHR_PHY, sta_selectors);
 
 	/*
 	 * We do not support EPD or GLK so never add them.
@@ -1155,6 +1233,11 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
 				       IEEE80211_CONN_BW_LIMIT_160);
 	}
 
+	if (conn->mode >= IEEE80211_CONN_MODE_UHR &&
+	    !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper,
+				     IEEE80211_CHAN_NO_UHR))
+		conn->mode = IEEE80211_CONN_MODE_EHT;
+
 	if (chanreq->oper.width != ap_chandef->width || ap_mode != conn->mode)
 		link_id_info(sdata, link_id,
 			     "regulatory prevented using AP config, downgraded\n");
@@ -1884,11 +1967,13 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata,
 
 	/*
 	 * careful - need to know about all the present elems before
-	 * calling ieee80211_assoc_add_ml_elem(), so add this one if
-	 * we're going to put it after the ML element
+	 * calling ieee80211_assoc_add_ml_elem(), so add these if
+	 * we're going to put them after the ML element
 	 */
 	if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT)
 		ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY);
+	if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR)
+		ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_UHR_CAPA);
 
 	if (link_id == assoc_data->assoc_link_id)
 		ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa,
@@ -1901,6 +1986,9 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata,
 		ieee80211_put_eht_cap(skb, sdata, sband,
 				      &assoc_data->link[link_id].conn);
 
+	if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR)
+		ieee80211_put_uhr_cap(skb, sdata, sband);
+
 	if (sband->band == NL80211_BAND_S1GHZ) {
 		ieee80211_add_aid_request_ie(sdata, skb);
 		ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb);
@@ -2135,6 +2223,9 @@ ieee80211_link_common_elems_size(struct ieee80211_sub_if_data *sdata,
 		sizeof(struct ieee80211_eht_mcs_nss_supp) +
 		IEEE80211_EHT_PPE_THRES_MAX_LEN;
 
+	size += 2 + 1 + sizeof(struct ieee80211_uhr_capa) +
+		sizeof(struct ieee80211_uhr_capa_phy);
+
 	return size;
 }
 
@@ -5531,6 +5622,18 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
 		bss_conf->epcs_support = false;
 	}
 
+	if (elems->uhr_oper && elems->uhr_capa &&
+	    link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) {
+		ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband,
+						    elems->uhr_capa,
+						    elems->uhr_capa_len,
+						    link_sta);
+
+		bss_conf->uhr_support = link_sta->pub->uhr_cap.has_uhr;
+	} else {
+		bss_conf->uhr_support = false;
+	}
+
 	if (elems->s1g_oper &&
 	    link->u.mgd.conn.mode == IEEE80211_CONN_MODE_S1G &&
 	    elems->s1g_capab)
@@ -5821,6 +5924,7 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
 	bool is_6ghz = sband->band == NL80211_BAND_6GHZ;
 	const struct ieee80211_sta_he_cap *he_cap;
 	const struct ieee80211_sta_eht_cap *eht_cap;
+	const struct ieee80211_sta_uhr_cap *uhr_cap;
 	struct ieee80211_sta_vht_cap vht_cap;
 
 	if (sband->band == NL80211_BAND_S1GHZ) {
@@ -5996,9 +6100,6 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
 				 "no EHT support, limiting to HE\n");
 		goto out;
 	}
-
-	/* we have EHT */
-
 	conn->mode = IEEE80211_CONN_MODE_EHT;
 
 	/* check bandwidth */
@@ -6009,6 +6110,20 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
 		mlme_link_id_dbg(sdata, link_id,
 				 "no EHT 320 MHz cap in 6 GHz, limiting to 160 MHz\n");
 
+	if (req && req->flags & ASSOC_REQ_DISABLE_UHR) {
+		mlme_link_id_dbg(sdata, link_id,
+				 "UHR disabled by flag, limiting to EHT\n");
+		goto out;
+	}
+
+	uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
+	if (!uhr_cap) {
+		mlme_link_id_dbg(sdata, link_id,
+				 "no UHR support, limiting to EHT\n");
+		goto out;
+	}
+	conn->mode = IEEE80211_CONN_MODE_UHR;
+
 out:
 	mlme_link_id_dbg(sdata, link_id,
 			 "determined local STA to be %s, BW limited to %d MHz\n",
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 667021bc60c6..558eb3385bf5 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -189,6 +189,26 @@ ieee80211_parse_extension_element(u32 *crc,
 			elems->ttlm_num++;
 		}
 		break;
+	case WLAN_EID_EXT_UHR_OPER:
+		if (params->mode < IEEE80211_CONN_MODE_UHR)
+			break;
+		calc_crc = true;
+		if (ieee80211_uhr_oper_size_ok(data, len,
+					       params->type == (IEEE80211_FTYPE_MGMT |
+								IEEE80211_STYPE_BEACON))) {
+			elems->uhr_oper = data;
+			elems->uhr_oper_len = len;
+		}
+		break;
+	case WLAN_EID_EXT_UHR_CAPA:
+		if (params->mode < IEEE80211_CONN_MODE_UHR)
+			break;
+		calc_crc = true;
+		if (ieee80211_uhr_capa_size_ok(data, len)) {
+			elems->uhr_capa = data;
+			elems->uhr_capa_len = len;
+		}
+		break;
 	}
 
 	if (crc && calc_crc)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 30b9b4d76357..8a99819fd6cb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -5518,6 +5518,27 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
 				      status->rate_idx, status->nss, status->eht.gi))
 				goto drop;
 			break;
+		case RX_ENC_UHR:
+			if (WARN_ONCE(!(status->rate_idx <= 15 ||
+					status->rate_idx == 17 ||
+					status->rate_idx == 19 ||
+					status->rate_idx == 20 ||
+					status->rate_idx == 23) ||
+				      !status->nss ||
+				      status->nss > 8 ||
+				      status->uhr.gi > NL80211_RATE_INFO_EHT_GI_3_2,
+				      "Rate marked as a UHR rate but data is invalid: MCS:%d, NSS:%d, GI:%d\n",
+				      status->rate_idx, status->nss, status->uhr.gi))
+				goto drop;
+			if (WARN_ONCE(status->uhr.elr &&
+				      (status->nss != 1 || status->rate_idx > 1 ||
+				       status->uhr.gi != NL80211_RATE_INFO_EHT_GI_1_6 ||
+				       status->bw != RATE_INFO_BW_20 || status->uhr.im),
+				      "bad UHR ELR MCS MCS:%d, NCSS:%d, GI:%d, BW:%d, IM:%d\n",
+				      status->rate_idx, status->nss, status->uhr.gi,
+				      status->bw, status->uhr.im))
+				goto drop;
+			break;
 		default:
 			WARN_ON_ONCE(1);
 			fallthrough;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 22e8561ad6fc..a2776196f97c 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -2567,6 +2567,17 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
 		rinfo->eht_gi = STA_STATS_GET(EHT_GI, rate);
 		rinfo->eht_ru_alloc = STA_STATS_GET(EHT_RU, rate);
 		break;
+	case STA_STATS_RATE_TYPE_UHR:
+		rinfo->flags = RATE_INFO_FLAGS_UHR_MCS;
+		rinfo->mcs = STA_STATS_GET(UHR_MCS, rate);
+		rinfo->nss = STA_STATS_GET(UHR_NSS, rate);
+		rinfo->eht_gi = STA_STATS_GET(UHR_GI, rate);
+		rinfo->eht_ru_alloc = STA_STATS_GET(UHR_RU, rate);
+		if (STA_STATS_GET(UHR_ELR, rate))
+			rinfo->flags |= RATE_INFO_FLAGS_UHR_ELR_MCS;
+		if (STA_STATS_GET(UHR_IM, rate))
+			rinfo->flags |= RATE_INFO_FLAGS_UHR_IM;
+		break;
 	}
 }
 
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b1edf8ed102f..24dda075820d 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -3,7 +3,7 @@
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright(c) 2020-2024 Intel Corporation
+ * Copyright(c) 2020-2025 Intel Corporation
  */
 
 #ifndef STA_INFO_H
@@ -1009,25 +1009,49 @@ enum sta_stats_type {
 	STA_STATS_RATE_TYPE_HE,
 	STA_STATS_RATE_TYPE_S1G,
 	STA_STATS_RATE_TYPE_EHT,
+	STA_STATS_RATE_TYPE_UHR,
 };
 
-#define STA_STATS_FIELD_HT_MCS		GENMASK( 7,  0)
-#define STA_STATS_FIELD_LEGACY_IDX	GENMASK( 3,  0)
-#define STA_STATS_FIELD_LEGACY_BAND	GENMASK( 7,  4)
-#define STA_STATS_FIELD_VHT_MCS		GENMASK( 3,  0)
-#define STA_STATS_FIELD_VHT_NSS		GENMASK( 7,  4)
-#define STA_STATS_FIELD_HE_MCS		GENMASK( 3,  0)
-#define STA_STATS_FIELD_HE_NSS		GENMASK( 7,  4)
-#define STA_STATS_FIELD_EHT_MCS		GENMASK( 3,  0)
-#define STA_STATS_FIELD_EHT_NSS		GENMASK( 7,  4)
-#define STA_STATS_FIELD_BW		GENMASK(12,  8)
-#define STA_STATS_FIELD_SGI		GENMASK(13, 13)
-#define STA_STATS_FIELD_TYPE		GENMASK(16, 14)
-#define STA_STATS_FIELD_HE_RU		GENMASK(19, 17)
-#define STA_STATS_FIELD_HE_GI		GENMASK(21, 20)
-#define STA_STATS_FIELD_HE_DCM		GENMASK(22, 22)
-#define STA_STATS_FIELD_EHT_RU		GENMASK(20, 17)
-#define STA_STATS_FIELD_EHT_GI		GENMASK(22, 21)
+/* common */
+#define STA_STATS_FIELD_TYPE		0x0000000F
+#define STA_STATS_FIELD_BW		0x000001F0
+#define STA_STATS_FIELD_RESERVED	0x00000E00
+
+/* STA_STATS_RATE_TYPE_LEGACY */
+#define STA_STATS_FIELD_LEGACY_IDX	0x0000F000
+#define STA_STATS_FIELD_LEGACY_BAND	0x000F0000
+
+/* STA_STATS_RATE_TYPE_HT */
+#define STA_STATS_FIELD_HT_MCS		0x000FF000
+
+/* STA_STATS_RATE_TYPE_VHT */
+#define STA_STATS_FIELD_VHT_MCS		0x0000F000
+#define STA_STATS_FIELD_VHT_NSS		0x000F0000
+
+/* HT & VHT */
+#define STA_STATS_FIELD_SGI		0x00100000
+
+/* STA_STATS_RATE_TYPE_HE */
+#define STA_STATS_FIELD_HE_MCS		0x0000F000
+#define STA_STATS_FIELD_HE_NSS		0x000F0000
+#define STA_STATS_FIELD_HE_RU		0x00700000
+#define STA_STATS_FIELD_HE_GI		0x01800000
+#define STA_STATS_FIELD_HE_DCM		0x02000000
+
+/* STA_STATS_RATE_TYPE_EHT */
+#define STA_STATS_FIELD_EHT_MCS		0x0000F000
+#define STA_STATS_FIELD_EHT_NSS		0x000F0000
+#define STA_STATS_FIELD_EHT_RU		0x00F00000
+#define STA_STATS_FIELD_EHT_GI		0x03000000
+
+/* STA_STATS_RATE_TYPE_UHR */
+#define STA_STATS_FIELD_UHR_MCS		0x0001F000
+#define STA_STATS_FIELD_UHR_NSS		0x001E0000
+#define STA_STATS_FIELD_UHR_RU		0x01E00000
+#define STA_STATS_FIELD_UHR_GI		0x06000000
+#define STA_STATS_FIELD_UHR_ELR		0x08000000
+#define STA_STATS_FIELD_UHR_IM		0x10000000
+
 
 #define STA_STATS_FIELD(_n, _v)		FIELD_PREP(STA_STATS_FIELD_ ## _n, _v)
 #define STA_STATS_GET(_n, _v)		FIELD_GET(STA_STATS_FIELD_ ## _n, _v)
@@ -1040,8 +1064,15 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
 
 	r = STA_STATS_FIELD(BW, s->bw);
 
-	if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
-		r |= STA_STATS_FIELD(SGI, 1);
+	switch (s->encoding) {
+	case RX_ENC_HT:
+	case RX_ENC_VHT:
+		if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
+			r |= STA_STATS_FIELD(SGI, 1);
+		break;
+	default:
+		break;
+	}
 
 	switch (s->encoding) {
 	case RX_ENC_VHT:
@@ -1073,6 +1104,15 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
 		r |= STA_STATS_FIELD(EHT_GI, s->eht.gi);
 		r |= STA_STATS_FIELD(EHT_RU, s->eht.ru);
 		break;
+	case RX_ENC_UHR:
+		r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_UHR);
+		r |= STA_STATS_FIELD(UHR_NSS, s->nss);
+		r |= STA_STATS_FIELD(UHR_MCS, s->rate_idx);
+		r |= STA_STATS_FIELD(UHR_GI, s->uhr.gi);
+		r |= STA_STATS_FIELD(UHR_RU, s->uhr.ru);
+		r |= STA_STATS_FIELD(UHR_ELR, s->uhr.elr);
+		r |= STA_STATS_FIELD(UHR_IM, s->uhr.im);
+		break;
 	default:
 		WARN_ON(1);
 		return STA_STATS_RATE_INVALID;
diff --git a/net/mac80211/uhr.c b/net/mac80211/uhr.c
new file mode 100644
index 000000000000..18029fa93f40
--- /dev/null
+++ b/net/mac80211/uhr.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UHR handling
+ *
+ * Copyright(c) 2025 Intel Corporation
+ */
+
+#include "ieee80211_i.h"
+
+void
+ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata,
+				    struct ieee80211_supported_band *sband,
+				    const struct ieee80211_uhr_capa *uhr_capa,
+				    u8 uhr_capa_len,
+				    struct link_sta_info *link_sta)
+{
+	struct ieee80211_sta_uhr_cap *uhr_cap = &link_sta->pub->uhr_cap;
+
+	memset(uhr_cap, 0, sizeof(*uhr_cap));
+
+	if (!uhr_capa ||
+	    !ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif))
+		return;
+
+	uhr_cap->has_uhr = true;
+
+	uhr_cap->mac = uhr_capa->mac;
+	uhr_cap->phy = *ieee80211_uhr_phy_cap(uhr_capa);
+}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 4d5680da7aa0..e9a625b6a8c4 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1421,6 +1421,13 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb,
 	if (err)
 		return err;
 
+	if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
+					 IEEE80211_CHAN_NO_UHR)) {
+		err = ieee80211_put_uhr_cap(skb, sdata, sband);
+		if (err)
+			return err;
+	}
+
 	/*
 	 * If adding more here, adjust code in main.c
 	 * that calculates local->scan_ies_len.
@@ -4527,6 +4534,32 @@ int ieee80211_put_eht_cap(struct sk_buff *skb,
 	return 0;
 }
 
+int ieee80211_put_uhr_cap(struct sk_buff *skb,
+			  struct ieee80211_sub_if_data *sdata,
+			  const struct ieee80211_supported_band *sband)
+{
+	const struct ieee80211_sta_uhr_cap *uhr_cap =
+		ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
+	int len;
+
+	if (!uhr_cap)
+		return 0;
+
+	len = 2 + 1 + sizeof(struct ieee80211_uhr_capa) +
+	      sizeof(struct ieee80211_uhr_capa_phy);
+
+	if (skb_tailroom(skb) < len)
+		return -ENOBUFS;
+
+	skb_put_u8(skb, WLAN_EID_EXTENSION);
+	skb_put_u8(skb, len - 2);
+	skb_put_u8(skb, WLAN_EID_EXT_UHR_CAPA);
+	skb_put_data(skb, &uhr_cap->mac, sizeof(uhr_cap->mac));
+	skb_put_data(skb, &uhr_cap->phy, sizeof(uhr_cap->phy));
+
+	return 0;
+}
+
 const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
 {
 	static const char * const modes[] = {
@@ -4536,6 +4569,7 @@ const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
 		[IEEE80211_CONN_MODE_VHT] = "VHT",
 		[IEEE80211_CONN_MODE_HE] = "HE",
 		[IEEE80211_CONN_MODE_EHT] = "EHT",
+		[IEEE80211_CONN_MODE_UHR] = "UHR",
 	};
 
 	if (WARN_ON(mode >= ARRAY_SIZE(modes)))
-- 
2.52.0


^ permalink raw reply related

* [PATCH wireless-next v3 2/3] wifi: cfg80211: add initial UHR support
From: Johannes Berg @ 2026-01-27 14:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127141005.583581-5-johannes@sipsolutions.net>

From: Johannes Berg <johannes.berg@intel.com>

Add initial support for making UHR connections (or suppressing
that), adding UHR capable stations on the AP side, encoding
and decoding UHR MCSes (except rate calculation for the new
MCSes 17, 19, 20 and 23) as well as regulatory support.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v3:
 - remove UHR capa pointer from AP settings, it's not in
   the beacon anyway
 - fix kernel-doc (Jeff Johnson)
---
 include/net/cfg80211.h       |  56 ++++++++++++++++++-
 include/uapi/linux/nl80211.h |  30 +++++++++++
 net/wireless/nl80211.c       | 102 +++++++++++++++++++++++++++++++++--
 net/wireless/reg.c           |   2 +
 net/wireless/util.c          |  96 +++++++++++++++++++++++++--------
 5 files changed, 259 insertions(+), 27 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0ae0aa7594a3..d497a0a131c7 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -126,6 +126,7 @@ struct wiphy;
  * @IEEE80211_CHAN_NO_4MHZ: 4 MHz bandwidth is not permitted on this channel.
  * @IEEE80211_CHAN_NO_8MHZ: 8 MHz bandwidth is not permitted on this channel.
  * @IEEE80211_CHAN_NO_16MHZ: 16 MHz bandwidth is not permitted on this channel.
+ * @IEEE80211_CHAN_NO_UHR: UHR operation is not permitted on this channel.
  */
 enum ieee80211_channel_flags {
 	IEEE80211_CHAN_DISABLED			= BIT(0),
@@ -143,6 +144,7 @@ enum ieee80211_channel_flags {
 	IEEE80211_CHAN_NO_10MHZ			= BIT(12),
 	IEEE80211_CHAN_NO_HE			= BIT(13),
 	/* can use free bits here */
+	IEEE80211_CHAN_NO_UHR			= BIT(18),
 	IEEE80211_CHAN_NO_320MHZ		= BIT(19),
 	IEEE80211_CHAN_NO_EHT			= BIT(20),
 	IEEE80211_CHAN_DFS_CONCURRENT		= BIT(21),
@@ -429,6 +431,18 @@ struct ieee80211_sta_eht_cap {
 	u8 eht_ppe_thres[IEEE80211_EHT_PPE_THRES_MAX_LEN];
 };
 
+/**
+ * struct ieee80211_sta_uhr_cap - STA's UHR capabilities
+ * @has_uhr: true iff UHR is supported and data is valid
+ * @mac: fixed MAC capabilities
+ * @phy: fixed PHY capabilities
+ */
+struct ieee80211_sta_uhr_cap {
+	bool has_uhr;
+	struct ieee80211_uhr_capa_mac mac;
+	struct ieee80211_uhr_capa_phy phy;
+};
+
 /* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
 #ifdef __CHECKER__
 /*
@@ -454,6 +468,7 @@ struct ieee80211_sta_eht_cap {
  * @he_6ghz_capa: HE 6 GHz capabilities, must be filled in for a
  *	6 GHz band channel (and 0 may be valid value).
  * @eht_cap: STA's EHT capabilities
+ * @uhr_cap: STA's UHR capabilities
  * @vendor_elems: vendor element(s) to advertise
  * @vendor_elems.data: vendor element(s) data
  * @vendor_elems.len: vendor element(s) length
@@ -463,6 +478,7 @@ struct ieee80211_sband_iftype_data {
 	struct ieee80211_sta_he_cap he_cap;
 	struct ieee80211_he_6ghz_capa he_6ghz_capa;
 	struct ieee80211_sta_eht_cap eht_cap;
+	struct ieee80211_sta_uhr_cap uhr_cap;
 	struct {
 		const u8 *data;
 		unsigned int len;
@@ -704,6 +720,26 @@ ieee80211_get_eht_iftype_cap(const struct ieee80211_supported_band *sband,
 	return NULL;
 }
 
+/**
+ * ieee80211_get_uhr_iftype_cap - return UHR capabilities for an sband's iftype
+ * @sband: the sband to search for the iftype on
+ * @iftype: enum nl80211_iftype
+ *
+ * Return: pointer to the struct ieee80211_sta_uhr_cap, or NULL is none found
+ */
+static inline const struct ieee80211_sta_uhr_cap *
+ieee80211_get_uhr_iftype_cap(const struct ieee80211_supported_band *sband,
+			     enum nl80211_iftype iftype)
+{
+	const struct ieee80211_sband_iftype_data *data =
+		ieee80211_get_sband_iftype_data(sband, iftype);
+
+	if (data && data->uhr_cap.has_uhr)
+		return &data->uhr_cap;
+
+	return NULL;
+}
+
 /**
  * wiphy_read_of_freq_limits - read frequency limits from device tree
  *
@@ -1486,6 +1522,7 @@ struct cfg80211_s1g_short_beacon {
  * @he_cap: HE capabilities (or %NULL if HE isn't enabled)
  * @eht_cap: EHT capabilities (or %NULL if EHT isn't enabled)
  * @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled)
+ * @uhr_oper: UHR operation (or %NULL if UHR isn't enabled)
  * @ht_required: stations must support HT
  * @vht_required: stations must support VHT
  * @twt_responder: Enable Target Wait Time
@@ -1525,6 +1562,7 @@ struct cfg80211_ap_settings {
 	const struct ieee80211_he_operation *he_oper;
 	const struct ieee80211_eht_cap_elem *eht_cap;
 	const struct ieee80211_eht_operation *eht_oper;
+	const struct ieee80211_uhr_oper *uhr_oper;
 	bool ht_required, vht_required, he_required, sae_h2e_required;
 	bool twt_responder;
 	u32 flags;
@@ -1698,6 +1736,8 @@ struct sta_txpwr {
  * @eht_capa: EHT capabilities of station
  * @eht_capa_len: the length of the EHT capabilities
  * @s1g_capa: S1G capabilities of station
+ * @uhr_capa: UHR capabilities of the station
+ * @uhr_capa_len: the length of the UHR capabilities
  */
 struct link_station_parameters {
 	const u8 *mld_mac;
@@ -1717,6 +1757,8 @@ struct link_station_parameters {
 	const struct ieee80211_eht_cap_elem *eht_capa;
 	u8 eht_capa_len;
 	const struct ieee80211_s1g_cap *s1g_capa;
+	const struct ieee80211_uhr_capa *uhr_capa;
+	u8 uhr_capa_len;
 };
 
 /**
@@ -1898,6 +1940,11 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
  * @RATE_INFO_FLAGS_EXTENDED_SC_DMG: 60GHz extended SC MCS
  * @RATE_INFO_FLAGS_EHT_MCS: EHT MCS information
  * @RATE_INFO_FLAGS_S1G_MCS: MCS field filled with S1G MCS
+ * @RATE_INFO_FLAGS_UHR_MCS: UHR MCS information
+ * @RATE_INFO_FLAGS_UHR_ELR_MCS: UHR ELR MCS was used
+ *	(set together with @RATE_INFO_FLAGS_UHR_MCS)
+ * @RATE_INFO_FLAGS_UHR_IM: UHR Interference Mitigation
+ *	was used
  */
 enum rate_info_flags {
 	RATE_INFO_FLAGS_MCS			= BIT(0),
@@ -1909,6 +1956,9 @@ enum rate_info_flags {
 	RATE_INFO_FLAGS_EXTENDED_SC_DMG		= BIT(6),
 	RATE_INFO_FLAGS_EHT_MCS			= BIT(7),
 	RATE_INFO_FLAGS_S1G_MCS			= BIT(8),
+	RATE_INFO_FLAGS_UHR_MCS			= BIT(9),
+	RATE_INFO_FLAGS_UHR_ELR_MCS		= BIT(10),
+	RATE_INFO_FLAGS_UHR_IM			= BIT(11),
 };
 
 /**
@@ -1924,7 +1974,7 @@ enum rate_info_flags {
  * @RATE_INFO_BW_160: 160 MHz bandwidth
  * @RATE_INFO_BW_HE_RU: bandwidth determined by HE RU allocation
  * @RATE_INFO_BW_320: 320 MHz bandwidth
- * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT RU allocation
+ * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT/UHR RU allocation
  * @RATE_INFO_BW_1: 1 MHz bandwidth
  * @RATE_INFO_BW_2: 2 MHz bandwidth
  * @RATE_INFO_BW_4: 4 MHz bandwidth
@@ -1955,7 +2005,7 @@ enum rate_info_bw {
  *
  * @flags: bitflag of flags from &enum rate_info_flags
  * @legacy: bitrate in 100kbit/s for 802.11abg
- * @mcs: mcs index if struct describes an HT/VHT/HE/EHT/S1G rate
+ * @mcs: mcs index if struct describes an HT/VHT/HE/EHT/S1G/UHR rate
  * @nss: number of streams (VHT & HE only)
  * @bw: bandwidth (from &enum rate_info_bw)
  * @he_gi: HE guard interval (from &enum nl80211_he_gi)
@@ -3265,6 +3315,7 @@ struct cfg80211_ml_reconf_req {
  *	Drivers shall disable MLO features for the current association if this
  *	flag is not set.
  * @ASSOC_REQ_SPP_AMSDU: SPP A-MSDUs will be used on this connection (if any)
+ * @ASSOC_REQ_DISABLE_UHR: Disable UHR
  */
 enum cfg80211_assoc_req_flags {
 	ASSOC_REQ_DISABLE_HT			= BIT(0),
@@ -3275,6 +3326,7 @@ enum cfg80211_assoc_req_flags {
 	ASSOC_REQ_DISABLE_EHT			= BIT(5),
 	CONNECT_REQ_MLO_SUPPORT			= BIT(6),
 	ASSOC_REQ_SPP_AMSDU			= BIT(7),
+	ASSOC_REQ_DISABLE_UHR			= BIT(8),
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 54ddbd9a5459..2254a6dead39 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2976,6 +2976,13 @@ enum nl80211_commands {
  * @NL80211_ATTR_EPP_PEER: A flag attribute to indicate if the peer is an EPP
  *	STA. Used with %NL80211_CMD_NEW_STA and %NL80211_CMD_ADD_LINK_STA
  *
+ * @NL80211_ATTR_UHR_CAPABILITY: UHR Capability information element (from
+ *	association request when used with NL80211_CMD_NEW_STATION). Can be set
+ *	only if HE/EHT are also available.
+ * @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable
+ *	this feature during association. This is a flag attribute.
+ *	Currently only supported in mac80211 drivers.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3546,6 +3553,9 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_EPP_PEER,
 
+	NL80211_ATTR_UHR_CAPABILITY,
+	NL80211_ATTR_DISABLE_UHR,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -3898,6 +3908,12 @@ enum nl80211_eht_ru_alloc {
  * @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
  * @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
  * @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
+ * @NL80211_RATE_INFO_UHR_MCS: UHR MCS index (u8, 0-15, 17, 19, 20, 23)
+ *	Note that the other EHT attributes (such as @NL80211_RATE_INFO_EHT_NSS)
+ *	are used in conjunction with this where applicable
+ * @NL80211_RATE_INFO_UHR_ELR: UHR ELR flag, which restricts NSS to 1,
+ *	MCS to 0 or 1, and GI to %NL80211_RATE_INFO_EHT_GI_1_6.
+ * @NL80211_RATE_INFO_UHR_IM: UHR Interference Mitigation flag
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -3931,6 +3947,9 @@ enum nl80211_rate_info {
 	NL80211_RATE_INFO_4_MHZ_WIDTH,
 	NL80211_RATE_INFO_8_MHZ_WIDTH,
 	NL80211_RATE_INFO_16_MHZ_WIDTH,
+	NL80211_RATE_INFO_UHR_MCS,
+	NL80211_RATE_INFO_UHR_ELR,
+	NL80211_RATE_INFO_UHR_IM,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -4253,6 +4272,10 @@ enum nl80211_mpath_info {
  *	capabilities element
  * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
  *	defined in EHT capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC: UHR MAC capabilities as in UHR
+ *	capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY: UHR PHY capabilities as in UHR
+ *	capabilities element
  * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
  * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
  */
@@ -4270,6 +4293,8 @@ enum nl80211_band_iftype_attr {
 	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
 	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
 	NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
+	NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC,
+	NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY,
 
 	/* keep last */
 	__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -4452,6 +4477,8 @@ enum nl80211_wmm_rule {
  * @NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY: Channel is not permitted for use
  *	as a primary channel. Does not prevent the channel from existing
  *	as a non-primary subchannel. Only applicable to S1G channels.
+ * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel
+ *	in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *	currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4501,6 +4528,7 @@ enum nl80211_frequency_attr {
 	NL80211_FREQUENCY_ATTR_NO_8MHZ,
 	NL80211_FREQUENCY_ATTR_NO_16MHZ,
 	NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY,
+	NL80211_FREQUENCY_ATTR_NO_UHR,
 
 	/* keep last */
 	__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4714,6 +4742,7 @@ enum nl80211_sched_scan_match_attr {
  *	despite NO_IR configuration.
  * @NL80211_RRF_ALLOW_20MHZ_ACTIVITY: Allow activity in 20 MHz bandwidth,
  *	despite NO_IR configuration.
+ * @NL80211_RRF_NO_UHR: UHR operation not allowed
  */
 enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_OFDM                 = 1 << 0,
@@ -4740,6 +4769,7 @@ enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_6GHZ_AFC_CLIENT      = 1 << 23,
 	NL80211_RRF_ALLOW_6GHZ_VLP_AP       = 1 << 24,
 	NL80211_RRF_ALLOW_20MHZ_ACTIVITY    = 1 << 25,
+	NL80211_RRF_NO_UHR		    = 1 << 26,
 };
 
 #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 85e30fda4c46..f33054a907fe 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -332,6 +332,15 @@ static int validate_nan_cluster_id(const struct nlattr *attr,
 	return 0;
 }
 
+static int validate_uhr_capa(const struct nlattr *attr,
+			     struct netlink_ext_ack *extack)
+{
+	const u8 *data = nla_data(attr);
+	unsigned int len = nla_len(attr);
+
+	return ieee80211_uhr_capa_size_ok(data, len);
+}
+
 /* policy for the attributes */
 static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
 
@@ -934,6 +943,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG },
 	[NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG },
 	[NL80211_ATTR_EPP_PEER] = { .type = NLA_FLAG },
+	[NL80211_ATTR_UHR_CAPABILITY] =
+		NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255),
+	[NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -1319,6 +1331,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
 		if ((chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) &&
 		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY))
 			goto nla_put_failure;
+		if ((chan->flags & IEEE80211_CHAN_NO_UHR) &&
+		    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHR))
+			goto nla_put_failure;
 	}
 
 	if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -1954,6 +1969,7 @@ nl80211_send_iftype_data(struct sk_buff *msg,
 {
 	const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
 	const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap;
+	const struct ieee80211_sta_uhr_cap *uhr_cap = &iftdata->uhr_cap;
 
 	if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
 				iftdata->types_mask))
@@ -2005,6 +2021,14 @@ nl80211_send_iftype_data(struct sk_buff *msg,
 			return -ENOBUFS;
 	}
 
+	if (uhr_cap->has_uhr) {
+		if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC,
+			    sizeof(uhr_cap->mac), &uhr_cap->mac) ||
+		    nla_put(msg, NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY,
+			    sizeof(uhr_cap->phy), &uhr_cap->phy))
+			return -ENOBUFS;
+	}
+
 	if (sband->band == NL80211_BAND_6GHZ &&
 	    nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
 		    sizeof(iftdata->he_6ghz_capa),
@@ -6462,6 +6486,17 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
 						cap->datalen - 1))
 			return -EINVAL;
 	}
+
+	cap = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len);
+	if (cap) {
+		if (!cap->datalen)
+			return -EINVAL;
+		params->uhr_oper = (void *)(cap->data + 1);
+		if (!ieee80211_uhr_oper_size_ok((const u8 *)params->uhr_oper,
+						cap->datalen - 1, true))
+			return -EINVAL;
+	}
+
 	return 0;
 }
 
@@ -6593,6 +6628,9 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params
 	    (channel->flags & IEEE80211_CHAN_NO_EHT))
 		return -EOPNOTSUPP;
 
+	if (params->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR))
+		return -EOPNOTSUPP;
+
 	return 0;
 }
 
@@ -7175,7 +7213,8 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
 		break;
 	case RATE_INFO_BW_EHT_RU:
 		rate_flg = 0;
-		WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS));
+		WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS) &&
+			!(info->flags & RATE_INFO_FLAGS_UHR_MCS));
 		break;
 	}
 
@@ -7228,6 +7267,23 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
 		    nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC,
 			       info->eht_ru_alloc))
 			return false;
+	} else if (info->flags & RATE_INFO_FLAGS_UHR_MCS) {
+		if (nla_put_u8(msg, NL80211_RATE_INFO_UHR_MCS, info->mcs))
+			return false;
+		if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_NSS, info->nss))
+			return false;
+		if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_GI, info->eht_gi))
+			return false;
+		if (info->bw == RATE_INFO_BW_EHT_RU &&
+		    nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC,
+			       info->eht_ru_alloc))
+			return false;
+		if (info->flags & RATE_INFO_FLAGS_UHR_ELR_MCS &&
+		    nla_put_flag(msg, NL80211_RATE_INFO_UHR_ELR))
+			return false;
+		if (info->flags & RATE_INFO_FLAGS_UHR_IM &&
+		    nla_put_flag(msg, NL80211_RATE_INFO_UHR_IM))
+			return false;
 	}
 
 	nla_nest_end(msg, rate);
@@ -8101,7 +8157,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
 		if (params->ext_capab || params->link_sta_params.ht_capa ||
 		    params->link_sta_params.vht_capa ||
 		    params->link_sta_params.he_capa ||
-		    params->link_sta_params.eht_capa)
+		    params->link_sta_params.eht_capa ||
+		    params->link_sta_params.uhr_capa)
 			return -EINVAL;
 		if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU))
 			return -EINVAL;
@@ -8321,6 +8378,16 @@ static int nl80211_set_station_tdls(struct genl_info *info,
 		}
 	}
 
+	if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+		if (!params->link_sta_params.eht_capa)
+			return -EINVAL;
+
+		params->link_sta_params.uhr_capa =
+			nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+		params->link_sta_params.uhr_capa_len =
+			nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+	}
+
 	if (info->attrs[NL80211_ATTR_S1G_CAPABILITY])
 		params->link_sta_params.s1g_capa =
 			nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]);
@@ -8641,6 +8708,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 		}
 	}
 
+	if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+		if (!params.link_sta_params.eht_capa)
+			return -EINVAL;
+
+		params.link_sta_params.uhr_capa =
+			nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+		params.link_sta_params.uhr_capa_len =
+			nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+	}
+
 	if (info->attrs[NL80211_ATTR_EML_CAPABILITY]) {
 		params.eml_cap_present = true;
 		params.eml_cap =
@@ -8700,10 +8777,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
 		params.link_sta_params.ht_capa = NULL;
 		params.link_sta_params.vht_capa = NULL;
 
-		/* HE and EHT require WME */
+		/* HE, EHT and UHR require WME */
 		if (params.link_sta_params.he_capa_len ||
 		    params.link_sta_params.he_6ghz_capa ||
-		    params.link_sta_params.eht_capa_len)
+		    params.link_sta_params.eht_capa_len ||
+		    params.link_sta_params.uhr_capa_len)
 			return -EINVAL;
 	}
 
@@ -12379,6 +12457,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
 	if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_EHT]))
 		req.flags |= ASSOC_REQ_DISABLE_EHT;
 
+	if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_UHR]))
+		req.flags |= ASSOC_REQ_DISABLE_UHR;
+
 	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
 		memcpy(&req.vht_capa_mask,
 		       nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
@@ -13258,6 +13339,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
 	if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_EHT]))
 		connect.flags |= ASSOC_REQ_DISABLE_EHT;
 
+	if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_UHR]))
+		connect.flags |= ASSOC_REQ_DISABLE_UHR;
+
 	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
 		memcpy(&connect.vht_capa_mask,
 		       nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
@@ -17690,6 +17774,16 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
 		}
 	}
 
+	if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+		if (!params.eht_capa)
+			return -EINVAL;
+
+		params.uhr_capa =
+			nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+		params.uhr_capa_len =
+			nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+	}
+
 	if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY])
 		params.he_6ghz_capa =
 			nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6cbfa3b78311..3bd4a64a0f92 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1605,6 +1605,8 @@ static u32 map_regdom_flags(u32 rd_flags)
 		channel_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
 	if (rd_flags & NL80211_RRF_ALLOW_20MHZ_ACTIVITY)
 		channel_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY;
+	if (rd_flags & NL80211_RRF_NO_UHR)
+		channel_flags |= IEEE80211_CHAN_NO_UHR;
 	return channel_flags;
 }
 
diff --git a/net/wireless/util.c b/net/wireless/util.c
index cc55b759694e..d91d80b6609e 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1572,26 +1572,30 @@ static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate)
 	return result / 10000;
 }
 
-static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
+static u32 _cfg80211_calculate_bitrate_eht_uhr(struct rate_info *rate)
 {
 #define SCALE 6144
-	static const u32 mcs_divisors[16] = {
-		102399, /* 16.666666... */
-		 51201, /*  8.333333... */
-		 34134, /*  5.555555... */
-		 25599, /*  4.166666... */
-		 17067, /*  2.777777... */
-		 12801, /*  2.083333... */
-		 11377, /*  1.851725... */
-		 10239, /*  1.666666... */
-		  8532, /*  1.388888... */
-		  7680, /*  1.250000... */
-		  6828, /*  1.111111... */
-		  6144, /*  1.000000... */
-		  5690, /*  0.926106... */
-		  5120, /*  0.833333... */
-		409600, /* 66.666666... */
-		204800, /* 33.333333... */
+	static const u32 mcs_divisors[] = {
+		[ 0] = 102399, /* 16.666666... */
+		[ 1] =  51201, /*  8.333333... */
+		[ 2] =  34134, /*  5.555555... */
+		[ 3] =  25599, /*  4.166666... */
+		[ 4] =  17067, /*  2.777777... */
+		[ 5] =  12801, /*  2.083333... */
+		[ 6] =  11377, /*  1.851725... */
+		[ 7] =  10239, /*  1.666666... */
+		[ 8] =   8532, /*  1.388888... */
+		[ 9] =   7680, /*  1.250000... */
+		[10] =   6828, /*  1.111111... */
+		[11] =   6144, /*  1.000000... */
+		[12] =   5690, /*  0.926106... */
+		[13] =   5120, /*  0.833333... */
+		[14] = 409600, /* 66.666666... */
+		[15] = 204800, /* 33.333333... */
+		[17] =  38400, /*  6.250180... */
+		[19] =  19200, /*  3.125090... */
+		[20] =  15360, /*  2.500000... */
+		[23] =   9600, /*  1.562545... */
 	};
 	static const u32 rates_996[3] =  { 480388888, 453700000, 408333333 };
 	static const u32 rates_484[3] =  { 229411111, 216666666, 195000000 };
@@ -1602,8 +1606,6 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
 	u64 tmp;
 	u32 result;
 
-	if (WARN_ON_ONCE(rate->mcs > 15))
-		return 0;
 	if (WARN_ON_ONCE(rate->eht_gi > NL80211_RATE_INFO_EHT_GI_3_2))
 		return 0;
 	if (WARN_ON_ONCE(rate->eht_ru_alloc >
@@ -1684,7 +1686,7 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
 		 rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_26)
 		result = rates_26[rate->eht_gi];
 	else {
-		WARN(1, "invalid EHT MCS: bw:%d, ru:%d\n",
+		WARN(1, "invalid EHT or UHR MCS: bw:%d, ru:%d\n",
 		     rate->bw, rate->eht_ru_alloc);
 		return 0;
 	}
@@ -1698,11 +1700,61 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
 	tmp *= rate->nss;
 	do_div(tmp, 8);
 
+	/* and handle interference mitigation - 0.9x */
+	if (rate->flags & RATE_INFO_FLAGS_UHR_IM) {
+		tmp *= 9000;
+		do_div(tmp, 10000);
+	}
+
 	result = tmp;
 
 	return result / 10000;
 }
 
+static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
+{
+	if (WARN_ONCE(rate->mcs > 15, "bad EHT MCS %d\n", rate->mcs))
+		return 0;
+
+	if (WARN_ONCE(rate->flags & (RATE_INFO_FLAGS_UHR_ELR_MCS |
+				     RATE_INFO_FLAGS_UHR_IM),
+		      "bad EHT MCS flags 0x%x\n", rate->flags))
+		return 0;
+
+	return _cfg80211_calculate_bitrate_eht_uhr(rate);
+}
+
+static u32 cfg80211_calculate_bitrate_uhr(struct rate_info *rate)
+{
+	if (rate->flags & RATE_INFO_FLAGS_UHR_ELR_MCS) {
+		WARN_ONCE(rate->eht_gi != NL80211_RATE_INFO_EHT_GI_1_6,
+			  "bad UHR ELR guard interval %d\n",
+			  rate->eht_gi);
+		WARN_ONCE(rate->mcs > 1, "bad UHR ELR MCS %d\n", rate->mcs);
+		WARN_ONCE(rate->nss != 1, "bad UHR ELR NSS %d\n", rate->nss);
+		WARN_ONCE(rate->bw != RATE_INFO_BW_20,
+			  "bad UHR ELR bandwidth %d\n",
+			  rate->bw);
+		WARN_ONCE(rate->flags & RATE_INFO_FLAGS_UHR_IM,
+			  "bad UHR MCS flags 0x%x\n", rate->flags);
+		if (rate->mcs == 0)
+			return 17;
+		return 33;
+	}
+
+	switch (rate->mcs) {
+	case 0 ... 15:
+	case 17:
+	case 19:
+	case 20:
+	case 23:
+		return _cfg80211_calculate_bitrate_eht_uhr(rate);
+	}
+
+	WARN_ONCE(1, "bad UHR MCS %d\n", rate->mcs);
+	return 0;
+}
+
 static u32 cfg80211_calculate_bitrate_s1g(struct rate_info *rate)
 {
 	/* For 1, 2, 4, 8 and 16 MHz channels */
@@ -1827,6 +1879,8 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
 		return cfg80211_calculate_bitrate_he(rate);
 	if (rate->flags & RATE_INFO_FLAGS_EHT_MCS)
 		return cfg80211_calculate_bitrate_eht(rate);
+	if (rate->flags & RATE_INFO_FLAGS_UHR_MCS)
+		return cfg80211_calculate_bitrate_uhr(rate);
 	if (rate->flags & RATE_INFO_FLAGS_S1G_MCS)
 		return cfg80211_calculate_bitrate_s1g(rate);
 
-- 
2.52.0


^ permalink raw reply related

* [PATCH wireless-next v3 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-27 14:09 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260127141005.583581-5-johannes@sipsolutions.net>

From: Johannes Berg <johannes.berg@intel.com>

This is based on Draft P802.11bn_D1.0, but that's still very
incomplete, so don't handle a number of things and make some
local decisions such as 40 bits for MAC capabilities etc.
Also, I fixed the NPCA channel to be 8 instead of 4 bits.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/linux/ieee80211-uhr.h | 176 ++++++++++++++++++++++++++++++++++
 include/linux/ieee80211.h     |  31 +++++-
 2 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 include/linux/ieee80211-uhr.h

diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
new file mode 100644
index 000000000000..e57181ba0182
--- /dev/null
+++ b/include/linux/ieee80211-uhr.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * IEEE 802.11 UHR definitions
+ *
+ * Copyright (c) 2025 Intel Corporation
+ */
+#ifndef LINUX_IEEE80211_UHR_H
+#define LINUX_IEEE80211_UHR_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define IEEE80211_UHR_OPER_PARAMS_DPS_ENA		0x0001
+#define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA		0x0002
+#define IEEE80211_UHR_OPER_PARAMS_DBE_ENA		0x0004
+#define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA		0x0008
+
+struct ieee80211_uhr_oper {
+	__le16 params;
+	u8 basic_mcs_nss_set[4];
+	u8 variable[];
+} __packed;
+
+#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN		0x000000FF
+#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH	0x00000F00
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY		0x0003F000
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY	0x00FC0000
+#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC		0x03000000
+#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN		0x04000000
+#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES	0x08000000
+
+struct ieee80211_uhr_npca_info {
+	__le32 params;
+	__le16 dis_subch_bmap[];
+} __packed;
+
+static inline bool ieee80211_uhr_oper_size_ok(const u8 *data, u8 len,
+					      bool beacon)
+{
+	const struct ieee80211_uhr_oper *oper = (const void *)data;
+	u8 needed = sizeof(*oper);
+
+	if (len < needed)
+		return false;
+
+	/* nothing else present in beacons */
+	if (beacon)
+		return true;
+
+	/* FIXME: DPS, DBE, P-EDCA (consider order, also relative to NPCA) */
+
+	if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) {
+		const struct ieee80211_uhr_npca_info *npca =
+			(const void *)oper->variable;
+
+		needed += sizeof(*npca);
+
+		if (len < needed)
+			return false;
+
+		if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
+			needed += sizeof(npca->dis_subch_bmap[0]);
+	}
+
+	return len >= needed;
+}
+
+/*
+ * Note: cannot call this on the element coming from a beacon,
+ * must ensure ieee80211_uhr_oper_size_ok(..., false) first
+ */
+static inline const struct ieee80211_uhr_npca_info *
+ieee80211_uhr_npca_info(const struct ieee80211_uhr_oper *oper)
+{
+	if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)))
+		return NULL;
+
+	/* FIXME: DPS */
+
+	return (const void *)oper->variable;
+}
+
+static inline const __le16 *
+ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_oper *oper)
+{
+	const struct ieee80211_uhr_npca_info *npca;
+
+	npca = ieee80211_uhr_npca_info(oper);
+	if (!npca)
+		return NULL;
+	if (!(npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)))
+		return NULL;
+	return npca->dis_subch_bmap;
+}
+
+#define IEEE80211_UHR_MAC_CAP0_DPS_SUPP			0x01
+#define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP		0x02
+#define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP	0x04
+#define IEEE80211_UHR_MAC_CAP0_ML_PM_SUPP		0x08
+#define IEEE80211_UHR_MAC_CAP0_NPCA_SUPP		0x10
+#define IEEE80211_UHR_MAC_CAP0_ENH_BSR_SUPP		0x20
+#define IEEE80211_UHR_MAC_CAP0_ADD_MAP_TID_SUPP		0x40
+#define IEEE80211_UHR_MAC_CAP0_EOTSP_SUPP		0x80
+
+#define IEEE80211_UHR_MAC_CAP1_DSO_SUPP			0x01
+#define IEEE80211_UHR_MAC_CAP1_PEDCA_SUPP		0x02
+#define IEEE80211_UHR_MAC_CAP1_DBE_SUPP			0x04
+#define IEEE80211_UHR_MAC_CAP1_UL_LLI_SUPP		0x08
+#define IEEE80211_UHR_MAC_CAP1_P2P_LLI_SUPP		0x10
+#define IEEE80211_UHR_MAC_CAP1_PUO_SUPP			0x20
+#define IEEE80211_UHR_MAC_CAP1_AP_PUO_SUPP		0x40
+#define IEEE80211_UHR_MAC_CAP1_DUO_SUPP			0x80
+
+#define IEEE80211_UHR_MAC_CAP2_OMC_UL_MU_DIS_RX_SUPP	0x01
+#define IEEE80211_UHR_MAC_CAP2_AOM_SUPP			0x02
+#define IEEE80211_UHR_MAC_CAP2_IFCS_LOC_SUPP		0x04
+#define IEEE80211_UHR_MAC_CAP2_UHR_TRS_SUPP		0x08
+#define IEEE80211_UHR_MAC_CAP2_TXSPG_SUPP		0x10
+#define IEEE80211_UHR_MAC_CAP2_TXOP_RET_IN_TXSPG	0x20
+#define IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW		0xC0
+
+#define IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH	0x03
+#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV	0x1C
+#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTVL_LOW	0xE0
+
+#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTVL_LOW	0x03
+#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE		0x04
+
+struct ieee80211_uhr_capa_mac {
+	u8 mac_cap[5];
+} __packed;
+
+struct ieee80211_uhr_capa {
+	struct ieee80211_uhr_capa_mac mac;
+	u8 variable[];
+} __packed;
+
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_LE80		0x01
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_LE80	0x02
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_160		0x04
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_160		0x08
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_320		0x10
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_MU_320		0x20
+#define IEEE80211_UHR_PHY_CAP_ELR_RX			0x40
+#define IEEE80211_UHR_PHY_CAP_ELR_TX			0x80
+
+struct ieee80211_uhr_capa_phy {
+	u8 cap;
+} __packed;
+
+static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len)
+{
+	/* FIXME - DBE */
+	return len >= sizeof(struct ieee80211_uhr_capa) +
+		      sizeof(struct ieee80211_uhr_capa_phy);
+}
+
+static inline const struct ieee80211_uhr_capa_phy *
+ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
+{
+	/* FIXME - skip variable DBE stuff, assume not present for now */
+	return (const void *)&cap->variable;
+}
+
+#define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD		0x01
+#define IEEE80211_SMD_INFO_CAPA_MAX_NUM_PREP		0x0E
+#define IEEE80211_SMD_INFO_CAPA_TYPE			0x10
+#define IEEE80211_SMD_INFO_CAPA_PTK_PER_AP_MLD		0x20
+
+struct ieee80211_smd_info {
+	u8 id[ETH_ALEN];
+	u8 capa;
+	__le16 timeout;
+} __packed;
+
+#endif /* LINUX_IEEE80211_UHR_H */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index fbde215c25aa..3dcfc4acf7d5 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1200,8 +1200,9 @@ struct ieee80211_mgmt {
 #define BSS_MEMBERSHIP_SELECTOR_SAE_H2E 123
 #define BSS_MEMBERSHIP_SELECTOR_HE_PHY	122
 #define BSS_MEMBERSHIP_SELECTOR_EHT_PHY	121
+#define BSS_MEMBERSHIP_SELECTOR_UHR_PHY	120
 
-#define BSS_MEMBERSHIP_SELECTOR_MIN	BSS_MEMBERSHIP_SELECTOR_EHT_PHY
+#define BSS_MEMBERSHIP_SELECTOR_MIN	BSS_MEMBERSHIP_SELECTOR_UHR_PHY
 
 /* mgmt header + 1 byte category code */
 #define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
@@ -1802,6 +1803,15 @@ enum ieee80211_eid_ext {
 	WLAN_EID_EXT_BANDWIDTH_INDICATION = 135,
 	WLAN_EID_EXT_KNOWN_STA_IDENTIFCATION = 136,
 	WLAN_EID_EXT_NON_AP_STA_REG_CON = 137,
+	WLAN_EID_EXT_UHR_OPER = 151,
+	WLAN_EID_EXT_UHR_CAPA = 152,
+	WLAN_EID_EXT_MACP = 153,
+	WLAN_EID_EXT_SMD = 154,
+	WLAN_EID_EXT_BSS_TRANS_PARAMS = 155,
+	WLAN_EID_EXT_CHAN_USAGE = 156,
+	WLAN_EID_EXT_UHR_MODE_CHG = 157,
+	WLAN_EID_EXT_UHR_PARAM_UPD = 158,
+	WLAN_EID_EXT_TXPI = 159,
 };
 
 /* Action category code */
@@ -2745,6 +2755,22 @@ static inline bool for_each_element_completed(const struct element *element,
 #define WLAN_RSNX_CAPA_PROTECTED_TWT BIT(4)
 #define WLAN_RSNX_CAPA_SAE_H2E BIT(5)
 
+/* EBPCC = Enhanced BSS Parameter Change Count */
+#define IEEE80211_ENH_CRIT_UPD_EBPCC		0x0F
+#define IEEE80211_ENH_CRIT_UPD_TYPE		0x70
+#define IEEE80211_ENH_CRIT_UPD_TYPE_NO_UHR	0
+#define IEEE80211_ENH_CRIT_UPD_TYPE_UHR		1
+#define IEEE80211_ENH_CRIT_UPD_ALL		0x80
+
+/**
+ * struct ieee80211_enh_crit_upd - enhanced critical update (UHR)
+ * @v: value of the enhanced critical update data,
+ *	see %IEEE80211_ENH_CRIT_UPD_* to parse the bits
+ */
+struct ieee80211_enh_crit_upd {
+	u8 v;
+} __packed;
+
 /*
  * reduced neighbor report, based on Draft P802.11ax_D6.1,
  * section 9.4.2.170 and accepted contributions.
@@ -2763,6 +2789,7 @@ static inline bool for_each_element_completed(const struct element *element,
 #define IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS			0x10
 #define IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE			0x20
 #define IEEE80211_RNR_TBTT_PARAMS_COLOC_AP			0x40
+#define IEEE80211_RNR_TBTT_PARAMS_SAME_SMD			0x80
 
 #define IEEE80211_RNR_TBTT_PARAMS_PSD_NO_LIMIT			127
 #define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED			-128
@@ -2815,12 +2842,14 @@ struct ieee80211_tbtt_info_ge_11 {
 	u8 bss_params;
 	s8 psd_20;
 	struct ieee80211_rnr_mld_params mld_params;
+	struct ieee80211_enh_crit_upd enh_crit_upd;
 } __packed;
 
 #include "ieee80211-ht.h"
 #include "ieee80211-vht.h"
 #include "ieee80211-he.h"
 #include "ieee80211-eht.h"
+#include "ieee80211-uhr.h"
 #include "ieee80211-mesh.h"
 #include "ieee80211-s1g.h"
 #include "ieee80211-p2p.h"
-- 
2.52.0


^ permalink raw reply related

* [PATCH wireless-next v3 0/3] wifi: initial UHR support
From: Johannes Berg @ 2026-01-27 14:09 UTC (permalink / raw)
  To: linux-wireless

No major changes from the previous version, and I do expect some
fairly big changes to this will happen with future drafts, but it
seems like we should start having something to work together on.

johannes


^ permalink raw reply

* Re: [DESIGN RFC v3] AP Architecture for Wi-Fi-8 Multi-AP Coordination (MAPC)
From: Johannes Berg @ 2026-01-27 13:57 UTC (permalink / raw)
  To: Abhishek Rajkapur Suryawanshi, Jeff Johnson, ath12k, hostap
  Cc: linux-wireless
In-Reply-To: <88f05d03-2742-4b61-964f-54beafdd2e70@oss.qualcomm.com>

On Thu, 2026-01-22 at 00:42 -0800, Abhishek Rajkapur Suryawanshi wrote:
> > Taking this specific example, it basically says "FW sends a request to
> > hostapd, hostapd does the handshake and installs the MAPC station. This
> > is how we think we should handle the MAPC stations."
> 
> hostapd controls and manages all MAPC related discovery and MAPC peer 
> creation part. No trigger from firmware for MAPC Discovery Phase.

Seems to me you're splitting hairs. Clearly you have the *negotiation*
trigger (NL80211_CMD_MAPC_NEGOTIATION_TRIGGER) prominently featured in
the diagrams etc. Sure, it maybe that doesn't explicitly trigger
*discovery*, but if hostapd doesn't already know about the station that
the FW/driver is requesting a negotiation with, it probably also has to
do discovery...

Anyway. I went to write a long email replying to some of your individual
points but deleted it again, I feel it's not worth your and my time.


Clearly, you have already decided on the architecture, and aren't
sharing all of it, based on reasons you aren't really sharing either.
Which is fine, I can't claim that we always share the full reasoning
behind architecture decisions.

We should all still make it transparent _how_ it's intended to work, but
that could also be just part of the documentation added to the kernel
when adding the necessary APIs.


Which then means that really in all the hundreds of lines of text you
just wanted to ask whether or not "Option-A" or "Option-B" for handling
the MAPC peers should be used, and I'd agree that duplicating APIs isn't
good, i.e. taking "Option-A". Note we did something similar for NAN
stations, even if they have different sets of attributes than regular AP
client stations or mesh peers etc.

johannes

^ permalink raw reply

* Re: [PATCH] Revert "wifi: iwlwifi: trans: remove STATUS_SUSPENDED"
From: Chris Bainbridge @ 2026-01-27 13:46 UTC (permalink / raw)
  To: Korenblit, Miriam Rachel
  Cc: kvalo@kernel.org, Berg, Johannes, benjamin@sipsolutions.net,
	gustavoars@kernel.org, linux-intel-wifi@intel.com,
	linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <DM3PPF63A6024A91EFB76D52F9590E82C30A390A@DM3PPF63A6024A9.namprd11.prod.outlook.com>

On Tue, Jan 27, 2026 at 01:22:06PM +0000, Korenblit, Miriam Rachel wrote:
> 
> 
> > -----Original Message-----
> > From: Chris Bainbridge <chris.bainbridge@gmail.com>
> > Sent: Tuesday, January 27, 2026 2:22 PM
> > To: Korenblit, Miriam Rachel <miriam.rachel.korenblit@intel.com>
> > Cc: kvalo@kernel.org; Berg, Johannes <johannes.berg@intel.com>;
> > benjamin@sipsolutions.net; gustavoars@kernel.org; linux-intel-wifi@intel.com;
> > linux-wireless@vger.kernel.org; netdev@vger.kernel.org; linux-
> > kernel@vger.kernel.org; stable@vger.kernel.org
> > Subject: Re: [PATCH] Revert "wifi: iwlwifi: trans: remove STATUS_SUSPENDED"
> > 
> > On Tue, Jan 27, 2026 at 05:05:31AM +0000, Korenblit, Miriam Rachel wrote:
> > >
> > > Just making sure: have you been able to reproduce the assert
> > (ADVANCED_SYSASSERT in the log), and then the panic didn't happen?
> > 
> > Yes, the ADVANCED_SYSASSERT was logged but there was no subsequent null
> > pointer dereference.
> > 
> > > If yes, please test the attached patch, which is supposed to fix the assert itself.
> > With this, you are not even supposed to see an assert.
> > 
> > I ran this through 15 suspend/resume cycles with network traffic. The
> > ADVANCED_SYSASSERT did not occur, so it looks like the patch does fix the
> > SYSASSERT. There was a warning logged once out of the 15 cycles:
> > 
> > I think that this is probably a different bug though - checking previous logs with
> > journalctl, I see the same warning occurred once with 6.17.0-06871-
> > gf79e772258df which predates the "remove STATUS_SUSPENDED"
> > commit.
> 
> Thanks for the help!
> 
> Regarding the warning, please open Bugzilla ticket for that
> 
> Miri

Sure, ticket is https://bugzilla.kernel.org/show_bug.cgi?id=221017

^ permalink raw reply

* RE: [PATCH] Revert "wifi: iwlwifi: trans: remove STATUS_SUSPENDED"
From: Korenblit, Miriam Rachel @ 2026-01-27 13:22 UTC (permalink / raw)
  To: Chris Bainbridge
  Cc: kvalo@kernel.org, Berg, Johannes, benjamin@sipsolutions.net,
	gustavoars@kernel.org, linux-intel-wifi@intel.com,
	linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <aXit5795WQLL290t@debian.local>



> -----Original Message-----
> From: Chris Bainbridge <chris.bainbridge@gmail.com>
> Sent: Tuesday, January 27, 2026 2:22 PM
> To: Korenblit, Miriam Rachel <miriam.rachel.korenblit@intel.com>
> Cc: kvalo@kernel.org; Berg, Johannes <johannes.berg@intel.com>;
> benjamin@sipsolutions.net; gustavoars@kernel.org; linux-intel-wifi@intel.com;
> linux-wireless@vger.kernel.org; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; stable@vger.kernel.org
> Subject: Re: [PATCH] Revert "wifi: iwlwifi: trans: remove STATUS_SUSPENDED"
> 
> On Tue, Jan 27, 2026 at 05:05:31AM +0000, Korenblit, Miriam Rachel wrote:
> >
> > Just making sure: have you been able to reproduce the assert
> (ADVANCED_SYSASSERT in the log), and then the panic didn't happen?
> 
> Yes, the ADVANCED_SYSASSERT was logged but there was no subsequent null
> pointer dereference.
> 
> > If yes, please test the attached patch, which is supposed to fix the assert itself.
> With this, you are not even supposed to see an assert.
> 
> I ran this through 15 suspend/resume cycles with network traffic. The
> ADVANCED_SYSASSERT did not occur, so it looks like the patch does fix the
> SYSASSERT. There was a warning logged once out of the 15 cycles:
> 
> I think that this is probably a different bug though - checking previous logs with
> journalctl, I see the same warning occurred once with 6.17.0-06871-
> gf79e772258df which predates the "remove STATUS_SUSPENDED"
> commit.

Thanks for the help!

Regarding the warning, please open Bugzilla ticket for that

Miri



^ permalink raw reply

* Re: [RFC v5 wireless-next 4/4] wifi: mac80211_hwsim: background CAC support
From: Johannes Berg @ 2026-01-27 13:07 UTC (permalink / raw)
  To: Janusz Dziedzic, linux-wireless
In-Reply-To: <20260125160353.34102-5-janusz.dziedzic@gmail.com>

On Sun, 2026-01-25 at 17:00 +0100, Janusz Dziedzic wrote:
> Report background CAC support and add allow
> to cancel background CAC and simulate radar.

It seems that perhaps this should be optional, so that we can continue
using the existing code paths too?

> +static ssize_t hwsim_background_cac_write(struct file *file,
> +					  const char __user *user_buf,
> +					  size_t count, loff_t *ppos)
> +{
> +	struct mac80211_hwsim_data *data = file->private_data;
> +	size_t buf_size;
> +	char buf[16];
> +
> +	buf_size = min(count, sizeof(buf) - 1);
> +	if (copy_from_user(buf, user_buf, buf_size))
> +		return -EFAULT;
> +
> +	buf[buf_size] = '\0';
> +
> +	/* Remove trailing newline if present */
> +	if (buf_size > 0 && buf[buf_size - 1] == '\n')
> +		buf[buf_size - 1] = '\0';

Perhaps simpler to just do buf[...] = {}, return an error if count >
sizeof(buf)-1, and then just copy?

> +	if (strcmp(buf, "radar") == 0)
> +		cfg80211_background_radar_event(data->hw->wiphy,
> +						&data->radar_background_chandef,
> +						GFP_KERNEL);
> +	else if (strcmp(buf, "cancel") == 0)
> +		cfg80211_background_cac_abort(data->hw->wiphy);

I wonder if this could be simplified by using sysfs_match_string(), but
I guess that'd require some enum or so and then isn't really worth it.
Surely though sysfs_streq() could be used instead of the manual trailing
newline handling, though honestly I feel it's kind of pointless anyway
since the user can just use "echo -n" instead of "echo" and not _have_
the \n in the first place :)

johannes

^ permalink raw reply

* Re: [RFC v5 wireless-next 2/4] wifi: cfg80211: set and report chandef CAC ongoing
From: Johannes Berg @ 2026-01-27 13:02 UTC (permalink / raw)
  To: Janusz Dziedzic, linux-wireless
In-Reply-To: <20260125160353.34102-3-janusz.dziedzic@gmail.com>

On Sun, 2026-01-25 at 17:00 +0100, Janusz Dziedzic wrote:
> 
> + * @cac_ongoing_time: timestamp (CLOCK_BOOTTIME, nanoseconds) when CAC was
> + *	started on this channel. Zero when CAC is not in progress.

Is "ongoing" really a good name? To me something like "CAC start
timestamp" would seem more descriptive?

>   * @psd: power spectral density (in dBm)
>   */
>  struct ieee80211_channel {
> @@ -205,6 +207,7 @@ struct ieee80211_channel {
>  	enum nl80211_dfs_state dfs_state;
>  	unsigned long dfs_state_entered;
>  	unsigned int dfs_cac_ms;
> +	u64 cac_ongoing_time;
>  	s8 psd;
>  };
>  
> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
> index b0f050e36fa4..7c23fd1b8ce9 100644
> --- a/include/uapi/linux/nl80211.h
> +++ b/include/uapi/linux/nl80211.h
> @@ -4452,6 +4452,10 @@ enum nl80211_wmm_rule {
>   * @NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY: Channel is not permitted for use
>   *	as a primary channel. Does not prevent the channel from existing
>   *	as a non-primary subchannel. Only applicable to S1G channels.
> + * @NL80211_FREQUENCY_ATTR_CAC_START_TIME: Channel Availability Check (CAC)
> + *	start time (CLOCK_BOOTTIME, nanoseconds). Only present when CAC is
> + *	currently in progress on this channel.

And here it _is_ "CAC start time".


johannes

^ permalink raw reply

* Re: [PATCH wireless-next 1/4] wifi: nl80211/cfg80211: add new FTM capabilities
From: Johannes Berg @ 2026-01-27 12:41 UTC (permalink / raw)
  To: Miri Korenblit, linux-wireless; +Cc: Avraham Stern
In-Reply-To: <20260111190221.bf43785c18f6.Ic98cf9790ddee84bf88e5720b93c46c23af3c96c@changeid>

On Sun, 2026-01-11 at 19:03 +0200, Miri Korenblit wrote:
> From: Avraham Stern <avraham.stern@intel.com>
> 
> Add new capabilities to the PMSR FTM capabilities list. The new
> capabilities include 6 GHz support, supported number of spatial streams
> and supported number of LTF repetitions.
> 
> Signed-off-by: Avraham Stern <avraham.stern@intel.com>
> Tested-by: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>
> tested: Miriam Rachel Korenblit <miriam.rachel.korenblit@intel.com>

"tested:" is not a customary tag, I've edited that out.

johannes

^ permalink raw reply

* Re: [PATCH] Revert "wifi: iwlwifi: trans: remove STATUS_SUSPENDED"
From: Chris Bainbridge @ 2026-01-27 12:21 UTC (permalink / raw)
  To: Korenblit, Miriam Rachel
  Cc: kvalo@kernel.org, Berg, Johannes, benjamin@sipsolutions.net,
	gustavoars@kernel.org, linux-intel-wifi@intel.com,
	linux-wireless@vger.kernel.org, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <DM3PPF63A6024A9FCE1CF29C0492A406E7AA390A@DM3PPF63A6024A9.namprd11.prod.outlook.com>

On Tue, Jan 27, 2026 at 05:05:31AM +0000, Korenblit, Miriam Rachel wrote:
> 
> Just making sure: have you been able to reproduce the assert (ADVANCED_SYSASSERT in the log), and then the panic didn't happen?

Yes, the ADVANCED_SYSASSERT was logged but there was no subsequent null pointer
dereference.

> If yes, please test the attached patch, which is supposed to fix the assert itself. With this, you are not even supposed to see an assert.

I ran this through 15 suspend/resume cycles with network traffic. The
ADVANCED_SYSASSERT did not occur, so it looks like the patch does fix
the SYSASSERT. There was a warning logged once out of the 15 cycles:

[  454.184873] ------------[ cut here ]------------
[  454.184894] WR pointer moved while flushing 216 -> 239
[  454.184898] WARNING: drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c:2570 at iwl_trans_pcie_wait_txq_empty+0x4f7/0x6d0 [iwlwifi], CPU#9: kworker/u64:38/4331
[  454.184927] Modules linked in: snd_seq_dummy snd_hrtimer snd_seq xt_conntrack nft_chain_nat xt_MASQUERADE nf_nat nf_conntrack_netlink nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 xfrm_user xfrm_algo xt_addrtype nft_compat x_tables nf_tables br_netfilter bridge stp llc ccm overlay qrtr rfcomm cmac algif_hash algif_skcipher af_alg bnep binfmt_misc ext4 mbcache jbd2 nls_ascii nls_cp437 vfat fat snd_acp3x_pdm_dma snd_soc_dmic snd_acp3x_rn snd_hda_codec_generic snd_soc_core intel_rapl_msr snd_compress amd_atl intel_rapl_common snd_hda_codec_hdmi iwlmvm mac80211 snd_hda_intel libarc4 snd_usb_audio uvcvideo kvm_amd snd_hda_codec snd_pci_acp6x btusb videobuf2_vmalloc snd_usbmidi_lib snd_intel_dspcfg snd_pci_acp5x btrtl videobuf2_memops snd_rawmidi snd_hwdep btintel kvm btbcm uvc snd_seq_device snd_hda_core btmtk irqbypass videobuf2_v4l2 snd_rn_pci_acp3x iwlwifi snd_pcm rapl videodev snd_acp_config bluetooth pcspkr cfg80211 snd_soc_acpi snd_timer wmi_bmof ecdh_generic videobuf2_common ee1004 ecc k10temp snd_pci_acp3x mc snd
[  454.185174]  rfkill soundcore ccp ac battery sg button amd_pmc acpi_tad joydev evdev msr parport_pc ppdev lp parport nvme_fabrics fuse efi_pstore configfs nfnetlink efivarfs autofs4 crc32c_cryptoapi btrfs blake2b libblake2b xor raid6_pq dm_crypt hid_microsoft ff_memless hid_cmedia r8153_ecm cdc_ether usbnet dm_mod r8152 mii libphy mdio_bus sd_mod uas usb_storage scsi_mod usbhid scsi_common amdgpu drm_client_lib i2c_algo_bit drm_ttm_helper ttm drm_exec drm_suballoc_helper drm_buddy drm_panel_backlight_quirks gpu_sched amdxcp hid_multitouch drm_display_helper ucsi_acpi hid_generic typec_ucsi drm_kms_helper roles xhci_pci sp5100_tco video xhci_hcd typec cec i2c_hid_acpi watchdog i2c_hid amd_sfh ghash_clmulni_intel serio_raw rc_core usbcore nvme thunderbolt hid i2c_piix4 crc16 i2c_smbus nvme_core fan usb_common drm wmi aesni_intel
[  454.185402] CPU: 9 UID: 0 PID: 4331 Comm: kworker/u64:38 Not tainted 6.19.0-rc6-00341-gf1d1ad131417 #406 PREEMPT(voluntary)
[  454.185408] Hardware name: HP HP Pavilion Aero Laptop 13-be0xxx/8916, BIOS F.17 12/18/2024
[  454.185412] Workqueue: async async_run_entry_fn
[  454.185423] RIP: 0010:iwl_trans_pcie_wait_txq_empty+0x4fe/0x6d0 [iwlwifi]
[  454.185446] Code: 48 c7 04 03 00 00 00 00 48 81 c4 88 00 00 00 89 d0 5b 5d 41 5c 41 5d 41 5e 41 5f c3 48 8d 3d 99 65 09 00 0f b6 d3 40 0f b6 f6 <67> 48 0f b9 3a ba 92 ff ff ff eb bb 48 89 c7 e8 8e 7a 05 de e9 4e
[  454.185450] RSP: 0018:ffff888110abf488 EFLAGS: 00010297
[  454.185456] RAX: fffffbfff4a42250 RBX: 00000000000033ef RCX: 0000000000000006
[  454.185460] RDX: 00000000000000ef RSI: 00000000000000d8 RDI: ffffffffc4c207b0
[  454.185464] RBP: dffffc0000000000 R08: 0000000000000001 R09: 0000000000000000
[  454.185468] R10: 000000000000335f R11: 0000000000000006 R12: ffffed102c4293b0
[  454.185471] R13: ffff888162149c00 R14: ffff888162149d80 R15: ffff888162149d00
[  454.185475] FS:  0000000000000000(0000) GS:ffff8883e3d30000(0000) knlGS:0000000000000000
[  454.185479] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  454.185483] CR2: 00007ff3d9400000 CR3: 00000002b9b2d000 CR4: 0000000000750ef0
[  454.185487] PKRU: 55555554
[  454.185490] Call Trace:
[  454.185494]  <TASK>
[  454.185506]  ? iwl_trans_pcie_rxq_dma_data+0x220/0x220 [iwlwifi]
[  454.185530]  ? __local_bh_enable_ip+0xab/0x130
[  454.185540]  iwl_mvm_wait_sta_queues_empty+0x8c/0xd0 [iwlmvm]
[  454.185568]  iwl_mvm_mac_flush+0x668/0xd20 [iwlmvm]
[  454.185591]  ? lock_is_held_type+0xd5/0x130
[  454.185602]  __ieee80211_flush_queues+0x3cd/0x7c0 [mac80211]
[  454.185679]  ieee80211_set_disassoc+0x123f/0x1ac0 [mac80211]
[  454.185752]  ? ieee80211_powersave_allowed+0x360/0x360 [mac80211]
[  454.185818]  ? unwind_next_frame+0x2e2/0x1ea0
[  454.185830]  ieee80211_mgd_deauth.cold+0xa3/0x71e [mac80211]
[  454.185900]  ? __lock_acquire+0x459/0x2170
[  454.185908]  ? ieee80211_mgd_assoc+0x4ae0/0x4ae0 [mac80211]
[  454.185988]  ? lock_is_held_type+0xd5/0x130
[  454.185999]  cfg80211_mlme_deauth+0x33b/0x890 [cfg80211]
[  454.186080]  ? cfg80211_mlme_assoc+0xa50/0xa50 [cfg80211]
[  454.186164]  cfg80211_mlme_down+0x17a/0x240 [cfg80211]
[  454.186236]  ? cfg80211_mlme_disassoc+0x540/0x540 [cfg80211]
[  454.186315]  cfg80211_disconnect+0x50f/0x800 [cfg80211]
[  454.186393]  wiphy_suspend+0x1f8/0x350 [cfg80211]
[  454.186472]  ? rdev_suspend+0x2a0/0x2a0 [cfg80211]
[  454.186544]  dpm_run_callback+0xfc/0x410
[  454.186554]  ? dpm_async_suspend_superior+0x2e0/0x2e0
[  454.186561]  ? pm_verb+0x90/0x90
[  454.186566]  ? _raw_spin_unlock_irqrestore+0x48/0x60
[  454.186578]  device_suspend+0x37b/0x1080
[  454.186587]  ? async_suspend_late+0x30/0x30
[  454.186594]  ? seqcount_lockdep_reader_access.constprop.0+0x82/0x90
[  454.186601]  ? lockdep_hardirqs_on+0x78/0x100
[  454.186612]  ? device_suspend+0x1080/0x1080
[  454.186616]  async_suspend+0x19/0x30
[  454.186622]  async_run_entry_fn+0x93/0x500
[  454.186633]  process_one_work+0x849/0x1420
[  454.186651]  ? pwq_dec_nr_in_flight+0xe00/0xe00
[  454.186665]  ? assign_work+0x168/0x240
[  454.186670]  ? lock_is_held_type+0xd5/0x130
[  454.186679]  worker_thread+0x5ef/0xfd0
[  454.186697]  ? process_one_work+0x1420/0x1420
[  454.186701]  kthread+0x3ad/0x760
[  454.186709]  ? kthread_is_per_cpu+0xb0/0xb0
[  454.186713]  ? ret_from_fork+0x71/0x800
[  454.186719]  ? ret_from_fork+0x71/0x800
[  454.186725]  ? _raw_spin_unlock_irq+0x24/0x50
[  454.186732]  ? kthread_is_per_cpu+0xb0/0xb0
[  454.186739]  ret_from_fork+0x54a/0x800
[  454.186746]  ? exit_thread+0x70/0x70
[  454.186754]  ? __switch_to+0x36f/0xd60
[  454.186761]  ? kthread_is_per_cpu+0xb0/0xb0
[  454.186768]  ret_from_fork_asm+0x11/0x20
[  454.186789]  </TASK>
[  454.186792] irq event stamp: 1499703
[  454.186795] hardirqs last  enabled at (1499709): [<ffffffffa241cdee>] __up_console_sem+0x5e/0x70
[  454.186802] hardirqs last disabled at (1499714): [<ffffffffa241cdd3>] __up_console_sem+0x43/0x70
[  454.186808] softirqs last  enabled at (1498148): [<ffffffffc4b8a152>] iwl_trans_pcie_wait_txq_empty+0x432/0x6d0 [iwlwifi]
[  454.186831] softirqs last disabled at (1498146): [<ffffffffc4b8a00f>] iwl_trans_pcie_wait_txq_empty+0x2ef/0x6d0 [iwlwifi]
[  454.186852] ---[ end trace 0000000000000000 ]---

I think that this is probably a different bug though - checking previous
logs with journalctl, I see the same warning occurred once with
6.17.0-06871-gf79e772258df which predates the "remove STATUS_SUSPENDED"
commit.

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox