Linux wireless drivers development
 help / color / mirror / Atom feed
* Problems building rtw89
From: Iohann Tachy @ 2026-01-29 12:12 UTC (permalink / raw)
  To: linux-wireless

Good morning,

Currently there's an issue where it's not possible to build the rtw89 driver.
The RTL8852BE card has a compatibility problem with Intel X99 chipset
and a hacked rtw89 driver is needed to make it work.

I kindly request to read the Github issue:
https://github.com/lwfinger/rtw89/issues/396


Regards,
Iohann

^ permalink raw reply

* Re: [PATCH wireless-next v5 1/3] wifi: ieee80211: add some initial UHR definitions
From: Pablo MARTIN-GOMEZ @ 2026-01-29 11:42 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260129092039.53d7f326bc8f.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>

On 29/01/2026 09:20, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> This is based on Draft P802.11bn_D1.2, but that's still very
> incomplete, so don't handle a number of things and make some
> local decisions such as using 40 bits for MAC capabilities
> and 8 bits for PHY capabilities.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> v5:
>    - use correct EHT MCS len (24 bits, not 24 bytes)
>    - handle DBE AP/non-AP in ieee80211_uhr_capa_size_ok()
> v4:
>    - update to D1.2, including DBE in UHR capabilities
>    - fold in suggestions from Pablo
> ---
>   include/linux/ieee80211-uhr.h | 219 ++++++++++++++++++++++++++++++++++
>   include/linux/ieee80211.h     |  33 ++++-
>   2 files changed, 250 insertions(+), 2 deletions(-)
>   create mode 100644 include/linux/ieee80211-uhr.h
[...]
> +
> +static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len, bool ap)
> +{
> +	const struct ieee80211_uhr_capa *cap = (const void *)data;
> +	size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_capa_phy);
> +
> +	if (len < needed)
> +		return false;
> +
> +	/*
> +	 * A non-AP STA does not include the DBE Capability Parameters field
> +	 * in the UHR MAC Capabilities Information field.
> +	 */
> +	if (ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
> +		u8 dbe;
> +
> +		needed += 1;
> +		if (len < needed)
> +			return false;
> +
> +		dbe = cap->variable[0];
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
> +			needed += 3;
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
> +			needed += 3;
> +	}
> +
> +	return len >= needed;
> +}
> +
> +static inline const struct ieee80211_uhr_capa_phy *
> +ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
> +{
> +	u8 offs = 0;
> +
> +	if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
Shouldn't we check for 'ap' here too?
> +		u8 dbe = cap->variable[0];
> +
> +		offs += 1;
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
> +			offs += 3;
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
> +			offs += 3;
> +	}
> +
> +	return (const void *)&cap->variable[offs];
> +}
> +

[...]

Pablo


^ permalink raw reply

* Re: [PATCH v2 0/2] ath11k: support IPQ9574
From: Vasanthakumar Thiagarajan @ 2026-01-29 11:17 UTC (permalink / raw)
  To: Alexandru Gagniuc, robh, krzk+dt, conor+dt, jjohnson, ath11k
  Cc: johannes, linux-wireless, devicetree, linux-kernel
In-Reply-To: <20260109044457.3074510-1-mr.nuke.me@gmail.com>



On 1/9/2026 10:14 AM, Alexandru Gagniuc wrote:
> This is my attempt to make ath11k work on IPQ9574. IPQ9574 is intended
> for wifi-7 devices, so it seems the exception rather than the rule to
> pair it with a wifi-6 chip. That's exactly what my Archer BE550 does.
> 
> This consists of a wifi-6 (QCN5024) chip connected via PCIe to the
> SoC, but exposed to the OS via AHB.

Pls note that IPQ9574 has internal wifi-6 which is connected AHB
not PCIe. Also, host driver does not really need to worry about QCN5024
(that is not connected over PCIe either anyway). You may also want to change
the commit message of "[PATCH v2 2/2] wifi: ath11k: support ipq9574"
accordingly.

Vasanth

^ permalink raw reply

* [GIT PULL] wireless-2026-01-29
From: Johannes Berg @ 2026-01-29 11:03 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless

Hi,

Here's just a single potential out-of-bounds read fix,
and I really hope that's the last for this cycle.

Please pull and let us know if there's any problem.

Thanks,
johannes



The following changes since commit 0a80e38d0fe1fe7b59c1e93ad908c4148a15926a:

  Merge tag 'net-6.19-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net (2026-01-22 09:32:11 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git tags/wireless-2026-01-29

for you to fetch changes up to 1eab33aa63c993685dd341e03bd5b267dd7403fa:

  wifi: mac80211: correctly decode TTLM with default link map (2026-01-29 11:46:43 +0100)

----------------------------------------------------------------
Just one fix, for a parsing error in mac80211 that might
result in a one byte out-of-bounds read.

----------------------------------------------------------------
Benjamin Berg (1):
      wifi: mac80211: correctly decode TTLM with default link map

 net/mac80211/mlme.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

^ permalink raw reply

* [GIT PULL] wireless-next-2026-01-29
From: Johannes Berg @ 2026-01-29 10:58 UTC (permalink / raw)
  To: netdev; +Cc: linux-wireless

Hi,

Here's another set of changes for net-next. Two things to note:

 1) This introduces a couple of new sparse warnings, because it
    cannot deal with guard(spinlock_bh)() which the drivers are
    now using. I previously fixed sparse for that for RCU, but
    given the context tracking work will remove this validation
    from sparse entirely, I haven't bothered trying to keep up.

 2) There's a core SDIO patch in here, but that's intentional
    and we agreed with the maintainer to merge it through this
    tree since only two wireless changes depend on it.

Please pull and let us know if there's any problem.

Thanks,
johannes



The following changes since commit 669aa3e3faa8ae876c169cde5b234828275caef8:

  Merge tag 'wireless-next-2026-01-12' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next (2026-01-12 17:02:02 -0800)

are available in the Git repository at:

  https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git tags/wireless-next-2026-01-29

for you to fetch changes up to c30e188bd2a886258be5facb970a804d8ef549b5:

  Merge tag 'iwlwifi-next-2026-01-21' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next (2026-01-27 13:54:12 +0100)

----------------------------------------------------------------
Another fairly large set of changes, notably:
 - cfg80211/mac80211
    - most of EPPKE/802.1X over auth frames support
    - additional FTM capabilities
    - split up drop reasons better, removing generic RX_DROP
    - NAN cleanups/fixes
 - ath11k:
    - support for Channel Frequency Response measurement
 - ath12k:
    - support for the QCC2072 chipset
 - iwlwifi:
    - partial NAN support
    - UNII-9 support
    - some UHR/802.11bn FW APIs
    - remove most of MLO/EHT from iwlmvm
      (such devices use iwlmld)
 - rtw89:
    - preparations for RTL8922DE support

----------------------------------------------------------------
Ainy Kumari (2):
      wifi: cfg80211: add support for EPPKE Authentication Protocol
      wifi: cfg80211: add feature flag for (re)association frame encryption

Alexander Minchev (1):
      wifi: ath12k: remove redundant pci_set_drvdata() call

Alexandru Gagniuc (1):
      wifi: ath11k: move .max_tx_ring to struct ath11k_hw_hal_params

Ali Tariq (1):
      wifi: rtl8xxxu: fix slab-out-of-bounds in rtl8xxxu_sta_add

Ally Heev (1):
      wifi: iwlwifi: mld: remove unused variable in d3.c

Avraham Stern (4):
      wifi: nl80211/cfg80211: add new FTM capabilities
      wifi: nl80211/cfg80211: clarify periodic FTM parameters for non-EDCA based ranging
      wifi: nl80211/cfg80211: add negotiated burst period to FTM result
      wifi: nl80211/cfg80211: support operating as RSTA in PMSR FTM request

Baochen Qiang (18):
      wifi: ath12k: do WoW offloads only on primary link
      wifi: ath12k: refactor PCI window register access
      wifi: ath12k: refactor REO CMD ring handling
      wifi: ath12k: refactor REO status ring handling
      wifi: ath12k: fix preferred hardware mode calculation
      wifi: ath12k: refactor 320 MHz bandwidth support parsing
      wifi: ath12k: fix mac phy capability parsing
      wifi: ath12k: add hardware registers for QCC2072
      wifi: ath12k: add hardware parameters for QCC2072
      wifi: ath12k: support LPASS_SHARED target memory type
      wifi: ath12k: support downloading auxiliary ucode image for QCC2072
      wifi: ath12k: add HAL descriptor and ops for QCC2072
      wifi: ath12k: add hardware ops support for QCC2072
      wifi: ath12k: handle REO CMD ring for QCC2072
      wifi: ath12k: handle REO status ring for QCC2072
      wifi: ath12k: limit number of channels per WMI command
      wifi: ath12k: send peer meta data version to firmware
      wifi: ath12k: enable QCC2072 support

Benjamin Berg (2):
      wifi: iwlwifi: mld: decode VHT information for sniffer
      wifi: iwlwifi: mld: change cluster_id type to u8 array

Bitterblue Smith (4):
      wifi: rtw88: 8822b: Avoid WARNING in rtw8822b_config_trx_mode()
      wifi: rtw88: Fix alignment fault in rtw_core_enable_beacon()
      wifi: rtw88: Use devm_kmemdup() in rtw_set_supported_band()
      wifi: rtw88: Fix inadvertent sharing of struct ieee80211_supported_band data

Chia-Yuan Li (1):
      wifi: rtw89: coex: update coex software control for RTL8922D

Chien Wong (1):
      wifi: ath11k: fix comment typo in monitor mode handling

Chih-Kang Chang (7):
      wifi: rtw89: refine C2H reg event polling timeout for LPS
      wifi: rtw89: setting TBTT AGG number when mac port initialization
      wifi: rtw89: mcc: reset probe counter when receiving beacon
      wifi: rtw89: refine TX nulldata judgement when scan with 2 OP channels
      wifi: rtw89: 8922a: configure FW version for SCAN_OFFLOAD_EXTRA_OP feature
      wifi: rtw89: rfk: add rtw89_fw_h2c_rf_pre_ntfy_mcc for new WiFi 7 firmware
      wifi: rtw89: rfk: update rtw89_fw_h2c_rf_pre_ntfy_mcc format

Chin-Yen Lee (4):
      wifi: rtw89: wow: use struct style to fill WOW CAM H2C command
      wifi: rtw89: wow: add WOW_CAM update function for 8922D
      wifi: rtw89: wow: abstract DMA check register for RTL8922DE
      wifi: rtw89: wow: add reason codes for disassociation in WoWLAN mode

Dan Carpenter (1):
      wifi: ath12k: clean up on error in ath12k_dp_setup()

Daniel Gabay (2):
      wifi: iwlwifi: mld: Remove wrong channel flags in scan cmd
      wifi: iwlwifi: mvm: Remove few redundant 6 GHz scan chan params

David Yang (1):
      wifi: mac80211: use u64_stats_t with u64_stats_sync properly

Dian-Syuan Yang (2):
      wifi: rtw89: enhance connection stability when triggering beacon loss
      wifi: rtw89: 8852b: refine hardware parameters for RFE type 5

Emmanuel Grumbach (8):
      wifi: iwlwifi: mld: trigger a dump upon notification if needed
      wifi: iwlwifi: move lari helper functions to the op_mode
      wifi: iwlwifi: move iwl_get_lari_config_bitmap to the op_mode
      wifi: iwlwifi: acpi: cache the DSM functions
      wifi: iwlwifi: uefi: cache the DSM functions
      wifi: iwlwifi: split bios_value_u32 to separate the header
      wifi: iwlwifi: support V13 of iwl_lari_config_change_cmd
      wifi: iwlwifi: rename struct iwl_mcc_allowed_ap_type_cmd::offset_map

Hsiu-Ming Chang (1):
      wifi: rtw88: rtw8821cu: Add ID for Mercusys MU6H

Ilan Peer (6):
      wifi: iwlwifi: mld: Add support for NAN
      wifi: iwlwifi: mld: Handle rate selection for NAN interface
      wifi: iwlwifi: mld: Declare support for NAN capabilities
      wifi: iwlwifi: mld: Extend the NAN configuration
      wifi: iwlwifi: mld: Support changing NAN configuration
      wifi: iwlwifi: mld: Advertise support for multicast RX registration

Jan Gerber (1):
      wifi: rtw89: 8852au: add support for TP TX30U Plus

Johannes Berg (12):
      Merge tag 'rtw-next-2026-01-15' of https://github.com/pkshih/rtw
      wifi: mac80211: remove RX_DROP
      wifi: mac80211: mark iface work SKBs as consumed
      wifi: iwlwifi: fix 22000 series SMEM parsing
      wifi: iwlwifi: adjust LINK context NPCA API
      wifi: iwlwifi: add UHR TLC mode
      wifi: iwlwifi: fw: api: add UHR data definitions
      wifi: iwlwifi: mld: fix HE SIG-B MCS/DCM/compression
      wifi: iwlwifi: fw: api: add HE type for UHR ELR
      wifi: iwlwifi: cfg: remove iwl_be221_name
      Merge tag 'ath-next-20260120' of git://git.kernel.org/pub/scm/linux/kernel/git/ath/ath
      Merge tag 'iwlwifi-next-2026-01-21' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next

Jose Ignacio Tornos Martinez (1):
      wifi: rtw89: 8922a: set random mac if efuse contains zeroes

Kavita Kavita (5):
      wifi: cfg80211: add support for key configuration before association
      wifi: mac80211: allow key installation before association
      wifi: mac80211: Check for MLE before appending in Authentication frame
      wifi: mac80211: add support for EPPKE authentication protocol in non-AP STA mode
      wifi: mac80211: add support for encryption/decryption of (Re)Association frames

Krzysztof Kozlowski (1):
      dt-bindings: net: wireless: ath11k: Combine two if:then: clauses

Kuan-Chung Chen (6):
      wifi: rtw89: 8852b: increase beacon loss to 6 seconds
      wifi: rtw89: mlo: fix missing TX null-data 1 during link switch
      wifi: rtw89: mlo: fix incorrect link address in management frames
      wifi: rtw89: define TX/RX aggregation and MPDU capability per chip
      wifi: rtw89: fix potential zero beacon interval in beacon tracking
      wifi: rtw89: support EHT GI/LTF setting

Lachlan Hodges (1):
      wifi: cfg80211: don't apply HT flags to S1G channels

Marco Crivellari (1):
      wifi: rtw89: add WQ_PERCPU to alloc_workqueue users

Martin Hrůza (1):
      wifi: rtw88: Increase the RX gain before scanning

Miaoqing Pan (1):
      wifi: ath12k: fix PCIE_LOCAL_REG_QRTR_NODE_ID definition for QCC2072

Miri Korenblit (11):
      wifi: cfg80211: allow only one NAN interface, also in multi radio
      wifi: cfg80211: stop NAN and P2P in cfg80211_leave
      wifi: cfg80211: limit NAN func management APIs to offloaded DE
      wifi: cfg80211: cleanup cluster_id when stopping NAN
      wifi: nl80211: ignore cluster id after NAN started
      wifi: iwlwifi: mvm: check the validity of noa_len
      wifi: iwlwifi: mld: support TLC command version 6
      wifi: iwlwifi: cfg: move the MODULE_FIRMWARE to the per-rf file
      wifi: iwlwifi: mld: fix chandef start calculation
      wifi: iwlwifi: mld: add an helper to update an EMLSR blocker
      wifi: iwlwifi: mld: prevent EMLSR when NAN is active

Nidhish A N (3):
      wifi: iwlwifi: mld: Fix primary link selection logic
      wifi: iwlwifi: mvm: Cleanup MLO code
      wifi: iwlwifi: mvm: Remove link_id from time_events

Pagadala Yesu Anjaneyulu (3):
      wifi: iwlwifi: fw: fix documentation reference for ap_type field
      wifi: iwlwifi: mld: refactor AP power type setting
      wifi: cfg80211: treat deprecated INDOOR_SP_AP_OLD control value as LPI mode

Peter Åstrand (1):
      wifi: wlcore: Add support for IGTK key

Ping-Ke Shih (36):
      wifi: rtw89: correct use sequence of driver_data in skb->info
      wifi: rtw89: mac: reset power state before switching to power on
      wifi: rtw89: warn unexpected polling value of XTAL SI
      wifi: rtw89: wow: change type of WoWLAN pattern mask to __le32
      wifi: rtw89: efuse: read hardware version from efuse for WiFi 7 chips
      wifi: rtw89: read chip ID for RTL8922D variants
      wifi: rtw89: add default quirks as features to chip_info
      wifi: rtw89: rfk: update RFK pre info V2 for RTL8922D
      wifi: rtw89: update TXWD v3 for RTL8922D
      wifi: rtw89: update query RXDESC v3 for RTL8922D
      wifi: rtw89: fw: add DMAC v3 H2C command for RTL8922D
      wifi: rtw89: fw: add CMAC H2C command to initialize default value for RTL8922D
      wifi: rtw89: fw: add CMAC H2C command for association for RTL8922D
      wifi: rtw89: fw: add CMAC H2C command for TX AMPDU for RTL8922D
      wifi: rtw89: fw: add CMAC H2C command for TX time for RTL8922D
      wifi: rtw89: fw: add CMAC H2C command for punctured for RTL8922D
      wifi: rtw89: fw: consider hardware AID for firmware elements
      wifi: rtw89: fw: set RACK bit every 4 H2C command for WiFi 6 chips only
      wifi: rtw89: coex: make coex scoreboard as chip info
      wifi: rtw89: coex: update scoreboard value according to power state for two BT
      wifi: rtw89: mac: remove unnecessary return from rtw89_fwdl_secure_idmem_share_mode()
      wifi: rtw89: mac: add dle_mem and ple_{min,max}_qt quota for RTL8922D
      wifi: rtw89: mac: set quota 13 for PLE SNRPT
      wifi: rtw89: mac: update MPDU quota according to chip DLE definition
      wifi: rtw89: mac: update WP quota for RTL8922D
      wifi: rtw89: mac: define preload_init for generations
      wifi: rtw89: mac: configure DMA_STOP1 by predefined mask
      wifi: rtw89: mac: separate functions of CMAC power and function enable
      wifi: rtw89: mac: add an entry to enable MAC function in preinit
      wifi: rtw89: mac: set EDCCA configurations for RTL8922D
      wifi: rtw89: pci: validate sequence number of TX release report
      wifi: rtw89: disable EHT protocol by chip capabilities
      wifi: rtw89: align CUSTID defined by firmware
      wifi: rtw89: mac: correct page number for CSI response
      wifi: rtw89: mac: consider RTL8922D in MAC common flow
      wifi: rtw89: pci: consider RTL8922D in PCI common flow

Qian Zhang (1):
      wifi: ath11k: Fix failure to connect to a 6 GHz AP

Randy Dunlap (4):
      wifi: ath5k: debug.h: fix enum ath5k_debug_level kernel-doc
      wifi: ath9k: debug.h: fix kernel-doc bad lines and struct ath_tx_stats
      wifi: ath9k: fix kernel-doc warnings in common-debug.h
      wifi: wil6210: fix a bunch of kernel-doc warnings

Rohit Chourasia (1):
      wifi: rtlwifi: fix typo 'received' in comment

Roman Peshkichev (1):
      wifi: rtw88: fix DTIM period handling when conf->dtim_period is zero

Rosen Penev (1):
      wifi: ath9k: add OF dependency to AHB

Ross Vandegrift (1):
      wifi: ath11k: add pm quirk for Thinkpad Z13/Z16 Gen1

Sai Pratyusha Magam (1):
      wifi: nl80211: Add support for EPP peer indication

Uwe Kleine-König (3):
      sdio: Provide a bustype shutdown function
      wifi: rsi: sdio: Migrate to use sdio specific shutdown function
      wifi: rtw88: sdio: Migrate to use sdio specific shutdown function

Venkateswara Naralasetty (6):
      wifi: ath11k: Add initialization and deinitialization sequence for CFR module
      wifi: ath11k: Register debugfs for CFR configuration
      wifi: ath11k: Add support unassociated client CFR
      wifi: ath11k: Register relayfs entries for CFR dump
      wifi: ath11k: Register DBR event handler for CFR data
      wifi: ath11k: Register handler for CFR capture event

Zenm Chen (2):
      wifi: rtw89: Add support for MSI AX1800 Nano (GUAX18N)
      wifi: rtw89: Add support for D-Link VR Air Bridge (DWA-F18)

Zilin Guan (1):
      wifi: p54: Fix memory leak in p54_beacon_update()

Ziyi Guo (2):
      wifi: iwlegacy: add missing mutex protection in il3945_store_measurement()
      wifi: iwlegacy: add missing mutex protection in il4965_store_tx_power()

Zong-Zhe Yang (11):
      wifi: rtw89: ser: enable error IMR after recovering from L1
      wifi: rtw89: ser: L1 skip polling status if FW runs event mode
      wifi: rtw89: debug: add ser_counters dbgfs
      wifi: rtw89: debug: support SER L0/L1 simulation via halt H2C
      wifi: rtw89: regd: 6 GHz power type marks default when inactive
      wifi: rtw89: pre-handle RF calibration on link when needed
      wifi: rtw89: fw: change FW feature map to a BITMAP
      wifi: rtw89: fw: introduce helper for disabling FW feature configuration
      wifi: rtw89: 8922a: tweak RFK_PRE_NOTIFY FW feature configuration to align handling
      wifi: rtw89: refine mis-ordered entries in FW feature table
      wifi: rtw89: fw: change WITH_RFK_PRE_NOTIFY to be a FW feature group

 .../bindings/net/wireless/qcom,ath11k.yaml         |    9 -
 drivers/mmc/core/sdio_bus.c                        |   25 +
 drivers/net/wireless/ath/ath11k/Kconfig            |   11 +
 drivers/net/wireless/ath/ath11k/Makefile           |    1 +
 drivers/net/wireless/ath/ath11k/cfr.c              | 1023 ++++++++++++++++++++
 drivers/net/wireless/ath/ath11k/cfr.h              |  308 ++++++
 drivers/net/wireless/ath/ath11k/core.c             |   81 +-
 drivers/net/wireless/ath/ath11k/core.h             |   19 +-
 drivers/net/wireless/ath/ath11k/dbring.c           |   50 +-
 drivers/net/wireless/ath/ath11k/dbring.h           |    8 +-
 drivers/net/wireless/ath/ath11k/debug.h            |    8 +-
 drivers/net/wireless/ath/ath11k/debugfs.c          |    2 +-
 drivers/net/wireless/ath/ath11k/debugfs_sta.c      |  142 ++-
 drivers/net/wireless/ath/ath11k/dp.c               |   12 +-
 drivers/net/wireless/ath/ath11k/dp.h               |    1 -
 drivers/net/wireless/ath/ath11k/dp_tx.c            |    9 +-
 drivers/net/wireless/ath/ath11k/hal.c              |    3 +-
 drivers/net/wireless/ath/ath11k/hw.c               |   19 +-
 drivers/net/wireless/ath/ath11k/hw.h               |    8 +-
 drivers/net/wireless/ath/ath11k/mac.c              |   23 +-
 drivers/net/wireless/ath/ath11k/reg.c              |    9 +-
 drivers/net/wireless/ath/ath11k/wmi.c              |  147 ++-
 drivers/net/wireless/ath/ath11k/wmi.h              |   97 +-
 drivers/net/wireless/ath/ath12k/core.h             |    3 +
 drivers/net/wireless/ath/ath12k/dp.c               |    2 +-
 drivers/net/wireless/ath/ath12k/fw.c               |   10 +-
 drivers/net/wireless/ath/ath12k/fw.h               |    3 +-
 drivers/net/wireless/ath/ath12k/hal.c              |   46 +
 drivers/net/wireless/ath/ath12k/hal.h              |   34 +
 drivers/net/wireless/ath/ath12k/hw.h               |    2 +
 drivers/net/wireless/ath/ath12k/mac.c              |    2 +-
 drivers/net/wireless/ath/ath12k/pci.c              |   18 +-
 drivers/net/wireless/ath/ath12k/pci.h              |    7 +
 drivers/net/wireless/ath/ath12k/qmi.c              |  180 +++-
 drivers/net/wireless/ath/ath12k/qmi.h              |   16 +
 drivers/net/wireless/ath/ath12k/wifi7/Makefile     |    3 +-
 drivers/net/wireless/ath/ath12k/wifi7/dp_rx.c      |   62 +-
 drivers/net/wireless/ath/ath12k/wifi7/dp_rx.h      |    1 +
 drivers/net/wireless/ath/ath12k/wifi7/hal.c        |    8 +
 drivers/net/wireless/ath/ath12k/wifi7/hal.h        |    3 -
 drivers/net/wireless/ath/ath12k/wifi7/hal_desc.h   |   33 +-
 .../net/wireless/ath/ath12k/wifi7/hal_qcc2072.c    |  503 ++++++++++
 .../net/wireless/ath/ath12k/wifi7/hal_qcc2072.h    |   13 +
 .../net/wireless/ath/ath12k/wifi7/hal_qcn9274.c    |    8 +-
 drivers/net/wireless/ath/ath12k/wifi7/hal_rx.c     |   97 +-
 drivers/net/wireless/ath/ath12k/wifi7/hal_rx.h     |   30 +-
 .../net/wireless/ath/ath12k/wifi7/hal_rx_desc.h    |   17 +
 .../net/wireless/ath/ath12k/wifi7/hal_wcn7850.c    |    8 +-
 .../net/wireless/ath/ath12k/wifi7/hal_wcn7850.h    |    1 +
 drivers/net/wireless/ath/ath12k/wifi7/hw.c         |  101 ++
 drivers/net/wireless/ath/ath12k/wifi7/pci.c        |   26 +-
 drivers/net/wireless/ath/ath12k/wifi7/wmi.c        |    5 +
 drivers/net/wireless/ath/ath12k/wmi.c              |   54 +-
 drivers/net/wireless/ath/ath12k/wmi.h              |    7 +
 drivers/net/wireless/ath/ath12k/wow.c              |   16 +
 drivers/net/wireless/ath/ath5k/debug.h             |    4 +-
 drivers/net/wireless/ath/ath9k/Kconfig             |    2 +-
 drivers/net/wireless/ath/ath9k/common-debug.h      |    8 +-
 drivers/net/wireless/ath/ath9k/debug.h             |   15 +-
 drivers/net/wireless/ath/wil6210/wil6210.h         |   33 +-
 drivers/net/wireless/intel/iwlegacy/3945-mac.c     |    2 +
 drivers/net/wireless/intel/iwlegacy/4965-mac.c     |    2 +
 drivers/net/wireless/intel/iwlwifi/cfg/bz.c        |   12 -
 drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c     |   12 +
 drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c     |    1 -
 drivers/net/wireless/intel/iwlwifi/fw/acpi.c       |  121 ++-
 .../net/wireless/intel/iwlwifi/fw/api/mac-cfg.h    |  159 ++-
 .../net/wireless/intel/iwlwifi/fw/api/nvm-reg.h    |   34 +-
 drivers/net/wireless/intel/iwlwifi/fw/api/power.h  |    2 +-
 drivers/net/wireless/intel/iwlwifi/fw/api/rs.h     |   41 +-
 drivers/net/wireless/intel/iwlwifi/fw/api/rx.h     |   95 +-
 drivers/net/wireless/intel/iwlwifi/fw/file.h       |    2 +
 drivers/net/wireless/intel/iwlwifi/fw/regulatory.c |  206 +---
 drivers/net/wireless/intel/iwlwifi/fw/regulatory.h |    9 +-
 drivers/net/wireless/intel/iwlwifi/fw/runtime.h    |   17 +-
 drivers/net/wireless/intel/iwlwifi/fw/smem.c       |    8 +-
 drivers/net/wireless/intel/iwlwifi/fw/uefi.c       |   74 +-
 drivers/net/wireless/intel/iwlwifi/fw/uefi.h       |    2 +-
 drivers/net/wireless/intel/iwlwifi/iwl-config.h    |    1 -
 drivers/net/wireless/intel/iwlwifi/mld/Makefile    |    2 +-
 drivers/net/wireless/intel/iwlwifi/mld/d3.c        |    2 -
 drivers/net/wireless/intel/iwlwifi/mld/debugfs.c   |    6 +-
 drivers/net/wireless/intel/iwlwifi/mld/iface.c     |   21 +-
 drivers/net/wireless/intel/iwlwifi/mld/iface.h     |    2 +
 drivers/net/wireless/intel/iwlwifi/mld/mac80211.c  |  114 ++-
 drivers/net/wireless/intel/iwlwifi/mld/mld.c       |    3 +
 drivers/net/wireless/intel/iwlwifi/mld/mld.h       |    3 +
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c       |   59 +-
 drivers/net/wireless/intel/iwlwifi/mld/mlo.h       |    5 +
 drivers/net/wireless/intel/iwlwifi/mld/nan.c       |  299 ++++++
 drivers/net/wireless/intel/iwlwifi/mld/nan.h       |   28 +
 drivers/net/wireless/intel/iwlwifi/mld/notif.c     |   13 +
 drivers/net/wireless/intel/iwlwifi/mld/notif.h     |    1 +
 drivers/net/wireless/intel/iwlwifi/mld/power.c     |   39 +-
 .../net/wireless/intel/iwlwifi/mld/regulatory.c    |   64 +-
 drivers/net/wireless/intel/iwlwifi/mld/rx.c        |  142 ++-
 drivers/net/wireless/intel/iwlwifi/mld/scan.c      |   12 +-
 drivers/net/wireless/intel/iwlwifi/mld/sta.c       |    3 +-
 drivers/net/wireless/intel/iwlwifi/mld/tlc.c       |   85 +-
 drivers/net/wireless/intel/iwlwifi/mld/tx.c        |   11 +
 drivers/net/wireless/intel/iwlwifi/mvm/d3.c        |   51 +-
 drivers/net/wireless/intel/iwlwifi/mvm/fw.c        |  202 +++-
 drivers/net/wireless/intel/iwlwifi/mvm/link.c      |   22 -
 drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c  |   25 +-
 drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c  |   68 +-
 drivers/net/wireless/intel/iwlwifi/mvm/mld-key.c   |    6 +-
 drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c   |   29 -
 .../net/wireless/intel/iwlwifi/mvm/mld-mac80211.c  |  193 +---
 drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c   |   28 +-
 drivers/net/wireless/intel/iwlwifi/mvm/mvm.h       |   60 +-
 drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c      |    8 -
 drivers/net/wireless/intel/iwlwifi/mvm/scan.c      |   18 +-
 drivers/net/wireless/intel/iwlwifi/mvm/tdls.c      |    4 +-
 .../net/wireless/intel/iwlwifi/mvm/time-event.c    |   39 +-
 .../net/wireless/intel/iwlwifi/mvm/time-event.h    |    4 +-
 drivers/net/wireless/intel/iwlwifi/mvm/tx.c        |   51 +-
 drivers/net/wireless/intel/iwlwifi/mvm/utils.c     |    4 -
 drivers/net/wireless/intersil/p54/main.c           |    4 +-
 drivers/net/wireless/realtek/rtl8xxxu/core.c       |    1 +
 drivers/net/wireless/realtek/rtlwifi/regd.c        |    2 +-
 drivers/net/wireless/realtek/rtw88/main.c          |   54 +-
 drivers/net/wireless/realtek/rtw88/main.h          |    2 +-
 drivers/net/wireless/realtek/rtw88/phy.c           |   20 +
 drivers/net/wireless/realtek/rtw88/phy.h           |    2 +
 drivers/net/wireless/realtek/rtw88/rtw8723cs.c     |    2 +-
 drivers/net/wireless/realtek/rtw88/rtw8723ds.c     |    2 +-
 drivers/net/wireless/realtek/rtw88/rtw8821cs.c     |    2 +-
 drivers/net/wireless/realtek/rtw88/rtw8821cu.c     |    2 +
 drivers/net/wireless/realtek/rtw88/rtw8822b.c      |    3 +-
 drivers/net/wireless/realtek/rtw88/rtw8822bs.c     |    2 +-
 drivers/net/wireless/realtek/rtw88/rtw8822cs.c     |    2 +-
 drivers/net/wireless/realtek/rtw88/sdio.c          |    3 +-
 drivers/net/wireless/realtek/rtw88/sdio.h          |    2 +-
 drivers/net/wireless/realtek/rtw89/cam.c           |  134 +++
 drivers/net/wireless/realtek/rtw89/cam.h           |  129 +++
 drivers/net/wireless/realtek/rtw89/chan.c          |   38 +-
 drivers/net/wireless/realtek/rtw89/chan.h          |    2 +
 drivers/net/wireless/realtek/rtw89/core.c          |  237 ++++-
 drivers/net/wireless/realtek/rtw89/core.h          |  158 ++-
 drivers/net/wireless/realtek/rtw89/debug.c         |   79 ++
 drivers/net/wireless/realtek/rtw89/efuse.c         |    4 -
 drivers/net/wireless/realtek/rtw89/efuse.h         |    6 +
 drivers/net/wireless/realtek/rtw89/efuse_be.c      |   26 +
 drivers/net/wireless/realtek/rtw89/fw.c            |  866 +++++++++++++++--
 drivers/net/wireless/realtek/rtw89/fw.h            |  331 +++++--
 drivers/net/wireless/realtek/rtw89/mac.c           |  176 +++-
 drivers/net/wireless/realtek/rtw89/mac.h           |   79 +-
 drivers/net/wireless/realtek/rtw89/mac80211.c      |   40 +-
 drivers/net/wireless/realtek/rtw89/mac_be.c        |  675 ++++++++++++-
 drivers/net/wireless/realtek/rtw89/pci.c           |    7 +-
 drivers/net/wireless/realtek/rtw89/pci.h           |   56 +-
 drivers/net/wireless/realtek/rtw89/pci_be.c        |  136 ++-
 drivers/net/wireless/realtek/rtw89/phy.c           |   57 +-
 drivers/net/wireless/realtek/rtw89/ps.c            |    2 +-
 drivers/net/wireless/realtek/rtw89/reg.h           |  566 ++++++++++-
 drivers/net/wireless/realtek/rtw89/regd.c          |    1 +
 drivers/net/wireless/realtek/rtw89/rtw8851b.c      |    9 +-
 drivers/net/wireless/realtek/rtw89/rtw8852a.c      |    9 +-
 drivers/net/wireless/realtek/rtw89/rtw8852au.c     |    4 +
 drivers/net/wireless/realtek/rtw89/rtw8852b.c      |   31 +-
 .../net/wireless/realtek/rtw89/rtw8852b_common.c   |    2 -
 drivers/net/wireless/realtek/rtw89/rtw8852bt.c     |    7 +
 drivers/net/wireless/realtek/rtw89/rtw8852bu.c     |    2 +
 drivers/net/wireless/realtek/rtw89/rtw8852c.c      |    9 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a.c      |   31 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c  |   48 +-
 drivers/net/wireless/realtek/rtw89/ser.c           |   10 +
 drivers/net/wireless/realtek/rtw89/txrx.h          |   22 +
 drivers/net/wireless/realtek/rtw89/usb.c           |    2 +-
 drivers/net/wireless/realtek/rtw89/wow.c           |    8 +-
 drivers/net/wireless/realtek/rtw89/wow.h           |    1 +
 drivers/net/wireless/rsi/rsi_91x_sdio.c            |    5 +-
 drivers/net/wireless/ti/wlcore/cmd.h               |    1 +
 drivers/net/wireless/ti/wlcore/main.c              |    9 +
 include/linux/ieee80211.h                          |    1 +
 include/linux/mmc/sdio_func.h                      |    1 +
 include/net/cfg80211.h                             |   37 +-
 include/net/mac80211.h                             |    2 +
 include/uapi/linux/nl80211.h                       |   73 +-
 net/mac80211/cfg.c                                 |   15 +-
 net/mac80211/drop.h                                |   46 +-
 net/mac80211/ieee80211_i.h                         |   10 +-
 net/mac80211/iface.c                               |    6 +-
 net/mac80211/mlme.c                                |   47 +-
 net/mac80211/rx.c                                  |  114 ++-
 net/mac80211/sta_info.c                            |   22 +-
 net/mac80211/sta_info.h                            |    4 +-
 net/mac80211/tx.c                                  |    4 +-
 net/mac80211/util.c                                |   12 +-
 net/mac80211/wpa.c                                 |    6 +-
 net/wireless/core.c                                |   14 +-
 net/wireless/nl80211.c                             |   67 +-
 net/wireless/pmsr.c                                |   27 +-
 net/wireless/reg.c                                 |   11 +-
 194 files changed, 8876 insertions(+), 1685 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath11k/cfr.c
 create mode 100644 drivers/net/wireless/ath/ath11k/cfr.h
 create mode 100644 drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.c
 create mode 100644 drivers/net/wireless/ath/ath12k/wifi7/hal_qcc2072.h
 create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/nan.c
 create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/nan.h

^ permalink raw reply

* Re: [PATCH wireless-next] wifi: mac80211: use wiphy_hrtimer_work for CAC timeout
From: Johannes Berg @ 2026-01-29 10:41 UTC (permalink / raw)
  To: Amith A; +Cc: linux-wireless
In-Reply-To: <20260128064856.3667270-1-amith.a@oss.qualcomm.com>

On Wed, 2026-01-28 at 12:18 +0530, Amith A wrote:
> 
> @@ -3851,6 +3851,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
>  					   u32 cac_time_ms, int link_id)
>  {
>  	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
> +	ktime_t ktime = ktime_set(0, cac_time_ms * NSEC_PER_MSEC);

ms_to_ktime()

johannes

^ permalink raw reply

* [PATCH] wifi: mac80211: correctly decode TTLM with default link map
From: Benjamin Berg @ 2026-01-29 10:33 UTC (permalink / raw)
  To: linux-wireless; +Cc: Benjamin Berg, Ruikai Peng, Johannes Berg

From: Benjamin Berg <benjamin.berg@intel.com>

TID-To-Link Mapping (TTLM) elements do not contain any link mapping
presence indicator if a default mapping is used and parsing needs to be
skipped.

Note that access points should not explicitly report an advertised TTLM
with a default mapping as that is the implied mapping if the element is
not included, this is even the case when switching back to the default
mapping. However, mac80211 would incorrectly parse the frame and would
also read one byte beyond the end of the element.

Reported-by: Ruikai Peng <ruikai@pwno.io>
Closes: https://lore.kernel.org/linux-wireless/CAFD3drMqc9YWvTCSHLyP89AOpBZsHdZ+pak6zVftYoZcUyF7gw@mail.gmail.com
Fixes: 702e80470a33 ("wifi: mac80211: support handling of advertised TID-to-link mapping")
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
---
 net/mac80211/mlme.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b72345c779c0..73f57b9e0ebf 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -8,7 +8,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
  */
 
 #include <linux/delay.h>
@@ -6190,8 +6190,10 @@ ieee80211_parse_adv_t2l(struct ieee80211_sub_if_data *sdata,
 		return -EINVAL;
 	}
 
-	link_map_presence = *pos;
-	pos++;
+	if (!(control & IEEE80211_TTLM_CONTROL_DEF_LINK_MAP)) {
+		link_map_presence = *pos;
+		pos++;
+	}
 
 	if (control & IEEE80211_TTLM_CONTROL_SWITCH_TIME_PRESENT) {
 		ttlm_info->switch_time = get_unaligned_le16(pos);
-- 
2.52.0


^ permalink raw reply related

* Re: [PATCH wireless-next] wifi: mac80211: use wiphy_hrtimer_work for CAC timeout
From: Amith A @ 2026-01-29 10:13 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <048f0d8d0686cea35267bff16f824105cfa81936.camel@sipsolutions.net>



On 1/29/2026 2:02 PM, Johannes Berg wrote:
> On Wed, 2026-01-28 at 12:18 +0530, Amith A wrote:
>> Prior initiating communication in a DFS channel, there should be a
>> monitoring of RADAR in that channel for a minimum of 600 seconds if it
>> is a Weather RADAR channel and 60 seconds for other DFS channels. This
>> Channel Availability Check(CAC) is currently implemented by scheduling
>> a work item for execution with a delay equal to an appropriate timeout.
>> But this work item is observed to take more delay than specified
>> (4-5 seconds in regular DFS channels and 25-30 seconds in Weather RADAR
>> channels). Even though this delay is expected in case of delayed work
>> queue as there is no guarantee that the work will be scheduled exactly
>> after the specified delay, a delay of more than 20 seconds is too much
>> for the AP to be in non-operational state.
>>
>> Recently commit 7ceba45a6658 ("wifi: cfg80211: add an hrtimer based
>> delayed work item") added an infrastructure to overcome this issue by
>> supporting high resolution timers for mac80211 delayed work, which do not
>> have this timeout latency. The other patches in that series converted some
>> mac80211 work items to use the new infrastructure.
>>
>> Unfortunately the CAC timeout work was not addressed as part of that series,
>> so address it now.
> This is ... a pretty grossly wrong characterisation. The previous work
> fixed a bunch of *correctness* issues. This fixes a performance thing at
> best, and you seem to be complaining those are the same and therefore it
> should've been fixed before. The other patches also went to wireless for
> being fixes, this really wouldn't, I'd argue, those two things really
> aren't the same.
>
> Please rewrite this commit message.
Will rewrite the commit message and send the next version.
>
> johannes


^ permalink raw reply

* [v7 PATCH 7/7] wifi: mt76: mt7925: add error logging for MLO ROC setup in set_links
From: Zac @ 2026-01-29  9:05 UTC (permalink / raw)
  To: zac
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, nbd, ryder.lee, sean.wang, sean.wang, zbowling
In-Reply-To: <20260129084611.187744-1-zac@zacbowling.com>

Add error logging in mt7925_mac_set_links() when mt7925_set_mlo_roc()
fails. Previously the error return was silently ignored since the
callback function is void.

The function now logs non-ENOLINK errors as warnings. ENOLINK errors
are expected during link transitions when the link configuration is
not yet ready, and mac80211 will retry the operation later.

This complements the error handling changes in mt7925_mcu_set_mlo_roc()
where WARN_ON_ONCE was replaced with proper -ENOLINK returns.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 0b088c448151..769c09e99d48 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1048,11 +1048,16 @@ mt7925_mac_set_links(struct mt76_dev *mdev, struct ieee80211_vif *vif)
 
 	if (band == NL80211_BAND_2GHZ ||
 	    (band == NL80211_BAND_5GHZ && secondary_band == NL80211_BAND_6GHZ)) {
+		int ret;
+
 		mt7925_abort_roc(mvif->phy, &mvif->bss_conf);
 
 		mt792x_mutex_acquire(dev);
 
-		mt7925_set_mlo_roc(mvif->phy, &mvif->bss_conf, sel_links);
+		ret = mt7925_set_mlo_roc(mvif->phy, &mvif->bss_conf, sel_links);
+		if (ret && ret != -ENOLINK)
+			dev_warn(dev->mt76.dev,
+				 "MLO ROC setup failed in set_links: %d\n", ret);
 
 		mt792x_mutex_release(dev);
 	}
-- 
2.52.0


^ permalink raw reply related

* [PATCH 2/6] wifi: mt76: mt7925: add NULL pointer protection for MLO state transitions
From: Zac @ 2026-01-29  8:46 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Add NULL pointer checks for functions that return pointers to link-related
structures throughout the mt7925 driver. During MLO state transitions,
these functions can return NULL when link configuration is not synchronized.

Functions protected:
- mt792x_vif_to_bss_conf(): Returns link BSS configuration
- mt792x_vif_to_link(): Returns driver link state
- mt792x_sta_to_link(): Returns station link state

Key changes:

1. mt7925_set_link_key():
   - Check link_conf, mconf, mlink before use
   - During MLO roaming, allow key removal to succeed if link is already gone

2. mt7925_mac_link_sta_add():
   - Check mlink and mconf before WCID allocation
   - Check link_conf before BSS info update
   - Add proper WCID cleanup on error paths (err_wcid label)
   - Check MCU return values and propagate errors

3. mt7925_mac_link_sta_assoc():
   - Check mlink before use
   - Check link_conf and mconf before BSS info update

4. mt7925_mac_link_sta_remove():
   - Check mlink before use
   - Check link_conf and mconf before cleanup operations

Prevents crashes during:
- BSSID roaming transitions
- MLO setup and teardown
- Hardware reset operations

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 67 ++++++++++++++-----
 1 file changed, 52 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index fad3b1505f67..1400633712b7 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -612,6 +612,17 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL;
 	mconf = mt792x_vif_to_link(mvif, link_id);
 	mlink = mt792x_sta_to_link(msta, link_id);
+
+	if (!link_conf || !mconf || !mlink) {
+		/* During MLO roaming, link state may be torn down before
+		 * mac80211 requests key removal. If removing a key and
+		 * the link is already gone, consider it successfully removed.
+		 */
+		if (cmd != SET_KEY)
+			return 0;
+		return -EINVAL;
+	}
+
 	wcid = &mlink->wcid;
 	wcid_keyidx = &wcid->hw_key_idx;
 
@@ -864,12 +875,17 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_id);
+	if (!mlink)
+		return -EINVAL;
+
+	mconf = mt792x_vif_to_link(mvif, link_id);
+	if (!mconf)
+		return -EINVAL;
 
 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
 	if (idx < 0)
 		return -ENOSPC;
 
-	mconf = mt792x_vif_to_link(mvif, link_id);
 	mt76_wcid_init(&mlink->wcid, 0);
 	mlink->wcid.sta = 1;
 	mlink->wcid.idx = idx;
@@ -888,21 +904,28 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 
 	ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
 	if (ret)
-		return ret;
+		goto err_wcid;
 
 	mt7925_mac_wtbl_update(dev, idx,
 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 
 	link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+	if (!link_conf) {
+		ret = -EINVAL;
+		goto err_wcid;
+	}
 
 	/* should update bss info before STA add */
 	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		if (ieee80211_vif_is_mld(vif))
-			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-						link_conf, link_sta, link_sta != mlink->pri_link);
+			ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						      link_conf, link_sta,
+						      link_sta != mlink->pri_link);
 		else
-			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-						link_conf, link_sta, false);
+			ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						      link_conf, link_sta, false);
+		if (ret)
+			goto err_wcid;
 	}
 
 	if (ieee80211_vif_is_mld(vif) &&
@@ -910,28 +933,35 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_NONE);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	} else if (ieee80211_vif_is_mld(vif) &&
 		   link_sta != mlink->pri_link) {
 		ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
 					    true, MT76_STA_INFO_STATE_ASSOC);
 		if (ret)
-			return ret;
+			goto err_wcid;
 
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_ASSOC);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	} else {
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_NONE);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	}
 
 	mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
 
 	return 0;
+
+err_wcid:
+	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+	mt76_wcid_cleanup(&dev->mt76, wcid);
+	mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+	mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+	return ret;
 }
 
 static int
@@ -1039,6 +1069,8 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+	if (!mlink)
+		return;
 
 	mt792x_mutex_acquire(dev);
 
@@ -1048,12 +1080,13 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
 		link_conf = mt792x_vif_to_bss_conf(vif, vif->bss_conf.link_id);
 	}
 
-	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+	if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		struct mt792x_bss_conf *mconf;
 
 		mconf = mt792x_link_conf_to_mconf(link_conf);
-		mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-					link_conf, link_sta, true);
+		if (mconf)
+			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						link_conf, link_sta, true);
 	}
 
 	ewma_avg_signal_init(&mlink->avg_ack_signal);
@@ -1100,6 +1133,8 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_id);
+	if (!mlink)
+		return;
 
 	mt7925_roc_abort_sync(dev);
 
@@ -1113,10 +1148,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 
 	link_conf = mt792x_vif_to_bss_conf(vif, link_id);
 
-	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+	if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		struct mt792x_bss_conf *mconf;
 
 		mconf = mt792x_link_conf_to_mconf(link_conf);
+		if (!mconf)
+			goto out;
 
 		if (ieee80211_vif_is_mld(vif))
 			mt792x_mac_link_bss_remove(dev, mconf, mlink);
@@ -1124,7 +1161,7 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf,
 						link_sta, false);
 	}
-
+out:
 	spin_lock_bh(&mdev->sta_poll_lock);
 	if (!list_empty(&mlink->wcid.poll_list))
 		list_del_init(&mlink->wcid.poll_list);
-- 
2.52.0


^ permalink raw reply related

* Re: [PATCH wireless-next] wifi: mac80211: use wiphy_hrtimer_work for CAC timeout
From: Johannes Berg @ 2026-01-29  8:32 UTC (permalink / raw)
  To: Amith A; +Cc: linux-wireless
In-Reply-To: <20260128064856.3667270-1-amith.a@oss.qualcomm.com>

On Wed, 2026-01-28 at 12:18 +0530, Amith A wrote:
> Prior initiating communication in a DFS channel, there should be a
> monitoring of RADAR in that channel for a minimum of 600 seconds if it
> is a Weather RADAR channel and 60 seconds for other DFS channels. This
> Channel Availability Check(CAC) is currently implemented by scheduling
> a work item for execution with a delay equal to an appropriate timeout.
> But this work item is observed to take more delay than specified
> (4-5 seconds in regular DFS channels and 25-30 seconds in Weather RADAR
> channels). Even though this delay is expected in case of delayed work
> queue as there is no guarantee that the work will be scheduled exactly
> after the specified delay, a delay of more than 20 seconds is too much
> for the AP to be in non-operational state.
> 
> Recently commit 7ceba45a6658 ("wifi: cfg80211: add an hrtimer based
> delayed work item") added an infrastructure to overcome this issue by
> supporting high resolution timers for mac80211 delayed work, which do not
> have this timeout latency. The other patches in that series converted some
> mac80211 work items to use the new infrastructure.
> 
> Unfortunately the CAC timeout work was not addressed as part of that series,
> so address it now.

This is ... a pretty grossly wrong characterisation. The previous work
fixed a bunch of *correctness* issues. This fixes a performance thing at
best, and you seem to be complaining those are the same and therefore it
should've been fixed before. The other patches also went to wireless for
being fixes, this really wouldn't, I'd argue, those two things really
aren't the same.

Please rewrite this commit message.

johannes

^ permalink raw reply

* [PATCH wireless-next v5 3/3] wifi: mac80211: add initial UHR support
From: Johannes Berg @ 2026-01-29  8:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260129092039.53d7f326bc8f.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>

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>
---
v5:
 - parse UHR capa as from AP
v4:
 - fix NPCA validation
v3:
 - use uhr_oper instead of removed uhr_capa
 - fix indentation (Jeff Johnson)
---
 include/net/mac80211.h     |  35 ++++++++++-
 net/mac80211/Makefile      |   2 +-
 net/mac80211/cfg.c         |  16 ++++-
 net/mac80211/ieee80211_i.h |  19 +++++-
 net/mac80211/main.c        |  15 ++++-
 net/mac80211/mlme.c        | 117 ++++++++++++++++++++++++++++++++++---
 net/mac80211/parse.c       |  22 ++++++-
 net/mac80211/rx.c          |  26 +++++++++
 net/mac80211/sta_info.c    |  13 ++++-
 net/mac80211/sta_info.h    |  80 ++++++++++++++++++-------
 net/mac80211/uhr.c         |  29 +++++++++
 net/mac80211/util.c        |  36 +++++++++++-
 12 files changed, 370 insertions(+), 40 deletions(-)
 create mode 100644 net/mac80211/uhr.c

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 36ae7fe9ddf3..7a55762f9af8 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7,7 +7,7 @@
  * Copyright 2007-2010	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
  */
 
 #ifndef 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..f83dda0755a7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5,7 +5,7 @@
  * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -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..b5e774ea6b07 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -5,7 +5,7 @@
  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
  * Copyright 2007-2010	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2015  Intel Mobile Communications GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #ifndef 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..486a0bf3c0de 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -5,7 +5,7 @@
  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2017     Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <net/mac80211.h>
@@ -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..df170556825f 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -8,7 +8,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
  */
 
 #include <linux/delay.h>
@@ -162,6 +162,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 +193,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 +216,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 +343,63 @@ 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) {
+			int width = cfg80211_chandef_get_width(chandef);
+			u8 offs = le32_get_bits(npca->params,
+						IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS);
+			u32 cf1 = chandef->center_freq1;
+			bool pri_upper, npca_upper;
+
+			pri_upper = chandef->chan->center_freq > cf1;
+			npca_upper = 20 * offs >= width / 2;
+
+			if (20 * offs >= cfg80211_chandef_get_width(chandef) ||
+			    pri_upper == npca_upper) {
+				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 +1150,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 +1168,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 +1217,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 +1951,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 +1970,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 +2207,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 +5606,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 +5908,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 +6084,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 +6094,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..cb238600652a 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -6,7 +6,7 @@
  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017	Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  *
  * element parsing for mac80211
  */
@@ -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, true)) {
+			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..69034d83a7b6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -5518,6 +5518,32 @@ 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, NSS:%d, GI:%d, BW:%d, IM:%d\n",
+				      status->rate_idx, status->nss, status->uhr.gi,
+				      status->bw, status->uhr.im))
+				goto drop;
+			if (WARN_ONCE(status->uhr.im &&
+				      (status->nss != 1 || status->rate_idx == 15),
+				      "bad UHR IM MCS MCS:%d, NSS:%d\n",
+				      status->rate_idx, status->nss))
+				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..a79ebeb43585 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -4,7 +4,7 @@
  * Copyright 2006-2007	Jiri Benc <jbenc@suse.cz>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/module.h>
@@ -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..2875ef7d7946 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-2026 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..377218987632
--- /dev/null
+++ b/net/mac80211/uhr.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UHR handling
+ *
+ * Copyright(c) 2025-2026 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..97433347c26d 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -6,7 +6,7 @@
  * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright (C) 2015-2017	Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  *
  * utilities for mac80211
  */
@@ -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 v5 2/3] wifi: cfg80211: add initial UHR support
From: Johannes Berg @ 2026-01-29  8:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260129092039.53d7f326bc8f.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>

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>
---
v5:
 - validate NL80211_ATTR_UHR_CAPABILITY for non-AP (only)
v4:
 - check for correct NSS/MCS for interference mitigation
v3:
 - remove UHR capa pointer from AP settings, it's not in
   the beacon anyway
 - fix kernel-doc (Jeff Johnson)
---
 include/net/cfg80211.h       |  58 ++++++++++++++++++--
 include/uapi/linux/nl80211.h |  30 +++++++++++
 net/wireless/nl80211.c       | 102 +++++++++++++++++++++++++++++++++--
 net/wireless/reg.c           |   4 +-
 net/wireless/util.c          | 101 ++++++++++++++++++++++++++--------
 5 files changed, 265 insertions(+), 30 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0ae0aa7594a3..7e7a76b314f9 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -7,7 +7,7 @@
  * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014 Intel Mobile Communications GmbH
  * Copyright 2015-2017	Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
  */
 
 #include <linux/ethtool.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..181205eae755 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, false);
+}
+
 /* 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..139cb27e5a81 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -5,7 +5,7 @@
  * Copyright 2008-2011	Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright      2017  Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -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..371149220031 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -5,7 +5,7 @@
  * Copyright 2007-2009	Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2017	Intel Deutschland GmbH
- * Copyright (C) 2018-2023, 2025 Intel Corporation
+ * Copyright (C) 2018-2023, 2025-2026 Intel Corporation
  */
 #include <linux/export.h>
 #include <linux/bitops.h>
@@ -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,64 @@ 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) {
+		if (WARN(rate->nss != 1 || rate->mcs != 15,
+			 "invalid NSS or MCS for UHR IM\n"))
+			return 0;
+		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 +1882,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 v5 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-29  8:20 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

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

This is based on Draft P802.11bn_D1.2, but that's still very
incomplete, so don't handle a number of things and make some
local decisions such as using 40 bits for MAC capabilities
and 8 bits for PHY capabilities.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v5:
  - use correct EHT MCS len (24 bits, not 24 bytes)
  - handle DBE AP/non-AP in ieee80211_uhr_capa_size_ok()
v4:
  - update to D1.2, including DBE in UHR capabilities
  - fold in suggestions from Pablo
---
 include/linux/ieee80211-uhr.h | 219 ++++++++++++++++++++++++++++++++++
 include/linux/ieee80211.h     |  33 ++++-
 2 files changed, 250 insertions(+), 2 deletions(-)
 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..05cf31ae3128
--- /dev/null
+++ b/include/linux/ieee80211-uhr.h
@@ -0,0 +1,219 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * IEEE 802.11 UHR definitions
+ *
+ * Copyright (c) 2025-2026 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_OFFS	0x0000000F
+#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH	0x000000F0
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY		0x00003F00
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY	0x000FC000
+#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC		0x00300000
+#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN		0x00400000
+#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES	0x00800000
+
+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_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_INTV_LOW	0xE0
+
+#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH	0x03
+#define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS		0x04
+#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE		0x08
+#define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP		0x10
+
+#define IEEE80211_UHR_MAC_CAP_DBE_MAX_BW		0x07
+#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES	0x08
+#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES	0x10
+
+struct ieee80211_uhr_capa_mac {
+	u8 mac_cap[5];
+} __packed;
+
+struct ieee80211_uhr_capa {
+	struct ieee80211_uhr_capa_mac mac;
+	/* DBE, PHY capabilities */
+	u8 variable[];
+} __packed;
+
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80	0x01
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80	0x02
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160	0x04
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160	0x08
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320	0x10
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_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, bool ap)
+{
+	const struct ieee80211_uhr_capa *cap = (const void *)data;
+	size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_capa_phy);
+
+	if (len < needed)
+		return false;
+
+	/*
+	 * A non-AP STA does not include the DBE Capability Parameters field
+	 * in the UHR MAC Capabilities Information field.
+	 */
+	if (ap && cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
+		u8 dbe;
+
+		needed += 1;
+		if (len < needed)
+			return false;
+
+		dbe = cap->variable[0];
+
+		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
+			needed += 3;
+
+		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+			needed += 3;
+	}
+
+	return len >= needed;
+}
+
+static inline const struct ieee80211_uhr_capa_phy *
+ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
+{
+	u8 offs = 0;
+
+	if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
+		u8 dbe = cap->variable[0];
+
+		offs += 1;
+
+		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
+			offs += 3;
+
+		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+			offs += 3;
+	}
+
+	return (const void *)&cap->variable[offs];
+}
+
+#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..82d797be95b9 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -9,7 +9,7 @@
  * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
  * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
  * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (c) 2018 - 2025 Intel Corporation
+ * Copyright (c) 2018 - 2026 Intel Corporation
  */
 
 #ifndef 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_SMD_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 v7 6/6] wifi: mt76: mt7925: fix MLO ROC setup error handling
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Replace noisy WARN_ON_ONCE checks with silent returns in
mt7925_mcu_set_mlo_roc(). During MLO setup, links may not be fully
configured when ROC is requested. The WARN_ON_ONCE statements were
triggering unnecessary kernel warnings during normal operation.

Changes:
- Replace WARN_ON_ONCE(!link_conf) with silent if (!link_conf)
- Replace WARN_ON_ONCE(!links[i].chan) with silent check
- Add explicit mconf NULL check before use
- Use -ENOLINK error code to indicate link not ready
- Replace continue with return to fail fast on invalid links

The -ENOLINK error code properly indicates that the link is not yet
ready for ROC, allowing upper layers to retry later without generating
spurious kernel warnings.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   | 20 +++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 2ed4af282120..5ca2106b1ce0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1341,15 +1341,23 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf
 	for (i = 0; i < ARRAY_SIZE(links); i++) {
 		links[i].id = i ? __ffs(~BIT(mconf->link_id) & sel_links) :
 				 mconf->link_id;
+
 		link_conf = mt792x_vif_to_bss_conf(vif, links[i].id);
-		if (WARN_ON_ONCE(!link_conf))
-			return -EPERM;
+		if (!link_conf)
+			return -ENOLINK;
 
 		links[i].chan = link_conf->chanreq.oper.chan;
-		if (WARN_ON_ONCE(!links[i].chan))
-			return -EPERM;
+		if (!links[i].chan)
+			/* Channel not configured yet - this can happen during
+			 * MLO AP setup when links are being added sequentially.
+			 * Return -ENOLINK to indicate link not ready.
+			 */
+			return -ENOLINK;
 
 		links[i].mconf = mt792x_vif_to_link(mvif, links[i].id);
+		if (!links[i].mconf)
+			return -ENOLINK;
+
 		links[i].tag = links[i].id == mconf->link_id ?
 			       UNI_ROC_ACQUIRE : UNI_ROC_SUB_LINK;
 
@@ -1364,8 +1372,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf
 		type = MT7925_ROC_REQ_JOIN;
 
 	for (i = 0; i < ARRAY_SIZE(links) && i < hweight16(vif->active_links); i++) {
-		if (WARN_ON_ONCE(!links[i].mconf || !links[i].chan))
-			continue;
+		if (!links[i].mconf || !links[i].chan)
+			return -ENOLINK;
 
 		chan = links[i].chan;
 		center_ch = ieee80211_frequency_to_channel(chan->center_freq);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 5/6] wifi: mt76: mt7925: add lockdep assertions for mutex verification
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Add lockdep_assert_held() calls to critical MCU functions to help catch
mutex violations during development and debugging. This follows the
pattern used in other mt76 drivers (mt7996, mt7915, mt7615).

Functions with new assertions:
- mt7925_mcu_add_bss_info(): Core BSS configuration MCU command
- mt7925_mcu_sta_update(): Station record update MCU command
- mt7925_mcu_uni_bss_ps(): Power save state MCU command

These functions modify firmware state and must be called with the
device mutex held to prevent race conditions. The lockdep assertions
will trigger warnings at runtime if code paths exist that call these
functions without proper mutex protection.

Also fixes a potential NULL pointer issue in mt7925_mcu_sta_update()
by initializing mlink to NULL and checking it before use.

Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 1379bf6a26b5..2ed4af282120 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1532,6 +1532,8 @@ int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev,
 		},
 	};
 
+	lockdep_assert_held(&dev->mt76.mutex);
+
 	if (link_conf->vif->type != NL80211_IFTYPE_STATION)
 		return -EOPNOTSUPP;
 
@@ -2032,13 +2034,15 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
 		.rcpi = to_rcpi(rssi),
 	};
 	struct mt792x_sta *msta;
-	struct mt792x_link_sta *mlink;
+	struct mt792x_link_sta *mlink = NULL;
+
+	lockdep_assert_held(&dev->mt76.mutex);
 
 	if (link_sta) {
 		msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 		mlink = mt792x_sta_to_link(msta, link_sta->link_id);
 	}
-	info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid;
+	info.wcid = (link_sta && mlink) ? &mlink->wcid : &mvif->sta.deflink.wcid;
 	info.newly = state != MT76_STA_INFO_STATE_ASSOC;
 
 	return mt7925_mcu_sta_cmd(&dev->mphy, &info);
@@ -2840,6 +2844,8 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
 	struct mt792x_link_sta *mlink_bc;
 	struct sk_buff *skb;
 
+	lockdep_assert_held(&dev->mt76.mutex);
+
 	skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
 					 MT7925_BSS_UPDATE_MAX_SIZE);
 	if (IS_ERR(skb))
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 4/6] wifi: mt76: mt7925: add MCU command error handling in ampdu_action
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Add proper error handling for MCU command return values that were
previously being ignored. Without proper error handling, failures in
MCU communication can leave the driver in an inconsistent state.

Changes:
- Check mt7925_mcu_uni_tx_ba() return value
- Check mt7925_mcu_uni_rx_ba() return value
- Return error to mac80211 on failure

Special case for IEEE80211_AMPDU_TX_STOP_CONT:
The ieee80211_stop_tx_ba_cb_irqsafe() callback is kept unconditional
because during beacon loss, the MCU command may fail but mac80211
MUST be notified to complete the BA session teardown. Otherwise the
state machine gets stuck and triggers WARN in
__ieee80211_stop_tx_ba_session(). This matches the behavior of mt7921
and mt7996 drivers.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 82de6f30ec27..8236edb1fb48 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1300,22 +1300,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	case IEEE80211_AMPDU_RX_START:
 		mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn,
 				   params->buf_size);
-		mt7925_mcu_uni_rx_ba(dev, params, true);
+		ret = mt7925_mcu_uni_rx_ba(dev, params, true);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
 		mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid);
-		mt7925_mcu_uni_rx_ba(dev, params, false);
+		ret = mt7925_mcu_uni_rx_ba(dev, params, false);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
 		mtxq->aggr = true;
 		mtxq->send_bar = false;
-		mt7925_mcu_uni_tx_ba(dev, params, true);
+		ret = mt7925_mcu_uni_tx_ba(dev, params, true);
 		break;
 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
 		mtxq->aggr = false;
 		clear_bit(tid, &msta->deflink.wcid.ampdu_state);
-		mt7925_mcu_uni_tx_ba(dev, params, false);
+		ret = mt7925_mcu_uni_tx_ba(dev, params, false);
 		break;
 	case IEEE80211_AMPDU_TX_START:
 		set_bit(tid, &msta->deflink.wcid.ampdu_state);
@@ -1324,6 +1324,11 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 	case IEEE80211_AMPDU_TX_STOP_CONT:
 		mtxq->aggr = false;
 		clear_bit(tid, &msta->deflink.wcid.ampdu_state);
+		/* MCU command may fail during beacon loss, but callback must
+		 * always be called to complete the BA session teardown in
+		 * mac80211. Otherwise the state machine gets stuck and triggers
+		 * WARN in __ieee80211_stop_tx_ba_session().
+		 */
 		mt7925_mcu_uni_tx_ba(dev, params, false);
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 		break;
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 3/6] wifi: mt76: mt7925: add mutex protection in critical paths
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Add proper mutex protection for mt7925 driver operations that access
hardware state without proper synchronization. This fixes race conditions
that can cause system instability during power management and recovery.

Fixes:

1. mac.c: mt7925_mac_reset_work()
   - Wrap ieee80211_iterate_active_interfaces() with mt792x_mutex
   - The vif_connect_iter callback accesses hardware state

2. main.c: mt7925_set_runtime_pm()
   - Add mutex protection around ieee80211_iterate_active_interfaces()
   - Runtime PM can race with other operations

These protections ensure consistent hardware state access during power
management transitions and recovery operations.

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/mac.c  | 2 ++
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index f1f0bc9eab04..88cf214ab452 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1330,9 +1330,11 @@ void mt7925_mac_reset_work(struct work_struct *work)
 	dev->hw_full_reset = false;
 	pm->suspended = false;
 	ieee80211_wake_queues(hw);
+	mt792x_mutex_acquire(dev);
 	ieee80211_iterate_active_interfaces(hw,
 					    IEEE80211_IFACE_ITER_RESUME_ALL,
 					    mt7925_vif_connect_iter, NULL);
+	mt792x_mutex_release(dev);
 	mt76_connac_power_save_sched(&dev->mt76.phy, pm);
 
 	mt7925_regd_change(&dev->phy, "00");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 88ee90709b75..82de6f30ec27 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -770,9 +770,11 @@ void mt7925_set_runtime_pm(struct mt792x_dev *dev)
 	bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
 
 	pm->enable = pm->enable_user && !monitor;
+	mt792x_mutex_acquire(dev);
 	ieee80211_iterate_active_interfaces(hw,
 					    IEEE80211_IFACE_ITER_RESUME_ALL,
 					    mt7925_pm_interface_iter, dev);
+	mt792x_mutex_release(dev);
 	pm->ds_enable = pm->ds_enable_user && !monitor;
 	mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
 }
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 2/6] wifi: mt76: mt7925: add NULL pointer protection for MLO state transitions
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Add NULL pointer checks for functions that return pointers to link-related
structures throughout the mt7925 driver. During MLO state transitions,
these functions can return NULL when link configuration is not synchronized.

Functions protected:
- mt792x_vif_to_bss_conf(): Returns link BSS configuration
- mt792x_vif_to_link(): Returns driver link state
- mt792x_sta_to_link(): Returns station link state

Key changes:

1. mt7925_set_link_key():
   - Check link_conf, mconf, mlink before use
   - During MLO roaming, allow key removal to succeed if link is already gone

2. mt7925_mac_link_sta_add():
   - Check mlink and mconf before WCID allocation
   - Check link_conf before BSS info update
   - Add proper WCID cleanup on error paths (err_wcid label)
   - Check MCU return values and propagate errors

3. mt7925_mac_link_sta_assoc():
   - Check mlink before use
   - Check link_conf and mconf before BSS info update

4. mt7925_mac_link_sta_remove():
   - Check mlink before use
   - Check link_conf and mconf before cleanup operations

Prevents crashes during:
- BSSID roaming transitions
- MLO setup and teardown
- Hardware reset operations

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 66 ++++++++++++++-----
 1 file changed, 51 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index fad3b1505f67..88ee90709b75 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -612,6 +612,17 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 	link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL;
 	mconf = mt792x_vif_to_link(mvif, link_id);
 	mlink = mt792x_sta_to_link(msta, link_id);
+
+	if (!link_conf || !mconf || !mlink) {
+		/* During MLO roaming, link state may be torn down before
+		 * mac80211 requests key removal. If removing a key and
+		 * the link is already gone, consider it successfully removed.
+		 */
+		if (cmd != SET_KEY)
+			return 0;
+		return -EINVAL;
+	}
+
 	wcid = &mlink->wcid;
 	wcid_keyidx = &wcid->hw_key_idx;
 
@@ -864,12 +875,17 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_id);
+	if (!mlink)
+		return -EINVAL;
+
+	mconf = mt792x_vif_to_link(mvif, link_id);
+	if (!mconf)
+		return -EINVAL;
 
 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
 	if (idx < 0)
 		return -ENOSPC;
 
-	mconf = mt792x_vif_to_link(mvif, link_id);
 	mt76_wcid_init(&mlink->wcid, 0);
 	mlink->wcid.sta = 1;
 	mlink->wcid.idx = idx;
@@ -888,21 +904,28 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 
 	ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
 	if (ret)
-		return ret;
+		goto err_wcid;
 
 	mt7925_mac_wtbl_update(dev, idx,
 			       MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
 
 	link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+	if (!link_conf) {
+		ret = -EINVAL;
+		goto err_wcid;
+	}
 
 	/* should update bss info before STA add */
 	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		if (ieee80211_vif_is_mld(vif))
-			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-						link_conf, link_sta, link_sta != mlink->pri_link);
+			ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						      link_conf, link_sta,
+						      link_sta != mlink->pri_link);
 		else
-			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-						link_conf, link_sta, false);
+			ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						      link_conf, link_sta, false);
+		if (ret)
+			goto err_wcid;
 	}
 
 	if (ieee80211_vif_is_mld(vif) &&
@@ -910,28 +933,34 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_NONE);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	} else if (ieee80211_vif_is_mld(vif) &&
 		   link_sta != mlink->pri_link) {
 		ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
 					    true, MT76_STA_INFO_STATE_ASSOC);
 		if (ret)
-			return ret;
+			goto err_wcid;
 
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_ASSOC);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	} else {
 		ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
 					    MT76_STA_INFO_STATE_NONE);
 		if (ret)
-			return ret;
+			goto err_wcid;
 	}
 
 	mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
 
 	return 0;
+
+err_wcid:
+	rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+	mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+	mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+	return ret;
 }
 
 static int
@@ -1039,6 +1068,8 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+	if (!mlink)
+		return;
 
 	mt792x_mutex_acquire(dev);
 
@@ -1048,12 +1079,13 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
 		link_conf = mt792x_vif_to_bss_conf(vif, vif->bss_conf.link_id);
 	}
 
-	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+	if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		struct mt792x_bss_conf *mconf;
 
 		mconf = mt792x_link_conf_to_mconf(link_conf);
-		mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
-					link_conf, link_sta, true);
+		if (mconf)
+			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+						link_conf, link_sta, true);
 	}
 
 	ewma_avg_signal_init(&mlink->avg_ack_signal);
@@ -1100,6 +1132,8 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 
 	msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
 	mlink = mt792x_sta_to_link(msta, link_id);
+	if (!mlink)
+		return;
 
 	mt7925_roc_abort_sync(dev);
 
@@ -1113,10 +1147,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 
 	link_conf = mt792x_vif_to_bss_conf(vif, link_id);
 
-	if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+	if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
 		struct mt792x_bss_conf *mconf;
 
 		mconf = mt792x_link_conf_to_mconf(link_conf);
+		if (!mconf)
+			goto out;
 
 		if (ieee80211_vif_is_mld(vif))
 			mt792x_mac_link_bss_remove(dev, mconf, mlink);
@@ -1124,7 +1160,7 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
 			mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf,
 						link_sta, false);
 	}
-
+out:
 	spin_lock_bh(&mdev->sta_poll_lock);
 	if (!list_empty(&mlink->wcid.poll_list))
 		list_del_init(&mlink->wcid.poll_list);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 1/6] wifi: mt76: mt7925: fix double wcid initialization race condition
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <20260129081839.179709-1-zac@zacbowling.com>

Remove duplicate mt76_wcid_init() call in mt7925_mac_link_sta_add that
occurs after the wcid is already published via rcu_assign_pointer().

The wcid is correctly initialized at line 873 after allocation.
However, a second mt76_wcid_init() call at line 885 reinitializes
the wcid after it has been published to RCU readers, which can cause:

 - List head corruption (tx_list, poll_list) if concurrent code is
   already using the wcid
 - Memory leaks from reinitializing the pktid IDR
 - Race conditions where readers see partially initialized state

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index afcc0fa4aa35..fad3b1505f67 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -882,7 +882,6 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
 	wcid = &mlink->wcid;
 	ewma_signal_init(&wcid->rssi);
 	rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid);
-	mt76_wcid_init(wcid, 0);
 	ewma_avg_signal_init(&mlink->avg_ack_signal);
 	memset(mlink->airtime_ac, 0,
 	       sizeof(msta->deflink.airtime_ac));
-- 
2.52.0


^ permalink raw reply related

* [PATCH v7 0/6] wifi: mt76: mt7925: MLO stability fixes
From: Zac @ 2026-01-29  8:18 UTC (permalink / raw)
  To: nbd
  Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
	linux, lorenzo, ryder.lee, sean.wang, sean.wang, zac, zbowling
In-Reply-To: <c9b81e89-cd29-44f2-b4e8-0179b2049c21@nbd.name>

From: Zac Bowling <zac@zacbowling.com>

This patch series addresses several stability issues in the mt7925 driver,
particularly around Multi-Link Operation (MLO) scenarios. These fixes address
kernel panics, deadlocks, and race conditions reported by users on systems
like Framework laptops with MT7925 WiFi adapters.

Changes since v6:
- Consolidated from 12 patches to 6 focused patches
- Removed patches that have been merged or superseded upstream
- Improved error handling in AMPDU actions
- Added lockdep assertions for better debugging

The series addresses:
1. Double wcid initialization race condition during station add
2. NULL pointer dereferences during MLO state transitions
3. Missing mutex protection in critical paths
4. MCU command error handling in AMPDU BA session management
5. Lockdep assertions for mutex verification
6. MLO ROC setup error handling

Tested on:
- Framework Laptop 16 with MT7925 (AMD variant)
- Kernel 6.18.x and nbd168/wireless mt76 branch
- Various MLO and non-MLO AP configurations

Zac Bowling (6):
  wifi: mt76: mt7925: fix double wcid initialization race condition
  wifi: mt76: mt7925: add NULL pointer protection for MLO operations
  wifi: mt76: mt7925: add mutex protection in critical paths
  wifi: mt76: mt7925: add MCU command error handling in ampdu_action
  wifi: mt76: mt7925: add lockdep assertions for mutex verification
  wifi: mt76: mt7925: fix MLO ROC setup error handling

 drivers/net/wireless/mediatek/mt76/mt7925/mac.c  |  3 ++
 drivers/net/wireless/mediatek/mt76/mt7925/main.c | 65 +++++++++++++++++++-----
 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c  | 24 +++++++--
 3 files changed, 75 insertions(+), 17 deletions(-)

--
2.52.0

^ permalink raw reply

* Re: [PATCH wireless-next v4 3/3] wifi: mac80211: add initial UHR support
From: Johannes Berg @ 2026-01-29  7:51 UTC (permalink / raw)
  To: Karthikeyan Kathirvel, linux-wireless
In-Reply-To: <415aaac2-eb63-446f-9fe5-315666ed3c64@oss.qualcomm.com>

On Thu, 2026-01-29 at 13:11 +0530, Karthikeyan Kathirvel wrote:

[lots of quotes - if there was another comment I missed it]


> > +	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));
> DBE not required for non-AP ?

"A non-AP STA does not include the DBE Capability Parameters field in
the UHR MAC Capabilities Information field."

johannes
> 

^ permalink raw reply

* Re: [PATCH wireless-next v4 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-29  7:48 UTC (permalink / raw)
  To: Karthikeyan Kathirvel, linux-wireless
In-Reply-To: <ec69981d-aa4d-4ce2-ad7f-71e68784029e@oss.qualcomm.com>

On Thu, 2026-01-29 at 13:03 +0530, Karthikeyan Kathirvel wrote:
> 
> > +	if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
> > +		u8 dbe;
> > +
> > +		needed += 1;
> > +		if (len < needed)
> > +			return false;
> > +
> > +		dbe = cap->variable[0];
> > +
> > +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
> cpu_to_le32 ?

It's only a u8.

> > +			needed += 24;
> It should be in bytes right offs += 3 ?
> > +
> > +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
> > +			needed += 24;
> Same here

And I _was_ wondering how this was so big ... oops.

Thanks!

(would be nice if you could trim the quotes further)

johannes

^ permalink raw reply

* Re: [PATCH wireless-next v4 3/3] wifi: mac80211: add initial UHR support
From: Karthikeyan Kathirvel @ 2026-01-29  7:41 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260128133033.fba9174f1fd1.Ieec940b58dbf8115dab7e1e24cb5513f52c8cb2f@changeid>



On 1/28/2026 6:00 PM, 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>
> ---
> v4:
>   - fix NPCA validation
> 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        | 117 ++++++++++++++++++++++++++++++++++---
>   net/mac80211/parse.c       |  20 +++++++
>   net/mac80211/rx.c          |  26 +++++++++
>   net/mac80211/sta_info.c    |  11 ++++
>   net/mac80211/sta_info.h    |  80 ++++++++++++++++++-------
>   net/mac80211/uhr.c         |  29 +++++++++
>   net/mac80211/util.c        |  36 +++++++++++-
>   12 files changed, 364 insertions(+), 34 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..df170556825f 100644
> --- a/net/mac80211/mlme.c
> +++ b/net/mac80211/mlme.c
> @@ -8,7 +8,7 @@
>    * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
>    * Copyright 2013-2014  Intel Mobile Communications GmbH
>    * Copyright (C) 2015 - 2017 Intel Deutschland GmbH
> - * Copyright (C) 2018 - 2025 Intel Corporation
> + * Copyright (C) 2018 - 2026 Intel Corporation
>    */
>   
>   #include <linux/delay.h>
> @@ -162,6 +162,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 +193,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 +216,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 +343,63 @@ 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) {
> +			int width = cfg80211_chandef_get_width(chandef);
> +			u8 offs = le32_get_bits(npca->params,
> +						IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS);
> +			u32 cf1 = chandef->center_freq1;
> +			bool pri_upper, npca_upper;
> +
> +			pri_upper = chandef->chan->center_freq > cf1;
> +			npca_upper = 20 * offs >= width / 2;
> +
> +			if (20 * offs >= cfg80211_chandef_get_width(chandef) ||
> +			    pri_upper == npca_upper) {
> +				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 +1150,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 +1168,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 +1217,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 +1951,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 +1970,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 +2207,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 +5606,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 +5908,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 +6084,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 +6094,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..69034d83a7b6 100644
> --- a/net/mac80211/rx.c
> +++ b/net/mac80211/rx.c
> @@ -5518,6 +5518,32 @@ 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, NSS:%d, GI:%d, BW:%d, IM:%d\n",
> +				      status->rate_idx, status->nss, status->uhr.gi,
> +				      status->bw, status->uhr.im))
> +				goto drop;
> +			if (WARN_ONCE(status->uhr.im &&
> +				      (status->nss != 1 || status->rate_idx == 15),
> +				      "bad UHR IM MCS MCS:%d, NSS:%d\n",
> +				      status->rate_idx, status->nss))
> +				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..97433347c26d 100644
> --- a/net/mac80211/util.c
> +++ b/net/mac80211/util.c
> @@ -6,7 +6,7 @@
>    * Copyright 2007	Johannes Berg <johannes@sipsolutions.net>
>    * Copyright 2013-2014  Intel Mobile Communications GmbH
>    * Copyright (C) 2015-2017	Intel Deutschland GmbH
> - * Copyright (C) 2018-2025 Intel Corporation
> + * Copyright (C) 2018-2026 Intel Corporation
>    *
>    * utilities for mac80211
>    */
> @@ -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));
DBE not required for non-AP ?
> +	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)))

^ permalink raw reply

* Re: [PATCH wireless-next v4 1/3] wifi: ieee80211: add some initial UHR definitions
From: Karthikeyan Kathirvel @ 2026-01-29  7:33 UTC (permalink / raw)
  To: Johannes Berg, linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260128133033.aa4a43982df0.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>



On 1/28/2026 6:00 PM, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
> 
> This is based on Draft P802.11bn_D1.2, but that's still very
> incomplete, so don't handle a number of things and make some
> local decisions such as using 40 bits for MAC capabilities
> and 8 bits for PHY capabilities.
> 
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> v4:
>    - update to D1.2, including DBE in UHR capabilities
>    - fold in suggestions from Pablo
> ---
>   include/linux/ieee80211-uhr.h | 215 ++++++++++++++++++++++++++++++++++
>   include/linux/ieee80211.h     |  33 +++++-
>   2 files changed, 246 insertions(+), 2 deletions(-)
>   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..06556867b004
> --- /dev/null
> +++ b/include/linux/ieee80211-uhr.h
> @@ -0,0 +1,215 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * IEEE 802.11 UHR definitions
> + *
> + * Copyright (c) 2025-2026 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_OFFS	0x0000000F
> +#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH	0x000000F0
> +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY		0x00003F00
> +#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY	0x000FC000
> +#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC		0x00300000
> +#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN		0x00400000
> +#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES	0x00800000
> +
> +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_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_INTV_LOW	0xE0
> +
> +#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH	0x03
> +#define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS		0x04
> +#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE		0x08
> +#define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP		0x10
> +
> +#define IEEE80211_UHR_MAC_CAP_DBE_MAX_BW		0x07
> +#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES	0x08
> +#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES	0x10
> +
> +struct ieee80211_uhr_capa_mac {
> +	u8 mac_cap[5];
> +} __packed;
> +
> +struct ieee80211_uhr_capa {
> +	struct ieee80211_uhr_capa_mac mac;
> +	/* DBE, PHY capabilities */
> +	u8 variable[];
> +} __packed;
> +
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80	0x01
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80	0x02
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160	0x04
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160	0x08
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320	0x10
> +#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_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)
> +{
> +	const struct ieee80211_uhr_capa *cap = (const void *)data;
> +	size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_capa_phy);
> +
> +	if (len < needed)
> +		return false;
> +
> +	if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
> +		u8 dbe;
> +
> +		needed += 1;
> +		if (len < needed)
> +			return false;
> +
> +		dbe = cap->variable[0];
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
cpu_to_le32 ?
> +			needed += 24;
It should be in bytes right offs += 3 ?
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
> +			needed += 24;
Same here
> +	}
> +
> +	return len >= needed;
> +}
> +
> +static inline const struct ieee80211_uhr_capa_phy *
> +ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
> +{
> +	u8 offs = 0;
> +
> +	if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
> +		u8 dbe = cap->variable[0];
> +
> +		offs += 1;
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
cpu_to_le32 ?
> +			offs += 24;
It should be in bytes right offs += 3 ?
> +
> +		if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
> +			offs += 24;
Same here
> +	}
> +
> +	return (const void *)&cap->variable[offs];
> +}
> +
> +#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..82d797be95b9 100644
> --- a/include/linux/ieee80211.h
> +++ b/include/linux/ieee80211.h
> @@ -9,7 +9,7 @@
>    * Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
>    * Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
>    * Copyright (c) 2016 - 2017 Intel Deutschland GmbH
> - * Copyright (c) 2018 - 2025 Intel Corporation
> + * Copyright (c) 2018 - 2026 Intel Corporation
>    */
>   
>   #ifndef 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_SMD_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"

^ 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