* [PATCH 2/2] wifi: ath12k: skip unknown direct buffer ring module IDs
From: Nazar Mokrynskyi @ 2026-05-05 17:24 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, jeff.johnson, Nazar Mokrynskyi
In-Reply-To: <20260505172415.566328-1-nazar@mokrynskyi.com>
The firmware may advertise direct buffer ring capabilities for module
IDs beyond what the driver currently knows about (WMI_DIRECT_BUF_MAX).
This happens with newer firmware versions that support additional ring
types not yet implemented in the driver.
The current code treats an unknown module_id as a fatal error, returning
-EINVAL and tearing down the entire driver initialization. This is
incorrect: the driver only needs to set up rings for types it uses
(SPECTRAL=0, CFR=1) and can safely ignore capability advertisements for
unknown types.
Change the unknown module_id handling to skip the entry with a debug
message rather than failing, allowing initialization to proceed.
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Signed-off-by: Nazar Mokrynskyi <nazar@mokrynskyi.com>
---
drivers/net/wireless/ath/ath12k/wmi.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 484fdd3b1..0e776a8d8 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -4814,10 +4814,10 @@ static int ath12k_wmi_dma_ring_caps(struct ath12k_base *ab,
dir_buff_caps = ab->db_caps;
for (i = 0; i < dma_caps_parse->n_dma_ring_caps; i++) {
if (le32_to_cpu(dma_caps[i].module_id) >= WMI_DIRECT_BUF_MAX) {
- ath12k_warn(ab, "Invalid module id %d\n",
- le32_to_cpu(dma_caps[i].module_id));
- ret = -EINVAL;
- goto free_dir_buff;
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "Skipping unknown direct buf ring module id %d\n",
+ le32_to_cpu(dma_caps[i].module_id));
+ continue;
}
dir_buff_caps[i].id = le32_to_cpu(dma_caps[i].module_id);
@@ -4829,10 +4829,6 @@ static int ath12k_wmi_dma_ring_caps(struct ath12k_base *ab,
}
return 0;
-
-free_dir_buff:
- ath12k_wmi_free_dbring_caps(ab);
- return ret;
}
static void
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 02/16] firmware: qcom: Add a generic PAS service
From: Mukesh Ojha @ 2026-05-05 17:32 UTC (permalink / raw)
To: Sumit Garg
Cc: andersson, konradybcio, linux-arm-msm, devicetree, dri-devel,
freedreno, linux-media, netdev, linux-wireless, ath12k,
linux-remoteproc, robh, krzk+dt, conor+dt, robin.clark, sean,
akhilpo, lumag, abhinav.kumar, jesszhan0024, marijn.suijten,
airlied, simona, vikash.garodia, dikshita.agarwal, bod, mchehab,
elder, andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
mathieu.poirier, trilokkumar.soni, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jens.wiklander, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Harshal Dev
In-Reply-To: <20260504130603.1474043-3-sumit.garg@kernel.org>
On Mon, May 04, 2026 at 06:35:49PM +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> Qcom platforms has the legacy of using non-standard SCM calls
> splintered over the various kernel drivers. These SCM calls aren't
> compliant with the standard SMC calling conventions which is a
> prerequisite to enable migration to the FF-A specifications from Arm.
>
> OP-TEE as an alternative trusted OS to Qualcomm TEE (QTEE) can't
> support these non-standard SCM calls. And even for newer architectures
> using S-EL2 with Hafnium support, QTEE won't be able to support SCM
> calls either with FF-A requirements coming in. And with both OP-TEE
> and QTEE drivers well integrated in the TEE subsystem, it makes further
> sense to reuse the TEE bus client drivers infrastructure.
>
> The added benefit of TEE bus infrastructure is that there is support
> for discoverable/enumerable services. With that client drivers don't
> have to manually invoke a special SCM call to know the service status.
>
> So enable the generic Peripheral Authentication Service (PAS) provided
> by the firmware. It acts as the common layer with different TZ
> backends plugged in whether it's an SCM implementation or a proper
> TEE bus based PAS service implementation.
>
> Reviewed-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
> Tested-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> # Lemans
> Reviewed-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> drivers/firmware/qcom/Kconfig | 8 +
> drivers/firmware/qcom/Makefile | 1 +
> drivers/firmware/qcom/qcom_pas.c | 291 +++++++++++++++++++++++++
> drivers/firmware/qcom/qcom_pas.h | 50 +++++
> include/linux/firmware/qcom/qcom_pas.h | 43 ++++
> 5 files changed, 393 insertions(+)
> create mode 100644 drivers/firmware/qcom/qcom_pas.c
> create mode 100644 drivers/firmware/qcom/qcom_pas.h
> create mode 100644 include/linux/firmware/qcom/qcom_pas.h
>
> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> index b477d54b495a..8653639d06db 100644
> --- a/drivers/firmware/qcom/Kconfig
> +++ b/drivers/firmware/qcom/Kconfig
> @@ -6,6 +6,14 @@
>
> menu "Qualcomm firmware drivers"
>
> +config QCOM_PAS
> + tristate
> + help
> + Enable the generic Peripheral Authentication Service (PAS) provided
> + by the firmware. It acts as the common layer with different TZ
> + backends plugged in whether it's an SCM implementation or a proper
> + TEE bus based PAS service implementation.
> +
> config QCOM_SCM
> select QCOM_TZMEM
> tristate
> diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
> index 0be40a1abc13..dc5ab45f906a 100644
> --- a/drivers/firmware/qcom/Makefile
> +++ b/drivers/firmware/qcom/Makefile
> @@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
> obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o
> obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o
> obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
> +obj-$(CONFIG_QCOM_PAS) += qcom_pas.o
> diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c
> new file mode 100644
> index 000000000000..025308adf553
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.c
> @@ -0,0 +1,291 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015 Linaro Ltd.
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/device/devres.h>
> +#include <linux/firmware/qcom/qcom_pas.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include "qcom_pas.h"
> +
> +static struct qcom_pas_ops *ops_ptr;
> +
> +/**
> + * devm_qcom_pas_context_alloc() - Allocate peripheral authentication service
> + * context for a given peripheral
> + *
> + * PAS context is device-resource managed, so the caller does not need
> + * to worry about freeing the context memory.
> + *
> + * @dev: PAS firmware device
> + * @pas_id: peripheral authentication service id
> + * @mem_phys: Subsystem reserve memory start address
> + * @mem_size: Subsystem reserve memory size
> + *
> + * Return: The new PAS context, or ERR_PTR() on failure.
> + */
> +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
> + u32 pas_id,
> + phys_addr_t mem_phys,
> + size_t mem_size)
> +{
> + struct qcom_pas_context *ctx;
> +
> + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> + if (!ctx)
> + return ERR_PTR(-ENOMEM);
> +
> + ctx->dev = dev;
> + ctx->pas_id = pas_id;
> + ctx->mem_phys = mem_phys;
> + ctx->mem_size = mem_size;
> +
> + return ctx;
> +}
> +EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc);
> +
> +/**
> + * qcom_pas_init_image() - Initialize peripheral authentication service state
> + * machine for a given peripheral, using the metadata
> + * @pas_id: peripheral authentication service id
> + * @metadata: pointer to memory containing ELF header, program header table
> + * and optional blob of data used for authenticating the metadata
> + * and the rest of the firmware
> + * @size: size of the metadata
> + * @ctx: optional pas context
> + *
> + * Return: 0 on success.
> + *
> + * Upon successful return, the PAS metadata context (@ctx) will be used to
> + * track the metadata allocation, this needs to be released by invoking
> + * qcom_pas_metadata_release() by the caller.
> + */
> +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> + struct qcom_pas_context *ctx)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> +
> + return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_init_image);
> +
> +/**
> + * qcom_pas_metadata_release() - release metadata context
> + * @ctx: pas context
> + */
> +void qcom_pas_metadata_release(struct qcom_pas_context *ctx)
> +{
> + if (!ops_ptr || !ctx || !ctx->ptr)
> + return;
> +
> + ops_ptr->metadata_release(ops_ptr->dev, ctx);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_metadata_release);
> +
> +/**
> + * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral
> + * for firmware loading
> + * @pas_id: peripheral authentication service id
> + * @addr: start address of memory area to prepare
> + * @size: size of the memory area to prepare
> + *
> + * Return: 0 on success.
> + */
> +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> +
> + return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_mem_setup);
> +
> +/**
> + * qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
> + * for a given peripheral.
> + *
> + * Qualcomm remote processor may rely on both static and dynamic resources for
> + * its functionality. Static resources typically refer to memory-mapped
> + * addresses required by the subsystem and are often embedded within the
> + * firmware binary and dynamic resources, such as shared memory in DDR etc.,
> + * are determined at runtime during the boot process.
> + *
> + * On Qualcomm Technologies devices, it's possible that static resources are
> + * not embedded in the firmware binary and instead are provided by TrustZone.
> + * However, dynamic resources are always expected to come from TrustZone. This
> + * indicates that for Qualcomm devices, all resources (static and dynamic) will
> + * be provided by TrustZone PAS service.
> + *
> + * If the remote processor firmware binary does contain static resources, they
> + * should be passed in input_rt. These will be forwarded to TrustZone for
> + * authentication. TrustZone will then append the dynamic resources and return
> + * the complete resource table in output_rt_tzm.
> + *
> + * If the remote processor firmware binary does not include a resource table,
> + * the caller of this function should set input_rt as NULL and input_rt_size
> + * as zero respectively.
> + *
> + * More about documentation on resource table data structures can be found in
> + * include/linux/remoteproc.h
> + *
> + * @ctx: PAS context
> + * @pas_id: peripheral authentication service id
> + * @input_rt: resource table buffer which is present in firmware binary
> + * @input_rt_size: size of the resource table present in firmware binary
> + * @output_rt_size: TrustZone expects caller should pass worst case size for
> + * the output_rt_tzm.
> + *
> + * Return:
> + * On success, returns a pointer to the allocated buffer containing the final
> + * resource table and output_rt_size will have actual resource table size from
> + * TrustZone. The caller is responsible for freeing the buffer. On failure,
> + * returns ERR_PTR(-errno).
> + */
> +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
> + void *input_rt,
> + size_t input_rt_size,
> + size_t *output_rt_size)
> +{
> + if (!ops_ptr)
> + return ERR_PTR(-ENODEV);
> + if (!ctx)
> + return ERR_PTR(-EINVAL);
> +
> + return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
> + input_rt_size, output_rt_size);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table);
> +
> +/**
> + * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware
> + * and reset the remote processor
> + * @pas_id: peripheral authentication service id
> + *
> + * Return: 0 on success.
> + */
> +int qcom_pas_auth_and_reset(u32 pas_id)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> +
> + return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset);
> +
> +/**
> + * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
> + * remote processor
> + *
> + * @ctx: Context saved during call to qcom_scm_pas_context_init()
devm_qcom_pas_context_alloc()
> + *
> + * This function performs the necessary steps to prepare a PAS subsystem,
> + * authenticate it using the provided metadata, and initiate a reset sequence.
> + *
> + * It should be used when Linux is in control setting up the IOMMU hardware
> + * for remote subsystem during secure firmware loading processes. The
> + * preparation step sets up a shmbridge over the firmware memory before
> + * TrustZone accesses the firmware memory region for authentication. The
> + * authentication step verifies the integrity and authenticity of the firmware
> + * or configuration using secure metadata. Finally, the reset step ensures the
> + * subsystem starts in a clean and sane state.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> + if (!ctx)
> + return -EINVAL;
> +
> + return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset);
> +
> +/**
> + * qcom_pas_set_remote_state() - Set the remote processor state
> + * @state: peripheral state
> + * @pas_id: peripheral authentication service id
> + *
> + * Return: 0 on success.
> + */
> +int qcom_pas_set_remote_state(u32 state, u32 pas_id)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> +
> + return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state);
> +
> +/**
> + * qcom_pas_shutdown() - Shut down the remote processor
> + * @pas_id: peripheral authentication service id
> + *
> + * Return: 0 on success.
> + */
> +int qcom_pas_shutdown(u32 pas_id)
> +{
> + if (!ops_ptr)
> + return -ENODEV;
> +
> + return ops_ptr->shutdown(ops_ptr->dev, pas_id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_shutdown);
> +
> +/**
> + * qcom_pas_supported() - Check if the peripheral authentication service is
> + * available for the given peripheral
> + * @pas_id: peripheral authentication service id
> + *
> + * Return: true if PAS is supported for this peripheral, otherwise false.
> + */
> +bool qcom_pas_supported(u32 pas_id)
> +{
> + if (!ops_ptr)
> + return false;
> +
> + return ops_ptr->supported(ops_ptr->dev, pas_id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_supported);
> +
> +bool qcom_pas_is_available(void)
> +{
> + /*
> + * The barrier for ops_ptr is intended to synchronize the data stores
> + * for the ops data structure when client drivers are in parallel
> + * checking for PAS service availability.
> + *
> + * Once the PAS backend becomes available, it is allowed for multiple
> + * threads to enter TZ for parallel bringup of co-processors during
> + * boot.
> + */
> + return !!smp_load_acquire(&ops_ptr);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_is_available);
> +
> +void qcom_pas_ops_register(struct qcom_pas_ops *ops)
> +{
> + if (!qcom_pas_is_available())
> + /* Paired with smp_load_acquire() in qcom_pas_is_available() */
> + smp_store_release(&ops_ptr, ops);
> + else
> + pr_err("qcom_pas: ops already registered by %s\n",
> + ops_ptr->drv_name);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
> +
> +void qcom_pas_ops_unregister(void)
> +{
> + /* Paired with smp_load_acquire() in qcom_pas_is_available() */
> + smp_store_release(&ops_ptr, NULL);
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver");
> diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h
> new file mode 100644
> index 000000000000..8643e2760602
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,50 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __QCOM_PAS_INT_H
> +#define __QCOM_PAS_INT_H
> +
> +struct device;
> +
> +/**
> + * struct qcom_pas_ops - Qcom Peripheral Authentication Service (PAS) ops
> + * @drv_name: PAS driver name.
> + * @dev: PAS device pointer.
> + * @supported: Peripheral supported callback.
> + * @init_image: Peripheral image initialization callback.
> + * @mem_setup: Peripheral memory setup callback.
> + * @get_rsc_table: Peripheral get resource table callback.
> + * @prepare_and_auth_reset: Peripheral prepare firmware authentication and
> + * reset callback.
> + * @auth_and_reset: Peripheral firmware authentication and reset
> + * callback.
> + * @set_remote_state: Peripheral set remote state callback.
> + * @shutdown: Peripheral shutdown callback.
> + * @metadata_release: Image metadata release callback.
> + */
> +struct qcom_pas_ops {
> + const char *drv_name;
> + struct device *dev;
> + bool (*supported)(struct device *dev, u32 pas_id);
> + int (*init_image)(struct device *dev, u32 pas_id, const void *metadata,
> + size_t size, struct qcom_pas_context *ctx);
> + int (*mem_setup)(struct device *dev, u32 pas_id, phys_addr_t addr,
> + phys_addr_t size);
> + void *(*get_rsc_table)(struct device *dev, struct qcom_pas_context *ctx,
> + void *input_rt, size_t input_rt_size,
> + size_t *output_rt_size);
> + int (*prepare_and_auth_reset)(struct device *dev,
> + struct qcom_pas_context *ctx);
> + int (*auth_and_reset)(struct device *dev, u32 pas_id);
> + int (*set_remote_state)(struct device *dev, u32 state, u32 pas_id);
> + int (*shutdown)(struct device *dev, u32 pas_id);
> + void (*metadata_release)(struct device *dev,
> + struct qcom_pas_context *ctx);
> +};
> +
> +void qcom_pas_ops_register(struct qcom_pas_ops *ops);
> +void qcom_pas_ops_unregister(void);
> +
> +#endif /* __QCOM_PAS_INT_H */
> diff --git a/include/linux/firmware/qcom/qcom_pas.h b/include/linux/firmware/qcom/qcom_pas.h
> new file mode 100644
> index 000000000000..65b1c9564458
> --- /dev/null
> +++ b/include/linux/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved.
> + * Copyright (C) 2015 Linaro Ltd.
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __QCOM_PAS_H
> +#define __QCOM_PAS_H
> +
> +#include <linux/err.h>
> +#include <linux/types.h>
> +
> +struct qcom_pas_context {
> + struct device *dev;
> + u32 pas_id;
> + phys_addr_t mem_phys;
> + size_t mem_size;
> + void *ptr;
> + dma_addr_t phys;
> + ssize_t size;
> + bool use_tzmem;
> +};
> +
> +bool qcom_pas_is_available(void);
> +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
> + u32 pas_id,
> + phys_addr_t mem_phys,
> + size_t mem_size);
> +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> + struct qcom_pas_context *ctx);
> +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
> + void *input_rt, size_t input_rt_size,
> + size_t *output_rt_size);
> +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size);
> +int qcom_pas_auth_and_reset(u32 pas_id);
> +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx);
> +int qcom_pas_set_remote_state(u32 state, u32 pas_id);
> +int qcom_pas_shutdown(u32 pas_id);
> +bool qcom_pas_supported(u32 pas_id);
> +void qcom_pas_metadata_release(struct qcom_pas_context *ctx);
> +
> +#endif /* __QCOM_PAS_H */
> --
> 2.51.0
>
--
-Mukesh Ojha
^ permalink raw reply
* Re: [RFC PATCH v2 0/8] Adding NO_STA flag and reworking RX link resolution
From: Ramasamy Kaliappan @ 2026-05-05 17:39 UTC (permalink / raw)
To: Benjamin Berg, linux-wireless, Jouni Malinen, johannes
Cc: Rameshkumar Sundaram
In-Reply-To: <640192308052e60589455505d31732cf2e00b1aa.camel@sipsolutions.net>
On 4/27/2026 4:43 PM, Benjamin Berg wrote:
> I believe that hostapd will need a bit of refactoring, including
> improved tracking to know whether a STA has been inserted into the
> driver and whether to TX the AUTH frames using the link address and
> NO_STA flag or using the MLD Address.
I agree with this design. Refactoring hostapd to handle all STA
consistently both in the driver and in hostapd in scenarios where a STA
roams with MLD and link address swaps occurs, in both legacy and MLO cases.
Additionally, the design should help accommodate upcoming requirements
such as Wi‑Fi 8 (802.11bi). In particular, it can address scenarios
where a non‑AP ML STA link address needs to be translated to a DS
address in the STA mode, while on the AP mode the link address
translated to the MLD address. I believe these aspects can be handled as
part of this design.
Thanks,
Ramasamy.
^ permalink raw reply
* Re: [PATCH v2] wifi: ath11k: fix use after free in ath11k_dp_rx_msdu_coalesce.
From: Jeff Johnson @ 2026-05-05 18:04 UTC (permalink / raw)
To: Willmar Knikker, jjohnson; +Cc: linux-wireless, ath11k
In-Reply-To: <20260505171709.547274-1-willmar@met-dubbel-l.nl>
On 5/5/2026 10:17 AM, Willmar Knikker wrote:
> In ath11k_dp_rx_msdu_coalesce the loop uses ->is_continuation after
> the dev_kfree_skb_any. This can cause a use after free kfence.
>
> Use flag for caching is_continuation for use after the
> dev_kfree_skb_any.
>
> Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
> Signed-off-by: Willmar Knikker <willmar@met-dubbel-l.nl>
for future reference the revision history should come after the "---"
https://www.kernel.org/doc/html/latest/process/submitting-patches.html#commentary
no need to re-submit for this; I can clean this up in my branch
> Changes in v2:
> - add bool _is_continuation for use after the free.
> - Add Fixes, label to commit.
> ---
> drivers/net/wireless/ath/ath11k/dp_rx.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
> index fe79109adc70..16364f76fc3c 100644
> --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
> +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
> @@ -1761,6 +1761,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
> int buf_first_hdr_len, buf_first_len;
> struct hal_rx_desc *ldesc;
> int space_extra, rem_len, buf_len;
> + bool is_continuation;
> u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz;
>
> /* As the msdu is spread across multiple rx buffers,
> @@ -1810,7 +1811,8 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
> rem_len = msdu_len - buf_first_len;
> while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) {
> rxcb = ATH11K_SKB_RXCB(skb);
> - if (rxcb->is_continuation)
> + is_continuation = rxcb->is_continuation;
> + if (is_continuation)
> buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz;
> else
> buf_len = rem_len;
> @@ -1828,7 +1830,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
> dev_kfree_skb_any(skb);
>
> rem_len -= buf_len;
> - if (!rxcb->is_continuation)
> + if (!is_continuation)
> break;
> }
>
^ permalink raw reply
* Re: [PATCH v2] wifi: ath9k: fix OOB access from firmware tx status queue ID
From: Jeff Johnson @ 2026-05-05 18:21 UTC (permalink / raw)
To: Tristan Madani
Cc: Toke Hoiland-Jorgensen, Johannes Berg, linux-wireless,
linux-kernel
In-Reply-To: <177792000685.1981216.17336161796486784437@gmail.com>
On 5/4/2026 11:40 AM, Tristan Madani wrote:
> Thanks for applying, Jeff.
>
> Would you mind adding a Reported-by tag as well? I am both the finder
> and the fixer:
>
> Reported-by: Tristan Madani <tristan@talencesecurity.com>
Unfortunately that is a public immutable branch so the commit can no longer be
modified.
/jeff
^ permalink raw reply
* [PATCH 1/2] wifi: mac80211: use aesgcm library
From: Johannes Berg @ 2026-05-05 21:18 UTC (permalink / raw)
To: linux-wireless; +Cc: linux-crypto, Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
Instead of dynamically allocating the gcm(aes) algorithm, use
the library. This is faster and avoids the extra allocation.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/mac80211/Kconfig | 2 +-
net/mac80211/aes_gcm.h | 39 +++++++++++++++++----------------------
net/mac80211/key.c | 11 +++--------
net/mac80211/key.h | 3 ++-
net/mac80211/wpa.c | 9 +++++----
5 files changed, 28 insertions(+), 36 deletions(-)
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index d6bc295e23a1..b51050257c01 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -5,9 +5,9 @@ config MAC80211
select CRYPTO
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_ARC4
+ select CRYPTO_LIB_AESGCM
select CRYPTO_AES
select CRYPTO_CCM
- select CRYPTO_GCM
select CRC32
help
This option enables the hardware independent IEEE 802.11
diff --git a/net/mac80211/aes_gcm.h b/net/mac80211/aes_gcm.h
index b14093b2f7a9..8124b81412c8 100644
--- a/net/mac80211/aes_gcm.h
+++ b/net/mac80211/aes_gcm.h
@@ -6,38 +6,33 @@
#ifndef AES_GCM_H
#define AES_GCM_H
-#include "aead_api.h"
+#include <crypto/gcm.h>
#define GCM_AAD_LEN 32
-static inline int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm,
- u8 *j_0, u8 *aad, u8 *data,
- size_t data_len, u8 *mic)
+static inline void ieee80211_aes_gcm_encrypt(struct aesgcm_ctx *ctx,
+ u8 *j_0, u8 *aad, u8 *data,
+ size_t data_len, u8 *mic)
{
- return aead_encrypt(tfm, j_0, aad + 2,
- be16_to_cpup((__be16 *)aad),
- data, data_len, mic);
+ aesgcm_encrypt(ctx, data, data, data_len,
+ aad + 2, be16_to_cpup((__be16 *)aad),
+ j_0, mic);
}
-static inline int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm,
- u8 *j_0, u8 *aad, u8 *data,
- size_t data_len, u8 *mic)
+static inline bool ieee80211_aes_gcm_decrypt(struct aesgcm_ctx *ctx,
+ u8 *j_0, u8 *aad, u8 *data,
+ size_t data_len, u8 *mic)
{
- return aead_decrypt(tfm, j_0, aad + 2,
- be16_to_cpup((__be16 *)aad),
- data, data_len, mic);
+ return aesgcm_decrypt(ctx, data, data, data_len,
+ aad + 2, be16_to_cpup((__be16 *)aad),
+ j_0, mic);
}
-static inline struct crypto_aead *
-ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len)
+static inline int
+ieee80211_aes_gcm_key_setup_encrypt(struct aesgcm_ctx *ctx,
+ const u8 key[], size_t key_len)
{
- return aead_key_setup_encrypt("gcm(aes)", key,
- key_len, IEEE80211_GCMP_MIC_LEN);
-}
-
-static inline void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
-{
- return aead_key_free(tfm);
+ return aesgcm_expandkey(ctx, key, key_len, IEEE80211_GCMP_MIC_LEN);
}
#endif /* AES_GCM_H */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 4b8965633df3..1a2092aebaf6 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -727,10 +727,9 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
/* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
*/
- key->u.gcmp.tfm = ieee80211_aes_gcm_key_setup_encrypt(key_data,
- key_len);
- if (IS_ERR(key->u.gcmp.tfm)) {
- err = PTR_ERR(key->u.gcmp.tfm);
+ err = ieee80211_aes_gcm_key_setup_encrypt(&key->u.gcmp.ctx,
+ key_data, key_len);
+ if (err) {
kfree(key);
return ERR_PTR(err);
}
@@ -753,10 +752,6 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
break;
- case WLAN_CIPHER_SUITE_GCMP:
- case WLAN_CIPHER_SUITE_GCMP_256:
- ieee80211_aes_gcm_key_free(key->u.gcmp.tfm);
- break;
}
kfree_sensitive(key);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 826e4e9387c5..65450d3474bb 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -13,6 +13,7 @@
#include <linux/crypto.h>
#include <linux/rcupdate.h>
#include <crypto/aes-cbc-macs.h>
+#include <crypto/gcm.h>
#include <crypto/arc4.h>
#include <net/mac80211.h>
@@ -111,7 +112,7 @@ struct ieee80211_key {
* Management frames.
*/
u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_GCMP_PN_LEN];
- struct crypto_aead *tfm;
+ struct aesgcm_ctx ctx;
u32 replays; /* dot11RSNAStatsGCMPReplays */
} gcmp;
struct {
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index be3a2e95303c..4440e09c5f80 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -709,8 +709,9 @@ static int gcmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
gcmp_special_blocks(skb, pn, j_0, aad,
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
false);
- return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
- skb_put(skb, IEEE80211_GCMP_MIC_LEN));
+ ieee80211_aes_gcm_encrypt(&key->u.gcmp.ctx, j_0, aad, pos, len,
+ skb_put(skb, IEEE80211_GCMP_MIC_LEN));
+ return 0;
}
ieee80211_tx_result
@@ -797,8 +798,8 @@ ieee80211_crypto_gcmp_decrypt(struct ieee80211_rx_data *rx)
key->conf.flags & IEEE80211_KEY_FLAG_SPP_AMSDU,
aad_nonce_computed);
- if (ieee80211_aes_gcm_decrypt(
- key->u.gcmp.tfm, j_0, aad,
+ if (!ieee80211_aes_gcm_decrypt(
+ &key->u.gcmp.ctx, j_0, aad,
skb->data + hdrlen + IEEE80211_GCMP_HDR_LEN,
data_len,
skb->data + skb->len -
--
2.53.0
^ permalink raw reply related
* [PATCH 2/2] wifi: mac80211: use gf128hash library
From: Johannes Berg @ 2026-05-05 21:18 UTC (permalink / raw)
To: linux-wireless; +Cc: linux-crypto, Johannes Berg
In-Reply-To: <20260505211841.669767-3-johannes@sipsolutions.net>
From: Johannes Berg <johannes.berg@intel.com>
Just like the previous conversion of aesgcm, it's simpler,
faster and avoids extra allocations, especially one in the
actual packet processing.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/mac80211/Kconfig | 1 +
net/mac80211/aes_gmac.c | 90 +++++++++++++++--------------------------
net/mac80211/aes_gmac.h | 14 ++++---
net/mac80211/key.c | 11 ++---
net/mac80211/key.h | 2 +-
net/mac80211/wpa.c | 15 +++----
6 files changed, 52 insertions(+), 81 deletions(-)
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index b51050257c01..ada7078db4cd 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -6,6 +6,7 @@ config MAC80211
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_AESGCM
+ select CRYPTO_LIB_GF128MUL
select CRYPTO_AES
select CRYPTO_CCM
select CRC32
diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c
index 811a83d8d525..c6bb5761cc63 100644
--- a/net/mac80211/aes_gmac.c
+++ b/net/mac80211/aes_gmac.c
@@ -7,88 +7,62 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/err.h>
-#include <crypto/aead.h>
-#include <crypto/aes.h>
+#include <crypto/gf128hash.h>
+#include <crypto/utils.h>
#include <net/mac80211.h>
#include "key.h"
#include "aes_gmac.h"
-int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+int ieee80211_aes_gmac(struct aesgcm_ctx *ctx, const u8 *aad, u8 *nonce,
const u8 *data, size_t data_len, u8 *mic)
{
- struct scatterlist sg[5];
- u8 *zero, *__aad, iv[AES_BLOCK_SIZE];
- struct aead_request *aead_req;
- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
+ static const u8 zero[GHASH_BLOCK_SIZE];
+ struct ghash_ctx ghash;
+ u8 iv[AES_BLOCK_SIZE];
+ size_t total_len = GMAC_AAD_LEN + data_len;
+ __be64 tail[2] = {
+ cpu_to_be64((u64)total_len * 8),
+ 0, /* no data since it's just GMAC */
+ };
+ u8 ghash_out[AES_BLOCK_SIZE];
+ u8 enc_ctr[AES_BLOCK_SIZE];
const __le16 *fc;
- int ret;
if (data_len < IEEE80211_GMAC_MIC_LEN)
return -EINVAL;
- aead_req = kzalloc(reqsize + IEEE80211_GMAC_MIC_LEN + GMAC_AAD_LEN,
- GFP_ATOMIC);
- if (!aead_req)
- return -ENOMEM;
+ ghash_init(&ghash, &ctx->ghash_key);
- zero = (u8 *)aead_req + reqsize;
- __aad = zero + IEEE80211_GMAC_MIC_LEN;
- memcpy(__aad, aad, GMAC_AAD_LEN);
+ ghash_update(&ghash, aad, GMAC_AAD_LEN);
fc = (const __le16 *)aad;
if (ieee80211_is_beacon(*fc)) {
/* mask Timestamp field to zero */
- sg_init_table(sg, 5);
- sg_set_buf(&sg[0], __aad, GMAC_AAD_LEN);
- sg_set_buf(&sg[1], zero, 8);
- sg_set_buf(&sg[2], data + 8,
- data_len - 8 - IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[3], zero, IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[4], mic, IEEE80211_GMAC_MIC_LEN);
+ ghash_update(&ghash, zero, 8);
+ ghash_update(&ghash, data + 8, data_len - 8 - IEEE80211_GMAC_MIC_LEN);
} else {
- sg_init_table(sg, 4);
- sg_set_buf(&sg[0], __aad, GMAC_AAD_LEN);
- sg_set_buf(&sg[1], data, data_len - IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[2], zero, IEEE80211_GMAC_MIC_LEN);
- sg_set_buf(&sg[3], mic, IEEE80211_GMAC_MIC_LEN);
+ ghash_update(&ghash, data, data_len - IEEE80211_GMAC_MIC_LEN);
}
+ /* set MIC value to zero */
+ ghash_update(&ghash, zero, IEEE80211_GMAC_MIC_LEN);
+ /* pad */
+ ghash_update(&ghash, zero, -total_len & (GHASH_BLOCK_SIZE - 1));
+
+ ghash_update(&ghash, (const u8 *)&tail, sizeof(tail));
+
+ ghash_final(&ghash, ghash_out);
+
memcpy(iv, nonce, GMAC_NONCE_LEN);
memset(iv + GMAC_NONCE_LEN, 0, sizeof(iv) - GMAC_NONCE_LEN);
iv[AES_BLOCK_SIZE - 1] = 0x01;
- aead_request_set_tfm(aead_req, tfm);
- aead_request_set_crypt(aead_req, sg, sg, 0, iv);
- aead_request_set_ad(aead_req, GMAC_AAD_LEN + data_len);
+ aes_encrypt(&ctx->aes_key, enc_ctr, (const u8 *)iv);
+ crypto_xor_cpy(mic, ghash_out, enc_ctr, IEEE80211_GMAC_MIC_LEN);
- ret = crypto_aead_encrypt(aead_req);
- kfree_sensitive(aead_req);
+ memzero_explicit(ghash_out, sizeof(ghash_out));
+ memzero_explicit(enc_ctr, sizeof(enc_ctr));
- return ret;
-}
-
-struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
- size_t key_len)
-{
- struct crypto_aead *tfm;
- int err;
-
- tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
- if (IS_ERR(tfm))
- return tfm;
-
- err = crypto_aead_setkey(tfm, key, key_len);
- if (!err)
- err = crypto_aead_setauthsize(tfm, IEEE80211_GMAC_MIC_LEN);
- if (!err)
- return tfm;
-
- crypto_free_aead(tfm);
- return ERR_PTR(err);
-}
-
-void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
-{
- crypto_free_aead(tfm);
+ return 0;
}
diff --git a/net/mac80211/aes_gmac.h b/net/mac80211/aes_gmac.h
index 206136b60bca..e1db4a47fc9c 100644
--- a/net/mac80211/aes_gmac.h
+++ b/net/mac80211/aes_gmac.h
@@ -6,15 +6,19 @@
#ifndef AES_GMAC_H
#define AES_GMAC_H
-#include <linux/crypto.h>
+#include <crypto/gcm.h>
#define GMAC_AAD_LEN 20
#define GMAC_NONCE_LEN 12
-struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
- size_t key_len);
-int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
+static inline int
+ieee80211_aes_gmac_key_setup(struct aesgcm_ctx *ctx,
+ const u8 key[], size_t key_len)
+{
+ return aesgcm_expandkey(ctx, key, key_len, IEEE80211_GCMP_MIC_LEN);
+}
+
+int ieee80211_aes_gmac(struct aesgcm_ctx *ctx, const u8 *aad, u8 *nonce,
const u8 *data, size_t data_len, u8 *mic);
-void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
#endif /* AES_GMAC_H */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 1a2092aebaf6..71cbd7a8b818 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -708,10 +708,9 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
/* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
*/
- key->u.aes_gmac.tfm =
- ieee80211_aes_gmac_key_setup(key_data, key_len);
- if (IS_ERR(key->u.aes_gmac.tfm)) {
- err = PTR_ERR(key->u.aes_gmac.tfm);
+ err = ieee80211_aes_gmac_key_setup(&key->u.aes_gmac.ctx,
+ key_data, key_len);
+ if (err) {
kfree(key);
return ERR_PTR(err);
}
@@ -748,10 +747,6 @@ static void ieee80211_key_free_common(struct ieee80211_key *key)
case WLAN_CIPHER_SUITE_CCMP_256:
ieee80211_aes_key_free(key->u.ccmp.tfm);
break;
- case WLAN_CIPHER_SUITE_BIP_GMAC_128:
- case WLAN_CIPHER_SUITE_BIP_GMAC_256:
- ieee80211_aes_gmac_key_free(key->u.aes_gmac.tfm);
- break;
}
kfree_sensitive(key);
}
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 65450d3474bb..0ee0548e02ed 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -101,7 +101,7 @@ struct ieee80211_key {
} aes_cmac;
struct {
u8 rx_pn[IEEE80211_GMAC_PN_LEN];
- struct crypto_aead *tfm;
+ struct aesgcm_ctx ctx;
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
} aes_gmac;
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 4440e09c5f80..0a55de4bc853 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -1008,8 +1008,8 @@ ieee80211_crypto_aes_gmac_encrypt(struct ieee80211_tx_data *tx)
bip_ipn_swap(nonce + ETH_ALEN, mmie->sequence_number);
/* MIC = AES-GMAC(IGTK, AAD || Management Frame Body || MMIE, 128) */
- if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
- skb->data + 24, skb->len - 24, mmie->mic) < 0)
+ if (ieee80211_aes_gmac(&key->u.aes_gmac.ctx, aad, nonce,
+ skb->data + 24, skb->len - 24, mmie->mic))
return TX_DROP;
return TX_CONTINUE;
@@ -1022,7 +1022,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_key *key = rx->key;
struct ieee80211_mmie_16 *mmie;
- u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
+ u8 aad[GMAC_AAD_LEN], ipn[6], nonce[GMAC_NONCE_LEN];
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (!ieee80211_is_mgmt(hdr->frame_control))
@@ -1047,24 +1047,21 @@ ieee80211_crypto_aes_gmac_decrypt(struct ieee80211_rx_data *rx)
}
if (!(status->flag & RX_FLAG_DECRYPTED)) {
+ u8 mic[IEEE80211_GMAC_MIC_LEN];
+
/* hardware didn't decrypt/verify MIC */
bip_aad(skb, aad);
memcpy(nonce, hdr->addr2, ETH_ALEN);
memcpy(nonce + ETH_ALEN, ipn, 6);
- mic = kmalloc(IEEE80211_GMAC_MIC_LEN, GFP_ATOMIC);
- if (!mic)
- return RX_DROP_U_OOM;
- if (ieee80211_aes_gmac(key->u.aes_gmac.tfm, aad, nonce,
+ if (ieee80211_aes_gmac(&key->u.aes_gmac.ctx, aad, nonce,
skb->data + 24, skb->len - 24,
mic) < 0 ||
crypto_memneq(mic, mmie->mic, sizeof(mmie->mic))) {
key->u.aes_gmac.icverrors++;
- kfree(mic);
return RX_DROP_U_MIC_FAIL;
}
- kfree(mic);
}
memcpy(key->u.aes_gmac.rx_pn, ipn, 6);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 1/2] wifi: mac80211: use aesgcm library
From: Eric Biggers @ 2026-05-05 22:16 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, linux-crypto, Johannes Berg
In-Reply-To: <20260505211841.669767-3-johannes@sipsolutions.net>
Hi Johannes,
On Tue, May 05, 2026 at 11:18:38PM +0200, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
>
> Instead of dynamically allocating the gcm(aes) algorithm, use
> the library. This is faster and avoids the extra allocation.
>
> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
> ---
> net/mac80211/Kconfig | 2 +-
> net/mac80211/aes_gcm.h | 39 +++++++++++++++++----------------------
> net/mac80211/key.c | 11 +++--------
> net/mac80211/key.h | 3 ++-
> net/mac80211/wpa.c | 9 +++++----
> 5 files changed, 28 insertions(+), 36 deletions(-)
I really appreciate the enthusiasm for the crypto library! And it isn't
surprising, since it's clearly the way to go. But I do think these two
patches are jumping the gun a bit, since we haven't yet migrated all the
optimized AES-GCM code into the library, or added an improved AES-GCM
API that provides enough functionality to fulfill all the in-kernel use
cases (for example, incremental computation of AES-GMAC).
So as-is these two patches could regress performance in some cases
(despite the library having less overhead). And also the AES-GCM API is
likely to change a bit. In particular I don't think code outside the
crypto subsystem should be constructing its own AES-GMAC by combining
the GHASH functions with the AES functions, as your second patch does.
Instead they should invoke an AES-GMAC API (or AES-GCM, of which
AES-GMAC is a special case) provided by lib/crypto/.
So I'd ask that we wait just a bit until I can finish getting the
AES-GCM library APIs into a good state. I got a lot of the prerequisite
work in for 7.0 and 7.1, and I'll see if I can finish it in 7.2. I've
just been a bit busy with other things in the past few weeks.
Thanks,
- Eric
^ permalink raw reply
* Re: [PATCH wireless] wifi: ath11k: fix monitor mode frame length by using correct descriptor size
From: Jeff Johnson @ 2026-05-05 22:32 UTC (permalink / raw)
To: Praneesh P, Joshua Klinesmith, linux-wireless; +Cc: ath11k
In-Reply-To: <299f83be-748c-458c-be07-df6a0fe02f38@oss.qualcomm.com>
On 4/29/2026 2:52 AM, Praneesh P wrote:
> On 4/7/2026 8:18 AM, Joshua Klinesmith wrote:
>> The monitor mode RX path in ath11k_dp_rx_mon_mpdu_pop() and
>> ath11k_dp_rx_full_mon_mpdu_pop() uses sizeof(struct hal_rx_desc) to
>> compute the packet buffer offset. This is the size of the union of all
>> chip-specific descriptors (the maximum), not the actual descriptor size
>> for the running chip. The later ath11k_dp_rx_msdus_set_payload() then
>> strips only hw_params.hal_desc_sz bytes (the chip-specific size) from
>> the front of the skb.
>>
>> On IPQ8074 and QCN9074, sizeof(struct hal_rx_desc) is 392 but
>> hal_desc_sz is 384, leaving 8 extra bytes of descriptor data at the
>> end of every monitor mode frame delivered to userspace. On WCN6855 the
>> sizes happen to match so the bug is not visible.
>>
>> The same mismatch in ath11k_dp_mon_set_frag_len() causes incorrect
>> fragment length calculation for multi-buffer MSDUs, under-counting
>> intermediate fragments by 8 bytes and over-counting the last fragment.
>>
>> Fix by using ar->ab->hw_params.hal_desc_sz consistently in both
>> monitor mpdu_pop functions and passing it through to set_frag_len.
>>
>> Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
>> Link: https://github.com/openwrt/openwrt/issues/16183
>> Signed-off-by: Joshua Klinesmith <joshuaklinesmith@gmail.com>
>> ---
>> drivers/net/wireless/ath/ath11k/dp_rx.c | 27 ++++++++++++++-----------
>> 1 file changed, 15 insertions(+), 12 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
>> index 85defe11750d..c86ffc203f15 100644
>> --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
>> +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
>> @@ -4511,10 +4511,11 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id)
>> return 0;
>> }
>>
>> -static void ath11k_dp_mon_set_frag_len(u32 *total_len, u32 *frag_len)
>> +static void ath11k_dp_mon_set_frag_len(u32 *total_len, u32 *frag_len,
>> + u32 hal_desc_sz)
>> {
>> - if (*total_len >= (DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc))) {
>> - *frag_len = DP_RX_BUFFER_SIZE - sizeof(struct hal_rx_desc);
>> + if (*total_len >= (DP_RX_BUFFER_SIZE - hal_desc_sz)) {
>> + *frag_len = DP_RX_BUFFER_SIZE - hal_desc_sz;
>> *total_len -= *frag_len;
>> } else {
>> *frag_len = *total_len;
>> @@ -4658,19 +4659,19 @@ static u32 ath11k_dp_rx_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id,
>>
>> static void ath11k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info,
>> bool *is_frag, u32 *total_len,
>> - u32 *frag_len, u32 *msdu_cnt)
>> + u32 *frag_len, u32 *msdu_cnt,
>> + u32 hal_desc_sz)
>> {
>> if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) {
>> if (!*is_frag) {
>> *total_len = info->msdu_len;
>> *is_frag = true;
>> }
>> - ath11k_dp_mon_set_frag_len(total_len,
>> - frag_len);
>> + ath11k_dp_mon_set_frag_len(total_len, frag_len, hal_desc_sz);
>> } else {
>> if (*is_frag) {
>> - ath11k_dp_mon_set_frag_len(total_len,
>> - frag_len);
>> + ath11k_dp_mon_set_frag_len(total_len, frag_len,
>> + hal_desc_sz);
>> } else {
>> *frag_len = info->msdu_len;
>> }
>> @@ -4792,7 +4793,7 @@ u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id,
>>
>> rx_desc = (struct hal_rx_desc *)msdu->data;
>>
>> - rx_pkt_offset = sizeof(struct hal_rx_desc);
>> + rx_pkt_offset = ar->ab->hw_params.hal_desc_sz;
>> l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, rx_desc);
>>
>> if (is_first_msdu) {
>> @@ -4823,7 +4824,8 @@ u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id,
>> }
>> ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i],
>> &is_frag, &total_len,
>> - &frag_len, &msdu_cnt);
>> + &frag_len, &msdu_cnt,
>> + rx_pkt_offset);
>> rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len;
>>
>> ath11k_dp_pkt_set_pktlen(msdu, rx_buf_size);
>> @@ -5424,7 +5426,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
>>
>> rx_desc = (struct hal_rx_desc *)msdu->data;
>>
>> - rx_pkt_offset = sizeof(struct hal_rx_desc);
>> + rx_pkt_offset = ar->ab->hw_params.hal_desc_sz;
>> l2_hdr_offset = ath11k_dp_rx_h_msdu_end_l3pad(ar->ab, rx_desc);
>>
>> if (is_first_msdu) {
>> @@ -5439,7 +5441,8 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
>>
>> ath11k_dp_mon_get_buf_len(&msdu_list.msdu_info[i],
>> &is_frag, &total_len,
>> - &frag_len, &msdu_cnt);
>> + &frag_len, &msdu_cnt,
>> + rx_pkt_offset);
>>
>> rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len;
>
> Thanks for fixing the offset handling in the monitor Rx path, but still
> there is another instance that still relies on sizeof(struct hal_rx_desc).
>
> ath11k_dp_rx_h_ppdu(), which is also invoked from the monitor path, uses:
>
> ath11k_dbg_dump(ab, ATH11K_DBG_DATA, NULL, "", rx_desc, sizeof(struct
> hal_rx_desc));
>
> This should likewise be converted to use hw_params.hal_desc_sz to avoid
> dumping beyond the chip-specific descriptor size on platforms where they
> differ.
Joshua, can you submit a v2 that addresses this observation?
/jeff
^ permalink raw reply
* [PATCH] wifi: iwlwifi: Fix typo in comment
From: Md Shofiqul Islam @ 2026-05-05 22:55 UTC (permalink / raw)
To: linux-wireless; +Cc: miriam.rachel.korenblit, linux-kernel, Md Shofiqul Islam
Fix spelling mistake in comment:
- sucess -> success
---
drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
index 4a33a032c..e8556de58 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c
@@ -111,7 +111,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd,
}
/*
- * We assume that the caller set the status to the sucess value
+ * We assume that the caller set the status to the success value
*/
int iwl_mvm_send_cmd_pdu_status(struct iwl_mvm *mvm, u32 id, u16 len,
const void *data, u32 *status)
--
2.51.1
^ permalink raw reply related
* [wireless:for-next] BUILD SUCCESS 4a142520d166f91627f27a7017525a228137c808
From: kernel test robot @ 2026-05-05 23:35 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git for-next
branch HEAD: 4a142520d166f91627f27a7017525a228137c808 wifi: libertas: notify firmware load wait on disconnect
elapsed time: 811m
configs tested: 359
configs skipped: 45
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc allyesconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc randconfig-001-20260505 gcc-8.5.0
arc randconfig-001-20260506 gcc-12.5.0
arc randconfig-002-20260505 gcc-8.5.0
arc randconfig-002-20260506 gcc-12.5.0
arm allnoconfig clang-23
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm allyesconfig gcc-15.2.0
arm defconfig gcc-15.2.0
arm exynos_defconfig clang-23
arm mmp2_defconfig gcc-15.2.0
arm randconfig-001-20260505 gcc-8.5.0
arm randconfig-001-20260506 gcc-12.5.0
arm randconfig-002-20260505 gcc-8.5.0
arm randconfig-002-20260506 gcc-12.5.0
arm randconfig-003-20260505 gcc-8.5.0
arm randconfig-003-20260506 gcc-12.5.0
arm randconfig-004-20260505 gcc-8.5.0
arm randconfig-004-20260506 gcc-12.5.0
arm64 allmodconfig clang-19
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001 gcc-14.3.0
arm64 randconfig-001-20260505 gcc-14.3.0
arm64 randconfig-001-20260506 gcc-15.2.0
arm64 randconfig-002 gcc-14.3.0
arm64 randconfig-002-20260505 gcc-14.3.0
arm64 randconfig-002-20260506 gcc-15.2.0
arm64 randconfig-003 gcc-14.3.0
arm64 randconfig-003-20260505 gcc-14.3.0
arm64 randconfig-003-20260506 gcc-15.2.0
arm64 randconfig-004 gcc-14.3.0
arm64 randconfig-004-20260505 gcc-14.3.0
arm64 randconfig-004-20260506 gcc-15.2.0
csky alldefconfig gcc-15.2.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001 gcc-14.3.0
csky randconfig-001-20260505 gcc-14.3.0
csky randconfig-001-20260506 gcc-15.2.0
csky randconfig-002 gcc-14.3.0
csky randconfig-002-20260505 gcc-14.3.0
csky randconfig-002-20260506 gcc-15.2.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260505 clang-23
hexagon randconfig-001-20260506 clang-23
hexagon randconfig-001-20260506 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260505 clang-23
hexagon randconfig-002-20260506 clang-23
hexagon randconfig-002-20260506 gcc-11.5.0
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001 clang-20
i386 buildonly-randconfig-001 gcc-14
i386 buildonly-randconfig-001-20260505 gcc-14
i386 buildonly-randconfig-001-20260506 clang-20
i386 buildonly-randconfig-002 clang-20
i386 buildonly-randconfig-002-20260505 gcc-14
i386 buildonly-randconfig-002-20260506 clang-20
i386 buildonly-randconfig-003 clang-20
i386 buildonly-randconfig-003-20260505 gcc-14
i386 buildonly-randconfig-003-20260506 clang-20
i386 buildonly-randconfig-004 clang-20
i386 buildonly-randconfig-004 gcc-14
i386 buildonly-randconfig-004-20260505 gcc-14
i386 buildonly-randconfig-004-20260506 clang-20
i386 buildonly-randconfig-005 clang-20
i386 buildonly-randconfig-005 gcc-14
i386 buildonly-randconfig-005-20260505 gcc-14
i386 buildonly-randconfig-005-20260506 clang-20
i386 buildonly-randconfig-006 clang-20
i386 buildonly-randconfig-006 gcc-14
i386 buildonly-randconfig-006-20260505 gcc-14
i386 buildonly-randconfig-006-20260506 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001 gcc-14
i386 randconfig-001-20260505 clang-20
i386 randconfig-001-20260505 gcc-14
i386 randconfig-001-20260506 gcc-14
i386 randconfig-002 gcc-14
i386 randconfig-002-20260505 clang-20
i386 randconfig-002-20260505 gcc-14
i386 randconfig-002-20260506 gcc-14
i386 randconfig-003 gcc-14
i386 randconfig-003-20260505 clang-20
i386 randconfig-003-20260506 gcc-14
i386 randconfig-004 gcc-14
i386 randconfig-004-20260505 clang-20
i386 randconfig-004-20260505 gcc-14
i386 randconfig-004-20260506 gcc-14
i386 randconfig-005 gcc-14
i386 randconfig-005-20260505 clang-20
i386 randconfig-005-20260506 gcc-14
i386 randconfig-006 gcc-14
i386 randconfig-006-20260505 clang-20
i386 randconfig-006-20260506 gcc-14
i386 randconfig-007 gcc-14
i386 randconfig-007-20260505 clang-20
i386 randconfig-007-20260505 gcc-14
i386 randconfig-007-20260506 gcc-14
i386 randconfig-011 clang-20
i386 randconfig-011-20260505 clang-20
i386 randconfig-011-20260506 clang-20
i386 randconfig-012 clang-20
i386 randconfig-012-20260505 clang-20
i386 randconfig-012-20260506 clang-20
i386 randconfig-013 clang-20
i386 randconfig-013-20260505 clang-20
i386 randconfig-013-20260506 clang-20
i386 randconfig-014 clang-20
i386 randconfig-014-20260505 clang-20
i386 randconfig-014-20260506 clang-20
i386 randconfig-014-20260506 gcc-14
i386 randconfig-015 clang-20
i386 randconfig-015-20260505 clang-20
i386 randconfig-015-20260506 clang-20
i386 randconfig-015-20260506 gcc-14
i386 randconfig-016 clang-20
i386 randconfig-016-20260505 clang-20
i386 randconfig-016-20260506 clang-20
i386 randconfig-017 clang-20
i386 randconfig-017-20260505 clang-20
i386 randconfig-017-20260506 clang-20
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260505 clang-23
loongarch randconfig-001-20260505 gcc-11.5.0
loongarch randconfig-001-20260506 clang-23
loongarch randconfig-001-20260506 gcc-11.5.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260505 clang-23
loongarch randconfig-002-20260505 gcc-11.5.0
loongarch randconfig-002-20260506 clang-23
loongarch randconfig-002-20260506 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k allyesconfig gcc-15.2.0
m68k defconfig clang-19
m68k defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
microblaze defconfig gcc-15.2.0
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
nios2 alldefconfig gcc-11.5.0
nios2 allmodconfig clang-23
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 defconfig gcc-15.2.0
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260505 clang-23
nios2 randconfig-001-20260505 gcc-11.5.0
nios2 randconfig-001-20260506 clang-23
nios2 randconfig-001-20260506 gcc-11.5.0
nios2 randconfig-001-20260506 gcc-8.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260505 clang-23
nios2 randconfig-002-20260505 gcc-11.5.0
nios2 randconfig-002-20260506 clang-23
nios2 randconfig-002-20260506 gcc-11.5.0
nios2 randconfig-002-20260506 gcc-8.5.0
openrisc allmodconfig clang-23
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-23
openrisc de0_nano_multicore_defconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260505 gcc-14.3.0
parisc randconfig-001-20260506 gcc-13.4.0
parisc randconfig-002-20260505 gcc-14.3.0
parisc randconfig-002-20260506 gcc-13.4.0
parisc64 defconfig clang-19
parisc64 defconfig gcc-15.2.0
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc mpc8315_rdb_defconfig clang-23
powerpc randconfig-001-20260505 gcc-14.3.0
powerpc randconfig-001-20260506 gcc-13.4.0
powerpc randconfig-002-20260505 gcc-14.3.0
powerpc randconfig-002-20260506 gcc-13.4.0
powerpc64 randconfig-001-20260505 gcc-14.3.0
powerpc64 randconfig-001-20260506 gcc-13.4.0
powerpc64 randconfig-002-20260505 gcc-14.3.0
powerpc64 randconfig-002-20260506 gcc-13.4.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260505 gcc-10.5.0
riscv randconfig-001-20260506 gcc-8.5.0
riscv randconfig-002-20260506 gcc-8.5.0
s390 allmodconfig clang-18
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260506 gcc-8.5.0
s390 randconfig-002-20260505 gcc-10.5.0
s390 randconfig-002-20260506 gcc-8.5.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh randconfig-001-20260505 gcc-10.5.0
sh randconfig-001-20260506 gcc-8.5.0
sh randconfig-002-20260505 gcc-10.5.0
sh randconfig-002-20260506 gcc-8.5.0
sh urquell_defconfig gcc-15.2.0
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001 gcc-15.2.0
sparc randconfig-001-20260505 gcc-15.2.0
sparc randconfig-001-20260506 gcc-11.5.0
sparc randconfig-002 gcc-15.2.0
sparc randconfig-002-20260505 gcc-15.2.0
sparc randconfig-002-20260506 gcc-11.5.0
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001 gcc-15.2.0
sparc64 randconfig-001-20260505 gcc-15.2.0
sparc64 randconfig-001-20260506 gcc-11.5.0
sparc64 randconfig-002 gcc-15.2.0
sparc64 randconfig-002-20260505 gcc-15.2.0
sparc64 randconfig-002-20260506 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001 gcc-15.2.0
um randconfig-001-20260505 gcc-15.2.0
um randconfig-001-20260506 gcc-11.5.0
um randconfig-002 gcc-15.2.0
um randconfig-002-20260505 gcc-15.2.0
um randconfig-002-20260506 gcc-11.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260505 clang-20
x86_64 buildonly-randconfig-001-20260506 clang-20
x86_64 buildonly-randconfig-002-20260505 clang-20
x86_64 buildonly-randconfig-002-20260506 clang-20
x86_64 buildonly-randconfig-002-20260506 gcc-14
x86_64 buildonly-randconfig-003-20260505 clang-20
x86_64 buildonly-randconfig-003-20260505 gcc-14
x86_64 buildonly-randconfig-003-20260506 clang-20
x86_64 buildonly-randconfig-004-20260505 clang-20
x86_64 buildonly-randconfig-004-20260506 clang-20
x86_64 buildonly-randconfig-005-20260505 clang-20
x86_64 buildonly-randconfig-005-20260505 gcc-14
x86_64 buildonly-randconfig-005-20260506 clang-20
x86_64 buildonly-randconfig-005-20260506 gcc-14
x86_64 buildonly-randconfig-006-20260505 clang-20
x86_64 buildonly-randconfig-006-20260505 gcc-14
x86_64 buildonly-randconfig-006-20260506 clang-20
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001 clang-20
x86_64 randconfig-001-20260505 clang-20
x86_64 randconfig-001-20260506 clang-20
x86_64 randconfig-002 clang-20
x86_64 randconfig-002-20260505 clang-20
x86_64 randconfig-002-20260506 clang-20
x86_64 randconfig-003 clang-20
x86_64 randconfig-003-20260505 clang-20
x86_64 randconfig-003-20260506 clang-20
x86_64 randconfig-004 clang-20
x86_64 randconfig-004-20260505 clang-20
x86_64 randconfig-004-20260506 clang-20
x86_64 randconfig-005 clang-20
x86_64 randconfig-005-20260505 clang-20
x86_64 randconfig-005-20260506 clang-20
x86_64 randconfig-006 clang-20
x86_64 randconfig-006-20260505 clang-20
x86_64 randconfig-006-20260506 clang-20
x86_64 randconfig-011-20260505 clang-20
x86_64 randconfig-011-20260506 clang-20
x86_64 randconfig-012-20260505 clang-20
x86_64 randconfig-012-20260506 clang-20
x86_64 randconfig-013-20260505 clang-20
x86_64 randconfig-013-20260506 clang-20
x86_64 randconfig-014-20260505 clang-20
x86_64 randconfig-014-20260506 clang-20
x86_64 randconfig-015-20260505 clang-20
x86_64 randconfig-015-20260506 clang-20
x86_64 randconfig-015-20260506 gcc-12
x86_64 randconfig-016-20260505 clang-20
x86_64 randconfig-016-20260506 clang-20
x86_64 randconfig-016-20260506 gcc-12
x86_64 randconfig-071 clang-20
x86_64 randconfig-071-20260505 clang-20
x86_64 randconfig-071-20260506 clang-20
x86_64 randconfig-072 clang-20
x86_64 randconfig-072-20260505 clang-20
x86_64 randconfig-072-20260506 clang-20
x86_64 randconfig-073 clang-20
x86_64 randconfig-073-20260505 clang-20
x86_64 randconfig-073-20260506 clang-20
x86_64 randconfig-074 clang-20
x86_64 randconfig-074-20260505 clang-20
x86_64 randconfig-074-20260506 clang-20
x86_64 randconfig-075 clang-20
x86_64 randconfig-075-20260505 clang-20
x86_64 randconfig-075-20260506 clang-20
x86_64 randconfig-076 clang-20
x86_64 randconfig-076-20260505 clang-20
x86_64 randconfig-076-20260506 clang-20
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa allyesconfig gcc-15.2.0
xtensa randconfig-001 gcc-15.2.0
xtensa randconfig-001-20260505 gcc-15.2.0
xtensa randconfig-001-20260506 gcc-11.5.0
xtensa randconfig-002 gcc-15.2.0
xtensa randconfig-002-20260505 gcc-15.2.0
xtensa randconfig-002-20260506 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH 1/2] wifi: mt76: mt792x: disable HW TX/RX encap offload to fix TDLS direct-link
From: ElXreno @ 2026-05-06 1:00 UTC (permalink / raw)
To: Sean Wang
Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Soul Huang, Ming Yen Hsieh, Deren Wu, linux-wireless,
linux-kernel, linux-arm-kernel, linux-mediatek, stable
In-Reply-To: <CAGp9LzogKfGovfDw+=m4BkqWAakFTStXH20cQ_FA_5-zo+rmGA@mail.gmail.com>
Hi Sean,
Thanks for the review. You're right that the global disable was too
broad. v2 scopes the fix per-TDLS-peer; rationale and changelog are in
the v2 cover letter.
Best regards,
ElXreno
^ permalink raw reply
* Re: [PATCH 6/9] dt-bindings: bluetooth: qcom: Add NVMEM BD address cell
From: Rob Herring (Arm) @ 2026-05-06 1:33 UTC (permalink / raw)
To: Loic Poulain
Cc: Bjorn Andersson, devicetree, Krzysztof Kozlowski, linux-arm-msm,
Marcel Holtmann, Konrad Dybcio, David S. Miller, linux-mmc,
Jens Axboe, netdev, Simon Horman, Rocky Liao, daniel,
linux-wireless, Ulf Hansson, Eric Dumazet, Balakrishna Godavarthi,
linux-kernel, Bartosz Golaszewski, Jakub Kicinski, Paolo Abeni,
linux-bluetooth, Conor Dooley, Luiz Augusto von Dentz,
Jeff Johnson, ath10k, linux-block, Johannes Berg
In-Reply-To: <20260428-block-as-nvmem-v1-6-6ad23e75190a@oss.qualcomm.com>
On Tue, 28 Apr 2026 16:23:11 +0200, Loic Poulain wrote:
> Add support for an NVMEM cell provider for "local-bd-address",
> allowing the Bluetooth stack to retrieve controller's BD address
> from non-volatile storage such as an EEPROM or an eMMC partition.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> .../bindings/net/bluetooth/qcom,bluetooth-common.yaml | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* [PATCH v2 0/2] wifi: mt76: fix TDLS direct-link on MediaTek MT7925
From: ElXreno @ 2026-05-06 1:39 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
ElXreno, stable
This series fixes two TDLS direct-link issues on the MediaTek mt76
driver, observed reproducibly with Samsung phones and other peers
that auto-initiate TDLS direct links on a shared BSS.
Patch 1 fixes silent loss of TDLS-peer data on mt76 chips with HW
TX encap offload. The HDR_TRANS TLV defaults to ToDS=1 for STATION
vifs, which makes the firmware build A1=BSSID for every WCID under
that vif -- including TDLS-peer WCIDs. Frames bound for a TDLS
peer therefore go on air to the AP, the AP silently discards them
per IEEE 802.11z, and no user data flows over the TDLS direct
link. v2 adds an MT_WCID_FLAG_TDLS_PEER flag (set in sta-add when
sta->tdls is true) and overrides the HDR_TRANS TLV to ToDS=0/
FromDS=0 (3-addr non-DS, per 802.11z) only for those peers. HW
encap stays on for AP and non-TDLS traffic. The override is
applied in three HDR_TRANS helpers:
- mt76_connac_mcu_wtbl_hdr_trans_tlv() for mt7915/mt7921/mt7922
- mt7925_mcu_sta_hdr_trans_tlv() for mt7925
- mt7996_mcu_sta_hdr_trans_tlv() for mt7996
Verified on mt7925e + a Samsung phone over 5 GHz HE 80 MHz:
iperf3 -t 30 transferred 2.90 GBytes at 830 Mbit/s with 0 TCP
retransmits. The other chips are not regression-tested for lack
of hardware; their HDR_TRANS handling mirrors the verified
mt7925 change.
Patch 2 fixes a regression introduced by the MLO refactor in
commit 3878b4333602 ("wifi: mt76: mt7925: update
mt7925_mac_link_sta_[add, assoc, remove] for MLO"): the cleanup
loop in mt7925_mac_sta_remove_links() unconditionally calls
mt7925_mcu_add_bss_info(..., enable=false) for every link of the
station being removed, including TDLS peers on a STATION vif which
share the AP's bss_conf -- wiping the AP-side rate-control context
on every TDLS teardown and collapsing rx bitrate to 6 Mbit/s for
tens of seconds.
v2 design notes:
I tried adding the TDLS-aware firmware-facing peer setup that
v1's commit message identified as missing first -- defining
CONNECTION_TDLS = (STA_TYPE_STA | NETWORK_INFRA | BIT(3)) and
setting it in mt76_connac_mcu_sta_basic_tlv() for sta->tdls peers
hung the MCU on STA_REC_UPDATE within ~5 seconds of iperf3
traffic, requiring a chip reset. The proprietary out-of-tree
mt_wifi driver also defines CONNECTION_TDLS in mt_cmd.h but never
assigns it; TDLS there is implemented purely as an 802.11
action-frame state machine on top of CONNECTION_INFRA_STA, with
no TDLS-specific MCU command, and is gated to Connac1-era chips
only -- no firmware-facing TDLS peer setup to align to. The
HDR_TRANS ToDS override is the simplest per-peer correction that
works.
A note on the methodology behind the misroute observation: my
v1 captures used an RTL8821AU USB monitor which is single-stream
and cannot decode mt7925's 2-stream HE data frames. The
misroute-to-AP pattern only became visible after swapping to a
2x2 Intel AC 7265 in monitor mode on a separate host -- frames
go on air with A1=BSSID to the AP, the AP MAC-ACKs and discards
per 802.11z, which matches the iw counters (tx_packets++ but no
TCP ACKs returning).
v2:
- patch 1 rewritten per Sean Wang's v1 NACK: per-TDLS-peer
HDR_TRANS override instead of global SUPPORTS_TX_ENCAP_OFFLOAD
removal in mt792x_init_wiphy(). Coverage extended to mt7915
and mt7996.
- patch 1 dropped Fixes: 5c14a5f944b9 and Cc: stable -- the new
fix introduces a flag plus three helper overrides; not
realistic for a clean cherry-pick into older stables.
- patch 2 unchanged in logic; inline comment trimmed.
v1: https://lore.kernel.org/linux-wireless/20260503-mt7925-tdls-fixes-v1-0-dde847e21081@gmail.com/
Signed-off-by: ElXreno <elxreno@gmail.com>
---
ElXreno (2):
wifi: mt76: route TDLS-peer frames as 3-addr non-DS in HW encap
wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 5 +++++
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 6 ++++++
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++++
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++++
8 files changed, 31 insertions(+)
---
base-commit: d2ffdadd4aca41d120e25f6a675dd6a4d77ce360
change-id: 20260503-mt7925-tdls-fixes-1d1397420900
Best regards,
--
ElXreno <elxreno@gmail.com>
^ permalink raw reply
* [PATCH v2 1/2] wifi: mt76: route TDLS-peer frames as 3-addr non-DS in HW encap
From: ElXreno @ 2026-05-06 1:39 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
ElXreno
In-Reply-To: <20260506-mt7925-tdls-fixes-v2-0-46aa826ba8bb@gmail.com>
With HW TX encap offload enabled, the mt76 firmware builds the 802.11
header for the 802.3 frame using the per-WCID context. For a STATION
vif the HDR_TRANS TLV currently sets ToDS=1, which makes the firmware
default to the BSSID as A1 and emit STA->AP-formatted frames
regardless of which peer the WCID points to.
For TDLS-paired peers this is wrong. Data frames go on air addressed
to the AP, the AP MAC-ACKs and silently drops them per IEEE 802.11z
(an AP must not forward to a TDLS-paired peer). Management and
control frames bypass the HW encap path and still reach the peer;
only user data fails.
Add MT_WCID_FLAG_TDLS_PEER, set it in mt7915, mt7921, mt7925 and
mt7996 sta-add paths when sta->tdls is true, and override the
HDR_TRANS TLV in mt76_connac_mcu_wtbl_hdr_trans_tlv() (Connac2 -
mt7915 / mt7921 / mt7922), mt7925_mcu_sta_hdr_trans_tlv() (mt7925)
and mt7996_mcu_sta_hdr_trans_tlv() (mt7996) to set ToDS=0, FromDS=0
when the flag is set. The 3-addr non-DS form matches what 802.11z
uses for direct links; the firmware then constructs the frame with
A1=peer rather than A1=BSSID. HW encap offload remains enabled for
AP and any non-TDLS traffic.
Verified on mt7925e + Samsung S938B over a 5 GHz HE 80 MHz channel
with iperf3 -t 30 to the TDLS peer:
before fix: over the TDLS direct link, 7 TDLS Setup action
frames and 3 RTS frames reach the peer; 0 QoS
Data frames make it through (mgmt/control paths
bypass HW encap, the data path does not). iperf3
stalls.
after fix: 2.90 GBytes transferred at 830 Mbit/s sustained,
0 TCP retransmits.
mt7915, mt7921, mt7922 and mt7996 are not regression-tested in this
change for lack of hardware. Their HDR_TRANS handling mirrors the
verified mt7925 change; the firmware behavior is shared across these
chips.
Signed-off-by: ElXreno <elxreno@gmail.com>
Assisted-by: Claude:claude-opus-4-7 bpftrace tcpdump
---
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c | 5 +++++
drivers/net/wireless/mediatek/mt76/mt7915/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 5 +++++
drivers/net/wireless/mediatek/mt76/mt7996/main.c | 3 +++
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5 +++++
8 files changed, 28 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 527bef97e122..07955555f84d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -361,6 +361,7 @@ enum mt76_wcid_flags {
MT_WCID_FLAG_PS,
MT_WCID_FLAG_4ADDR,
MT_WCID_FLAG_HDR_TRANS,
+ MT_WCID_FLAG_TDLS_PEER,
};
#define MT76_N_WCIDS 1088
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 89bd52ea8bf7..f61d0625ef51 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -491,6 +491,11 @@ void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb,
htr->to_ds = true;
htr->from_ds = true;
}
+
+ if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) {
+ htr->to_ds = false;
+ htr->from_ds = false;
+ }
}
EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_hdr_trans_tlv);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index e1d83052aa6d..51643a48ed15 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -760,6 +760,9 @@ int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
msta->wcid.phy_idx = ext_phy;
msta->jiffies = jiffies;
+ if (sta->tdls)
+ set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->wcid.flags);
+
ewma_avg_signal_init(&msta->avg_ack_signal);
mt7915_mac_wtbl_update(dev, idx,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 3d74fabe7408..d39cb881d75e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -828,6 +828,9 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
msta->deflink.last_txs = jiffies;
msta->deflink.sta = msta;
+ if (sta->tdls)
+ set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->deflink.wcid.flags);
+
ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
if (ret)
return ret;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..61330e3c18b2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -887,6 +887,9 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
mlink->wcid.link_valid = !!link_sta->sta->valid_links;
mlink->sta = msta;
+ if (link_sta->sta->tdls)
+ set_bit(MT_WCID_FLAG_TDLS_PEER, &mlink->wcid.flags);
+
wcid = &mlink->wcid;
ewma_signal_init(&wcid->rssi);
rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 22bad3cba8df..333cacfaca9c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1091,6 +1091,11 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
hdr_trans->to_ds = true;
hdr_trans->from_ds = true;
}
+
+ if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) {
+ hdr_trans->to_ds = false;
+ hdr_trans->from_ds = false;
+ }
}
int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index a8a6552d49f6..9b8db6efb5ac 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -1164,6 +1164,9 @@ mt7996_mac_sta_init_link(struct mt7996_dev *dev,
msta_link->wcid.link_valid = !!sta->valid_links;
msta_link->wcid.def_wcid = &msta->deflink.wcid;
+ if (link_sta->sta->tdls)
+ set_bit(MT_WCID_FLAG_TDLS_PEER, &msta_link->wcid.flags);
+
ewma_avg_signal_init(&msta_link->avg_ack_signal);
ewma_signal_init(&msta_link->wcid.rssi);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 16420375112d..6aaf3ed94221 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -2139,6 +2139,11 @@ mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
hdr_trans->from_ds = true;
hdr_trans->mesh = true;
}
+
+ if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) {
+ hdr_trans->to_ds = false;
+ hdr_trans->from_ds = false;
+ }
}
static enum mcu_mmps_mode
--
2.53.0
^ permalink raw reply related
* [PATCH v2 2/2] wifi: mt76: mt7925: don't disable AP BSS when removing TDLS peer
From: ElXreno @ 2026-05-06 1:39 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
Ming Yen Hsieh, Deren Wu
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek,
ElXreno, stable
In-Reply-To: <20260506-mt7925-tdls-fixes-v2-0-46aa826ba8bb@gmail.com>
On a STATION vif, removing a TDLS peer takes the mt7925_mac_sta_remove
-> mt7925_mac_sta_remove_links path. The first loop in that function
calls mt7925_mcu_add_bss_info(..., enable=false) for every link of the
station being removed. For a non-MLO STATION vif there is exactly one
link, link 0, whose bss_conf is the AP's. TDLS peers do not have their
own bss_conf - they share the AP's BSS.
The result is that every TDLS peer teardown sends a BSS_INFO_UPDATE
with enable=0 for the AP's BSS to the firmware, which wipes the AP-side
rate-control context. The connection stays associated and TX from the
host still works at the negotiated rate, but the AP's downlink to us
collapses to the lowest mandatory OFDM rate (HE-MCS 0 / 6 Mbit/s OFDM)
and only slowly recovers as rate adaptation re-learns under sustained
traffic. With brief or bursty traffic the link can stay at 6-72 Mbit/s
indefinitely, requiring a manual reconnect.
mt7925_mac_link_sta_remove() already guards its own
mt7925_mcu_add_bss_info(..., false) call with
"vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls".
Add the equivalent guard at the top of the cleanup loop in
mt7925_mac_sta_remove_links(), above the link_sta / link_conf /
mlink / mconf lookups, so TDLS peer teardown skips the loop body
entirely without doing the per-link work that would just be thrown
away.
Verified on mt7925e by triggering Samsung-S938B auto-TDLS via iperf3
and watching iw rx bitrate after teardown:
Before: rx bitrate collapses to 6.0-72.0 Mbit/s, oscillates 17/72/
137/288/432 Mbit/s for 30+ seconds, no full recovery without
a manual reassoc.
After: rx bitrate stays at 1200.9 Mbit/s HE-MCS 11 NSS 2 80 MHz
across the entire TDLS lifecycle.
bpftrace confirms a single mt7925_mcu_add_bss_info(enable=0) call per
teardown before the fix; zero such calls after.
Fixes: 3878b4333602 ("wifi: mt76: mt7925: update mt7925_mac_link_sta_[add, assoc, remove] for MLO")
Cc: stable@vger.kernel.org
Signed-off-by: ElXreno <elxreno@gmail.com>
Assisted-by: Claude:claude-opus-4-7 bpftrace
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 61330e3c18b2..041d06143ef1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1268,6 +1268,9 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,
if (vif->type == NL80211_IFTYPE_AP)
break;
+ if (vif->type == NL80211_IFTYPE_STATION && sta->tdls)
+ continue;
+
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
if (!link_sta)
continue;
--
2.53.0
^ permalink raw reply related
* [wireless-next:main] BUILD SUCCESS d2ffdadd4aca41d120e25f6a675dd6a4d77ce360
From: kernel test robot @ 2026-05-06 1:48 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
branch HEAD: d2ffdadd4aca41d120e25f6a675dd6a4d77ce360 wifi: mac80211: check AP using NPCA has NPCA capability
elapsed time: 732m
configs tested: 291
configs skipped: 29
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-23
arc defconfig gcc-15.2.0
arc randconfig-001-20260506 gcc-12.5.0
arc randconfig-002-20260506 gcc-12.5.0
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm exynos_defconfig clang-23
arm randconfig-001-20260506 gcc-12.5.0
arm randconfig-002-20260506 gcc-12.5.0
arm randconfig-003-20260506 gcc-12.5.0
arm randconfig-004-20260506 gcc-12.5.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001 gcc-14.3.0
arm64 randconfig-001-20260505 gcc-14.3.0
arm64 randconfig-001-20260506 gcc-15.2.0
arm64 randconfig-002 gcc-14.3.0
arm64 randconfig-002-20260505 gcc-14.3.0
arm64 randconfig-002-20260506 gcc-15.2.0
arm64 randconfig-003 gcc-14.3.0
arm64 randconfig-003-20260505 gcc-14.3.0
arm64 randconfig-003-20260506 gcc-15.2.0
arm64 randconfig-004 gcc-14.3.0
arm64 randconfig-004-20260505 gcc-14.3.0
arm64 randconfig-004-20260506 gcc-15.2.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001 gcc-14.3.0
csky randconfig-001-20260505 gcc-14.3.0
csky randconfig-001-20260506 gcc-15.2.0
csky randconfig-002 gcc-14.3.0
csky randconfig-002-20260505 gcc-14.3.0
csky randconfig-002-20260506 gcc-15.2.0
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260505 clang-23
hexagon randconfig-001-20260506 clang-23
hexagon randconfig-001-20260506 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260505 clang-23
hexagon randconfig-002-20260506 clang-23
hexagon randconfig-002-20260506 gcc-11.5.0
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001 clang-20
i386 buildonly-randconfig-001-20260505 gcc-14
i386 buildonly-randconfig-001-20260506 clang-20
i386 buildonly-randconfig-002 clang-20
i386 buildonly-randconfig-002-20260505 gcc-14
i386 buildonly-randconfig-002-20260506 clang-20
i386 buildonly-randconfig-003 clang-20
i386 buildonly-randconfig-003-20260505 gcc-14
i386 buildonly-randconfig-003-20260506 clang-20
i386 buildonly-randconfig-004 clang-20
i386 buildonly-randconfig-004-20260505 gcc-14
i386 buildonly-randconfig-004-20260506 clang-20
i386 buildonly-randconfig-005 clang-20
i386 buildonly-randconfig-005-20260505 gcc-14
i386 buildonly-randconfig-005-20260506 clang-20
i386 buildonly-randconfig-006 clang-20
i386 buildonly-randconfig-006-20260505 gcc-14
i386 buildonly-randconfig-006-20260506 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001 gcc-14
i386 randconfig-001-20260505 clang-20
i386 randconfig-001-20260506 gcc-14
i386 randconfig-002 gcc-14
i386 randconfig-002-20260505 clang-20
i386 randconfig-002-20260506 gcc-14
i386 randconfig-003 gcc-14
i386 randconfig-003-20260505 clang-20
i386 randconfig-003-20260506 gcc-14
i386 randconfig-004 gcc-14
i386 randconfig-004-20260505 clang-20
i386 randconfig-004-20260506 gcc-14
i386 randconfig-005 gcc-14
i386 randconfig-005-20260505 clang-20
i386 randconfig-005-20260506 gcc-14
i386 randconfig-006 gcc-14
i386 randconfig-006-20260505 clang-20
i386 randconfig-006-20260506 gcc-14
i386 randconfig-007 gcc-14
i386 randconfig-007-20260505 clang-20
i386 randconfig-007-20260506 gcc-14
i386 randconfig-011-20260506 clang-20
i386 randconfig-012-20260506 clang-20
i386 randconfig-013-20260506 clang-20
i386 randconfig-014-20260506 clang-20
i386 randconfig-014-20260506 gcc-14
i386 randconfig-015-20260506 clang-20
i386 randconfig-015-20260506 gcc-14
i386 randconfig-016-20260506 clang-20
i386 randconfig-017-20260506 clang-20
loongarch allmodconfig clang-23
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260505 clang-23
loongarch randconfig-001-20260506 clang-23
loongarch randconfig-001-20260506 gcc-11.5.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260505 clang-23
loongarch randconfig-002-20260506 clang-23
loongarch randconfig-002-20260506 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
m68k defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
microblaze defconfig gcc-15.2.0
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
nios2 alldefconfig gcc-11.5.0
nios2 allmodconfig clang-23
nios2 allnoconfig clang-23
nios2 defconfig clang-19
nios2 defconfig gcc-15.2.0
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260505 clang-23
nios2 randconfig-001-20260506 clang-23
nios2 randconfig-001-20260506 gcc-11.5.0
nios2 randconfig-001-20260506 gcc-8.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260505 clang-23
nios2 randconfig-002-20260506 clang-23
nios2 randconfig-002-20260506 gcc-11.5.0
nios2 randconfig-002-20260506 gcc-8.5.0
openrisc allmodconfig clang-23
openrisc allnoconfig clang-23
openrisc de0_nano_multicore_defconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-23
parisc allyesconfig clang-19
parisc defconfig gcc-15.2.0
parisc randconfig-001 gcc-13.4.0
parisc randconfig-001-20260505 gcc-14.3.0
parisc randconfig-001-20260506 gcc-13.4.0
parisc randconfig-002 gcc-13.4.0
parisc randconfig-002-20260505 gcc-14.3.0
parisc randconfig-002-20260506 gcc-13.4.0
parisc64 defconfig clang-19
parisc64 defconfig gcc-15.2.0
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-23
powerpc mpc8315_rdb_defconfig clang-23
powerpc randconfig-001 gcc-13.4.0
powerpc randconfig-001-20260505 gcc-14.3.0
powerpc randconfig-001-20260506 gcc-13.4.0
powerpc randconfig-002 gcc-13.4.0
powerpc randconfig-002-20260505 gcc-14.3.0
powerpc randconfig-002-20260506 gcc-13.4.0
powerpc64 randconfig-001 gcc-13.4.0
powerpc64 randconfig-001-20260505 gcc-14.3.0
powerpc64 randconfig-001-20260506 gcc-13.4.0
powerpc64 randconfig-002 gcc-13.4.0
powerpc64 randconfig-002-20260505 gcc-14.3.0
powerpc64 randconfig-002-20260506 gcc-13.4.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260506 gcc-8.5.0
riscv randconfig-002-20260506 gcc-8.5.0
s390 allmodconfig clang-19
s390 allnoconfig clang-23
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260506 gcc-8.5.0
s390 randconfig-002-20260506 gcc-8.5.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-23
sh allyesconfig clang-19
sh ap325rxa_defconfig gcc-15.2.0
sh defconfig gcc-14
sh randconfig-001-20260506 gcc-8.5.0
sh randconfig-002-20260506 gcc-8.5.0
sparc allnoconfig clang-23
sparc defconfig gcc-15.2.0
sparc randconfig-001 gcc-15.2.0
sparc randconfig-001-20260505 gcc-15.2.0
sparc randconfig-001-20260506 gcc-11.5.0
sparc randconfig-002 gcc-15.2.0
sparc randconfig-002-20260505 gcc-15.2.0
sparc randconfig-002-20260506 gcc-11.5.0
sparc64 allmodconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001 gcc-15.2.0
sparc64 randconfig-001-20260505 gcc-15.2.0
sparc64 randconfig-001-20260506 gcc-11.5.0
sparc64 randconfig-002 gcc-15.2.0
sparc64 randconfig-002-20260505 gcc-15.2.0
sparc64 randconfig-002-20260506 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-23
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001 gcc-15.2.0
um randconfig-001-20260505 gcc-15.2.0
um randconfig-001-20260506 gcc-11.5.0
um randconfig-002 gcc-15.2.0
um randconfig-002-20260505 gcc-15.2.0
um randconfig-002-20260506 gcc-11.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260506 clang-20
x86_64 buildonly-randconfig-002-20260506 clang-20
x86_64 buildonly-randconfig-002-20260506 gcc-14
x86_64 buildonly-randconfig-003-20260505 gcc-14
x86_64 buildonly-randconfig-003-20260506 clang-20
x86_64 buildonly-randconfig-004-20260506 clang-20
x86_64 buildonly-randconfig-005-20260505 gcc-14
x86_64 buildonly-randconfig-005-20260506 clang-20
x86_64 buildonly-randconfig-005-20260506 gcc-14
x86_64 buildonly-randconfig-006-20260505 gcc-14
x86_64 buildonly-randconfig-006-20260506 clang-20
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260505 clang-20
x86_64 randconfig-001-20260506 clang-20
x86_64 randconfig-002-20260505 clang-20
x86_64 randconfig-002-20260506 clang-20
x86_64 randconfig-003-20260505 clang-20
x86_64 randconfig-003-20260506 clang-20
x86_64 randconfig-004-20260505 clang-20
x86_64 randconfig-004-20260506 clang-20
x86_64 randconfig-005-20260505 clang-20
x86_64 randconfig-005-20260506 clang-20
x86_64 randconfig-006-20260505 clang-20
x86_64 randconfig-006-20260506 clang-20
x86_64 randconfig-011-20260506 clang-20
x86_64 randconfig-012-20260506 clang-20
x86_64 randconfig-013-20260506 clang-20
x86_64 randconfig-014-20260506 clang-20
x86_64 randconfig-015-20260506 clang-20
x86_64 randconfig-015-20260506 gcc-12
x86_64 randconfig-016-20260506 clang-20
x86_64 randconfig-016-20260506 gcc-12
x86_64 randconfig-071 clang-20
x86_64 randconfig-071-20260505 clang-20
x86_64 randconfig-071-20260506 clang-20
x86_64 randconfig-072 clang-20
x86_64 randconfig-072-20260505 clang-20
x86_64 randconfig-072-20260506 clang-20
x86_64 randconfig-073 clang-20
x86_64 randconfig-073-20260505 clang-20
x86_64 randconfig-073-20260506 clang-20
x86_64 randconfig-074 clang-20
x86_64 randconfig-074-20260505 clang-20
x86_64 randconfig-074-20260506 clang-20
x86_64 randconfig-075 clang-20
x86_64 randconfig-075-20260505 clang-20
x86_64 randconfig-075-20260506 clang-20
x86_64 randconfig-076 clang-20
x86_64 randconfig-076-20260505 clang-20
x86_64 randconfig-076-20260506 clang-20
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-23
xtensa allyesconfig clang-23
xtensa randconfig-001 gcc-15.2.0
xtensa randconfig-001-20260505 gcc-15.2.0
xtensa randconfig-001-20260506 gcc-11.5.0
xtensa randconfig-002 gcc-15.2.0
xtensa randconfig-002-20260505 gcc-15.2.0
xtensa randconfig-002-20260506 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v2 wireless-next 00/14] wifi: mac80211_hwsim: some more NAN patches
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless
Hi,
This series completes the hwsim support for NAN.
Thanks,
Miri
---
Benjamin Berg (5):
wifi: mac80211_hwsim: limit TX of frames to the NAN DW
wifi: mac80211_hwsim: select NAN TX channel based on current TSF
wifi: mac80211_hwsim: only RX on NAN when active on a slot
wifi: mac80211_hwsim: protect tsf_offset using a spinlock
wifi: mac80211_hwsim: implement NAN synchronization
Daniel Gabay (7):
wifi: mac80211_hwsim: add NAN_DATA interface limits
wifi: mac80211_hwsim: add NAN PHY capabilities
wifi: mac80211_hwsim: implement NAN schedule callbacks
wifi: mac80211_hwsim: set HAS_RATE_CONTROL when using NAN
wifi: mac80211_hwsim: add NAN data path TX/RX support
wifi: mac80211_hwsim: Declare support for secure NAN
wifi: mac80211_hwsim: enable NAN_DATA interface simulation support
Ilan Peer (2):
wifi: mac80211_hwsim: Do not declare support for NDPE
wifi: mac80211_hwsim: Support Tx of multicast data on NAN
.../net/wireless/virtual/mac80211_hwsim_i.h | 33 +-
.../wireless/virtual/mac80211_hwsim_main.c | 263 +++-
.../net/wireless/virtual/mac80211_hwsim_nan.c | 1191 ++++++++++++++++-
.../net/wireless/virtual/mac80211_hwsim_nan.h | 74 +-
4 files changed, 1454 insertions(+), 107 deletions(-)
--
2.34.1
---
v2: fix checkpatch in one of the patches
^ permalink raw reply
* [PATCH v2 wireless-next 01/14] wifi: mac80211_hwsim: limit TX of frames to the NAN DW
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Frames submitted on the NAN device interface should only be transmitted
during one of the discovery windows (DWs). It is assumed that software
submits frames from the DW end notifications for the next DW period.
Simulate this behaviour by checking that we are currently in a DW before
transmitting from ieee80211_hwsim_wake_tx_queue. As frames will be
queued up at the start of a DW, wake the management TX queue every time
a DW is started. Do so with a randomized offset just to avoid every
client transmitting at the same time.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 3 +
.../wireless/virtual/mac80211_hwsim_main.c | 11 ++-
.../net/wireless/virtual/mac80211_hwsim_nan.c | 79 +++++++++++++++++++
.../net/wireless/virtual/mac80211_hwsim_nan.h | 6 ++
4 files changed, 97 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index 6b2a5dccb106..5432de92beab 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -136,4 +136,7 @@ u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
+void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+
#endif /* __MAC80211_HWSIM_I_H */
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 3a0c4366dfdb..6740504b30e6 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -2235,14 +2235,18 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
ieee80211_tx_status_irqsafe(hw, skb);
}
-static void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
+void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
{
struct ieee80211_tx_control control = {
.sta = txq->sta,
};
struct sk_buff *skb;
+ if (txq->vif->type == NL80211_IFTYPE_NAN &&
+ !mac80211_hwsim_nan_txq_transmitting(hw, txq))
+ return;
+
while ((skb = ieee80211_tx_dequeue(hw, txq)))
mac80211_hwsim_tx(hw, &control, skb);
}
@@ -5603,6 +5607,9 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hrtimer_setup(&data->nan.slot_timer,
mac80211_hwsim_nan_slot_timer,
CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
+ hrtimer_setup(&data->nan.resume_txqs_timer,
+ mac80211_hwsim_nan_resume_txqs_timer,
+ CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
}
data->if_combination.radar_detect_widths =
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
index aa4aef0920f4..22805c3723e6 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -26,8 +26,13 @@
static_assert(16 * DWST_TU * 1024 == 8192 * 1024);
static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
+/* Quiet time at the end of each slot where TX is suppressed */
+#define NAN_CHAN_SWITCH_TIME_US 256
+
static u8 hwsim_nan_cluster_id[ETH_ALEN];
+static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data);
+
static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
{
ktime_t expires = hrtimer_get_expires(&data->nan.slot_timer);
@@ -130,6 +135,8 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
cfg80211_next_nan_dw_notif(wdev, notify_dw_chan, GFP_ATOMIC);
}
+ mac80211_hwsim_nan_resume_txqs(data);
+
mac80211_hwsim_nan_schedule_slot(data, slot + 1);
return HRTIMER_RESTART;
@@ -190,6 +197,7 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
return -EINVAL;
hrtimer_cancel(&data->nan.slot_timer);
+ hrtimer_cancel(&data->nan.resume_txqs_timer);
data->nan.device_vif = NULL;
spin_lock_bh(&hwsim_radio_lock);
@@ -231,3 +239,74 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
return 0;
}
+
+static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data)
+{
+ u32 timeout_ns;
+
+ /* Nothing to do if we are not in a DW */
+ if (!mac80211_hwsim_nan_txq_transmitting(data->hw,
+ data->nan.device_vif->txq_mgmt))
+ return;
+
+ /*
+ * Wait a bit and also randomize things so that not everyone is TXing
+ * at the same time. Each slot is 16 TU long, this waits between 100 us
+ * and 5 ms before starting to TX (unless a new frame arrives).
+ */
+ timeout_ns = get_random_u32_inclusive(100 * NSEC_PER_USEC,
+ 5 * NSEC_PER_MSEC);
+
+ hrtimer_start(&data->nan.resume_txqs_timer,
+ ns_to_ktime(timeout_ns),
+ HRTIMER_MODE_REL_SOFT);
+}
+
+enum hrtimer_restart
+mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer)
+{
+ struct mac80211_hwsim_data *data =
+ container_of(timer, struct mac80211_hwsim_data,
+ nan.resume_txqs_timer);
+
+ guard(rcu)();
+
+ /* Wake TX queue for management frames on the NAN device interface */
+ if (mac80211_hwsim_nan_txq_transmitting(data->hw,
+ data->nan.device_vif->txq_mgmt))
+ ieee80211_hwsim_wake_tx_queue(data->hw,
+ data->nan.device_vif->txq_mgmt);
+
+ return HRTIMER_NORESTART;
+}
+
+bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ u64 tsf;
+ u8 slot;
+
+ if (WARN_ON_ONCE(!data->nan.device_vif))
+ return true;
+
+ tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);
+ slot = hwsim_nan_slot_from_tsf(tsf);
+
+ /* Enforce a maximum channel switch time and guard against TX delays */
+ if (slot != hwsim_nan_slot_from_tsf(tsf + NAN_CHAN_SWITCH_TIME_US))
+ return false;
+
+ /* Check NAN device interface management frame transmission */
+ if (!txq->sta) {
+ /* Only transmit these during one of the DWs */
+ if (slot == SLOT_24GHZ_DW ||
+ (slot == SLOT_5GHZ_DW &&
+ (data->nan.bands & BIT(NL80211_BAND_5GHZ))))
+ return true;
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
index e86e7f9e9a3c..6a0780797273 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -15,11 +15,14 @@ struct mac80211_hwsim_nan_data {
struct ieee80211_channel *channel;
struct hrtimer slot_timer;
+ struct hrtimer resume_txqs_timer;
bool notify_dw;
};
enum hrtimer_restart
mac80211_hwsim_nan_slot_timer(struct hrtimer *timer);
+enum hrtimer_restart
+mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer);
int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -33,4 +36,7 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
struct cfg80211_nan_conf *conf,
u32 changes);
+bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+
#endif /* __MAC80211_HWSIM_NAN_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 02/14] wifi: mac80211_hwsim: select NAN TX channel based on current TSF
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Move the TX channel selection into the NAN specific file and select the
channel based on the current slot.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_main.c | 6 +-----
.../net/wireless/virtual/mac80211_hwsim_nan.c | 18 ++++++++++++++++++
.../net/wireless/virtual/mac80211_hwsim_nan.h | 3 +++
3 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 6740504b30e6..f0f6cb7fa894 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -2084,11 +2084,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw,
hdr = (void *)skb->data;
if (vif && vif->type == NL80211_IFTYPE_NAN && !data->tmp_chan) {
- /* For NAN Device simulation purposes, assume that NAN is always
- * on channel 6 or channel 149, unless a ROC is in progress (for
- * USD use cases).
- */
- channel = data->nan.channel;
+ channel = mac80211_hwsim_nan_get_tx_channel(hw);
if (WARN_ON(!channel)) {
ieee80211_free_txskb(hw, skb);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
index 22805c3723e6..10bbac9a4b55 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -310,3 +310,21 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
return true;
}
+
+struct ieee80211_channel *
+mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ u64 tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
+ u8 slot = hwsim_nan_slot_from_tsf(tsf);
+
+ if (slot == SLOT_24GHZ_DW)
+ return ieee80211_get_channel(hw->wiphy, 2437);
+
+ if (slot == SLOT_5GHZ_DW &&
+ data->nan.bands & BIT(NL80211_BAND_5GHZ))
+ return ieee80211_get_channel(hw->wiphy, 5745);
+
+ /* drop frame and warn, NAN_CHAN_SWITCH_TIME_US should avoid races */
+ return NULL;
+}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
index 6a0780797273..796cc17d194e 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -39,4 +39,7 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
+struct ieee80211_channel *
+mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw);
+
#endif /* __MAC80211_HWSIM_NAN_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 03/14] wifi: mac80211_hwsim: only RX on NAN when active on a slot
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
This moves the NAN receive into the main code and changes it so that
frame RX only happens when the device is active on the channel. This
limits RX to the DW slots as there is currently no datapath.
With this the globally stored channel is obsolete, remove it.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/virtual/mac80211_hwsim_main.c | 26 ++++++++----
.../net/wireless/virtual/mac80211_hwsim_nan.c | 41 +++++++++++++++++--
.../net/wireless/virtual/mac80211_hwsim_nan.h | 7 ++--
3 files changed, 58 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index f0f6cb7fa894..76f1028bc42e 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1731,6 +1731,8 @@ static bool hwsim_chans_compat(struct ieee80211_channel *c1,
struct tx_iter_data {
struct ieee80211_channel *channel;
+ struct ieee80211_rx_status *rx_status;
+ struct ieee80211_hw *hw;
bool receive;
};
@@ -1740,13 +1742,10 @@ static void mac80211_hwsim_tx_iter(void *_data, u8 *addr,
struct tx_iter_data *data = _data;
int i;
- /* For NAN Device simulation purposes, assume that NAN is always
- * on channel 6 or channel 149.
- */
if (vif->type == NL80211_IFTYPE_NAN) {
- data->receive = (data->channel &&
- (data->channel->center_freq == 2437 ||
- data->channel->center_freq == 5745));
+ data->receive = mac80211_hwsim_nan_receive(data->hw,
+ data->channel,
+ data->rx_status);
return;
}
@@ -1923,7 +1922,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
struct sk_buff *nskb;
struct tx_iter_data tx_iter_data = {
.receive = false,
+ .hw = data2->hw,
.channel = chan,
+ .rx_status = &rx_status,
};
if (data == data2)
@@ -1939,6 +1940,12 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (data->netgroup != data2->netgroup)
continue;
+ /*
+ * Set mactime early since NAN RX filtering relies on it
+ * for slot calculation
+ */
+ rx_status.mactime = sim_tsf + data2->tsf_offset;
+
if (!hwsim_chans_compat(chan, data2->tmp_chan) &&
!hwsim_chans_compat(chan, data2->channel)) {
ieee80211_iterate_active_interfaces_atomic(
@@ -1975,8 +1982,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- rx_status.mactime = sim_tsf + data2->tsf_offset;
-
mac80211_hwsim_rx(data2, &rx_status, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -6286,7 +6291,10 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
/* A frame is received from user space */
memset(&rx_status, 0, sizeof(rx_status));
if (info->attrs[HWSIM_ATTR_FREQ]) {
- struct tx_iter_data iter_data = {};
+ struct tx_iter_data iter_data = {
+ .hw = data2->hw,
+ .rx_status = &rx_status,
+ };
/* throw away off-channel packets, but allow both the temporary
* ("hw" scan/remain-on-channel), regular channels and links,
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
index 10bbac9a4b55..805848172605 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -96,7 +96,6 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
case SLOT_24GHZ_DW:
wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n",
dwst_of_dw0);
- data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437);
break;
case SLOT_24GHZ_DW + 1:
@@ -111,8 +110,6 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
case SLOT_5GHZ_DW:
if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n");
- data->nan.channel =
- ieee80211_get_channel(hw->wiphy, 5745);
}
break;
@@ -158,7 +155,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
/* set this before starting the timer, as preemption might occur */
data->nan.device_vif = vif;
data->nan.bands = conf->bands;
- data->nan.channel = ieee80211_get_channel(hw->wiphy, 2437);
/* Just run this "soon" and start in a random schedule position */
hrtimer_start(&data->nan.slot_timer,
@@ -328,3 +324,40 @@ mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw)
/* drop frame and warn, NAN_CHAN_SWITCH_TIME_US should avoid races */
return NULL;
}
+
+bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
+ struct ieee80211_channel *channel,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ u8 slot;
+
+ if (WARN_ON_ONCE(!data->nan.device_vif))
+ return false;
+
+ if (rx_status->rx_flags & RX_FLAG_MACTIME) {
+ slot = hwsim_nan_slot_from_tsf(rx_status->mactime);
+ } else {
+ u64 tsf;
+
+ /*
+ * This is not perfect, but that should be fine.
+ *
+ * Assume the frame might be a bit early in relation to our
+ * own TSF. This is largely because the TSF sync is going to be
+ * pretty bad when the frame was RXed via NL and the beacon as
+ * well as RX timestamps are not accurate.
+ */
+ tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
+ slot = hwsim_nan_slot_from_tsf(tsf + 128);
+ }
+
+ if (slot == SLOT_24GHZ_DW && channel->center_freq == 2437)
+ return true;
+
+ if (slot == SLOT_5GHZ_DW && data->nan.bands & BIT(NL80211_BAND_5GHZ) &&
+ channel->center_freq == 5745)
+ return true;
+
+ return false;
+}
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
index 796cc17d194e..af8dd7ff00cc 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -11,9 +11,6 @@ struct mac80211_hwsim_nan_data {
struct ieee80211_vif *device_vif;
u8 bands;
- /* Current channel of the NAN device */
- struct ieee80211_channel *channel;
-
struct hrtimer slot_timer;
struct hrtimer resume_txqs_timer;
bool notify_dw;
@@ -42,4 +39,8 @@ bool mac80211_hwsim_nan_txq_transmitting(struct ieee80211_hw *hw,
struct ieee80211_channel *
mac80211_hwsim_nan_get_tx_channel(struct ieee80211_hw *hw);
+bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
+ struct ieee80211_channel *channel,
+ struct ieee80211_rx_status *rx_status);
+
#endif /* __MAC80211_HWSIM_NAN_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 04/14] wifi: mac80211_hwsim: protect tsf_offset using a spinlock
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
To implement NAN synchronization in hwsim, the TSF needs to be adjusted
regularly from the RX path. Add a spinlock so that this can be done in a
safe manner.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 1 +
.../wireless/virtual/mac80211_hwsim_main.c | 34 ++++++++++++++-----
2 files changed, 26 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index 5432de92beab..7a6495ea79e7 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -102,6 +102,7 @@ struct mac80211_hwsim_data {
u32 wmediumd;
/* difference between this hw's clock and the real clock, in usecs */
+ spinlock_t tsf_offset_lock;
s64 tsf_offset;
/* Stats */
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 76f1028bc42e..2de44c5fb1ff 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -1256,13 +1256,17 @@ static inline u64 mac80211_hwsim_get_sim_tsf(void)
ktime_t mac80211_hwsim_tsf_to_boottime(struct mac80211_hwsim_data *data,
u64 tsf)
{
- return us_to_ktime(tsf - data->tsf_offset);
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ return us_to_ktime(tsf - data->tsf_offset);
+ }
}
u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
ktime_t ts)
{
- return ktime_to_us(ts + data->tsf_offset);
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ return ktime_to_us(ts) + data->tsf_offset;
+ }
}
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
@@ -1271,14 +1275,18 @@ u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct mac80211_hwsim_data *data = hw->priv;
u64 sim_time = mac80211_hwsim_get_sim_tsf();
- return sim_time + data->tsf_offset;
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ return sim_time + data->tsf_offset;
+ }
}
static __le64 __mac80211_hwsim_get_tsf(struct mac80211_hwsim_data *data)
{
u64 sim_time = mac80211_hwsim_get_sim_tsf();
- return cpu_to_le64(sim_time + data->tsf_offset);
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ return cpu_to_le64(sim_time + data->tsf_offset);
+ }
}
static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
@@ -1293,11 +1301,13 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw,
if (conf && !conf->enable_beacon)
return;
- /* adjust after beaconing with new timestamp at old TBTT */
- if (tsf > now)
- data->tsf_offset += delta;
- else
- data->tsf_offset -= delta;
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ /* adjust after beaconing with new timestamp at old TBTT */
+ if (tsf > now)
+ data->tsf_offset += delta;
+ else
+ data->tsf_offset -= delta;
+ }
}
static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw,
@@ -1577,6 +1587,8 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data,
/* TODO: get MCS */
int bitrate = 100;
+ spin_lock_bh(&data->tsf_offset_lock);
+
txrate = ieee80211_get_tx_rate(data->hw, info);
if (txrate)
bitrate = txrate->bitrate;
@@ -1602,6 +1614,8 @@ static void mac80211_hwsim_write_tsf(struct mac80211_hwsim_data *data,
10 * 8 * 10 /
bitrate);
}
+
+ spin_unlock_bh(&data->tsf_offset_lock);
}
static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
@@ -5664,6 +5678,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hw->wiphy->mbssid_max_interfaces = 8;
hw->wiphy->ema_max_profile_periodicity = 3;
+ spin_lock_init(&data->tsf_offset_lock);
+
data->rx_rssi = DEFAULT_RX_RSSI;
INIT_DELAYED_WORK(&data->roc_start, hw_roc_start);
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 05/14] wifi: mac80211_hwsim: implement NAN synchronization
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Benjamin Berg
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Benjamin Berg <benjamin.berg@intel.com>
Add all the handling to do NAN synchronization on 2.4 GHz including
sending out beacons. With this, the mac80211_hwsim NAN device also works
when used in conjunction with an external medium simulation.
Note that the TSF sync is not ideal in case of an external medium
simulation. This is because the mactime for received frames needs to be
estimated and the simulation may not update the timestamp of beacons
to the actual time that the frame was transmitted.
The implementation has an initial short phase where it scans for
clusters. This facilitates cluster joining and avoids creating a new
cluster immediately, which would result in two cluster join
notifications. It does not scan otherwise and will only see another
cluster appearing if a discovery beacon happens to be sent during the
2.4 GHz discovery window (DW).
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../net/wireless/virtual/mac80211_hwsim_i.h | 4 +
.../wireless/virtual/mac80211_hwsim_main.c | 18 +-
.../net/wireless/virtual/mac80211_hwsim_nan.c | 723 +++++++++++++++++-
.../net/wireless/virtual/mac80211_hwsim_nan.h | 43 ++
4 files changed, 743 insertions(+), 45 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index 7a6495ea79e7..5378f721c299 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -137,6 +137,10 @@ u64 mac80211_hwsim_boottime_to_tsf(struct mac80211_hwsim_data *data,
u64 mac80211_hwsim_get_tsf(struct ieee80211_hw *hw,
struct ieee80211_vif *vif);
+void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ieee80211_channel *chan);
+
void ieee80211_hwsim_wake_tx_queue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 2de44c5fb1ff..969ebc28cb1e 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -957,10 +957,6 @@ static int hwsim_get_chanwidth(enum nl80211_chan_width bw)
return INT_MAX;
}
-static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct ieee80211_channel *chan);
-
/* sysfs attributes */
static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
{
@@ -1865,6 +1861,9 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
mac80211_hwsim_add_vendor_rtap(skb);
+ if (data->nan.device_vif)
+ mac80211_hwsim_nan_rx(data->hw, skb);
+
data->rx_pkts++;
data->rx_bytes += skb->len;
ieee80211_rx_irqsafe(data->hw, skb);
@@ -2359,9 +2358,9 @@ static void mac80211_hwsim_remove_interface(
mac80211_hwsim_config_mac_nl(hw, vif->addr, false);
}
-static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb,
- struct ieee80211_channel *chan)
+void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+ struct sk_buff *skb,
+ struct ieee80211_channel *chan)
{
struct mac80211_hwsim_data *data = hw->priv;
u32 _portid = READ_ONCE(data->wmediumd);
@@ -5625,6 +5624,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
hrtimer_setup(&data->nan.resume_txqs_timer,
mac80211_hwsim_nan_resume_txqs_timer,
CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
+ hrtimer_setup(&data->nan.discovery_beacon_timer,
+ mac80211_hwsim_nan_discovery_beacon_timer,
+ CLOCK_BOOTTIME, HRTIMER_MODE_ABS_SOFT);
+
+ spin_lock_init(&data->nan.state_lock);
}
data->if_combination.radar_detect_widths =
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
index 805848172605..6053b6f8f91f 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.c
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mac80211_hwsim_nan - NAN software simulation for mac80211_hwsim
- * Copyright (C) 2025 Intel Corporation
+ * Copyright (C) 2025-2026 Intel Corporation
*/
+#include <net/cfg80211.h>
#include "mac80211_hwsim_i.h"
/* Defined as the lower 23 bits being zero */
@@ -26,11 +27,15 @@
static_assert(16 * DWST_TU * 1024 == 8192 * 1024);
static_assert(DW0_TSF_MASK + 1 == 8192 * 1024);
+/* warmup phase should be 120 seconds, which is approximately 225 DWSTs */
+#define NAN_WARMUP_DWST 225
+
+#define NAN_RSSI_CLOSE (-60)
+#define NAN_RSSI_MIDDLE (-75)
+
/* Quiet time at the end of each slot where TX is suppressed */
#define NAN_CHAN_SWITCH_TIME_US 256
-static u8 hwsim_nan_cluster_id[ETH_ALEN];
-
static void mac80211_hwsim_nan_resume_txqs(struct mac80211_hwsim_data *data);
static u64 hwsim_nan_get_timer_tsf(struct mac80211_hwsim_data *data)
@@ -45,10 +50,43 @@ static u8 hwsim_nan_slot_from_tsf(u64 tsf)
return (tsf & DWST_TSF_MASK) / ieee80211_tu_to_usec(SLOT_TU);
}
+static u64 hwsim_nan_encode_master_rank(u8 master_pref, u8 random_factor,
+ const u8 *addr)
+{
+ return ((u64)master_pref << 56) +
+ ((u64)random_factor << 48) +
+ ((u64)addr[5] << 40) +
+ ((u64)addr[4] << 32) +
+ ((u64)addr[3] << 24) +
+ ((u64)addr[2] << 16) +
+ ((u64)addr[1] << 8) +
+ ((u64)addr[0] << 0);
+}
+
+static u64 hwsim_nan_get_master_rank(struct mac80211_hwsim_data *data)
+{
+ u8 master_pref = 0;
+ u8 random_factor = 0;
+
+ if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) {
+ master_pref = data->nan.master_pref;
+ random_factor = data->nan.random_factor;
+ }
+
+ return hwsim_nan_encode_master_rank(master_pref, random_factor,
+ data->nan.device_vif->addr);
+}
+
static void
-mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot)
+mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot,
+ bool discontinuity)
{
- u64 tsf = hwsim_nan_get_timer_tsf(data);
+ u64 tsf;
+
+ if (!discontinuity)
+ tsf = hwsim_nan_get_timer_tsf(data);
+ else
+ tsf = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
/* Only called by mac80211_hwsim_nan_dw_timer from softirq context */
lockdep_assert_in_softirq();
@@ -60,16 +98,561 @@ mac80211_hwsim_nan_schedule_slot(struct mac80211_hwsim_data *data, u8 slot)
mac80211_hwsim_tsf_to_boottime(data, tsf));
}
+void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ struct mac80211_hwsim_data *data = hw->priv;
+ const struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ struct element *nan_elem = (void *)mgmt->u.beacon.variable;
+ struct ieee80211_nan_anchor_master_info *ami = NULL;
+ const struct ieee80211_nan_attr *nan_attr;
+ struct ieee80211_rx_status rx_status;
+ bool joined_cluster = false;
+ bool adopt_tsf = false;
+ bool is_sync_beacon;
+ bool is_same_cluster;
+ u64 master_rank = 0;
+ ssize_t data_len;
+ u8 slot;
+
+ /* Need a NAN vendor element at the start */
+ if (skb->len < (offsetofend(struct ieee80211_mgmt, u.beacon) + 6) ||
+ !ieee80211_is_beacon(mgmt->frame_control))
+ return;
+
+ data_len = skb->len - offsetofend(struct ieee80211_mgmt, u.beacon);
+
+ /* Copy the RX status to add a MAC timestamp if needed */
+ memcpy(&rx_status, IEEE80211_SKB_RXCB(skb),
+ sizeof(struct ieee80211_rx_status));
+
+ /* And deal with the lack of mac time stamp */
+ if ((rx_status.flag & RX_FLAG_MACTIME) != RX_FLAG_MACTIME_START) {
+ u64 tsf = mac80211_hwsim_get_tsf(hw, data->nan.device_vif);;
+
+ /* In that case there should be no timestamp */
+ WARN_ON_ONCE(rx_status.flag & RX_FLAG_MACTIME);
+
+ /* No mac timestamp, set current TSF for the frame end */
+ rx_status.flag |= RX_FLAG_MACTIME_END;
+ rx_status.mactime = tsf;
+
+ /* And translate to the start for the rest of the code */
+ rx_status.mactime =
+ ieee80211_calculate_rx_timestamp(hw, &rx_status,
+ skb->len, 0);
+ rx_status.flag &= ~RX_FLAG_MACTIME;
+ rx_status.flag |= RX_FLAG_MACTIME_START;
+
+ /* Match mac80211_hwsim_nan_receive, see comment there */
+ slot = hwsim_nan_slot_from_tsf(tsf + 128);
+ } else {
+ slot = hwsim_nan_slot_from_tsf(rx_status.mactime);
+ }
+
+ /*
+ * (overly) simplify things, only track 2.4 GHz here. Also, ignore
+ * frames outside of the 2.4 GHz DW slot, unless in the initial SCAN
+ * phase.
+ */
+ if ((slot != SLOT_24GHZ_DW &&
+ data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN) ||
+ rx_status.freq != 2437)
+ return;
+
+ /* Just ignore low RSSI beacons that we cannot sync to */
+ if (rx_status.signal < NAN_RSSI_MIDDLE)
+ return;
+
+ /* Needs to be a valid NAN cluster ID in A3 */
+ if (get_unaligned_be32(mgmt->bssid) != ((WLAN_OUI_WFA << 8) | 0x01))
+ return;
+
+ /* We are only interested in NAN beacons */
+ if (nan_elem->id != WLAN_EID_VENDOR_SPECIFIC ||
+ nan_elem->datalen < 4 ||
+ get_unaligned_be32(nan_elem->data) !=
+ (WLAN_OUI_WFA << 8 | WLAN_OUI_TYPE_WFA_NAN))
+ return;
+
+ u8 *nan_defragmented __free(kfree) = kzalloc(data_len, GFP_ATOMIC);
+ if (!nan_defragmented)
+ return;
+
+ data_len = cfg80211_defragment_element(nan_elem,
+ mgmt->u.beacon.variable,
+ data_len,
+ nan_defragmented, data_len,
+ WLAN_EID_FRAGMENT);
+
+ if (data_len < 0)
+ return;
+
+ /* Assume it is a synchronization beacon if beacon_int is 512 TUs */
+ is_sync_beacon = le16_to_cpu(mgmt->u.beacon.beacon_int) == DWST_TU;
+ is_same_cluster = ether_addr_equal(mgmt->bssid, data->nan.cluster_id);
+
+ for_each_nan_attr(nan_attr, nan_defragmented + 4, data_len - 4) {
+ if (nan_attr->attr == NAN_ATTR_MASTER_INDICATION &&
+ le16_to_cpu(nan_attr->length) >=
+ sizeof(struct ieee80211_nan_master_indication)) {
+ struct ieee80211_nan_master_indication *mi =
+ (void *)nan_attr->data;
+
+ master_rank =
+ hwsim_nan_encode_master_rank(mi->master_pref,
+ mi->random_factor,
+ mgmt->sa);
+ }
+
+ if (nan_attr->attr == NAN_ATTR_CLUSTER_INFO &&
+ le16_to_cpu(nan_attr->length) >=
+ sizeof(struct ieee80211_nan_anchor_master_info)) {
+ ami = (void *)nan_attr->data;
+
+ /*
+ * The AMBTT should be set to the beacon timestamp when
+ * the sender is the anchor master. We can simply
+ * modify the structure because we created a copy when
+ * defragmenting the NAN element.
+ */
+ if (ami->hop_count == 0)
+ ami->ambtt = cpu_to_le32(
+ le64_to_cpu(mgmt->u.beacon.timestamp));
+ }
+ }
+
+ /* Do the rest of the processing under lock */
+ spin_lock_bh(&data->nan.state_lock);
+
+ /*
+ * sync beacon should be discarded if the master rank is the same
+ * and the AMBTT is older than 16 * 512 TUs compared to our own TSF.
+ *
+ * Subtract the AMBTT from the lowered TSF. If the AMBTT is older
+ * (smaller) then the calculation will not underflow.
+ */
+ if (is_sync_beacon && ami &&
+ ami->master_rank == data->nan.current_ami.master_rank &&
+ (((u32)rx_status.mactime -
+ ieee80211_tu_to_usec(16 * 512)) -
+ le32_to_cpu(ami->ambtt)) < 0x8000000) {
+ wiphy_dbg(hw->wiphy,
+ "NAN: ignoring sync beacon with old AMBTT\n");
+ is_sync_beacon = false;
+ }
+
+ if (is_same_cluster && is_sync_beacon &&
+ master_rank > hwsim_nan_get_master_rank(data)) {
+ if (rx_status.signal > NAN_RSSI_CLOSE)
+ data->nan.master_transition_score += 3;
+ else
+ data->nan.master_transition_score += 1;
+ }
+
+ if (is_same_cluster && is_sync_beacon && ami &&
+ ((ami->master_rank == data->nan.current_ami.master_rank &&
+ ami->hop_count < data->nan.current_ami.hop_count) ||
+ (master_rank > hwsim_nan_get_master_rank(data) &&
+ ami->hop_count == data->nan.current_ami.hop_count))) {
+ if (rx_status.signal > NAN_RSSI_CLOSE)
+ data->nan.sync_transition_score += 3;
+ else
+ data->nan.sync_transition_score += 1;
+ }
+
+ /*
+ * Decide on TSF adjustments before updating any other state
+ */
+ if (is_same_cluster && is_sync_beacon && ami &&
+ data->nan.current_ami.hop_count != 0) {
+ if (le64_to_cpu(ami->master_rank) >
+ le64_to_cpu(data->nan.current_ami.master_rank) &&
+ ami->master_rank != data->nan.last_ami.master_rank)
+ adopt_tsf = true;
+
+ if (le64_to_cpu(ami->master_rank) >
+ le64_to_cpu(data->nan.current_ami.master_rank) &&
+ ami->master_rank == data->nan.last_ami.master_rank &&
+ le32_to_cpu(ami->ambtt) >
+ le32_to_cpu(data->nan.last_ami.ambtt))
+ adopt_tsf = true;
+
+ if (le64_to_cpu(ami->master_rank) <
+ le64_to_cpu(data->nan.current_ami.master_rank) &&
+ le64_to_cpu(ami->master_rank) >
+ hwsim_nan_get_master_rank(data) &&
+ ether_addr_equal(ami->master_addr,
+ data->nan.current_ami.master_addr))
+ adopt_tsf = true;
+
+ if (ami->master_rank == data->nan.current_ami.master_rank &&
+ le32_to_cpu(ami->ambtt) >
+ le32_to_cpu(data->nan.current_ami.ambtt))
+ adopt_tsf = true;
+
+ /* Anchor Master case is handled below */
+ }
+
+ /*
+ * NAN Cluster merging
+ */
+ if (!is_same_cluster && ami) {
+ u64 curr_amr;
+ u64 own_cg;
+ u64 frame_amr;
+ u64 cg;
+
+ /* Shifted down by 19 bits compared to spec */
+ frame_amr = le64_to_cpu(ami->master_rank);
+ cg = (u64)ami->master_pref << (64 - 19);
+ cg += le64_to_cpu(mgmt->u.beacon.timestamp) >> 19;
+
+ curr_amr = le64_to_cpu(data->nan.current_ami.master_rank);
+ own_cg = (u64)data->nan.current_ami.master_pref << (64 - 19);
+ own_cg += rx_status.mactime >> 19;
+
+ /*
+ * Check if the cluster shall be joined
+ *
+ * When in the "scan" phase, just join immediately.
+ */
+ if (cg > own_cg ||
+ (cg == own_cg && frame_amr > curr_amr) ||
+ data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) {
+ /* Avoid a state transition */
+ data->nan.master_transition_score = 0;
+ data->nan.sync_transition_score = 0;
+
+ /*
+ * NOTE: The spec says we should TX sync beacons on the
+ * old schedule after joining. We do not implement this.
+ */
+
+ wiphy_dbg(hw->wiphy, "NAN: joining cluster %pM\n",
+ mgmt->bssid);
+
+ joined_cluster = true;
+ adopt_tsf = true;
+
+ memcpy(&data->nan.last_ami, &data->nan.current_ami,
+ sizeof(data->nan.last_ami));
+ memcpy(&data->nan.current_ami, ami,
+ sizeof(data->nan.last_ami));
+ data->nan.current_ami.hop_count += 1;
+
+ memcpy(data->nan.cluster_id, mgmt->bssid, ETH_ALEN);
+
+ /*
+ * Assume we are UP if we joined a cluster.
+ *
+ * If the other anchor master is still in the warmup
+ * phase, then we may temporarily become the anchor
+ * master until it sets its own master preference to
+ * be non-zero.
+ */
+ data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP;
+ data->nan.random_factor_valid_dwst = 0;
+ }
+ }
+
+ /*
+ * Anchor master selection
+ */
+ /* We are not anchor master */
+ if (is_same_cluster && is_sync_beacon && ami &&
+ data->nan.current_ami.hop_count != 0) {
+ if (le64_to_cpu(data->nan.current_ami.master_rank) <
+ le64_to_cpu(ami->master_rank)) {
+ if (ami->master_rank == data->nan.last_ami.master_rank &&
+ le32_to_cpu(ami->ambtt) <=
+ le32_to_cpu(data->nan.last_ami.ambtt)) {
+ /* disregard frame */
+ } else {
+ memcpy(&data->nan.last_ami,
+ &data->nan.current_ami,
+ sizeof(data->nan.last_ami));
+ memcpy(&data->nan.current_ami, ami,
+ sizeof(data->nan.last_ami));
+ data->nan.current_ami.hop_count += 1;
+ }
+ }
+
+ if (le64_to_cpu(data->nan.current_ami.master_rank) >
+ le64_to_cpu(ami->master_rank)) {
+ if (!ether_addr_equal(data->nan.current_ami.master_addr,
+ ami->master_addr)) {
+ /* disregard frame */
+ } else {
+ u64 amr = hwsim_nan_get_master_rank(data);
+
+ if (amr > le64_to_cpu(ami->master_rank)) {
+ /* assume ourselves as anchor master */
+ wiphy_dbg(hw->wiphy,
+ "NAN: assume anchor master role\n");
+ data->nan.current_ami.master_rank =
+ cpu_to_le64(amr);
+ data->nan.current_ami.hop_count = 0;
+ memset(&data->nan.last_ami, 0,
+ sizeof(data->nan.last_ami));
+ data->nan.last_ami.ambtt =
+ data->nan.current_ami.ambtt;
+ data->nan.current_ami.ambtt = 0;
+ } else {
+ memcpy(&data->nan.last_ami,
+ &data->nan.current_ami,
+ sizeof(data->nan.last_ami));
+ memcpy(&data->nan.current_ami, ami,
+ sizeof(data->nan.last_ami));
+ data->nan.current_ami.hop_count += 1;
+ }
+ }
+ }
+
+ if (data->nan.current_ami.master_rank == ami->master_rank) {
+ if (le32_to_cpu(data->nan.current_ami.ambtt) <
+ le32_to_cpu(ami->ambtt)) {
+ data->nan.current_ami.ambtt = ami->ambtt;
+ }
+
+ if (data->nan.current_ami.hop_count >
+ ami->hop_count + 1) {
+ data->nan.current_ami.hop_count =
+ ami->hop_count + 1;
+ }
+ }
+ }
+
+ /* We are anchor master */
+ if (is_same_cluster && is_sync_beacon && ami &&
+ data->nan.current_ami.hop_count == 0) {
+ WARN_ON_ONCE(!ether_addr_equal(data->nan.current_ami.master_addr,
+ data->nan.device_vif->addr));
+
+ if (le64_to_cpu(ami->master_rank) <
+ le64_to_cpu(data->nan.current_ami.master_rank) ||
+ ether_addr_equal(ami->master_addr,
+ data->nan.current_ami.master_addr)) {
+ /* disregard */
+ } else {
+ wiphy_dbg(hw->wiphy, "NAN: lost anchor master role\n");
+ adopt_tsf = true;
+ memcpy(&data->nan.last_ami, &data->nan.current_ami,
+ sizeof(data->nan.last_ami));
+ memcpy(&data->nan.current_ami, ami,
+ sizeof(data->nan.last_ami));
+ data->nan.current_ami.hop_count += 1;
+ }
+ }
+
+ if (adopt_tsf && !data->nan.tsf_adjusted) {
+ int threshold = 5;
+ s64 adjustment;
+
+ /* Timestamp is likely inaccurate (and late) in this case */
+ if (!(IEEE80211_SKB_RXCB(skb)->flag & RX_FLAG_MACTIME))
+ threshold = 128;
+
+ adjustment =
+ le64_to_cpu(mgmt->u.beacon.timestamp) -
+ ieee80211_calculate_rx_timestamp(hw, &rx_status,
+ skb->len, 24);
+
+ scoped_guard(spinlock_bh, &data->tsf_offset_lock) {
+ if (adjustment < -threshold || adjustment > threshold) {
+ if (adjustment < -(s64)ieee80211_tu_to_usec(4) ||
+ adjustment > (s64)ieee80211_tu_to_usec(4))
+ data->nan.tsf_discontinuity = true;
+
+ wiphy_debug(hw->wiphy,
+ "NAN: Adjusting TSF by +/- %d us or more: %lld us (discontinuity: %d, from: %pM, old offset: %lld)\n",
+ threshold, adjustment,
+ data->nan.tsf_discontinuity, mgmt->sa,
+ data->tsf_offset);
+ } else {
+ /* smooth things out a little bit */
+ adjustment /= 2;
+ }
+
+ /*
+ * Do the TSF adjustment
+ * The flag prevents further adjustments until the next
+ * 2.4 GHz DW starts to avoid race conditions for
+ * in-flight packets.
+ */
+ data->nan.tsf_adjusted = true;
+ data->tsf_offset += adjustment;
+ }
+ }
+
+ spin_unlock_bh(&data->nan.state_lock);
+
+ if (joined_cluster)
+ ieee80211_nan_cluster_joined(data->nan.device_vif,
+ data->nan.cluster_id, false,
+ GFP_ATOMIC);
+}
+
static void
mac80211_hwsim_nan_exec_state_transitions(struct mac80211_hwsim_data *data)
{
+ bool notify_join = false;
+
/*
* Handle NAN role and state transitions at the end of the DW period
* in accordance to Wi-Fi Aware version 4.0 section 3.3.7 point 2, i.e.
* end of 5 GHz DW if enabled else at the end of the 2.4 GHz DW.
- *
- * TODO: Implement
*/
+
+ spin_lock(&data->nan.state_lock);
+
+ /* Handle role transitions, Wi-Fi Aware version 4.0 section 3.3.6 */
+ if (data->nan.master_transition_score < 3)
+ data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER;
+ else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER &&
+ data->nan.master_transition_score >= 3)
+ data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC;
+ else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_SYNC &&
+ data->nan.sync_transition_score >= 3)
+ data->nan.role = MAC80211_HWSIM_NAN_ROLE_NON_SYNC;
+ else if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_NON_SYNC &&
+ data->nan.sync_transition_score < 3)
+ data->nan.role = MAC80211_HWSIM_NAN_ROLE_SYNC;
+
+ /*
+ * The discovery beacon timer will stop automatically. Make sure it is
+ * running if we are master. Do not bother with a proper alignment it
+ * will sync itself to the TSF after the first TX.
+ */
+ if (data->nan.role == MAC80211_HWSIM_NAN_ROLE_MASTER &&
+ !hrtimer_active(&data->nan.discovery_beacon_timer))
+ hrtimer_start(&data->nan.discovery_beacon_timer,
+ ns_to_ktime(10 * NSEC_PER_USEC),
+ HRTIMER_MODE_REL_SOFT);
+
+ data->nan.master_transition_score = 0;
+ data->nan.sync_transition_score = 0;
+
+ if (data->nan.random_factor_valid_dwst == 0) {
+ u64 amr;
+
+ if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN) {
+ data->nan.phase = MAC80211_HWSIM_NAN_PHASE_WARMUP;
+ data->nan.random_factor_valid_dwst = NAN_WARMUP_DWST;
+
+ notify_join = true;
+ } else {
+ data->nan.phase = MAC80211_HWSIM_NAN_PHASE_UP;
+ data->nan.random_factor_valid_dwst =
+ get_random_u32_inclusive(120, 240);
+ data->nan.random_factor = get_random_u8();
+ }
+
+ amr = hwsim_nan_get_master_rank(data);
+
+ if (data->nan.current_ami.hop_count == 0) {
+ /* Update if we are already anchor master */
+ data->nan.current_ami.master_rank = cpu_to_le64(amr);
+ } else if (le64_to_cpu(data->nan.current_ami.master_rank) < amr) {
+ /* assume role if we have a higher rank */
+ wiphy_dbg(data->hw->wiphy,
+ "NAN: assume anchor master role\n");
+ data->nan.current_ami.master_rank = cpu_to_le64(amr);
+ data->nan.current_ami.hop_count = 0;
+ memset(&data->nan.last_ami, 0,
+ sizeof(data->nan.last_ami));
+ data->nan.last_ami.ambtt = data->nan.current_ami.ambtt;
+ data->nan.current_ami.ambtt = 0;
+ }
+ } else {
+ data->nan.random_factor_valid_dwst--;
+ }
+
+ spin_unlock(&data->nan.state_lock);
+
+ if (notify_join)
+ ieee80211_nan_cluster_joined(data->nan.device_vif,
+ data->nan.cluster_id, true,
+ GFP_ATOMIC);
+}
+
+static void
+mac80211_hwsim_nan_tx_beacon(struct mac80211_hwsim_data *data,
+ bool is_discovery,
+ struct ieee80211_channel *channel)
+{
+ struct ieee80211_vendor_ie nan_ie = {
+ .element_id = WLAN_EID_VENDOR_SPECIFIC,
+ .len = 27 - 2,
+ .oui = { u32_get_bits(WLAN_OUI_WFA, 0xff0000),
+ u32_get_bits(WLAN_OUI_WFA, 0xff00),
+ u32_get_bits(WLAN_OUI_WFA, 0xff) },
+ .oui_type = WLAN_OUI_TYPE_WFA_NAN,
+ };
+ size_t alloc_size =
+ IEEE80211_TX_STATUS_HEADROOM +
+ offsetofend(struct ieee80211_mgmt, u.beacon) +
+ 27 /* size of NAN vendor element */;
+ struct ieee80211_nan_master_indication master_indication;
+ struct ieee80211_nan_attr nan_attr;
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+
+ /*
+ * TODO: Should the configured vendor elements or NAN attributes be
+ * included in some of these beacons?
+ */
+
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (!skb)
+ return;
+
+ spin_lock(&data->nan.state_lock);
+
+ skb_reserve(skb, IEEE80211_TX_STATUS_HEADROOM);
+ mgmt = skb_put(skb, offsetofend(struct ieee80211_mgmt, u.beacon));
+
+ memset(mgmt, 0, offsetofend(struct ieee80211_mgmt, u.beacon));
+ memcpy(mgmt->sa, data->nan.device_vif->addr, ETH_ALEN);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->bssid, data->nan.cluster_id, ETH_ALEN);
+
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON);
+ mgmt->u.beacon.beacon_int = cpu_to_le16(is_discovery ? 100 : DWST_TU);
+ mgmt->u.beacon.capab_info =
+ cpu_to_le16(WLAN_CAPABILITY_SHORT_SLOT_TIME |
+ WLAN_CAPABILITY_SHORT_PREAMBLE);
+
+ /* FIXME: set these to saner values? */
+ mgmt->duration = 0;
+ mgmt->seq_ctrl = 0;
+
+ /* Put the NAN element */
+ skb_put_data(skb, &nan_ie, sizeof(nan_ie));
+
+ nan_attr.attr = NAN_ATTR_MASTER_INDICATION;
+ nan_attr.length = cpu_to_le16(sizeof(master_indication));
+ if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_UP) {
+ master_indication.master_pref = data->nan.master_pref;
+ master_indication.random_factor = data->nan.random_factor;
+ } else {
+ master_indication.master_pref = 0;
+ master_indication.random_factor = 0;
+ }
+
+ skb_put_data(skb, &nan_attr, sizeof(nan_attr));
+ skb_put_data(skb, &master_indication, sizeof(master_indication));
+
+ nan_attr.attr = NAN_ATTR_CLUSTER_INFO;
+ nan_attr.length = cpu_to_le16(sizeof(data->nan.current_ami));
+ skb_put_data(skb, &nan_attr, sizeof(nan_attr));
+ skb_put_data(skb, &data->nan.current_ami,
+ sizeof(data->nan.current_ami));
+
+ spin_unlock(&data->nan.state_lock);
+
+ mac80211_hwsim_tx_frame(data->hw, skb, channel);
}
enum hrtimer_restart
@@ -80,10 +663,12 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
nan.slot_timer);
struct ieee80211_hw *hw = data->hw;
struct ieee80211_channel *notify_dw_chan = NULL;
+ struct ieee80211_channel *beacon_sync_chan = NULL;
u64 tsf = hwsim_nan_get_timer_tsf(data);
u8 slot = hwsim_nan_slot_from_tsf(tsf);
bool dwst_of_dw0 = false;
bool dw_end = false;
+ bool tx_sync_beacon;
if (!data->nan.device_vif)
return HRTIMER_NORESTART;
@@ -92,10 +677,28 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
dwst_of_dw0 = true;
+ scoped_guard(spinlock, &data->nan.state_lock) {
+ if (data->nan.tsf_discontinuity) {
+ data->nan.tsf_discontinuity = false;
+
+ mac80211_hwsim_nan_schedule_slot(data, 32, true);
+
+ return HRTIMER_RESTART;
+ }
+
+ if (slot == SLOT_24GHZ_DW)
+ data->nan.tsf_adjusted = false;
+
+ tx_sync_beacon =
+ data->nan.phase != MAC80211_HWSIM_NAN_PHASE_SCAN &&
+ data->nan.role != MAC80211_HWSIM_NAN_ROLE_NON_SYNC;
+ }
+
switch (slot) {
case SLOT_24GHZ_DW:
wiphy_dbg(data->hw->wiphy, "Start of 2.4 GHz DW, is DW0=%d\n",
dwst_of_dw0);
+ beacon_sync_chan = ieee80211_get_channel(hw->wiphy, 2437);
break;
case SLOT_24GHZ_DW + 1:
@@ -110,6 +713,8 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
case SLOT_5GHZ_DW:
if (data->nan.bands & BIT(NL80211_BAND_5GHZ)) {
wiphy_dbg(data->hw->wiphy, "Start of 5 GHz DW\n");
+ beacon_sync_chan =
+ ieee80211_get_channel(hw->wiphy, 5745);
}
break;
@@ -122,6 +727,10 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
break;
}
+ /* TODO: This does not implement DW contention mitigation */
+ if (beacon_sync_chan && tx_sync_beacon)
+ mac80211_hwsim_nan_tx_beacon(data, false, beacon_sync_chan);
+
if (dw_end)
mac80211_hwsim_nan_exec_state_transitions(data);
@@ -134,7 +743,50 @@ mac80211_hwsim_nan_slot_timer(struct hrtimer *timer)
mac80211_hwsim_nan_resume_txqs(data);
- mac80211_hwsim_nan_schedule_slot(data, slot + 1);
+ mac80211_hwsim_nan_schedule_slot(data, slot + 1, false);
+
+ return HRTIMER_RESTART;
+}
+
+enum hrtimer_restart
+mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer)
+{
+ struct mac80211_hwsim_data *data =
+ container_of(timer, struct mac80211_hwsim_data,
+ nan.discovery_beacon_timer);
+ u32 remainder;
+ u64 tsf_now;
+ u64 tbtt;
+
+ if (!data->nan.device_vif)
+ return HRTIMER_NORESTART;
+
+ scoped_guard(spinlock, &data->nan.state_lock) {
+ if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN ||
+ data->nan.role != MAC80211_HWSIM_NAN_ROLE_MASTER)
+ return HRTIMER_NORESTART;
+ }
+
+ mac80211_hwsim_nan_tx_beacon(
+ data, true, ieee80211_get_channel(data->hw->wiphy, 2437));
+
+ if (data->nan.bands & BIT(NL80211_BAND_5GHZ))
+ mac80211_hwsim_nan_tx_beacon(
+ data, true,
+ ieee80211_get_channel(data->hw->wiphy, 5745));
+
+ /* Read the TSF from the current time in case of adjustments */
+ tsf_now = mac80211_hwsim_get_tsf(data->hw, data->nan.device_vif);
+
+ /* Wrap value to be after the next TBTT */
+ tbtt = tsf_now + ieee80211_tu_to_usec(100);
+
+ /* Round TBTT down to the correct time */
+ div_u64_rem(tbtt, ieee80211_tu_to_usec(100), &remainder);
+ tbtt = tbtt - remainder;
+
+ hrtimer_set_expires(&data->nan.discovery_beacon_timer,
+ mac80211_hwsim_tsf_to_boottime(data, tbtt));
return HRTIMER_RESTART;
}
@@ -144,7 +796,6 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct cfg80211_nan_conf *conf)
{
struct mac80211_hwsim_data *data = hw->priv;
- struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif);
if (vif->type != NL80211_IFTYPE_NAN)
return -EINVAL;
@@ -156,28 +807,29 @@ int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
data->nan.device_vif = vif;
data->nan.bands = conf->bands;
+ scoped_guard(spinlock_bh, &data->nan.state_lock) {
+ /* Start in the "scan" phase and stay there for a little bit */
+ data->nan.phase = MAC80211_HWSIM_NAN_PHASE_SCAN;
+ data->nan.random_factor_valid_dwst = 1;
+ data->nan.random_factor = 0;
+ data->nan.master_pref = conf->master_pref;
+ data->nan.role = MAC80211_HWSIM_NAN_ROLE_MASTER;
+ memset(&data->nan.current_ami, 0,
+ sizeof(data->nan.current_ami));
+ memset(&data->nan.last_ami, 0, sizeof(data->nan.last_ami));
+ data->nan.current_ami.master_rank =
+ cpu_to_le64(hwsim_nan_get_master_rank(data));
+ }
+
/* Just run this "soon" and start in a random schedule position */
hrtimer_start(&data->nan.slot_timer,
ns_to_ktime(10 * NSEC_PER_USEC),
HRTIMER_MODE_REL_SOFT);
- if (!is_zero_ether_addr(conf->cluster_id) &&
- is_zero_ether_addr(hwsim_nan_cluster_id)) {
- memcpy(hwsim_nan_cluster_id, conf->cluster_id, ETH_ALEN);
- } else if (is_zero_ether_addr(hwsim_nan_cluster_id)) {
- hwsim_nan_cluster_id[0] = 0x50;
- hwsim_nan_cluster_id[1] = 0x6f;
- hwsim_nan_cluster_id[2] = 0x9a;
- hwsim_nan_cluster_id[3] = 0x01;
- hwsim_nan_cluster_id[4] = get_random_u8();
- hwsim_nan_cluster_id[5] = get_random_u8();
- }
+ ether_addr_copy(data->nan.cluster_id, conf->cluster_id);
data->nan.notify_dw = conf->enable_dw_notification;
- cfg80211_nan_cluster_joined(wdev, hwsim_nan_cluster_id, true,
- GFP_KERNEL);
-
return 0;
}
@@ -185,8 +837,6 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mac80211_hwsim_data *data = hw->priv;
- struct mac80211_hwsim_data *data2;
- bool nan_cluster_running = false;
if (vif->type != NL80211_IFTYPE_NAN || !data->nan.device_vif ||
data->nan.device_vif != vif)
@@ -194,20 +844,9 @@ int mac80211_hwsim_nan_stop(struct ieee80211_hw *hw,
hrtimer_cancel(&data->nan.slot_timer);
hrtimer_cancel(&data->nan.resume_txqs_timer);
+ hrtimer_cancel(&data->nan.discovery_beacon_timer);
data->nan.device_vif = NULL;
- spin_lock_bh(&hwsim_radio_lock);
- list_for_each_entry(data2, &hwsim_radios, list) {
- if (data2->nan.device_vif) {
- nan_cluster_running = true;
- break;
- }
- }
- spin_unlock_bh(&hwsim_radio_lock);
-
- if (!nan_cluster_running)
- memset(hwsim_nan_cluster_id, 0, ETH_ALEN);
-
return 0;
}
@@ -233,6 +872,11 @@ int mac80211_hwsim_nan_change_config(struct ieee80211_hw *hw,
if (changes & CFG80211_NAN_CONF_CHANGED_CONFIG)
data->nan.notify_dw = conf->enable_dw_notification;
+ if (changes & CFG80211_NAN_CONF_CHANGED_PREF) {
+ scoped_guard(spinlock_bh, &data->nan.state_lock)
+ data->nan.master_pref = conf->master_pref;
+ }
+
return 0;
}
@@ -335,7 +979,10 @@ bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
if (WARN_ON_ONCE(!data->nan.device_vif))
return false;
- if (rx_status->rx_flags & RX_FLAG_MACTIME) {
+ if (data->nan.phase == MAC80211_HWSIM_NAN_PHASE_SCAN)
+ return channel->center_freq == 2437;
+
+ if (rx_status->flag & RX_FLAG_MACTIME) {
slot = hwsim_nan_slot_from_tsf(rx_status->mactime);
} else {
u64 tsf;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
index af8dd7ff00cc..3199e5c5376b 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_nan.h
@@ -7,6 +7,18 @@
#ifndef __MAC80211_HWSIM_NAN_H
#define __MAC80211_HWSIM_NAN_H
+enum mac80211_hwsim_nan_phase {
+ MAC80211_HWSIM_NAN_PHASE_SCAN,
+ MAC80211_HWSIM_NAN_PHASE_WARMUP,
+ MAC80211_HWSIM_NAN_PHASE_UP,
+};
+
+enum mac80211_hwsim_nan_role {
+ MAC80211_HWSIM_NAN_ROLE_MASTER,
+ MAC80211_HWSIM_NAN_ROLE_SYNC,
+ MAC80211_HWSIM_NAN_ROLE_NON_SYNC,
+};
+
struct mac80211_hwsim_nan_data {
struct ieee80211_vif *device_vif;
u8 bands;
@@ -14,12 +26,40 @@ struct mac80211_hwsim_nan_data {
struct hrtimer slot_timer;
struct hrtimer resume_txqs_timer;
bool notify_dw;
+
+ struct hrtimer discovery_beacon_timer;
+
+ /* Later members are protected by this lock */
+ spinlock_t state_lock;
+
+ u8 master_pref;
+ u8 random_factor;
+
+ u8 random_factor_valid_dwst;
+
+ enum mac80211_hwsim_nan_phase phase;
+ enum mac80211_hwsim_nan_role role;
+
+ u8 cluster_id[ETH_ALEN];
+
+ struct ieee80211_nan_anchor_master_info current_ami;
+ struct ieee80211_nan_anchor_master_info last_ami;
+
+ /* Wi-Fi Aware version 4.0, section 3.3.6.1 and 3.3.6.2 */
+ int master_transition_score;
+ /* Wi-Fi Aware version 4.0, section 3.3.6.3 and 3.3.6.4 */
+ int sync_transition_score;
+
+ bool tsf_adjusted;
+ bool tsf_discontinuity;
};
enum hrtimer_restart
mac80211_hwsim_nan_slot_timer(struct hrtimer *timer);
enum hrtimer_restart
mac80211_hwsim_nan_resume_txqs_timer(struct hrtimer *timer);
+enum hrtimer_restart
+mac80211_hwsim_nan_discovery_beacon_timer(struct hrtimer *timer);
int mac80211_hwsim_nan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -43,4 +83,7 @@ bool mac80211_hwsim_nan_receive(struct ieee80211_hw *hw,
struct ieee80211_channel *channel,
struct ieee80211_rx_status *rx_status);
+void mac80211_hwsim_nan_rx(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
#endif /* __MAC80211_HWSIM_NAN_H */
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 06/14] wifi: mac80211_hwsim: add NAN_DATA interface limits
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Gabay
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Daniel Gabay <daniel.gabay@intel.com>
Increase interface limits for NAN_DATA interface.
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/virtual/mac80211_hwsim_i.h | 4 ++--
drivers/net/wireless/virtual/mac80211_hwsim_main.c | 8 +++++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_i.h b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
index 5378f721c299..d182b5117bfb 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_i.h
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_i.h
@@ -4,7 +4,7 @@
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
#ifndef __MAC80211_HWSIM_I_H
@@ -39,7 +39,7 @@ struct mac80211_hwsim_data {
struct ieee80211_channel channels_s1g[HWSIM_NUM_S1G_CHANNELS_US];
struct ieee80211_rate rates[HWSIM_NUM_RATES];
struct ieee80211_iface_combination if_combination;
- struct ieee80211_iface_limit if_limits[4];
+ struct ieee80211_iface_limit if_limits[5];
int n_if_limits;
/* Storage space for channels, etc. */
struct mac80211_hwsim_phy_data *phy_data;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 969ebc28cb1e..3397acfe61c8 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -4,7 +4,7 @@
* Copyright (c) 2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2011, Javier Lopez <jlopex@gmail.com>
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
/*
@@ -5631,6 +5631,12 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
spin_lock_init(&data->nan.state_lock);
}
+ if (param->iftypes & BIT(NL80211_IFTYPE_NAN_DATA)) {
+ data->if_limits[n_limits].max = 2;
+ data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_NAN_DATA);
+ n_limits++;
+ }
+
data->if_combination.radar_detect_widths =
BIT(NL80211_CHAN_WIDTH_5) |
BIT(NL80211_CHAN_WIDTH_10) |
--
2.34.1
^ permalink raw reply related
* [PATCH v2 wireless-next 07/14] wifi: mac80211_hwsim: add NAN PHY capabilities
From: Miri Korenblit @ 2026-05-06 3:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Daniel Gabay
In-Reply-To: <20260506034433.3328362-1-miriam.rachel.korenblit@intel.com>
From: Daniel Gabay <daniel.gabay@intel.com>
Add static HT, VHT and HE PHY capabilities to the NAN capabilities
structure. These are based on the existing band capability structures
and initialization in mac80211_hwsim.
The NAN PHY capabilities are used by mac80211 and nl80211 to
advertise device capabilities for NAN data interfaces.
Signed-off-by: Daniel Gabay <daniel.gabay@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
.../wireless/virtual/mac80211_hwsim_main.c | 102 ++++++++++++++++--
1 file changed, 94 insertions(+), 8 deletions(-)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim_main.c b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
index 3397acfe61c8..fc940b38c52c 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim_main.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim_main.c
@@ -5412,6 +5412,95 @@ static const struct ieee80211_sband_iftype_data sband_capa_6ghz[] = {
#endif
};
+#define HWSIM_VHT_MCS_MAP \
+ (IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | \
+ IEEE80211_VHT_MCS_SUPPORT_0_9 << 14)
+
+static const struct ieee80211_sta_ht_cap hwsim_nan_ht_cap = {
+ .ht_supported = true,
+ .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
+ IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 |
+ IEEE80211_HT_CAP_DSSSCCK40,
+ .ampdu_factor = 0x3,
+ .ampdu_density = 0x6,
+ .mcs = {
+ .rx_mask = { 0xff, 0xff },
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+};
+
+static const struct ieee80211_sta_vht_cap hwsim_nan_vht_cap = {
+ .vht_supported = true,
+ .cap = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 |
+ IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ |
+ IEEE80211_VHT_CAP_RXLDPC |
+ IEEE80211_VHT_CAP_SHORT_GI_80 |
+ IEEE80211_VHT_CAP_SHORT_GI_160 |
+ IEEE80211_VHT_CAP_TXSTBC |
+ IEEE80211_VHT_CAP_RXSTBC_4 |
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
+ .vht_mcs = {
+ .rx_mcs_map = cpu_to_le16(HWSIM_VHT_MCS_MAP),
+ .tx_mcs_map = cpu_to_le16(HWSIM_VHT_MCS_MAP),
+ },
+};
+
+static const struct ieee80211_sta_he_cap hwsim_nan_he_cap = {
+ .has_he = true,
+ .he_cap_elem = {
+ .mac_cap_info[0] =
+ IEEE80211_HE_MAC_CAP0_HTC_HE,
+ .mac_cap_info[1] =
+ IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US |
+ IEEE80211_HE_MAC_CAP1_MULTI_TID_AGG_RX_QOS_8,
+ .mac_cap_info[2] =
+ IEEE80211_HE_MAC_CAP2_BSR |
+ IEEE80211_HE_MAC_CAP2_MU_CASCADING |
+ IEEE80211_HE_MAC_CAP2_ACK_EN,
+ .mac_cap_info[3] =
+ IEEE80211_HE_MAC_CAP3_OMI_CONTROL |
+ IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3,
+ .mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU,
+ .phy_cap_info[0] =
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
+ IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G,
+ .phy_cap_info[1] =
+ IEEE80211_HE_PHY_CAP1_PREAMBLE_PUNC_RX_MASK |
+ IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A |
+ IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD |
+ IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS,
+ .phy_cap_info[2] =
+ IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US |
+ IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ |
+ IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ |
+ IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO |
+ IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO,
+
+ /*
+ * Leave all the other PHY capability bytes
+ * unset, as DCM, beam forming, RU and PPE
+ * threshold information are not supported
+ */
+ },
+ .he_mcs_nss_supp = {
+ .rx_mcs_80 = cpu_to_le16(0xfffa),
+ .tx_mcs_80 = cpu_to_le16(0xfffa),
+ .rx_mcs_160 = cpu_to_le16(0xfffa),
+ .tx_mcs_160 = cpu_to_le16(0xfffa),
+ .rx_mcs_80p80 = cpu_to_le16(0xfffa),
+ .tx_mcs_80p80 = cpu_to_le16(0xfffa),
+ },
+};
+
static void mac80211_hwsim_sband_capab(struct ieee80211_supported_band *sband)
{
switch (sband->band) {
@@ -5635,6 +5724,10 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
data->if_limits[n_limits].max = 2;
data->if_limits[n_limits].types = BIT(NL80211_IFTYPE_NAN_DATA);
n_limits++;
+
+ hw->wiphy->nan_capa.phy.ht = hwsim_nan_ht_cap;
+ hw->wiphy->nan_capa.phy.vht = hwsim_nan_vht_cap;
+ hw->wiphy->nan_capa.phy.he = hwsim_nan_he_cap;
}
data->if_combination.radar_detect_widths =
@@ -5816,14 +5909,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
IEEE80211_VHT_CAP_RXSTBC_4 |
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
sband->vht_cap.vht_mcs.rx_mcs_map =
- cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 6 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 8 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 14);
+ cpu_to_le16(HWSIM_VHT_MCS_MAP);
sband->vht_cap.vht_mcs.tx_mcs_map =
sband->vht_cap.vht_mcs.rx_mcs_map;
break;
--
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