* [PATCH iwlwifi-next 00/15] wifi: iwlwifi: updates - 2026-05-13
From: Miri Korenblit @ 2026-05-13 5:43 UTC (permalink / raw)
To: linux-wireless
Hi,
Features, cleanups and fixes from our internal tree.
Thanks,
Miri
---
Avinash Bhatt (2):
wifi: iwlwifi: mld: implement PSD/EIRP RSSI adjustment
wifi: iwlwifi: mld: update link grading tables per bandwidth
Avraham Stern (1):
wifi: iwlwifi: mld: add handler for NAN ULW attribute notification
Emmanuel Grumbach (4):
wifi: iwlwifi: led_compensation is needed for iwldvm only
wifi: iwlwifi: shadow_ram_support is needed for iwldvm only.
wifi: iwlwifi: max_event_log_size is needed for iwldvm only
wifi: iwlwifi: smem_offset smem_len are not needed from 22000 and up
Ilan Peer (2):
wifi: iwlwifi: mld: Replace static declarations of IWL_MLD_ALLOC_FN
wifi: iwlwifi: mld: Add support for NAN multicast data
Johannes Berg (4):
wifi: iwlwifi: mld: support NPCA capability for UHR devices
wifi: iwlwifi: mld: implement UHR DPS
wifi: iwlwifi: mld: give link STA debugfs files a namespace
wifi: iwlwifi: mld: set correct key mask for NAN
Miri Korenblit (2):
wifi: iwlwifi: bump core version for BZ/SC/DR to 104
wifi: iwlwifi: define MODULE_FIRMWARE with the correct API
.../net/wireless/intel/iwlwifi/cfg/22000.c | 23 +-
drivers/net/wireless/intel/iwlwifi/cfg/7000.c | 5 +-
drivers/net/wireless/intel/iwlwifi/cfg/8000.c | 5 +-
drivers/net/wireless/intel/iwlwifi/cfg/9000.c | 5 +-
.../net/wireless/intel/iwlwifi/cfg/ax210.c | 38 +-
drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 13 +-
drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 13 +-
.../net/wireless/intel/iwlwifi/cfg/rf-gf.c | 13 -
.../net/wireless/intel/iwlwifi/cfg/rf-hr.c | 24 --
drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 13 +-
.../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 32 +-
.../wireless/intel/iwlwifi/iwl-nvm-parse.c | 4 +
.../net/wireless/intel/iwlwifi/mld/debugfs.c | 18 +-
.../net/wireless/intel/iwlwifi/mld/iface.c | 10 +-
.../net/wireless/intel/iwlwifi/mld/iface.h | 5 +
drivers/net/wireless/intel/iwlwifi/mld/key.c | 50 ++-
drivers/net/wireless/intel/iwlwifi/mld/link.c | 396 +++++++++++++++---
.../net/wireless/intel/iwlwifi/mld/mac80211.c | 72 +++-
drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 +
drivers/net/wireless/intel/iwlwifi/mld/mld.h | 3 +
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 62 ++-
drivers/net/wireless/intel/iwlwifi/mld/nan.h | 7 +
.../net/wireless/intel/iwlwifi/mld/notif.c | 3 +
drivers/net/wireless/intel/iwlwifi/mld/phy.c | 24 +-
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 104 ++++-
drivers/net/wireless/intel/iwlwifi/mld/sta.h | 11 +
.../intel/iwlwifi/mld/tests/link-selection.c | 6 +-
.../wireless/intel/iwlwifi/mld/tests/utils.c | 6 +-
drivers/net/wireless/intel/iwlwifi/mld/tx.c | 11 +-
29 files changed, 780 insertions(+), 197 deletions(-)
--
2.34.1
^ permalink raw reply
* Re: [PATCH v2] wifi: rtw88: remove rtw_txq_dequeue
From: Ping-Ke Shih @ 2026-05-13 3:59 UTC (permalink / raw)
To: Martin Kaiser, Ping-Ke Shih
Cc: Bitterblue Smith, linux-wireless, linux-kernel, Martin Kaiser
In-Reply-To: <20260507082238.889656-1-martin@kaiser.cx>
Martin Kaiser <martin@kaiser.cx> wrote:
> Remove the rtw_txq_dequeue helper. It's a wrapper around
> ieee80211_tx_dequeue with just one caller.
>
> Call ieee80211_tx_dequeue directly in rtw_txq_push. There's no need to
> fetch txq for every frame, we can do this once outside of the rcu lock.
>
> Make the loop variable i unsigned long, it should have the same type as
> frames.
>
> Signed-off-by: Martin Kaiser <martin@kaiser.cx>
> Acked-by: Ping-Ke Shih <pkshih@realtek.com>
1 patch(es) applied to rtw-next branch of rtw.git, thanks.
4c13e00cc679 wifi: rtw88: remove rtw_txq_dequeue
---
https://github.com/pkshih/rtw.git
^ permalink raw reply
* Re: [PATCH rtw-next 01/14] wifi: rtw89: add AMPDU to radiotap
From: Ping-Ke Shih @ 2026-05-13 3:53 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless
In-Reply-To: <20260506131000.1706298-2-pkshih@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> The RX desc can report current frame is in AMPDU, but no way point out
> if it is a last one in AMPDU. Update AMPDU reference only.
>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
14 patch(es) applied to rtw-next branch of rtw.git, thanks.
756dc69e5639 wifi: rtw89: add AMPDU to radiotap
9535e2279ba9 wifi: rtw89: add VHT beamformed to radiotap
a371139d8be9 wifi: rtw89: SNIFFER_MODE bit along IEEE80211_CONF_MONITOR
c847d0c14c47 wifi: rtw89: phy: define PHY status IE length for generations
847758f15f46 wifi: rtw89: phy: enable IE-09/IE-10 PHY status report for monitor mode
bc1006b68b89 wifi: rtw89: move HE radiotap to an individual function
236b25d9308d wifi: rtw89: fill VHT radiotap
16a1becdd1fb wifi: rtw89: fill HE-SU/HE-TB/HE-MU/HE-EXT_SU radiotap
41d78f3f3596 wifi: rtw89: debug: make implementation of beacon_info entry in order
cd92d278c186 wifi: rtw89: add debugfs entry of monitor mode options to capture HE-MU packets
884495c39de1 wifi: rtw89: phy: check length before parsing PHY status IE
48402f736f93 wifi: rtw89: phy: skip trailing 8-byte zeros of PHY status IE for RTL8922D
0805ddc2fdb6 wifi: rtw89: phy: support PHY status IE-09 GEN2 for RTL8922D
f77199a0cf66 wifi: rtw89: check skb headroom before adding radiotap
---
https://github.com/pkshih/rtw.git
^ permalink raw reply
* Re: [PATCH net-next v3 0/2] Rework pci_device_id initialisation
From: patchwork-bot+netdevbpf @ 2026-05-13 3:45 UTC (permalink / raw)
To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig_=28The_Capable_Hub=29_=3Cu=2Ekleine-koenig?=,
=?utf-8?q?=40baylibre=2Ecom=3E?=
Cc: mgr, andrew+netdev, davem, edumazet, kuba, pabeni, mkl, mailhol,
johannes, klassert, dave, ionut, mark.einon, rmody,
GR-Linux-NIC-Dev, manishc, bharat, kirjanov, shaojijie,
shenjian15, cai.huoqing, gongfan1, anthony.l.nguyen,
przemyslaw.kitszel, tariqt, saeedm, leon, mbloch, idosch, petrm,
dong100, hkallweit1, nic_swsd, jiri, romieu, venza, chessman,
jiawenwu, mengyuanlou, kevin.curtis, arend.vanspriel,
stas.yakovlev, richardcochran, kees, aleksandr.loktionov, tglx,
jacob.e.keller, fourier.thomas, mingo, kory.maincent, zilin,
vadim.fedorenko, marco.crivellari, bhelgaas, darinzon, yyyynoom,
benato.denis96, liuyonglong, andriy.shevchenko, rdunlap,
yiconghui, danishanwar, nathan, enelsonmoore, larysa.zaremba,
ian.lin, colin.i.king, double.lo, msp, horms, netdev,
linux-kernel, linux-can, linux-parisc, intel-wired-lan,
linux-rdma, oss-drivers, linux-wireless, brcm80211,
brcm80211-dev-list.pdl
In-Reply-To: <20260511090023.1634387-4-u.kleine-koenig@baylibre.com>
Hello:
This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 11 May 2026 11:00:22 +0200 you wrote:
> Hello,
>
> the previous iteration of this series (v2) is available at
> https://lore.kernel.org/netdev/cover.1778149923.git.u.kleine-koenig@baylibre.com
>
> Changes since v2:
>
> [...]
Here is the summary with links:
- [net-next,v3,1/2] net: nfp: Drop PCI class entries with .class_mask = 0
https://git.kernel.org/netdev/net-next/c/0f5b8332b0b2
- [net-next,v3,2/2] net: Consistently define pci_device_ids using named initializers
https://git.kernel.org/netdev/net-next/c/733334d6bdaf
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v2] wifi: ath11k: Add two missing Lenovo IDs to the quirk table
From: Kyle Farnung @ 2026-05-13 3:30 UTC (permalink / raw)
To: Jeff Johnson
Cc: Santiago Ruano Rincón, Jeff Johnson, linux-wireless, ath11k,
linux-kernel, kfarnung, Mark Pearson, koike
In-Reply-To: <45f1c979-ea8d-4118-8551-eff93966b4e1@oss.qualcomm.com>
On Tue, May 12, 2026 at 5:03 PM Jeff Johnson
<jeff.johnson@oss.qualcomm.com> wrote:
>
> On 5/6/2026 12:54 PM, Santiago Ruano Rincón wrote:
> > Commit 0eb002c93c3b4 ("wifi: ath11k: Add missing platform IDs for quirk
> > table") added some Lenovo platform IDs to the quirk table to address a
> > wakeup from suspend issue [1]. However, at least P14s Gen 5 AMD, as
> > reported by Kyle Farnung [2], and P14s Gen 3 AMD are missing in the
> > table. This commit adds the two corresponding Lenovo version IDs for
> > each.
> >
> > [1] https://bugzilla.kernel.org/show_bug.cgi?id=219196
> > [2] https://bugzilla.kernel.org/show_bug.cgi?id=219196#c23
> >
> > Tested-on: P14s G3 AMD running 7.0.3.
> >
> > Fixes: ce8669a27016 ("wifi: ath11k: determine PM policy based on machine model")
> > Co-authored-by: Kyle Farnung <kfarnung@gmail.com>
>
> This is not a standard Linux kernel commit tag.
> Co-developed-by: could be used, but I'd need a Signed-off-by: from Kyle
>
> Kyle, if you'd like these added please reply with both a Co-developed-by and
> a Signed-off-by tag
I re-tested the bug behavior on the v7.0.4 tag and found that I'm not able
to reproduce it on my device. I put the device to sleep and then unplugged
and replugged power; it never woke up until I intentionally woke it.
Based on discussion in [3], I narrowed my issue with wi-fi disassociation
on wake to a suspect commit [4]. I'm developing a patch that clears the rdp
and wrp buffers during wake to clear out any stale state in the LMAC rings.
I'll submit that once I'm done testing it.
Santiago: Are you seeing the sleep/wake bug from [1] or something similar
to what I was seeing around disconnections on wake?
[3] https://lore.kernel.org/all/CAOPSVF04q6uvVdq8GTRLHBrVMdpt9=o9wVcFMc6f-yhmSBcZqQ@mail.gmail.com/
[4] 32be3ca4cf78b ("wifi: ath11k: HAL SRNG: don't deinitialize and
re-initialize again")
>
> I can apply these fixes when I process the patch in my tree.
>
> https://www.kernel.org/doc/html/latest/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by
>
> > Signed-off-by: Santiago Ruano Rincón <santiagorr@riseup.net>
> > ---
> > Changes in v2:
> > - Merged the Lenovo P14s G5 AMD #1 and #2 related changes proposed by
> > Kyle Farnung in
> > https://lore.kernel.org/all/20260330-p14s-pm-quirk-v2-1-ef18ce07996b@gmail.com/
> > - Added Kyle Farnung as Co-author of the commit
> > - Added the second ID of Lenovo P14s G3 AMD, and adapt the comments
> > accordingly
> > - v1: https://lore.kernel.org/ath11k/20260423211458.458911-1-santiagorr@riseup.net/
> > ---
> > drivers/net/wireless/ath/ath11k/core.c | 28 ++++++++++++++++++++++++++
> > 1 file changed, 28 insertions(+)
> >
> > diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
> > index 3f6f4db5b7ee..c1ce0a11af44 100644
> > --- a/drivers/net/wireless/ath/ath11k/core.c
> > +++ b/drivers/net/wireless/ath/ath11k/core.c
> > @@ -957,6 +957,20 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = {
> > DMI_MATCH(DMI_PRODUCT_NAME, "21K4"),
> > },
> > },
> > + {
> > + .driver_data = (void *)ATH11K_PM_WOW,
> > + .matches = { /* P14s G3 AMD #1 */
> > + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> > + DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
> > + },
> > + },
> > + {
> > + .driver_data = (void *)ATH11K_PM_WOW,
> > + .matches = { /* P14s G3 AMD #2 */
> > + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> > + DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
> > + },
> > + },
> > {
> > .driver_data = (void *)ATH11K_PM_WOW,
> > .matches = { /* P14s G4 AMD #1 */
> > @@ -971,6 +985,20 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = {
> > DMI_MATCH(DMI_PRODUCT_NAME, "21K6"),
> > },
> > },
> > + {
> > + .driver_data = (void *)ATH11K_PM_WOW,
> > + .matches = { /* P14s G5 AMD #1 */
> > + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> > + DMI_MATCH(DMI_PRODUCT_NAME, "21ME"),
> > + },
> > + },
> > + {
> > + .driver_data = (void *)ATH11K_PM_WOW,
> > + .matches = { /* P14s G5 AMD #2 */
> > + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> > + DMI_MATCH(DMI_PRODUCT_NAME, "21MF"),
> > + },
> > + },
> > {
> > .driver_data = (void *)ATH11K_PM_WOW,
> > .matches = { /* T16 G2 AMD #1 */
>
^ permalink raw reply
* Re: ath12k WCN7850: Q6 Hexagon fault at WLAON region 0x1792000 ~2s post-AUTHORIZE on X1E80100
From: Baochen Qiang @ 2026-05-13 3:05 UTC (permalink / raw)
To: Marcus Glocker, Maxim Storetvedt
Cc: ath12k, linux-wireless, jjohnson, kvalo, manivannan.sadhasivam,
Mark Kettenis
In-Reply-To: <tzkh5t3bmiqff67g72rihapizz2u2dlj4xfrr324d6yg4rtltt@3qrsymzw7c2c>
On 5/13/2026 3:59 AM, Marcus Glocker wrote:
> On Tue, May 12, 2026 at 11:38:06AM +0800, Baochen Qiang wrote:
>
>>
>>
>> On 5/5/2026 5:08 AM, Marcus Glocker wrote:
>>> Hi,
>>>
>>> We're porting ath12k to OpenBSD as the qwz(4) driver, targeting Samsung
>>> Galaxy Book4 Edge (X1E80100 SoC, WCN7850 hw2.0). Scan, auth, 4-way
>>> handshake all complete; ~2 seconds after WPA2 AUTHORIZE the WCN7850
>>> firmware crashes deterministically with:
>>>
>>> dlpager_main.c:147 Non Page Fault Exception cause code 0x 23
>>> at Address: 0x 1792000
>>>
>>> Cause code 0x23 isn't a valid arm64 exception -- the fault is on the
>>> WCN7850's on-die Hexagon Q6 DSP, with QURT's generic exception handler
>>> (which happens to live in dlpager_main.c) printing it. So this is not
>>> a host CPU fault.
>>>
>>> Per the RDDM segment table (at the start of the dump), VA 0x01792000
>>> is the start of the chip's WLAON_DUMP region (size 0x820). The Q6 is
>>> trying to read its own always-on hardware state region and the chip
>>> refuses the access.
>>>
>>> (Samsung, Asus, Honor) with multiple FW builds. Currently testing
>>> with WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
>>> (fw 0x110cffff, 2025-06-25) -- the exact blob a Linux ath12k user
>>> runs successfully on the identical Samsung hardware. Same board-2.bin,
>>> same compiled DTB (upstream hamoa.dtsi based).
>>>
>>> We've field-compared qwz against ath12k and ruled out (byte-level or
>>> wire-level):
>>>
>>> * QMI host_cap, m3_info, wlan_cfg, wlan_ini, bdf_download (all
>>> fields including ce_config, svc_to_ce_map, shadow_reg_v3,
>>> feature_list, m3 paddr/size, nm_modem)
>>> * MHI bringup ordering (BHI -> wait SBL EE -> wait M0 -> BHIE)
>>> * BHI/BHIE DMA coherency
>>> * ASPM disable before MHI start
>>> * WLAON_WARM_SW_ENTRY zeroing + QFPROM_PWR_CTRL VDD4BLOW clear
>>> * static_window_map=false + window-bank register init
>>> * Per-chunk vs monolithic respond_mem allocation
>>> * WMI_PEER_MIMO_PS_STATE = WMI_PEER_SMPS_PS_NONE (added matching
>>> ath12k_setup_peer_smps; doesn't help)
>>> * FW image variation (c5 and c7 both fail identically)
>>>
>>> Specifically NOT involved (we have evidence either way):
>>>
>>> * Gunyah -- X1E80100 is reportedly run in EL2 without Gunyah by
>>> users where ath12k works; so Gunyah isn't programming WLAON
>>> access for the Q6.
>>> * SMMU / pcie_smmu -- pcie_smmu is status="reserved" upstream,
>>> pcie4 has no iommus property; PCIe DMA bypasses SMMU.
>>> * SCM/PAS -- ath12k's PCIe path makes no qcom_scm_* calls.
>>>
>>> Question: what subsystem inside the WCN7850 firmware touches the
>>> WLAON region at 0x01792000 around 2 seconds after the host sends
>>> WMI_PEER_AUTHORIZE? And what host-side configuration (WMI command,
>>> HTT message, MHI state, etc.) primes that path so the access
>>> succeeds on Linux?
>>>
>>> Even a pointer at the right Linux code path or the right FW-side
>>> component would unblock us. We have full RDDM dumps and dmesg
>>> captures available; happy to share off-list or as attachments.
>>
>> please help collect ath12k successful dmesg log and qwz failed dmesg log for compare.
>>
>> Please enable verbose ath12k log when loading ath12k driver:
>>
>> If you are using the latest upstream ath12k:
>>
>> sudo modprobe ath12k debug_mask=0xffffffff
>> sudo modprobe ath12k_wifi7
>>
>> If you are using an old ath12k:
>>
>> sudo modprobe ath12k debug_mask=0xffffffff
>>
>>>
>>> Thanks,
>>> Marcus
>>>
>>
>
> Hi Baochen,
>
> Thanks for coming back on this topic.
>
> Attached the OpenBSD dmesg, with full ath12k driver debug logging
the dmesg shows several WMI_INIT cmd instances which is not expected, because in normal
operation this command should be sent only once.
cat dmesg |grep -w 'sending WMI command 0x1'
May 12 19:35:46 x1e /bsd: qwz_wmi_cmd_send_nowait: sending WMI command 0x1
May 12 19:37:20 x1e /bsd: qwz_wmi_cmd_send_nowait: sending WMI command 0x1
May 12 19:37:41 x1e /bsd: qwz_wmi_cmd_send_nowait: sending WMI command 0x1
May 12 19:37:46 x1e /bsd: qwz_wmi_cmd_send_nowait: sending WMI command 0x1
May 12 19:37:50 x1e /bsd: qwz_wmi_cmd_send_nowait: sending WMI command 0x1
other than that I don't find any other clues.
> enabled, plus the resulting RDDM binary after the firmware crash:
how did you collect the RDDM binary, seems not in the right format, my tool can not parse
it correctly. Looking into the binary, at least the magic 'ATH12K-FW-DUMP' is not present
at the very beginning.
And from which Linux version you take the ath12k codebase?
>
> https://nazgul.ch/pub/qwz0-rddm.bin.gz
>
> The command sequence on OpenBSD to re-produce that was:
>
> ifconfig qwz0 up # Bring the ath12k device up
> ifconfig qwz0 scan # Scan for networks
> ifconfig qwz0 nwid nazgul wpakey xxx # Start association
>
> Hi Max,
>
> Since you have Linux running on exactly the same Samsung Galaxy Book4
> Edge 14" laptop, where ath12k works, would you be so kind and also
> provide the dmesg output showing an successful association with the
> ath12k driver debug logging enabled? See above how to enable that.
> That would be very helpful!
>
> Thanks and Regards,
> Marcus
^ permalink raw reply
* [PATCH] wifi: mt76: mt7921: fix uninitialized variable warning
From: Hongling Zeng @ 2026-05-13 2:14 UTC (permalink / raw)
To: nbd, lorenzo, ryder.lee, shayne.chen, sean.wang, matthias.bgg,
angelogioacchino.delregno, xiong.huang, madhurkumar004
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
zhongling0719, Hongling Zeng, kernel test robot
Move the initialization of 'dev' before the error path that uses it.
This fixes the compiler warning:
drivers/net/wireless/mediatek/mt76/mt7921/pci.c:347:6: warning:
variable 'dev' is used uninitialized whenever 'if' condition is true
Fixes: 0d56655a74ef ("wifi: mt76: mt7921: fix resource leak in probe error path")
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202605130432.S8Ozipm2-lkp@intel.com/
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 4bb436465a3d..49a37185f056 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -343,13 +343,14 @@ static int mt7921_pci_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, mdev);
+ dev = container_of(mdev, struct mt792x_dev, mt76);
+
regs = pcim_iomap_region(pdev, 0, pci_name(pdev));
if (IS_ERR(regs)) {
ret = PTR_ERR(regs);
goto err_free_dev;
}
- dev = container_of(mdev, struct mt792x_dev, mt76);
dev->fw_features = features;
dev->hif_ops = &mt7921_pcie_ops;
dev->irq_map = &irq_map;
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v2] wifi: ath11k: Add two missing Lenovo IDs to the quirk table
From: Jeff Johnson @ 2026-05-13 0:03 UTC (permalink / raw)
To: Santiago Ruano Rincón, Jeff Johnson, linux-wireless, ath11k,
linux-kernel, kfarnung, Kyle Farnung
Cc: Mark Pearson, koike
In-Reply-To: <20260506200249.317690-1-santiagorr@riseup.net>
On 5/6/2026 12:54 PM, Santiago Ruano Rincón wrote:
> Commit 0eb002c93c3b4 ("wifi: ath11k: Add missing platform IDs for quirk
> table") added some Lenovo platform IDs to the quirk table to address a
> wakeup from suspend issue [1]. However, at least P14s Gen 5 AMD, as
> reported by Kyle Farnung [2], and P14s Gen 3 AMD are missing in the
> table. This commit adds the two corresponding Lenovo version IDs for
> each.
>
> [1] https://bugzilla.kernel.org/show_bug.cgi?id=219196
> [2] https://bugzilla.kernel.org/show_bug.cgi?id=219196#c23
>
> Tested-on: P14s G3 AMD running 7.0.3.
>
> Fixes: ce8669a27016 ("wifi: ath11k: determine PM policy based on machine model")
> Co-authored-by: Kyle Farnung <kfarnung@gmail.com>
This is not a standard Linux kernel commit tag.
Co-developed-by: could be used, but I'd need a Signed-off-by: from Kyle
Kyle, if you'd like these added please reply with both a Co-developed-by and
a Signed-off-by tag
I can apply these fixes when I process the patch in my tree.
https://www.kernel.org/doc/html/latest/process/submitting-patches.html#when-to-use-acked-by-cc-and-co-developed-by
> Signed-off-by: Santiago Ruano Rincón <santiagorr@riseup.net>
> ---
> Changes in v2:
> - Merged the Lenovo P14s G5 AMD #1 and #2 related changes proposed by
> Kyle Farnung in
> https://lore.kernel.org/all/20260330-p14s-pm-quirk-v2-1-ef18ce07996b@gmail.com/
> - Added Kyle Farnung as Co-author of the commit
> - Added the second ID of Lenovo P14s G3 AMD, and adapt the comments
> accordingly
> - v1: https://lore.kernel.org/ath11k/20260423211458.458911-1-santiagorr@riseup.net/
> ---
> drivers/net/wireless/ath/ath11k/core.c | 28 ++++++++++++++++++++++++++
> 1 file changed, 28 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c
> index 3f6f4db5b7ee..c1ce0a11af44 100644
> --- a/drivers/net/wireless/ath/ath11k/core.c
> +++ b/drivers/net/wireless/ath/ath11k/core.c
> @@ -957,6 +957,20 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = {
> DMI_MATCH(DMI_PRODUCT_NAME, "21K4"),
> },
> },
> + {
> + .driver_data = (void *)ATH11K_PM_WOW,
> + .matches = { /* P14s G3 AMD #1 */
> + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "21J5"),
> + },
> + },
> + {
> + .driver_data = (void *)ATH11K_PM_WOW,
> + .matches = { /* P14s G3 AMD #2 */
> + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "21J6"),
> + },
> + },
> {
> .driver_data = (void *)ATH11K_PM_WOW,
> .matches = { /* P14s G4 AMD #1 */
> @@ -971,6 +985,20 @@ static const struct dmi_system_id ath11k_pm_quirk_table[] = {
> DMI_MATCH(DMI_PRODUCT_NAME, "21K6"),
> },
> },
> + {
> + .driver_data = (void *)ATH11K_PM_WOW,
> + .matches = { /* P14s G5 AMD #1 */
> + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "21ME"),
> + },
> + },
> + {
> + .driver_data = (void *)ATH11K_PM_WOW,
> + .matches = { /* P14s G5 AMD #2 */
> + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "21MF"),
> + },
> + },
> {
> .driver_data = (void *)ATH11K_PM_WOW,
> .matches = { /* T16 G2 AMD #1 */
^ permalink raw reply
* Re: [PATCH RESEND RFC 3/3] net: ath11k: add lockup simulation via debugfs
From: Jeff Johnson @ 2026-05-12 23:19 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson; +Cc: linux-wireless, ath11k, linux-kernel, kernel
In-Reply-To: <20260330-ath11k-lockup-fixes-v1-3-7ed21095c2c4@collabora.com>
On 3/30/2026 3:05 AM, Matthew Leach wrote:
> Add a debugfs command to simulate a firmware lockup.
>
> This does not hang the hardware. Instead, it forces the driver down an
> error path that reproduces the sequence observed during real lockups:
>
> ath11k_pci 0000:03:00.0: failed to transmit frame -12
> ath11k_pci 0000:03:00.0: failed to transmit frame -12
> ath11k_pci 0000:03:00.0: failed to transmit frame -12
> ...
> ath11k_pci 0000:03:00.0: wmi command 28680 timeout
> ath11k_pci 0000:03:00.0: failed to submit WMI_MGMT_TX_SEND_CMDID cmd
> ath11k_pci 0000:03:00.0: failed to send mgmt frame: -11
>
> This allows validation of the firmware lockup detection and recovery
> mechanism without requiring a real hardware failure.
>
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
> drivers/net/wireless/ath/ath11k/core.h | 1 +
> drivers/net/wireless/ath/ath11k/debugfs.c | 7 ++++++-
> drivers/net/wireless/ath/ath11k/hal.c | 7 +++++--
> drivers/net/wireless/ath/ath11k/htc.c | 2 +-
> drivers/net/wireless/ath/ath11k/wmi.c | 6 +++++-
> 5 files changed, 18 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
> index 221dcd23b3dd..44b02ae1e85b 100644
> --- a/drivers/net/wireless/ath/ath11k/core.h
> +++ b/drivers/net/wireless/ath/ath11k/core.h
> @@ -1041,6 +1041,7 @@ struct ath11k_base {
> struct ath11k_dbring_cap *db_caps;
> u32 num_db_cap;
> u64 last_frame_tx_error_jiffies;
> + bool simulate_lockup;
>
> /* To synchronize 11d scan vdev id */
> struct mutex vdev_id_11d_lock;
> diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c
> index 0c1138407838..ca0b72a3e0b0 100644
> --- a/drivers/net/wireless/ath/ath11k/debugfs.c
> +++ b/drivers/net/wireless/ath/ath11k/debugfs.c
> @@ -356,7 +356,8 @@ static ssize_t ath11k_read_simulate_fw_crash(struct file *file,
> const char buf[] =
> "To simulate firmware crash write one of the keywords to this file:\n"
> "`assert` - this will send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"
> - "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n";
> + "`hw-restart` - this will simply queue hw restart without fw/hw actually crashing.\n"
> + "`lockup` - simulate a firmware lockup without the h/w actually hanging.\n";
>
> return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
> }
> @@ -413,6 +414,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file,
> ath11k_info(ab, "user requested hw restart\n");
> queue_work(ab->workqueue_aux, &ab->reset_work);
> ret = 0;
> + } else if (!strcmp(buf, "lockup")) {
> + ath11k_info(ab, "simulating lockup\n");
> + ab->simulate_lockup = true;
> + ret = 0;
> } else {
> ret = -EINVAL;
> goto exit;
> diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c
> index e821e5a62c1c..e01fb17a4734 100644
> --- a/drivers/net/wireless/ath/ath11k/hal.c
> +++ b/drivers/net/wireless/ath/ath11k/hal.c
> @@ -691,7 +691,7 @@ int ath11k_hal_srng_dst_num_free(struct ath11k_base *ab, struct hal_srng *srng,
>
> tp = srng->u.dst_ring.tp;
>
> - if (sync_hw_ptr) {
> + if (sync_hw_ptr && !ab->simulate_lockup) {
> hp = *srng->u.dst_ring.hp_addr;
> srng->u.dst_ring.cached_hp = hp;
> } else {
> @@ -743,7 +743,7 @@ u32 *ath11k_hal_srng_src_get_next_entry(struct ath11k_base *ab,
> */
> next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size;
>
> - if (next_hp == srng->u.src_ring.cached_tp)
> + if (next_hp == srng->u.src_ring.cached_tp || ab->simulate_lockup)
> return NULL;
>
> desc = srng->ring_base_vaddr + srng->u.src_ring.hp;
> @@ -828,6 +828,9 @@ void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng)
>
> lockdep_assert_held(&srng->lock);
>
> + if (ab->simulate_lockup)
> + return;
> +
> if (srng->ring_dir == HAL_SRNG_DIR_SRC) {
> srng->u.src_ring.cached_tp =
> *(volatile u32 *)srng->u.src_ring.tp_addr;
> diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c
> index 4571d01cc33d..b05d04a1f5e8 100644
> --- a/drivers/net/wireless/ath/ath11k/htc.c
> +++ b/drivers/net/wireless/ath/ath11k/htc.c
> @@ -208,7 +208,7 @@ static int ath11k_htc_process_trailer(struct ath11k_htc *htc,
> break;
> }
>
> - if (ab->hw_params.credit_flow) {
> + if (ab->hw_params.credit_flow && !ab->simulate_lockup) {
> switch (record->hdr.id) {
> case ATH11K_HTC_RECORD_CREDITS:
> len = sizeof(struct ath11k_htc_credit_report);
> diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
> index 7d9f0bcbb3b0..27d6d4a2f803 100644
> --- a/drivers/net/wireless/ath/ath11k/wmi.c
> +++ b/drivers/net/wireless/ath/ath11k/wmi.c
> @@ -345,9 +345,13 @@ int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,
>
> if (time_in_range64(ab->last_frame_tx_error_jiffies,
> range_start, jiffies_64) &&
> - queue_work(ab->workqueue_aux, &ab->reset_work))
> + queue_work(ab->workqueue_aux, &ab->reset_work)) {
> ath11k_err(wmi_ab->ab,
> "Firmware lockup detected. Resetting.");
> +
> + /* Assume that reset gets us out of lockup. */
> + ab->simulate_lockup = false;
> + }
> }
>
> if (ret == -ENOBUFS)
>
My 1st impression of this patch is that the datapath folks are not going to
like the ab->simulate_lockup checks in the hot path. But I'll let the
engineers speak for themselves.
/jeff
^ permalink raw reply
* Re: [PATCH RESEND RFC 1/3] net: ath11k: fix redundant reset from stale pending workqueue bit
From: Jeff Johnson @ 2026-05-12 23:09 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson; +Cc: linux-wireless, ath11k, linux-kernel, kernel
In-Reply-To: <20260330-ath11k-lockup-fixes-v1-1-7ed21095c2c4@collabora.com>
On 3/30/2026 3:05 AM, Matthew Leach wrote:
> During a firmware lockup, WMI commands time out in rapid succession,
> each calling queue_work() to schedule ath11k_core_reset(). This can
> cause a spurious extra reset after recovery completes:
>
> 1. First WMI timeout calls queue_work(), sets the pending bit and
> schedules ath11k_core_reset(). The workqueue clears the pending bit
> before invoking the work function. reset_count becomes 1 and the reset
> is kicked off asynchronously. ath11k_core_reset() returns.
>
> 2. Second WMI timeout calls queue_work() and re-queues the work. When it
> runs after step 1 returns, it sees reset_count > 1 and blocks in
> wait_for_completion(). The pending bit is again cleared.
>
> 3. Third WMI timeout calls queue_work(), the pending bit was cleared in
> step 2, so this succeeds and arms another execution.
>
> 4. The asynchronous reset finishes. ath11k_mac_op_reconfig_complete()
> decrements reset_count and calls complete(). The blocked worker from
> step 2 wakes, takes the early-exit path, and decrements reset_count to
> 0.
>
> 5. The workqueue sees the pending bit from step 3 and runs
> ath11k_core_reset() again. reset_count is 0, triggering a
> full redundant hardware reset.
>
> Fix this by calling cancel_work() on reset_work in
> ath11k_mac_op_reconfig_complete() before signalling completion. This
> clears any stale pending bit, preventing the spurious re-execution.
>
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
> drivers/net/wireless/ath/ath11k/mac.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index e4ee2ba1f669..748f779b3d1b 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -9274,6 +9274,10 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw,
> * the recovery has to be done for each radio
> */
> if (recovery_count == ab->num_radios) {
> + /* Cancel any pending work, preventing a second redudant
nits:
1) networking no longer uses a different block comment style so use the
standard style where /* is on a line by itself
2: s/redudant/redundant/ (subject has it right)
but don't post a new version just for these -- wait for any other comments.
I'm pinging the development team to look at this thread.
> + * reset.
> + */
> + cancel_work(&ab->reset_work);
> atomic_dec(&ab->reset_count);
> complete(&ab->reset_complete);
> ab->is_reset = false;
>
^ permalink raw reply
* Re: [PATCH] wifi: mt76: mt7925: Add coredump callback in pci_driver
From: Sean Wang @ 2026-05-12 21:30 UTC (permalink / raw)
To: JB Tsai
Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang, jeff.hsu
In-Reply-To: <20260505055252.2558613-1-jb.tsai@mediatek.com>
Hi,
On Tue, May 5, 2026 at 12:53 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> From: Jeff Hsu <Jeff.Hsu@mediatek.com>
>
> Register coredump callback(mt7925_coredump) in pci_driver,
> it can let platform can trigger core dump to collect the
> log and recover back after coredump complete without debugfs
>
> Some platform not have debugfs
>
> Signed-off-by: Jeff Hsu <Jeff.Hsu@mediatek.com>
> ---
> drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 1 +
> drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 12 ++++++++++++
> 2 files changed, 13 insertions(+)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
> index 37cdf3e8a067..cd502166a7f9 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
> @@ -1014,6 +1014,7 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd)
> return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG),
> &req, sizeof(req), false);
> }
> +EXPORT_SYMBOL_GPL(mt7925_mcu_chip_config);
>
> int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable)
> {
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
> index c4161754c01d..5b3e62e5f157 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
> @@ -610,6 +610,17 @@ static int mt7925_pci_restore(struct device *device)
> return _mt7925_pci_resume(device, true);
> }
>
> +static void mt7925_coredump(struct device *dev)
> +{
> + struct pci_dev *pdev = to_pci_dev(device);
There is a build error on this line.
../drivers/net/wireless/mediatek/mt76/mt7925/pci.c: In function
‘mt7925_coredump’:
../drivers/net/wireless/mediatek/mt76/mt7925/pci.c:682:43: error:
‘device’ undeclared (first use in this function)
682 | struct pci_dev *pdev = to_pci_dev(device);
| ^~~~~~
../include/linux/container_of.h:20:33: note: in definition of macro
‘container_of’
20 | void *__mptr = (void *)(ptr);
\
| ^~~
../drivers/net/wireless/mediatek/mt76/mt7925/pci.c:682:32: note: in
expansion of macro ‘to_pci_dev’
682 | struct pci_dev *pdev = to_pci_dev(device);
| ^~~~~~~~~~
../drivers/net/wireless/mediatek/mt76/mt7925/pci.c:682:43: note: each
undeclared identifier is reported only once for each function it
appears in
682 | struct pci_dev *pdev = to_pci_dev(device);
| ^~~~~~
../include/linux/container_of.h:20:33: note: in definition of macro
‘container_of’
20 | void *__mptr = (void *)(ptr);
> + struct mt76_dev *mdev = pci_get_drvdata(pdev);
> + struct mt792x_dev *dev_792x = container_of(mdev, struct mt792x_dev, mt76);
> +
> + mt792x_mutex_acquire(dev_792x);
> + mt7925_mcu_chip_config(dev_792x, "assert");
> + mt792x_mutex_release(dev_792x);
> +}
> +
> static const struct dev_pm_ops mt7925_pm_ops = {
> .suspend = pm_sleep_ptr(mt7925_pci_suspend),
> .resume = pm_sleep_ptr(mt7925_pci_resume),
> @@ -626,6 +637,7 @@ static struct pci_driver mt7925_pci_driver = {
> .remove = mt7925_pci_remove,
> .shutdown = mt7925_pci_shutdown,
> .driver.pm = pm_sleep_ptr(&mt7925_pm_ops),
> + .driver.coredump = mt7925_coredump,
> };
>
> module_pci_driver(mt7925_pci_driver);
> --
> 2.45.2
>
>
^ permalink raw reply
* Re: [PATCH v2 0/4] treewide: fixup gfp_t printks
From: Andrew Morton @ 2026-05-12 21:05 UTC (permalink / raw)
To: Brendan Jackman
Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Stanislaw Gruszka, Alexander Potapenko,
Marco Elver, Dmitry Vyukov, Allison Henderson, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
dri-devel, linux-kernel, linux-wireless, kasan-dev, linux-mm,
netdev, linux-rdma, rds-devel
In-Reply-To: <DIGPS83BGEGA.5I7VLH8TV7XE@google.com>
On Tue, 12 May 2026 12:58:20 +0000 Brendan Jackman <jackmanb@google.com> wrote:
> On Thu Mar 26, 2026 at 12:31 PM UTC, Brendan Jackman wrote:
> > This patchset used to be about switching gfp_t to unsigned long. That is
> > probably not gonna happen any more but while writing it I found these
> > cleanups that seem worthwhile regardless.
> >
>
> ...
>
> Any chance someone can take these?
[2/4] is merged into the wireless tree (I assume) and the other three
are in mm.git's mm-unstable branch.
^ permalink raw reply
* Re: (subset) [PATCH v5 0/3] ath10k: Introduce a devicetree quirk to skip host cap QMI requests
From: Bjorn Andersson @ 2026-05-12 20:22 UTC (permalink / raw)
To: Johannes Berg, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jeff Johnson, Konrad Dybcio, Paul Sajna, David Heidelberg
Cc: Baochen Qiang, Vasanthakumar Thiagarajan, Dmitry Baryshkov,
Amit Pundir, linux-wireless, devicetree, ath10k, linux-kernel,
linux-arm-msm, phone-devel
In-Reply-To: <20260407-skip-host-cam-qmi-req-v5-0-dfa8a05c6538@ixit.cz>
On Tue, 07 Apr 2026 08:43:53 +0200, David Heidelberg wrote:
> This quirk is used so far used on:
> - LG G7 ThinQ
> - Xiaomi Poco F1
>
> I'm resending it after ~ 4 years since initial send due to Snapdragon
> 845 being one of best supported platform for mobile phones running
> Linux, so it would be shame to not have shiny support.
>
> [...]
Applied, thanks!
[3/3] arm64: dts: qcom: sdm845-xiaomi-beryllium: Enable ath10k host-cap skip quirk
commit: 113343d57607937102d843775560fea0ce5cbe6f
Best regards,
--
Bjorn Andersson <andersson@kernel.org>
^ permalink raw reply
* Re: ath12k WCN7850: Q6 Hexagon fault at WLAON region 0x1792000 ~2s post-AUTHORIZE on X1E80100
From: Marcus Glocker @ 2026-05-12 19:59 UTC (permalink / raw)
To: Baochen Qiang, Maxim Storetvedt
Cc: ath12k, linux-wireless, jjohnson, kvalo, manivannan.sadhasivam,
Mark Kettenis
In-Reply-To: <0b450204-9afc-4ba1-b9c5-1876b5a7078a@oss.qualcomm.com>
[-- Attachment #1: Type: text/plain, Size: 4562 bytes --]
On Tue, May 12, 2026 at 11:38:06AM +0800, Baochen Qiang wrote:
>
>
> On 5/5/2026 5:08 AM, Marcus Glocker wrote:
> > Hi,
> >
> > We're porting ath12k to OpenBSD as the qwz(4) driver, targeting Samsung
> > Galaxy Book4 Edge (X1E80100 SoC, WCN7850 hw2.0). Scan, auth, 4-way
> > handshake all complete; ~2 seconds after WPA2 AUTHORIZE the WCN7850
> > firmware crashes deterministically with:
> >
> > dlpager_main.c:147 Non Page Fault Exception cause code 0x 23
> > at Address: 0x 1792000
> >
> > Cause code 0x23 isn't a valid arm64 exception -- the fault is on the
> > WCN7850's on-die Hexagon Q6 DSP, with QURT's generic exception handler
> > (which happens to live in dlpager_main.c) printing it. So this is not
> > a host CPU fault.
> >
> > Per the RDDM segment table (at the start of the dump), VA 0x01792000
> > is the start of the chip's WLAON_DUMP region (size 0x820). The Q6 is
> > trying to read its own always-on hardware state region and the chip
> > refuses the access.
> >
> > (Samsung, Asus, Honor) with multiple FW builds. Currently testing
> > with WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
> > (fw 0x110cffff, 2025-06-25) -- the exact blob a Linux ath12k user
> > runs successfully on the identical Samsung hardware. Same board-2.bin,
> > same compiled DTB (upstream hamoa.dtsi based).
> >
> > We've field-compared qwz against ath12k and ruled out (byte-level or
> > wire-level):
> >
> > * QMI host_cap, m3_info, wlan_cfg, wlan_ini, bdf_download (all
> > fields including ce_config, svc_to_ce_map, shadow_reg_v3,
> > feature_list, m3 paddr/size, nm_modem)
> > * MHI bringup ordering (BHI -> wait SBL EE -> wait M0 -> BHIE)
> > * BHI/BHIE DMA coherency
> > * ASPM disable before MHI start
> > * WLAON_WARM_SW_ENTRY zeroing + QFPROM_PWR_CTRL VDD4BLOW clear
> > * static_window_map=false + window-bank register init
> > * Per-chunk vs monolithic respond_mem allocation
> > * WMI_PEER_MIMO_PS_STATE = WMI_PEER_SMPS_PS_NONE (added matching
> > ath12k_setup_peer_smps; doesn't help)
> > * FW image variation (c5 and c7 both fail identically)
> >
> > Specifically NOT involved (we have evidence either way):
> >
> > * Gunyah -- X1E80100 is reportedly run in EL2 without Gunyah by
> > users where ath12k works; so Gunyah isn't programming WLAON
> > access for the Q6.
> > * SMMU / pcie_smmu -- pcie_smmu is status="reserved" upstream,
> > pcie4 has no iommus property; PCIe DMA bypasses SMMU.
> > * SCM/PAS -- ath12k's PCIe path makes no qcom_scm_* calls.
> >
> > Question: what subsystem inside the WCN7850 firmware touches the
> > WLAON region at 0x01792000 around 2 seconds after the host sends
> > WMI_PEER_AUTHORIZE? And what host-side configuration (WMI command,
> > HTT message, MHI state, etc.) primes that path so the access
> > succeeds on Linux?
> >
> > Even a pointer at the right Linux code path or the right FW-side
> > component would unblock us. We have full RDDM dumps and dmesg
> > captures available; happy to share off-list or as attachments.
>
> please help collect ath12k successful dmesg log and qwz failed dmesg log for compare.
>
> Please enable verbose ath12k log when loading ath12k driver:
>
> If you are using the latest upstream ath12k:
>
> sudo modprobe ath12k debug_mask=0xffffffff
> sudo modprobe ath12k_wifi7
>
> If you are using an old ath12k:
>
> sudo modprobe ath12k debug_mask=0xffffffff
>
> >
> > Thanks,
> > Marcus
> >
>
Hi Baochen,
Thanks for coming back on this topic.
Attached the OpenBSD dmesg, with full ath12k driver debug logging
enabled, plus the resulting RDDM binary after the firmware crash:
https://nazgul.ch/pub/qwz0-rddm.bin.gz
The command sequence on OpenBSD to re-produce that was:
ifconfig qwz0 up # Bring the ath12k device up
ifconfig qwz0 scan # Scan for networks
ifconfig qwz0 nwid nazgul wpakey xxx # Start association
Hi Max,
Since you have Linux running on exactly the same Samsung Galaxy Book4
Edge 14" laptop, where ath12k works, would you be so kind and also
provide the dmesg output showing an successful association with the
ath12k driver debug logging enabled? See above how to enable that.
That would be very helpful!
Thanks and Regards,
Marcus
[-- Attachment #2: dmesg.gz --]
[-- Type: application/x-gunzip, Size: 83823 bytes --]
^ permalink raw reply
* [PATCH wireless-next] wifi: mac80211_hwsim: advertise NPCA capability
From: Miri Korenblit @ 2026-05-12 19:47 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This doesn't really do anything, but we can advertise it and then
check that the AP can be configured and client can connect, etc.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/virtual/mac80211_hwsim_main.c | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index dc9775cd9f54..67867c0fcc24 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -4554,6 +4554,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -4667,6 +4670,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_2ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -4841,6 +4847,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -4971,6 +4980,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_5ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -5169,6 +5181,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -5320,6 +5335,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
@@ -5416,6 +5434,9 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
},
.uhr_cap = {
.has_uhr = true,
+ .mac.mac_cap = {
+ [0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP,
+ },
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
IEEE80211_UHR_PHY_CAP_ELR_TX,
},
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: Add KUnit tests for channel-load thresholds
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Avinash Bhatt <avinash.bhatt@intel.com>
Add a KUnit test suite for iwl_mld_chan_load_requires_scan,
covering level-up, level-down, and no-change transitions.
The test directly sets channel-load values, validating
scan-trigger decisions and updated load levels
Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/link.c | 1 +
.../wireless/intel/iwlwifi/mld/tests/Makefile | 1 +
.../iwlwifi/mld/tests/chan_load_thresh.c | 139 ++++++++++++++++++
3 files changed, 141 insertions(+)
create mode 100644 drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index c07a0d6b0bd4..805f2e2eac38 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -852,6 +852,7 @@ bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
return scan_trig;
}
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_chan_load_requires_scan);
static unsigned int
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile
index 36317feb923b..efa61638b8ee 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o
+iwlmld-tests-y += chan_load_thresh.o
ccflags-y += -I$(src)/../
obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c
new file mode 100644
index 000000000000..87e29e09949b
--- /dev/null
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/chan_load_thresh.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * KUnit tests for channel helper functions
+ *
+ * Copyright (C) 2026 Intel Corporation
+ */
+#include <kunit/static_stub.h>
+#include "mld.h"
+#include "link.h"
+#include "iface.h"
+#include "utils.h"
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+struct test_chan_load_case {
+ const char *desc;
+ u32 load;
+ enum iwl_mld_link_chan_load_level old_lvl;
+ enum iwl_mld_link_chan_load_level expected_lvl;
+ bool expected_scan_trig;
+};
+
+static const struct test_chan_load_case test_chan_load_thresh_cases[] = {
+ /* Level-up transitions */
+ {
+ .desc = "Transition NONE->NONE",
+ .load = 20,
+ .old_lvl = LINK_CHAN_LOAD_LVL_NONE,
+ .expected_lvl = LINK_CHAN_LOAD_LVL_NONE,
+ .expected_scan_trig = false,
+ },
+ {
+ .desc = "Transition NONE->LVL1",
+ .load = 50,
+ .old_lvl = LINK_CHAN_LOAD_LVL_NONE,
+ .expected_lvl = LINK_CHAN_LOAD_LVL1,
+ .expected_scan_trig = true,
+ },
+ {
+ .desc = "Transition LVL1->LVL2",
+ .load = 75,
+ .old_lvl = LINK_CHAN_LOAD_LVL1,
+ .expected_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_scan_trig = true,
+ },
+ {
+ .desc = "Transition LVL2->LVL3",
+ .load = 90,
+ .old_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_lvl = LINK_CHAN_LOAD_LVL3,
+ .expected_scan_trig = true,
+ },
+
+ /* Level-down transitions */
+ {
+ .desc = "Transition LVL1->NONE",
+ .load = 30,
+ .old_lvl = LINK_CHAN_LOAD_LVL1,
+ .expected_lvl = LINK_CHAN_LOAD_LVL_NONE,
+ .expected_scan_trig = false,
+ },
+ {
+ .desc = "Transition LVL2->LVL1",
+ .load = 60,
+ .old_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_lvl = LINK_CHAN_LOAD_LVL1,
+ .expected_scan_trig = false,
+ },
+ {
+ .desc = "Transition LVL3->LVL2",
+ .load = 70,
+ .old_lvl = LINK_CHAN_LOAD_LVL3,
+ .expected_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_scan_trig = false,
+ },
+
+ /* No change */
+ {
+ .desc = "Transition LVL2->LVL2",
+ .load = 72,
+ .old_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_lvl = LINK_CHAN_LOAD_LVL2,
+ .expected_scan_trig = false,
+ },
+};
+
+KUNIT_ARRAY_PARAM_DESC(test_chan_load_thresh_cases,
+ test_chan_load_thresh_cases, desc);
+
+static void test_chan_load_thresholds(struct kunit *test)
+{
+ const struct test_chan_load_case *tc = test->param_value;
+ struct iwl_mld *mld = test->priv;
+ struct ieee80211_vif *vif;
+ struct iwl_mld_vif *mld_vif;
+ struct ieee80211_bss_conf *link_conf;
+ struct iwl_mld_link *mld_link;
+ struct iwl_mld_kunit_link assoc_link = {
+ .id = 0,
+ .chandef = &chandef_6ghz_160mhz,
+ };
+ bool scan_trig;
+ u32 chan_load;
+
+ /* Setup associated non-MLO station */
+ vif = iwlmld_kunit_setup_non_mlo_assoc(&assoc_link);
+ mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ link_conf = &vif->bss_conf;
+ mld_link = &mld_vif->deflink;
+
+ chan_load = tc->load;
+ mld_link->chan_load_lvl = tc->old_lvl;
+
+ /* Execute function under test */
+ wiphy_lock(mld->wiphy);
+ scan_trig = iwl_mld_chan_load_requires_scan(mld, link_conf, chan_load);
+ wiphy_unlock(mld->wiphy);
+
+ /* Check return value */
+ KUNIT_EXPECT_EQ(test, tc->expected_scan_trig, scan_trig);
+
+ /* Check updated channel-load level */
+ KUNIT_EXPECT_EQ(test, tc->expected_lvl, mld_link->chan_load_lvl);
+}
+
+static struct kunit_case chan_load_thresh_test_cases[] = {
+ KUNIT_CASE_PARAM(test_chan_load_thresholds,
+ test_chan_load_thresh_cases_gen_params),
+ {}
+};
+
+static struct kunit_suite chan_load_thresh_test_suite = {
+ .name = "iwl_mld_chan_load_threshold_tests",
+ .init = iwlmld_kunit_test_init,
+ .test_cases = chan_load_thresh_test_cases,
+};
+
+kunit_test_suite(chan_load_thresh_test_suite);
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 14/15] wifi: iwlwifi: mld: Add support for multiple NAN Management stations
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Ilan Peer
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Ilan Peer <ilan.peer@intel.com>
The following limitations arise from the current FW support for NAN:
- While NAN synchronization and discovery beacons are sent
internally by the firmware, the BIGTK is configured to the
auxiliary station associated with the NAN operation. Thus,
the beacons are transmitted unprotected.
- The auxiliary station cannot be configured with support for
management frame protection as this is not supported by the
firmware. Thus, there is no way to protect the SDFs and the
NAFs.
To overcome the above limitations the firmware introduced the
following new station types:
- NAN broadcast station: Used for NAN synchronization and
discovery. i.e., used for beacon transmissions. A BIGTK
can be configured to this station and thus beacons can
be transmitted with protection.
- NAN management station: Used for sending SDFs and NAFs.
This station can be configured with support for management
frame protection etc.
Modify the iwlmld logic to support the older and the newer
firmware designs. As no Tx queue is needed for the NAN
broadcast station, modify the internal station support
to allow adding/removing a station without a queue. In
addition, since no links are associated with these stations,
modify the internal station support to allow adding a station
without a link mask.
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 15 ++-
.../net/wireless/intel/iwlwifi/mld/iface.c | 8 ++
.../net/wireless/intel/iwlwifi/mld/iface.h | 6 +
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 123 +++++++++++++++---
drivers/net/wireless/intel/iwlwifi/mld/nan.h | 2 +
drivers/net/wireless/intel/iwlwifi/mld/sta.c | 69 +++++++---
drivers/net/wireless/intel/iwlwifi/mld/sta.h | 13 ++
drivers/net/wireless/intel/iwlwifi/mld/tx.c | 5 +-
8 files changed, 200 insertions(+), 41 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index dd850aeebdec..1df81df5830e 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -735,6 +735,10 @@ struct iwl_link_config_cmd {
* @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
* of this type can have any number of links (even none) set in the
* link_mask. (Supported since version 3.)
+ * @STATION_TYPE_NAN_BCAST: NAN station used for synchronization and
+ * discovery. No queue is associated with this station.
+ * @STATION_TYPE_NAN_MGMT: NAN station used for NAN management frames, e.g.,
+ * SDFs and NAFs.
* @STATION_TYPE_MAX: maximum number of FW station types
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
* for the aux sta, so this type is only for driver - internal use.
@@ -745,6 +749,8 @@ enum iwl_fw_sta_type {
STATION_TYPE_MCAST,
STATION_TYPE_NAN_PEER_NMI,
STATION_TYPE_NAN_PEER_NDI,
+ STATION_TYPE_NAN_BCAST,
+ STATION_TYPE_NAN_MGMT,
STATION_TYPE_MAX,
STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
}; /* STATION_TYPE_E_VER_1, _VER_2 */
@@ -882,7 +888,9 @@ struct iwl_sta_cfg_cmd_v2 {
* ( STA_CONFIG_CMD = 0xA )
*
* @sta_id: index of station in uCode's station table
- * @link_mask: bitmap of link FW IDs used with this STA
+ * @link_mask: bitmap of link FW IDs used with this STA. Should be set to 0
+ * for STATION_TYPE_NAN_BCAST and STATION_TYPE_NAN_MGMT as they are not
+ * associated with any link added by the driver.
* @peer_mld_address: the peers mld address
* @reserved_for_peer_mld_address: reserved
* @peer_link_address: the address of the link that is used to communicate
@@ -1213,7 +1221,8 @@ enum iwl_nan_flags {
* @discovery_beacon_interval: discovery beacon interval in TUs
* @cluster_id: lower last two bytes of the cluster ID, in case the local
* device starts a cluster
- * @sta_id: station ID of the NAN station
+ * @sta_id: station ID of the NAN station. Used only in version 1, in version 2
+ * it is reserved.
* @hb_channel: channel for 5 GHz if the device supports operation on 5 GHz.
* Valid values are 44 and 149, which correspond to the 5 GHz channel, and
* 0 which means that NAN operation on the 5 GHz band is disabled.
@@ -1251,7 +1260,7 @@ struct iwl_nan_config_cmd {
__le32 nan_attr_len;
__le32 nan_vendor_elems_len;
u8 beacon_data[];
-} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */
+} __packed; /* NAN_CONFIG_CMD_API_S_VER_1, NAN_CONFIG_CMD_API_S_VER_2 */
/**
* struct iwl_nan_schedule_cmd_v1 - NAN schedule command
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 6f2590f9a69b..2a270d689de8 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -61,6 +61,11 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
/* Clean up NAN links */
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++)
iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]);
+
+ if (mld_vif->nan.bcast_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->nan.bcast_sta);
+ if (mld_vif->nan.mgmt_sta.sta_id != IWL_INVALID_STA)
+ iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta);
}
CLEANUP_STRUCT(mld_vif);
@@ -526,6 +531,9 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i]));
mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID;
}
+
+ iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta);
+ iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta);
}
iwl_mld_init_internal_sta(&mld_vif->aux_sta);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index d6fb58e785e5..75b6727503d3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -155,6 +155,10 @@ struct iwl_mld_emlsr {
* @nan: NAN parameters
* @nan.links: NAN links for FW (indexed by FW link ID)
* @nan.mac_added: track whether or not the MAC was added to FW
+ * @nan.bcast_sta: internal station used for NAN synchronization and discovery
+ * activities. No queue is associated with it.
+ * @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs
+ * and NAFs.
*/
struct iwl_mld_vif {
/* Add here fields that need clean up on restart */
@@ -181,6 +185,8 @@ struct iwl_mld_vif {
/* use only with wiphy protection */
struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS];
bool mac_added;
+ struct iwl_mld_int_sta bcast_sta;
+ struct iwl_mld_int_sta mgmt_sta;
} nan;
struct iwl_mld_emlsr emlsr;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index 53a0c3b9bb33..351c726be51f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -50,6 +50,98 @@ static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
return iwl_mld_send_cmd(mld, &hcmd);
}
+static bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld)
+{
+ /*
+ * If the FW supports version 1 of the NAN config command, it means that
+ * it needs to receive the station ID of the auxiliary station in the
+ * NAN configuration command. Otherwise, use the NAN dedicated station
+ * types.
+ */
+ return iwl_fw_lookup_cmd_ver(mld->fw,
+ WIDE_ID(MAC_CONF_GROUP,
+ NAN_CFG_CMD), 1) != 1;
+}
+
+static const struct iwl_mld_int_sta *
+iwl_mld_nan_get_mgmt_sta(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ const struct iwl_mld_int_sta *sta;
+
+ if (iwl_mld_nan_use_nan_stations(mld))
+ sta = &mld_vif->nan.mgmt_sta;
+ else
+ sta = &mld_vif->aux_sta;
+
+ if (WARN_ON(sta->sta_id == IWL_INVALID_STA))
+ return NULL;
+
+ return sta;
+}
+
+int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
+
+ if (!sta)
+ return IWL_MLD_INVALID_QUEUE;
+
+ return sta->queue_id;
+}
+
+static void iwl_mld_nan_flush(struct iwl_mld *mld, struct ieee80211_vif *vif)
+{
+ const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
+
+ if (!sta)
+ return;
+
+ if (WARN_ON(sta->queue_id == IWL_MLD_INVALID_QUEUE))
+ return;
+
+ IWL_DEBUG_INFO(mld, "NAN: flush queues for sta=%u\n",
+ sta->sta_id);
+
+ iwl_mld_flush_link_sta_txqs(mld, sta->sta_id);
+}
+
+static void iwl_mld_nan_remove_stations(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+ iwl_mld_nan_flush(mld, vif);
+
+ if (!iwl_mld_nan_use_nan_stations(mld)) {
+ iwl_mld_remove_aux_sta(mld, vif);
+ return;
+ }
+
+ iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+ iwl_mld_remove_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
+}
+
+static int iwl_mld_nan_add_stations(struct iwl_mld *mld,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ int ret;
+
+ if (!iwl_mld_nan_use_nan_stations(mld))
+ return iwl_mld_add_aux_sta(mld, &mld_vif->aux_sta);
+
+ ret = iwl_mld_add_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+ if (ret)
+ return ret;
+
+ ret = iwl_mld_add_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
+ if (ret)
+ iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
+
+ return ret;
+}
+
static int iwl_mld_nan_config(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf,
@@ -126,7 +218,12 @@ static int iwl_mld_nan_config(struct iwl_mld *mld,
conf->vendor_elems_len);
}
- cmd.sta_id = mld_vif->aux_sta.sta_id;
+ /* FW needs to know about the station ID only with version 1 of the
+ * NAN configuration command
+ */
+ if (!iwl_mld_nan_use_nan_stations(mld))
+ cmd.sta_id = mld_vif->aux_sta.sta_id;
+
return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
conf->extra_nan_attrs_len +
conf->vendor_elems_len);
@@ -136,8 +233,6 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct cfg80211_nan_conf *conf)
{
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
- struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
- struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta;
int ret;
IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
@@ -146,19 +241,20 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (ret)
return ret;
- ret = iwl_mld_add_aux_sta(mld, aux_sta);
+ ret = iwl_mld_nan_add_stations(mld, vif);
if (ret)
goto unblock_emlsr;
ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
if (ret) {
IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
- goto remove_aux;
+ goto remove_stas;
}
+
return 0;
-remove_aux:
- iwl_mld_remove_aux_sta(mld, vif);
+remove_stas:
+ iwl_mld_nan_remove_stations(mld, vif);
unblock_emlsr:
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
@@ -186,7 +282,6 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
- struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
struct iwl_nan_config_cmd cmd = {
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
};
@@ -203,9 +298,7 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
/* assume that higher layer guarantees that no additional frames are
* added before calling this callback
*/
- if (!WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
- iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
- iwl_mld_remove_aux_sta(mld, vif);
+ iwl_mld_nan_remove_stations(mld, vif);
/* cancel based on object type being NAN, as the NAN objects do
* not have a unique identifier associated with them
@@ -279,13 +372,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
"NAN: DW end without NAN started\n"))
return;
- if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
- return;
-
- IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
- mld_vif->aux_sta.sta_id);
-
- iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
+ iwl_mld_nan_flush(mld, mld->nan_device_vif);
/* TODO: currently the notification specified the band on which the DW
* ended. Need to change that to the actual channel on which the next DW
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.h b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
index 80e18c4ddb33..caa98dbb4a75 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.h
@@ -53,4 +53,6 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
struct ieee80211_sta *sta);
+int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif);
+
#endif /* __iwl_mld_nan_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
index f794f80b0fdd..1fae5a6ba8d4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c
@@ -430,6 +430,8 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
} else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
+ cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_BCAST) &&
+ cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_MGMT) &&
hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
return -EINVAL;
}
@@ -1063,8 +1065,7 @@ static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld,
static int
iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
const struct iwl_mld_int_sta *internal_sta,
- u8 fw_link_id,
- const u8 *addr)
+ u32 link_mask, const u8 *addr)
{
struct iwl_sta_cfg_cmd cmd = {};
@@ -1072,7 +1073,7 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
- cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
+ cmd.link_mask = cpu_to_le32(link_mask);
cmd.station_type = cpu_to_le32(internal_sta->sta_type);
/* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP.
@@ -1094,7 +1095,8 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta,
enum iwl_fw_sta_type sta_type,
- u8 fw_link_id, const u8 *addr, u8 tid)
+ u32 link_mask, const u8 *addr,
+ u8 tid, bool add_txq)
{
int ret, queue_id;
@@ -1106,11 +1108,14 @@ static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
internal_sta->sta_type = sta_type;
- ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
+ ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, link_mask,
addr);
if (ret)
goto err;
+ if (!add_txq)
+ return 0;
+
queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid);
if (queue_id < 0) {
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
@@ -1145,8 +1150,8 @@ int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
STATION_TYPE_BCAST_MGMT,
- mld_link->fw_id, addr,
- IWL_MGMT_TID);
+ BIT(mld_link->fw_id), addr,
+ IWL_MGMT_TID, true);
}
int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
@@ -1165,14 +1170,16 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
STATION_TYPE_MCAST,
- mld_link->fw_id, mcast_addr, 0);
+ BIT(mld_link->fw_id), mcast_addr,
+ 0, true);
}
int iwl_mld_add_aux_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta)
{
return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX,
- 0, NULL, IWL_MAX_TID_COUNT);
+ 0, NULL, IWL_MAX_TID_COUNT,
+ true);
}
int iwl_mld_add_mon_sta(struct iwl_mld *mld,
@@ -1189,23 +1196,25 @@ int iwl_mld_add_mon_sta(struct iwl_mld *mld,
return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta,
STATION_TYPE_BCAST_MGMT,
- mld_link->fw_id, NULL,
- IWL_MAX_TID_COUNT);
+ BIT(mld_link->fw_id), NULL,
+ IWL_MAX_TID_COUNT,
+ true);
}
static void iwl_mld_remove_internal_sta(struct iwl_mld *mld,
struct iwl_mld_int_sta *internal_sta,
bool flush, u8 tid)
{
- if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA ||
- internal_sta->queue_id == IWL_MLD_INVALID_QUEUE))
+ if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA))
return;
- if (flush)
+ if (flush && !WARN_ON_ONCE(internal_sta->queue_id ==
+ IWL_MLD_INVALID_QUEUE))
iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id);
- iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
- tid, internal_sta->queue_id);
+ if (internal_sta->queue_id != IWL_MLD_INVALID_QUEUE)
+ iwl_mld_free_txq(mld, BIT(internal_sta->sta_id),
+ tid, internal_sta->queue_id);
iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id);
@@ -1406,3 +1415,31 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
return ret;
}
+
+int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_BCAST,
+ 0, bcast_addr, 0, false);
+}
+
+int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_MGMT,
+ 0, NULL, IWL_MAX_TID_COUNT, true);
+}
+
+void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ iwl_mld_remove_internal_sta(mld, sta, false, 0);
+}
+
+void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta)
+{
+ iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT);
+}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
index dff14ff0a5af..df859a9e5230 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/sta.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h
@@ -276,4 +276,17 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
u16 old_links, u16 new_links);
+
+int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
+int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
+void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
+void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
+ struct iwl_mld_int_sta *sta);
+
#endif /* __iwl_mld_sta_h__ */
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
index 636d24633e57..39e1d959e42c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
@@ -680,11 +680,8 @@ iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq,
WARN_ON(!ieee80211_is_mgmt(fc));
return mld_vif->aux_sta.queue_id;
case NL80211_IFTYPE_NAN:
- mld_vif = iwl_mld_vif_from_mac80211(info->control.vif);
-
WARN_ON(!ieee80211_is_mgmt(fc));
-
- return mld_vif->aux_sta.queue_id;
+ return iwl_mld_nan_get_mgmt_queue(mld, info->control.vif);
default:
WARN_ONCE(1, "Unsupported vif type\n");
break;
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 13/15] wifi: iwlwifi: mld: add support for nan schedule config command version 2
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Avraham Stern
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Avraham Stern <avraham.stern@intel.com>
Version 2 of the NAN schedule config command adds support for deferred
schedule update and passing the availability attribute blob to
firmware.
Add support for the new command version. Currently the new
functionality is not supported.
Signed-off-by: Avraham Stern <avraham.stern@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 40 ++++++++++++++++++-
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 22 ++++++++--
2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
index 75b477319096..dd850aeebdec 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h
@@ -1253,6 +1253,27 @@ struct iwl_nan_config_cmd {
u8 beacon_data[];
} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */
+/**
+ * struct iwl_nan_schedule_cmd_v1 - NAN schedule command
+ * @channels: per channel information
+ * @channels.availability_map: bitmap of slots this channel is advertising
+ * availability on, will be ULW'ed out if no link/inactive link is
+ * referenced by the link ID below
+ * @channels.channel_entry: NAN channel entry descriptor
+ * @channels.link_id: FW link ID, or %0xFF for unset
+ * @channels.reserved: (reserved)
+ */
+struct iwl_nan_schedule_cmd_v1 {
+ struct {
+ __le32 availability_map;
+ u8 channel_entry[6];
+ u8 link_id;
+ u8 reserved;
+ } __packed channels[NUM_PHY_CTX];
+} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_1 */
+
+#define IWL_MAX_AVAILABILITY_ATTR_LEN 54
+
/**
* struct iwl_nan_schedule_cmd - NAN schedule command
* @channels: per channel information
@@ -1262,6 +1283,14 @@ struct iwl_nan_config_cmd {
* @channels.channel_entry: NAN channel entry descriptor
* @channels.link_id: FW link ID, or %0xFF for unset
* @channels.reserved: (reserved)
+ * @avail_attr: NAN availability attribute information
+ * @avail_attr.attr_len: length of the availability attribute
+ * @avail_attr.reserved: reserved
+ * @avail_attr.attr: the availability attribute including the attribute header
+ * @deferred: true if the firmware should defer applying the schedule until
+ * notifying all peers. For a deferred schedule update, the firmware should
+ * send a notification to the driver after the new schedule is applied.
+ * @reserved: reserved
*/
struct iwl_nan_schedule_cmd {
struct {
@@ -1270,7 +1299,16 @@ struct iwl_nan_schedule_cmd {
u8 link_id;
u8 reserved;
} __packed channels[NUM_PHY_CTX];
-} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_1 */
+
+ struct {
+ u8 attr_len;
+ u8 reserved;
+ u8 attr[IWL_MAX_AVAILABILITY_ATTR_LEN];
+ } __packed avail_attr;
+
+ u8 deferred;
+ u8 reserved[3];
+} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_2 */
/**
* struct iwl_nan_peer_cmd - NAN peer command
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index 913f0acf172a..53a0c3b9bb33 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -546,11 +546,27 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
bool added_links = false;
bool empty_schedule = true;
int ret, i;
+ u16 cmd_size;
+ u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD);
+ u8 version = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED))
return;
- for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+ switch (version) {
+ case 1:
+ cmd_size = sizeof(struct iwl_nan_schedule_cmd_v1);
+ break;
+ case 2:
+ cmd_size = sizeof(struct iwl_nan_schedule_cmd);
+ break;
+ default:
+ IWL_ERR(mld, "NAN: unsupported NAN schedule cmd version %d\n",
+ version);
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
if (!sched_cfg->channels[i].chanreq.oper.chan)
continue;
empty_schedule = false;
@@ -647,9 +663,7 @@ void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
cmd.channels[chan_idx].availability_map |= cpu_to_le32(BIT(i));
}
- ret = iwl_mld_send_cmd_pdu(mld,
- WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD),
- &cmd);
+ ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size);
if (ret)
IWL_ERR(mld, "NAN: failed to update schedule (%d)\n", ret);
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 12/15] wifi: iwlwifi: mld: add duplicated beacon RSSI adjustment
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Avinash Bhatt <avinash.bhatt@intel.com>
For 6 GHz duplicated beacons, the RSSI is measured only on the 20 MHz
primary channel while the actual beacon energy spans the full
operational bandwidth. This leads to underestimated link quality.
Detect duplicated beacons and apply bandwidth-based RSSI adjustments.
Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/link.c | 67 ++++++++++++++++++-
1 file changed, 64 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index a8d146edc4bd..c07a0d6b0bd4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -894,6 +894,60 @@ iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
}
+static s8
+iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf)
+{
+ const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
+ const struct cfg80211_bss_ies *beacon_ies;
+ const struct element *elem;
+
+ /* Duplicated beacon feature is only specific to 6 GHz */
+ if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ,
+ "Unexpected band %d\n",
+ link_conf->chanreq.oper.chan->band))
+ return 0;
+
+ lockdep_assert_wiphy(mld->wiphy);
+
+ beacon_ies = wiphy_dereference(mld->wiphy, link_conf->bss->beacon_ies);
+ if (!beacon_ies)
+ return 0;
+
+ elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION,
+ beacon_ies->data, beacon_ies->len);
+ if (!elem ||
+ elem->datalen < sizeof(struct ieee80211_he_operation) + 1 ||
+ elem->datalen < ieee80211_he_oper_size(&elem->data[1]))
+ return 0;
+
+ he_6ghz_oper = ieee80211_he_6ghz_oper((const void *)&elem->data[1]);
+ if (!he_6ghz_oper)
+ return 0;
+
+ if (!(he_6ghz_oper->control & IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON))
+ return 0;
+
+ /* Apply adjustment based on operational bandwidth */
+ switch (link_conf->chanreq.oper.width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ return 0;
+ case NL80211_CHAN_WIDTH_40:
+ return 3;
+ case NL80211_CHAN_WIDTH_80:
+ return 6;
+ case NL80211_CHAN_WIDTH_160:
+ return 9;
+ case NL80211_CHAN_WIDTH_320:
+ return 12;
+ default:
+ WARN_ONCE(1, "Unexpected channel width: %d\n",
+ link_conf->chanreq.oper.width);
+ return 0;
+ }
+}
+
/* This function calculates the grade of a link. Returns 0 in error case */
unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf)
@@ -903,7 +957,7 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
s32 link_rssi;
unsigned int grade = MAX_GRADE;
- if (WARN_ON_ONCE(!link_conf))
+ if (WARN_ON_ONCE(!link_conf || !link_conf->bss))
return 0;
band = link_conf->chanreq.oper.chan->band;
@@ -918,8 +972,15 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
* For 6 GHz the RSSI of the beacons is lower than
* the RSSI of the data.
*/
- if (band == NL80211_BAND_6GHZ && link_rssi)
- link_rssi += 4;
+ if (band == NL80211_BAND_6GHZ && link_rssi) {
+ s8 rssi_adj_6g =
+ iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf);
+
+ if (!rssi_adj_6g)
+ rssi_adj_6g = 4;
+
+ link_rssi += rssi_adj_6g;
+ }
rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 11/15] wifi: iwlwifi: implement the new RSC notification
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Add support for a new RSC notification that arrives on DATA queues.
The same RSC handling previously done in the WOWLAN flow is now done
through this notification, with backward compatibility maintained.
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/intel/iwlwifi/fw/api/datapath.h | 9 +-
drivers/net/wireless/intel/iwlwifi/mld/d3.c | 148 ++++++++++++++++--
drivers/net/wireless/intel/iwlwifi/mld/d3.h | 6 +-
.../net/wireless/intel/iwlwifi/mld/iface.c | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 +
.../net/wireless/intel/iwlwifi/mld/notif.c | 6 +
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 22 +++
drivers/net/wireless/intel/iwlwifi/mld/rx.h | 5 +-
8 files changed, 183 insertions(+), 18 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
index 06370c161fe4..e494e5b18d22 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
* Copyright (C) 2012-2014, 2018-2022 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
@@ -91,6 +91,13 @@ enum iwl_data_path_subcmd_ids {
*/
SEC_KEY_CMD = 0x18,
+ /**
+ * @RSC_NOTIF: notification to update each Rx queue with the RSC. This
+ * notification is sent after resume and uses
+ * &struct iwl_wowlan_all_rsc_tsc_v5.
+ */
+ RSC_NOTIF = 0xF1,
+
/**
* @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode,
* uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index c44f02f225ce..ca4222a9a6ff 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -43,6 +43,12 @@ struct iwl_mld_resume_key_iter_data {
struct iwl_mld_wowlan_status *wowlan_status;
};
+struct iwl_mld_rsc_resume_iter_data {
+ struct iwl_mld *mld;
+ const struct iwl_wowlan_all_rsc_tsc_v5 *notif;
+ int queue;
+};
+
struct iwl_mld_suspend_key_iter_data {
struct iwl_wowlan_rsc_tsc_params_cmd *rsc;
bool have_rsc;
@@ -282,7 +288,8 @@ static void
iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
struct iwl_mld_wowlan_status *wowlan_status,
const struct iwl_wowlan_gtk_status *gtk_data,
- const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
+ const struct iwl_wowlan_all_rsc_tsc_v5 *sc,
+ int rsc_notif_ver)
{
int status_idx = 0;
@@ -305,14 +312,18 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
wowlan_status->gtk[status_idx].id =
wowlan_status->gtk[status_idx].flags &
IWL_WOWLAN_GTK_IDX_MASK;
- /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc
- * The gtk ids can be any two numbers between 0 and 3,
- * the id_map maps between the key id and the index in sc->mcast
- */
- rsc_idx =
- sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
- iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
- sc, rsc_idx);
+ /* If RSC_NOTIF is not supported */
+ if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN) {
+ /* The rsc for both gtk keys are stored in
+ * gtk[0]->sc->mcast_rsc. The gtk ids can be any two
+ * numbers between 0 and 3, the id_map maps between the
+ * key id and the index in sc->mcast
+ */
+ rsc_idx =
+ sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id];
+ iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx],
+ sc, rsc_idx);
+ }
if (key_status == IWL_WOWLAN_STATUS_NEW_KEY) {
memcpy(wowlan_status->gtk[status_idx].key,
@@ -598,6 +609,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
PROT_OFFLOAD_GROUP,
WOWLAN_INFO_NOTIFICATION,
IWL_FW_CMD_VER_UNKNOWN);
+ int rsc_notif_ver = iwl_fw_lookup_notif_ver(mld->fw,
+ DATA_PATH_GROUP,
+ RSC_NOTIF,
+ IWL_FW_CMD_VER_UNKNOWN);
if (wowlan_info_ver == 5) {
/* v5 format - validate before conversion */
@@ -642,8 +657,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
return true;
iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk,
- ¬if->gtk[0].sc);
- iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc);
+ ¬if->gtk[0].sc, rsc_notif_ver);
+ if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN)
+ iwl_mld_convert_ptk_resume_seq(mld, wowlan_status,
+ ¬if->gtk[0].sc);
/* only one igtk is passed by FW */
iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]);
iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk);
@@ -902,8 +919,14 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
struct iwl_mld_resume_key_iter_data *data = _data;
struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status;
u8 status_idx;
-
- if (key->keyidx >= 0 && key->keyidx <= 3) {
+ int rsc_notif_ver = iwl_fw_lookup_notif_ver(data->mld->fw,
+ DATA_PATH_GROUP,
+ RSC_NOTIF,
+ IWL_FW_CMD_VER_UNKNOWN);
+
+ /* If RSC_NOTIF is not supported */
+ if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN &&
+ key->keyidx >= 0 && key->keyidx <= 3) {
/* PTK */
if (sta) {
iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
@@ -932,6 +955,105 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
}
}
+static void
+iwl_mld_rsc_update_key_iter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key,
+ void *_data)
+{
+ struct iwl_mld_rsc_resume_iter_data *data = _data;
+ struct ieee80211_key_seq seq;
+
+ if (key->keyidx > 3)
+ return;
+
+ if (sta) {
+ /* PTK */
+ BUILD_BUG_ON(ARRAY_SIZE(data->notif->ucast_rsc) !=
+ IWL_MAX_TID_COUNT);
+
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
+ /* TKIP: just update key sequences */
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ iwl_mld_le64_to_tkip_seq(data->notif->ucast_rsc[tid],
+ &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ }
+ } else {
+ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_ptk_pn *mld_ptk_pn =
+ rcu_dereference_wiphy(data->mld->wiphy,
+ mld_sta->ptk_pn[key->keyidx]);
+
+ if (WARN_ON(!mld_ptk_pn))
+ return;
+
+ if (WARN_ON(data->queue >=
+ data->mld->trans->info.num_rxqs))
+ return;
+
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ iwl_mld_le64_to_aes_seq(data->notif->ucast_rsc[tid],
+ &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ memcpy(mld_ptk_pn->q[data->queue].pn[tid],
+ seq.ccmp.pn,
+ IEEE80211_CCMP_PN_LEN);
+ }
+ }
+
+ IWL_DEBUG_WOWLAN(data->mld,
+ "Updated PTK RSC for key %d on queue %d\n",
+ key->keyidx, data->queue);
+ } else {
+ /* GTK */
+ int rsc_idx = data->notif->mcast_key_id_map[key->keyidx];
+
+ if (rsc_idx == IWL_MCAST_KEY_MAP_INVALID)
+ return;
+
+ if (IWL_FW_CHECK(data->mld,
+ rsc_idx >= ARRAY_SIZE(data->notif->mcast_rsc),
+ "Invalid mcast key mapping: %d for key %d\n",
+ rsc_idx, key->keyidx))
+ return;
+
+ for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
+ __le64 rsc =
+ data->notif->mcast_rsc[rsc_idx][tid];
+
+ if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ iwl_mld_le64_to_tkip_seq(rsc, &seq);
+ else
+ iwl_mld_le64_to_aes_seq(rsc, &seq);
+ ieee80211_set_key_rx_seq(key, tid, &seq);
+ }
+
+ IWL_DEBUG_WOWLAN(data->mld,
+ "Updated GTK %d RSC (rsc_idx %d) on queue %d\n",
+ key->keyidx, rsc_idx, data->queue);
+ }
+}
+
+void
+iwl_mld_process_rsc_notification(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
+ int queue)
+{
+ struct iwl_mld_rsc_resume_iter_data iter_data = {
+ .mld = mld,
+ .notif = notif,
+ .queue = queue,
+ };
+
+ /* Iterate through all active keys and update RSC */
+ ieee80211_iter_keys_rcu(mld->hw, vif,
+ iwl_mld_rsc_update_key_iter,
+ &iter_data);
+}
+
static void
iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.h b/drivers/net/wireless/intel/iwlwifi/mld/d3.h
index 618d6fb3c796..c2e8ba877042 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024, 2026 Intel Corporation
*/
#ifndef __iwl_mld_d3_h__
#define __iwl_mld_d3_h__
@@ -42,6 +42,10 @@ int iwl_mld_wowlan_resume(struct iwl_mld *mld);
void iwl_mld_set_rekey_data(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_gtk_rekey_data *data);
+void iwl_mld_process_rsc_notification(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
+ int queue);
#if IS_ENABLED(CONFIG_IPV6)
void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 4fe57d79daa6..6f2590f9a69b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -853,6 +853,6 @@ struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)
fw_id = __ffs(fw_id_bitmap);
- return wiphy_dereference(mld->wiphy,
- mld->fw_id_to_vif[fw_id]);
+ return rcu_dereference_wiphy(mld->wiphy,
+ mld->fw_id_to_vif[fw_id]);
}
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 25bab6ab6375..3caa76b9b2cb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -260,6 +260,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),
HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
HCMD_NAME(SEC_KEY_CMD),
+ HCMD_NAME(RSC_NOTIF),
HCMD_NAME(ESR_MODE_NOTIF),
HCMD_NAME(MONITOR_NOTIF),
HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 1c81152042ab..14834395de3a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -599,6 +599,9 @@ void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
RX_QUEUES_NOTIFICATION)))
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
+ else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
+ RSC_NOTIF)))
+ iwl_mld_handle_rsc_notif(mld, pkt, 0);
else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, PHY_AIR_SNIFFER_NOTIF))
iwl_mld_handle_phy_air_sniffer_notif(mld, napi, pkt);
else
@@ -622,6 +625,9 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue);
else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))
iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue);
+ else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
+ RSC_NOTIF)))
+ iwl_mld_handle_rsc_notif(mld, pkt, queue);
}
void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 01603dc07f0a..7e023fe51e3b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -2260,6 +2260,28 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
wake_up(&mld->rxq_sync.waitq);
}
+void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt, int queue)
+{
+ const struct iwl_wowlan_all_rsc_tsc_v5 *notif = (void *)pkt->data;
+ u32 len = iwl_rx_packet_payload_len(pkt);
+ struct ieee80211_vif *bss_vif;
+
+ if (IWL_FW_CHECK(mld, len != sizeof(*notif),
+ "invalid notification size %u (%zu)\n",
+ len, sizeof(*notif)))
+ return;
+
+ /* for the bss lookup and updating the keys' pn */
+ guard(rcu)();
+
+ bss_vif = iwl_mld_get_bss_vif(mld);
+ if (WARN_ON(!bss_vif))
+ return;
+
+ iwl_mld_process_rsc_notification(mld, bss_vif, notif, queue);
+}
+
static void iwl_mld_no_data_rx(struct iwl_mld *mld,
struct napi_struct *napi,
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
index 09dddbd40f55..f7ef70e69143 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#ifndef __iwl_mld_rx_h__
#define __iwl_mld_rx_h__
@@ -61,6 +61,9 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
struct napi_struct *napi,
struct iwl_rx_packet *pkt, int queue);
+void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
+ struct iwl_rx_packet *pkt, int queue);
+
void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
struct napi_struct *napi,
struct sk_buff *skb, int queue,
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 10/15] wifi: iwlwifi: mld: add chan-load hysteresis for MLO scan triggers
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Avinash Bhatt <avinash.bhatt@intel.com>
Introduce a three-level, table-driven hysteresis mechanism for
deciding when to trigger internal MLO scans based on MCLM Chan
Load. Prevents repeated triggers under fluctuating load conditions.
Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/link.c | 61 +++++++++++++++++++
drivers/net/wireless/intel/iwlwifi/mld/link.h | 15 +++++
drivers/net/wireless/intel/iwlwifi/mld/mlo.c | 29 ++++++++-
.../net/wireless/intel/iwlwifi/mld/stats.c | 4 ++
4 files changed, 106 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index 9e40b334ee1f..a8d146edc4bd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -16,6 +16,23 @@
#include "fw/api/context.h"
#include "fw/dbg.h"
+/**
+ * struct iwl_mld_link_chan_load_threshold - channel load thresholds
+ * @high_lim: level up transition thresholds, in percentage
+ * @low_lim: level down transition thresholds, in percentage
+ */
+struct iwl_mld_link_chan_load_threshold {
+ u8 high_lim;
+ u8 low_lim;
+};
+
+static const struct iwl_mld_link_chan_load_threshold
+link_chan_load_thresh_tbl[] = {
+ [LINK_CHAN_LOAD_LVL1] = { .high_lim = 45, .low_lim = 40 },
+ [LINK_CHAN_LOAD_LVL2] = { .high_lim = 70, .low_lim = 65 },
+ [LINK_CHAN_LOAD_LVL3] = { .high_lim = 85, .low_lim = 80 },
+};
+
int iwl_mld_send_link_cmd(struct iwl_mld *mld,
struct iwl_link_config_cmd *cmd,
enum iwl_ctxt_action action)
@@ -792,6 +809,50 @@ int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
return chan_load;
}
+/* Returns whether internal MLO Scan needs to be triggered */
+bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ u32 new_chan_load)
+{
+ struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
+ enum iwl_mld_link_chan_load_level new_lvl;
+ bool scan_trig = false;
+
+ if (WARN_ON(!mld_link))
+ return false;
+
+ /* For each Level,
+ * First check if high limit threshold crosses
+ * If not then, check if low limit threshold crosses
+ * Set new level based on low limit thresh only if old level
+ * is not lower than level threshold
+ */
+ for (new_lvl = LINK_CHAN_LOAD_LVL_MAX;
+ new_lvl > LINK_CHAN_LOAD_LVL_NONE; new_lvl--) {
+ if (new_chan_load >=
+ link_chan_load_thresh_tbl[new_lvl].high_lim)
+ break;
+ if (new_chan_load >=
+ link_chan_load_thresh_tbl[new_lvl].low_lim &&
+ mld_link->chan_load_lvl >= new_lvl)
+ break;
+ }
+
+ /* Trigger scan only for Level Up Transition */
+ if (new_lvl > mld_link->chan_load_lvl)
+ scan_trig = true;
+
+ IWL_DEBUG_EHT(mld,
+ "Link %d: chan_load=%d%%, old_lvl=%d, new_lvl=%d, scan_trig=%d\n",
+ link_conf->link_id, new_chan_load,
+ mld_link->chan_load_lvl, new_lvl, scan_trig);
+
+ /* Update computed new level */
+ mld_link->chan_load_lvl = new_lvl;
+
+ return scan_trig;
+}
+
static unsigned int
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 2b3e6b55367f..4527f054ce92 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -10,6 +10,14 @@
#include "mld.h"
#include "sta.h"
+enum iwl_mld_link_chan_load_level {
+ LINK_CHAN_LOAD_LVL_NONE,
+ LINK_CHAN_LOAD_LVL1,
+ LINK_CHAN_LOAD_LVL2,
+ LINK_CHAN_LOAD_LVL3,
+ LINK_CHAN_LOAD_LVL_MAX = LINK_CHAN_LOAD_LVL3
+};
+
/**
* struct iwl_probe_resp_data - data for NoA/CSA updates
* @rcu_head: used for freeing the data on update
@@ -50,6 +58,8 @@ struct iwl_probe_resp_data {
* @silent_deactivation: next deactivation needs to be silent.
* @probe_resp_data: data from FW notification to store NOA related data to be
* inserted into probe response.
+ * @chan_load_lvl: current channel load level for a link, computed based on
+ * channel load by others on a link.
*/
struct iwl_mld_link {
struct rcu_head rcu_head;
@@ -63,6 +73,7 @@ struct iwl_mld_link {
bool he_ru_2mhz_block;
struct ieee80211_key_conf *tx_igtk;
struct ieee80211_key_conf __rcu *bigtks[2];
+ enum iwl_mld_link_chan_load_level chan_load_lvl;
);
/* And here fields that survive a fw restart */
struct iwl_mld_int_sta bcast_sta;
@@ -135,6 +146,10 @@ int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
struct ieee80211_bss_conf *link_conf,
bool expect_active_link);
+bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
+ struct ieee80211_bss_conf *link_conf,
+ u32 new_chan_load);
+
void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index 8227ccb31d60..2a3b2c883fc4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include "mlo.h"
#include "phy.h"
@@ -1081,8 +1081,13 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
container_of((const void *)phy, struct ieee80211_chanctx_conf,
drv_priv);
struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld;
- struct ieee80211_bss_conf *prim_link;
+ u32 new_chan_load = phy->avg_channel_load_not_by_us;
+ struct ieee80211_bss_conf *prim_link, *link_conf;
unsigned int prim_link_id;
+ int link_id;
+
+ if (!ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) <= 1)
+ return;
prim_link_id = iwl_mld_get_primary_link(vif);
prim_link = link_conf_dereference_protected(vif, prim_link_id);
@@ -1090,6 +1095,25 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
if (WARN_ON(!prim_link))
return;
+ /* Evaluate MLO Internal Scan for high chan load beyond thresholds */
+ for_each_vif_active_link(vif, link_conf, link_id) {
+ if (rcu_access_pointer(link_conf->chanctx_conf) != chanctx)
+ continue;
+
+ if (iwl_mld_chan_load_requires_scan(mld,
+ link_conf,
+ new_chan_load)) {
+ /* When EMLSR is active, only trigger scan based on
+ * primary link
+ */
+ if (iwl_mld_emlsr_active(vif) && link_conf != prim_link)
+ continue;
+
+ iwl_mld_int_mlo_scan(mld, vif);
+ return;
+ }
+ }
+
if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
return;
@@ -1107,7 +1131,6 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
prim_link_id);
} else {
u32 old_chan_load = data->prev_chan_load_not_by_us;
- u32 new_chan_load = phy->avg_channel_load_not_by_us;
u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx);
#define THRESHOLD_CROSSED(threshold) \
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
index 6e826797f637..b93e0f8ab5fb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/stats.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c
@@ -515,6 +515,10 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw,
(old_load >> 1);
}
+ IWL_DEBUG_EHT(phy->mld,
+ "PHY %d: load_by_us=%u%% load_not_by_us=%u%%\n",
+ phy->fw_id, phy->channel_load_by_us, new_load);
+
iwl_mld_emlsr_check_chan_load(hw, phy, old_load);
}
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 09/15] wifi: iwlwifi: mld: validate aux sta before flush in stop_nan
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Gabay, Ilan Peer
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Daniel Gabay <daniel.gabay@intel.com>
iwl_mld_stop_nan() calls iwl_mld_flush_link_sta_txqs() without checking
that aux_sta.sta_id is valid. The DW end handler correctly guards this
with a WARN_ON check. Add the same defensive check to stop_nan for
consistency and to avoid sending a flush command with an invalid sta_id.
Fixes: 600941464991 ("wifi: iwlwifi: mld: Declare support for NAN capabilities")
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index deb72e401e3c..913f0acf172a 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -203,7 +203,8 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
/* assume that higher layer guarantees that no additional frames are
* added before calling this callback
*/
- iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
+ if (!WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
+ iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
iwl_mld_remove_aux_sta(mld, vif);
/* cancel based on object type being NAN, as the NAN objects do
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 08/15] wifi: iwlwifi: mld: add NULL check for channel in DW end handler
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Gabay, Ilan Peer
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Daniel Gabay <daniel.gabay@intel.com>
ieee80211_get_channel() can return NULL if the frequency is not
registered in the wiphy (e.g. due to regulatory domain restrictions).
The returned channel pointer is passed directly to
cfg80211_next_nan_dw_notif() which dereferences it unconditionally
in both the tracepoint and the netlink message, causing a NULL
pointer dereference.
Add a NULL check before using the channel pointer.
Fixes: 600941464991 ("wifi: iwlwifi: mld: Declare support for NAN capabilities")
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Reviewed-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index 264ea7a9a896..deb72e401e3c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -305,6 +305,9 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
return;
}
+ if (WARN_ON_ONCE(!chan))
+ return;
+
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
}
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 07/15] wifi: iwlwifi: mld: fix NAN DW end notification handler
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Gabay
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Daniel Gabay <daniel.gabay@intel.com>
Use IWL_DEBUG_INFO instead of IWL_INFO for logging the DW end
notification, as this is a recurring event during NAN operation
and should not spam the kernel log.
Also fix a coding style issue - missing space after 'if'.
Fixes: 9e978d8ebbe9 ("wifi: iwlwifi: mld: Add support for NAN")
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/nan.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/nan.c b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
index cb7a3e01adb4..264ea7a9a896 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/nan.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/nan.c
@@ -269,7 +269,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
struct wireless_dev *wdev;
struct ieee80211_channel *chan;
- IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
+ IWL_DEBUG_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
return;
--
2.34.1
^ permalink raw reply related
* [PATCH iwlwifi-next 06/15] wifi: iwlwifi: Transition to basic uAPSD with MAC_PM_POWER_TABLE API VER_3
From: Miri Korenblit @ 2026-05-12 19:34 UTC (permalink / raw)
To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260512193441.2352467-1-miriam.rachel.korenblit@intel.com>
From: Avinash Bhatt <avinash.bhatt@intel.com>
uAPSD is transitioning to a certification-only feature. The new
firmware API version 3 removes advanced uAPSD fields, keeping only
basic parameters needed for certification testing.
Support the new VER_3 API in the MLD driver while maintaining
backward compatibility with VER_1/2. The MVM driver continues
using VER_2 only. Remove the obsolete PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION
notification from the MLD driver
Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/intel/iwlwifi/fw/api/power.h | 48 ++++-
.../net/wireless/intel/iwlwifi/mld/iface.c | 18 --
.../net/wireless/intel/iwlwifi/mld/iface.h | 3 -
drivers/net/wireless/intel/iwlwifi/mld/mld.c | 1 -
.../net/wireless/intel/iwlwifi/mld/notif.c | 6 -
.../net/wireless/intel/iwlwifi/mld/power.c | 177 ++++++++++++++++--
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 +-
.../net/wireless/intel/iwlwifi/mvm/power.c | 14 +-
8 files changed, 214 insertions(+), 57 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
index a3f916630df2..115e65ba19f8 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -85,12 +85,13 @@ struct iwl_ltr_config_cmd {
* '1' PM could sleep over DTIM till listen Interval.
* @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
* access categories are both delivery and trigger enabled.
+ * (Not supported since version 3)
* @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
* PBW Snoozing enabled
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
* @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
- * detection enablement
+ * detection enablement (Not supported since version 3)
* @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif
*/
enum iwl_power_flags {
@@ -175,9 +176,9 @@ struct iwl_device_power_cmd {
} __packed;
/**
- * struct iwl_mac_power_cmd - New power command containing uAPSD support
+ * struct iwl_mac_power_cmd_v2 - power command V2 containing uAPSD support
* MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
- * @id_and_color: MAC contex identifier, &enum iwl_ctxt_id_and_color
+ * @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
* @flags: Power table command flags from POWER_FLAGS_*
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
* Minimum allowed:- 3 * DTIM. Keep alive period must be
@@ -216,7 +217,7 @@ struct iwl_device_power_cmd {
* @limited_ps_threshold: (unused)
* @reserved: reserved (padding)
*/
-struct iwl_mac_power_cmd {
+struct iwl_mac_power_cmd_v2 {
/* CONTEXT_DESC_API_T_VER_1 */
__le32 id_and_color;
@@ -242,6 +243,43 @@ struct iwl_mac_power_cmd {
u8 reserved;
} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */
+/**
+ * struct iwl_mac_power_cmd - power command
+ * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
+ * @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
+ * @flags: Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ * Minimum allowed:- 3 * DTIM. Keep alive period must be
+ * set regardless of power scheme or current power state.
+ * FW use this value also when PM is disabled.
+ * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - legacy PM
+ * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - legacy PM
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ * Default: 80dbm
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ * is set. For example, if it is required to skip over
+ * one DTIM, this value need to be set to 2 (DTIM periods).
+ * @qndp_tid: TID client shall use for uAPSD QNDP triggers
+ * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
+ * each corresponding AC.
+ * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
+ */
+struct iwl_mac_power_cmd {
+ /* CONTEXT_DESC_API_T_VER_1 */
+ __le32 id_and_color;
+
+ __le16 flags;
+ __le16 keep_alive_seconds;
+ __le32 rx_data_timeout;
+ __le32 tx_data_timeout;
+ u8 lprx_rssi_threshold;
+ u8 skip_dtim_periods;
+ u8 qndp_tid;
+ u8 uapsd_ac_flags;
+} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_3 */
+
/*
* struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
* associated AP is identified as improperly implementing uAPSD protocol.
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
index 5fc3f6729455..4fe57d79daa6 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c
@@ -739,24 +739,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
kfree_rcu(old_data, rcu_head);
}
-void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
- struct iwl_rx_packet *pkt)
-{
- struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
- struct ieee80211_vif *vif;
-
- if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif),
- "mac id is invalid: %d\n", notif->mac_id))
- return;
-
- vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]);
-
- if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif))
- return;
-
- IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid);
-}
-
void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt)
{
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
index 71c794204475..d6fb58e785e5 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/iface.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h
@@ -274,9 +274,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld,
struct iwl_rx_packet *pkt);
-void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld,
- struct iwl_rx_packet *pkt);
-
void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld,
struct ieee80211_vif *vif);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 9a3b768fce7a..25bab6ab6375 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -162,7 +162,6 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
HCMD_NAME(PHY_CONFIGURATION_CMD),
HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
HCMD_NAME(POWER_TABLE_CMD),
- HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
HCMD_NAME(BEACON_NOTIFICATION),
HCMD_NAME(BEACON_TEMPLATE_CMD),
HCMD_NAME(TX_ANT_CONFIGURATION_CMD),
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index 4e9f3768c381..1c81152042ab 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -339,8 +339,6 @@ CMD_VERSIONS(emlsr_mode_notif,
CMD_VER_ENTRY(2, iwl_esr_mode_notif))
CMD_VERSIONS(emlsr_trans_fail_notif,
CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))
-CMD_VERSIONS(uapsd_misbehaving_ap_notif,
- CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif))
CMD_VERSIONS(time_msmt_notif,
CMD_VER_ENTRY(1, iwl_time_msmt_notify))
CMD_VERSIONS(time_sync_confirm_notif,
@@ -363,8 +361,6 @@ DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, uid)
DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,
mac_id)
-DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,
- mac_id)
DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)
DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id)
@@ -455,8 +451,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
emlsr_mode_notif, RX_HANDLER_ASYNC)
RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF,
emlsr_trans_fail_notif, RX_HANDLER_ASYNC)
- RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
- uapsd_misbehaving_ap_notif)
RX_HANDLER_NO_OBJECT(LEGACY_GROUP,
WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION,
time_msmt_notif, RX_HANDLER_SYNC)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c
index fe71da8b9c89..da065a446f81 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c
@@ -88,10 +88,10 @@ static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR;
}
-static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
- struct iwl_mld_link *link,
- struct iwl_mac_power_cmd *cmd,
- bool ps_poll)
+static void iwl_mld_power_configure_uapsd_v2(struct iwl_mld *mld,
+ struct iwl_mld_link *link,
+ struct iwl_mac_power_cmd_v2 *cmd,
+ bool ps_poll)
{
bool tid_found = false;
@@ -150,10 +150,54 @@ static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
}
+static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
+ struct iwl_mld_link *link,
+ struct iwl_mac_power_cmd *cmd,
+ bool ps_poll)
+{
+ bool tid_found = false;
+
+ /* set advanced pm flag with no uapsd ACs to enable ps-poll */
+ if (ps_poll) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+ return;
+ }
+
+ for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO;
+ ac <= IEEE80211_AC_BK;
+ ac++) {
+ if (!link->queue_params[ac].uapsd)
+ continue;
+
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+ cmd->uapsd_ac_flags |= BIT(ac);
+
+ /* QNDP TID - the highest TID with no admission control */
+ if (!tid_found && !link->queue_params[ac].acm) {
+ tid_found = true;
+ switch (ac) {
+ case IEEE80211_AC_VO:
+ cmd->qndp_tid = 6;
+ break;
+ case IEEE80211_AC_VI:
+ cmd->qndp_tid = 5;
+ break;
+ case IEEE80211_AC_BE:
+ cmd->qndp_tid = 0;
+ break;
+ case IEEE80211_AC_BK:
+ cmd->qndp_tid = 1;
+ break;
+ }
+ }
+ }
+}
+
static void
iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
const struct ieee80211_bss_conf *link_conf,
- struct iwl_mac_power_cmd *cmd)
+ u8 *skip_dtim_periods, __le16 *flags)
{
unsigned int dtimper_tu;
unsigned int dtimper;
@@ -171,15 +215,15 @@ iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
/* configure skip over dtim up to 900 TU DTIM interval */
skip = max_t(int, 1, 900 / dtimper_tu);
- cmd->skip_dtim_periods = skip;
- cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+ *skip_dtim_periods = skip;
+ *flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
}
#define POWER_KEEP_ALIVE_PERIOD_SEC 25
-static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
- struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd,
- bool d3)
+static void iwl_mld_power_build_cmd_v2(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd_v2 *cmd,
+ bool d3)
{
int dtimper, bi;
int keep_alive;
@@ -238,7 +282,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
}
if (d3) {
- iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
+ iwl_mld_power_config_skip_dtim(mld, link_conf,
+ &cmd->skip_dtim_periods,
+ &cmd->flags);
cmd->rx_data_timeout =
cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
cmd->tx_data_timeout =
@@ -259,6 +305,95 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
* mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd
* which will look at what mac80211 is saying.
*/
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ ps_poll = mld_vif->use_ps_poll;
+#endif
+ iwl_mld_power_configure_uapsd_v2(mld, link, cmd, ps_poll);
+}
+
+static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd,
+ bool d3)
+{
+ int dtimper, bi;
+ int keep_alive;
+ struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
+ struct ieee80211_bss_conf *link_conf = &vif->bss_conf;
+ struct iwl_mld_link *link = &mld_vif->deflink;
+ bool ps_poll = false;
+ __le32 fw_id = cpu_to_le32(mld_vif->fw_id);
+
+ if (ieee80211_vif_is_mld(vif)) {
+ int link_id;
+
+ if (WARN_ON(!vif->active_links))
+ return;
+
+ /* The firmware consumes one single configuration for the vif
+ * and can't differentiate between links, just pick the lowest
+ * link_id's configuration and use that.
+ */
+ link_id = __ffs(vif->active_links);
+ link_conf = link_conf_dereference_check(vif, link_id);
+ link = iwl_mld_link_dereference_check(mld_vif, link_id);
+
+ if (WARN_ON(!link_conf || !link))
+ return;
+ }
+ dtimper = link_conf->dtim_period;
+ bi = link_conf->beacon_int;
+
+ /* Regardless of power management state the driver must set
+ * keep alive period. FW will use it for sending keep alive NDPs
+ * immediately after association. Check that keep alive period
+ * is at least 3 * DTIM
+ */
+ keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
+ USEC_PER_SEC);
+ keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
+
+ cmd->id_and_color = fw_id;
+ cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
+
+ if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
+
+ if (vif->cfg.ps && iwl_mld_tdls_sta_count(mld) == 0) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK);
+
+ /* firmware supports LPRX for beacons at rate 1 Mbps or
+ * 6 Mbps only
+ */
+ if (link_conf->beacon_rate &&
+ (link_conf->beacon_rate->bitrate == 10 ||
+ link_conf->beacon_rate->bitrate == 60)) {
+ cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
+ cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
+ }
+ }
+
+ if (d3) {
+ iwl_mld_power_config_skip_dtim(mld, link_conf,
+ &cmd->skip_dtim_periods,
+ &cmd->flags);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT);
+ } else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) {
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT);
+ } else {
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT);
+ }
+
#ifdef CONFIG_IWLWIFI_DEBUGFS
ps_poll = mld_vif->use_ps_poll;
#endif
@@ -268,11 +403,23 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
bool d3)
{
- struct iwl_mac_power_cmd cmd = {};
+ int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0);
- iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
+ if (cmd_ver >= 3) {
+ struct iwl_mac_power_cmd cmd = {};
- return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
+ iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
+ return iwl_mld_send_cmd_with_flags_pdu(mld,
+ MAC_PM_POWER_TABLE, 0,
+ &cmd, sizeof(cmd));
+ } else {
+ struct iwl_mac_power_cmd_v2 cmd = {};
+
+ iwl_mld_power_build_cmd_v2(mld, vif, &cmd, d3);
+ return iwl_mld_send_cmd_with_flags_pdu(mld,
+ MAC_PM_POWER_TABLE, 0,
+ &cmd, sizeof(cmd));
+ }
}
static void
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 402ba5dee8b2..be89b84204fb 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2016-2017 Intel Deutschland GmbH
*/
@@ -469,7 +469,7 @@ struct iwl_mvm_vif {
struct dentry *dbgfs_slink;
struct iwl_dbgfs_pm dbgfs_pm;
struct iwl_dbgfs_bf dbgfs_bf;
- struct iwl_mac_power_cmd mac_pwr_cmd;
+ struct iwl_mac_power_cmd_v2 mac_pwr_cmd;
int dbgfs_quota_min;
bool ftm_unprotected;
#endif
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
index 610de29b7be0..46792c508753 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -83,7 +83,7 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
}
static void iwl_mvm_power_log(struct iwl_mvm *mvm,
- struct iwl_mac_power_cmd *cmd)
+ struct iwl_mac_power_cmd_v2 *cmd)
{
IWL_DEBUG_POWER(mvm,
"Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
@@ -121,7 +121,7 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd)
+ struct iwl_mac_power_cmd_v2 *cmd)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
enum ieee80211_ac_numbers ac;
@@ -296,7 +296,7 @@ static bool iwl_mvm_power_is_radar(struct ieee80211_bss_conf *link_conf)
static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd)
+ struct iwl_mac_power_cmd_v2 *cmd)
{
struct ieee80211_bss_conf *link_conf;
unsigned int min_link_skip = ~0;
@@ -344,7 +344,7 @@ static void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct iwl_mac_power_cmd *cmd)
+ struct iwl_mac_power_cmd_v2 *cmd)
{
int dtimper, bi;
int keep_alive;
@@ -466,7 +466,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
- struct iwl_mac_power_cmd cmd = {};
+ struct iwl_mac_power_cmd_v2 cmd = {};
iwl_mvm_power_build_cmd(mvm, vif, &cmd);
iwl_mvm_power_log(mvm, &cmd);
@@ -717,7 +717,7 @@ int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
int bufsz)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- struct iwl_mac_power_cmd cmd = {};
+ struct iwl_mac_power_cmd_v2 cmd = {};
int pos = 0;
mutex_lock(&mvm->mutex);
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox