* Re: [PATCH] mac80211: stop hardware before clearing driver state on reconfig failure
From: Johannes Berg @ 2026-03-13 7:08 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, Christian Lamparter
In-Reply-To: <AM7PPF5613FA0B697F9C2D2DC13F462DC029444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On Thu, 2026-03-12 at 15:30 +0100, Masi Osmani wrote:
> When ieee80211_handle_reconfig_failure() is called after a failed HW
> reconfiguration, it clears IEEE80211_SDATA_IN_DRIVER flags on all
> interfaces but does not stop the hardware.
Yeah, but ieee80211_do_stop() via cfg80211_shutdown_all_interfaces()
should call it later? Now you're calling it twice, which seems odd?
> This creates a race window:
> cfg80211_shutdown_all_interfaces() subsequently calls ieee80211_do_stop()
> which runs sta_info_flush() to destroy stations, while the driver's RX
> path may still be delivering frames that reference station data being
> freed.
How is that possible?
> This race was observed with the carl9170 driver: when firmware
> deadlocks during a restart attempt, ieee80211_reconfig() fails
> at drv_add_interface(). The subsequent interface teardown triggers
> sta_info_destroy_part2() while the USB RX tasklet still calls
> ieee80211_rx_napi(), causing a use-after-free kernel panic.
That doesn't make sense, station lookups should be protected, either by
locking or by RCU; there's synchronize_net() in __sta_info_flush() for
that.
Can you please report the actual bug?
> The fix stops the hardware in ieee80211_handle_reconfig_failure() before
> clearing IN_DRIVER state, ensuring no driver can deliver RX frames once
> the teardown begins.
I don't think that really is a good fix, and if it that crash can happen
here then it can likely also happen during normal teardown, and we
should fix it differently.
johannes
^ permalink raw reply
* Re: [PATCH] wifi: mac80211: fix WARN_ON_ONCE in ieee80211_tdls_oper
From: Johannes Berg @ 2026-03-13 7:12 UTC (permalink / raw)
To: Deepanshu Kartikey
Cc: linux-wireless, linux-kernel, syzbot+56b6a844a4ea74487b7b
In-Reply-To: <20260310160013.515096-1-kartikey406@gmail.com>
On Tue, 2026-03-10 at 21:30 +0530, Deepanshu Kartikey wrote:
>
> - WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
> - !ether_addr_equal(sdata->u.mgd.tdls_peer, peer));
> + if (is_zero_ether_addr(sdata->u.mgd.tdls_peer) ||
> + !ether_addr_equal(sdata->u.mgd.tdls_peer, peer))
> + return -ENOLINK;
>
I think that check needs to be earlier, otherwise side effects happen
(TDLS_PEER_AUTH flag).
Also, I'm a bit confused, how is it possible the sta_info_get() worked,
but there's no TDLS? Maybe really what it needs is
sta = sta_info_get(sdata, peer);
- if (!sta)
+ if (!sta || !sta->sta.tdls)
return -ENOLINK;
instead?
johannes
^ permalink raw reply
* Re: [PATCH 3/4] net: mhi_net: Implement runtime PM support
From: Krishna Chaitanya Chundru @ 2026-03-13 7:16 UTC (permalink / raw)
To: Bjorn Andersson
Cc: Manivannan Sadhasivam, Jeff Hugo, Carl Vanderlip, Oded Gabbay,
Jeff Johnson, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, mhi, linux-arm-msm, linux-kernel,
dri-devel, linux-wireless, ath11k, ath12k, netdev, mayank.rana,
quic_vbadigan, vivek.pernamitta
In-Reply-To: <nj4ovttt4i7hsqfdv5zhdotxrx3upxfq4ozuligwuheubnsmkd@e6bwzgkn55kl>
On 12/3/2025 3:07 AM, Bjorn Andersson wrote:
> On Mon, Dec 01, 2025 at 06:13:19PM +0530, Krishna Chaitanya Chundru wrote:
>> Add runtime power management support to the mhi_net driver to align with
>> the updated MHI framework, which expects runtime PM to be enabled by client
>> drivers. This ensures the controller remains active during data transfers
>> and can autosuspend when idle.
> This last sentence hints at there being an actual problem with the
> current code. Perhaps we do this because it's the right thing to do,
> perhaps we're making this change because devices are crashing and
> burning?
we are doing because its right thing to do, with current code we are not
seeing
any issues.
> Start your commit message with making the reason for your change clear.
>
> Ask yourself https://en.wikipedia.org/wiki/Five_whys when you come up
> with your problem description.
ack.
>> The driver now uses pm_runtime_get() and pm_runtime_put() around
>> transmit, receive, and buffer refill operations. Runtime PM is initialized
>> during probe with autosuspend enabled and a 100ms delay. The device is
>> marked with pm_runtime_no_callbacks() to notify PM framework that there
>> are no callbacks for this driver.
> This looks like an AI prompt, does it add value to the commit message?
>
> It can mostly be summarized as "Implement pm_runtime in the driver". The
> only part that's not obvious is the 100ms autosuspend delay, but you
> skipped explaining why you did choose this number.
100ms taken from pci port bus driver reference, I will add the reason
for 100ms
in next patch.
>> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
>> ---
>> drivers/net/mhi_net.c | 13 +++++++++++++
>> 1 file changed, 13 insertions(+)
>>
>> diff --git a/drivers/net/mhi_net.c b/drivers/net/mhi_net.c
>> index ae169929a9d8e449b5a427993abf68e8d032fae2..c5c697f29e69e9bc874b6cfff4699de12a4af114 100644
>> --- a/drivers/net/mhi_net.c
>> +++ b/drivers/net/mhi_net.c
>> @@ -9,6 +9,7 @@
>> #include <linux/mod_devicetable.h>
>> #include <linux/module.h>
>> #include <linux/netdevice.h>
>> +#include <linux/pm_runtime.h>
>> #include <linux/skbuff.h>
>> #include <linux/u64_stats_sync.h>
>>
>> @@ -76,6 +77,7 @@ static netdev_tx_t mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev)
>> struct mhi_device *mdev = mhi_netdev->mdev;
>> int err;
>>
>> + pm_runtime_get(&mdev->dev);
> What happened to your error handling?
ack I will add error handling.
- Krishna Chaitanya.
>
> Regards,
> Bjorn
>
>> err = mhi_queue_skb(mdev, DMA_TO_DEVICE, skb, skb->len, MHI_EOT);
>> if (unlikely(err)) {
>> net_err_ratelimited("%s: Failed to queue TX buf (%d)\n",
>> @@ -94,6 +96,7 @@ static netdev_tx_t mhi_ndo_xmit(struct sk_buff *skb, struct net_device *ndev)
>> u64_stats_inc(&mhi_netdev->stats.tx_dropped);
>> u64_stats_update_end(&mhi_netdev->stats.tx_syncp);
>>
>> + pm_runtime_put_autosuspend(&mdev->dev);
>> return NETDEV_TX_OK;
>> }
>>
>> @@ -261,6 +264,7 @@ static void mhi_net_ul_callback(struct mhi_device *mhi_dev,
>> }
>> u64_stats_update_end(&mhi_netdev->stats.tx_syncp);
>>
>> + pm_runtime_put_autosuspend(&mdev->dev);
>> if (netif_queue_stopped(ndev) && !mhi_queue_is_full(mdev, DMA_TO_DEVICE))
>> netif_wake_queue(ndev);
>> }
>> @@ -277,6 +281,7 @@ static void mhi_net_rx_refill_work(struct work_struct *work)
>>
>> size = mhi_netdev->mru ? mhi_netdev->mru : READ_ONCE(ndev->mtu);
>>
>> + pm_runtime_get_sync(&mdev->dev);
>> while (!mhi_queue_is_full(mdev, DMA_FROM_DEVICE)) {
>> skb = netdev_alloc_skb(ndev, size);
>> if (unlikely(!skb))
>> @@ -296,6 +301,7 @@ static void mhi_net_rx_refill_work(struct work_struct *work)
>> cond_resched();
>> }
>>
>> + pm_runtime_put_autosuspend(&mdev->dev);
>> /* If we're still starved of rx buffers, reschedule later */
>> if (mhi_get_free_desc_count(mdev, DMA_FROM_DEVICE) == mhi_netdev->rx_queue_sz)
>> schedule_delayed_work(&mhi_netdev->rx_refill, HZ / 2);
>> @@ -362,12 +368,19 @@ static int mhi_net_probe(struct mhi_device *mhi_dev,
>>
>> SET_NETDEV_DEV(ndev, &mhi_dev->dev);
>>
>> + pm_runtime_no_callbacks(&mhi_dev->dev);
>> + devm_pm_runtime_set_active_enabled(&mhi_dev->dev);
>> + pm_runtime_set_autosuspend_delay(&mhi_dev->dev, 100);
>> + pm_runtime_use_autosuspend(&mhi_dev->dev);
>> + pm_runtime_get(&mhi_dev->dev);
>> err = mhi_net_newlink(mhi_dev, ndev);
>> if (err) {
>> free_netdev(ndev);
>> + pm_runtime_put_autosuspend(&mhi_dev->dev);
>> return err;
>> }
>>
>> + pm_runtime_put_autosuspend(&mhi_dev->dev);
>> return 0;
>> }
>>
>>
>> --
>> 2.34.1
>>
>>
^ permalink raw reply
* Re: [PATCH v2 02/15] firmware: qcom: Add a generic PAS service
From: Mukesh Ojha @ 2026-03-13 7:24 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, 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
In-Reply-To: <20260312062756.694390-3-sumit.garg@kernel.org>
On Thu, Mar 12, 2026 at 11:57:43AM +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
> with S-EL2 and Hafnium support, QTEE won't be able to support SCM
using S‑EL2 with Hafnium
> 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.
>
> 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 | 298 +++++++++++++++++++++++++
> drivers/firmware/qcom/qcom_pas.h | 53 +++++
> include/linux/firmware/qcom/qcom_pas.h | 41 ++++
> 5 files changed, 401 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..beb1bae55546
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.c
> @@ -0,0 +1,298 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * 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"
> +
> +struct qcom_pas_ops *ops_ptr;
Should this be static ?
> +
> +/**
> + * 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 ops_ptr->init_image(ops_ptr->dev, pas_id,
> + metadata, size, ctx);
> +
> + return -ENODEV;
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 (!ctx || !ctx->ptr)
> + return;
> +
> + if (ops_ptr)
> + 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 ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
> + input_rt_size, output_rt_size);
> +
> + return ERR_PTR(-ENODEV);
> +}
> +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 ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
> +
> + return -ENODEV;
> +}
> +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()
> + *
> + * 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 ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->shutdown(ops_ptr->dev, pas_id);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->supported(ops_ptr->dev, pas_id);
> +
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_supported);
> +
> +/**
> + * qcom_pas_is_available() - Check for PAS service
> + *
Name of the function is self sufficient, we can avoid for one liner
documentation.
> + * Return: true on success.
> + */
> +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);
> +
> +/**
> + * qcom_pas_ops_register() - Register PAS service ops
> + * @ops: PAS service ops pointer
> + */
same here..
> +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\n");
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
> +
> +/**
> + * qcom_pas_ops_unregister() - Unregister PAS service ops
> + */
same here to avoid verbose..
> +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 common 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..4ebed22178f8
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,53 @@
> +/* 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);
I think, some of them can be unwrapped to look cleaner..
> +};
> +
> +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..ef7328ecfa47
> --- /dev/null
> +++ b/include/linux/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
Should this not carry all copyright coming from qcom_scm.h
> + */
> +
> +#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: [PATCH v2 02/15] firmware: qcom: Add a generic PAS service
From: Mukesh Ojha @ 2026-03-13 7:31 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, 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
In-Reply-To: <20260313072450.sx7vqtvh62nflhff@hu-mojha-hyd.qualcomm.com>
On Fri, Mar 13, 2026 at 12:54:50PM +0530, Mukesh Ojha wrote:
> On Thu, Mar 12, 2026 at 11:57:43AM +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
> > with S-EL2 and Hafnium support, QTEE won't be able to support SCM
>
> using S‑EL2 with Hafnium
>
> > 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.
> >
> > 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 | 298 +++++++++++++++++++++++++
> > drivers/firmware/qcom/qcom_pas.h | 53 +++++
> > include/linux/firmware/qcom/qcom_pas.h | 41 ++++
> > 5 files changed, 401 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..beb1bae55546
> > --- /dev/null
> > +++ b/drivers/firmware/qcom/qcom_pas.c
> > @@ -0,0 +1,298 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * 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"
> > +
> > +struct qcom_pas_ops *ops_ptr;
>
> Should this be static ?
>
> > +
> > +/**
> > + * 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)
please, align this with previous line '(' for all the functions.
-Mukesh
> > +{
> > + if (ops_ptr)
> > + return ops_ptr->init_image(ops_ptr->dev, pas_id,
> > + metadata, size, ctx);
> > +
> > + return -ENODEV;
>
>
> 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 (!ctx || !ctx->ptr)
> > + return;
> > +
> > + if (ops_ptr)
> > + 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 ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
> > +
> > + return -ENODEV;
> > +}
> > +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 ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
> > + input_rt_size, output_rt_size);
> > +
> > + return ERR_PTR(-ENODEV);
> > +}
> > +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 ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
> > +
> > + return -ENODEV;
> > +}
> > +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()
> > + *
> > + * 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 ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
> > +
> > + return -ENODEV;
> > +}
> > +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 ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
> > +
> > + return -ENODEV;
> > +}
> > +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 ops_ptr->shutdown(ops_ptr->dev, pas_id);
> > +
> > + return -ENODEV;
> > +}
> > +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 ops_ptr->supported(ops_ptr->dev, pas_id);
> > +
> > + return false;
> > +}
> > +EXPORT_SYMBOL_GPL(qcom_pas_supported);
> > +
> > +/**
> > + * qcom_pas_is_available() - Check for PAS service
> > + *
>
> Name of the function is self sufficient, we can avoid for one liner
> documentation.
>
> > + * Return: true on success.
> > + */
> > +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);
> > +
> > +/**
> > + * qcom_pas_ops_register() - Register PAS service ops
> > + * @ops: PAS service ops pointer
> > + */
>
> same here..
>
> > +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\n");
> > +}
> > +EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
> > +
> > +/**
> > + * qcom_pas_ops_unregister() - Unregister PAS service ops
> > + */
>
> same here to avoid verbose..
>
> > +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 common 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..4ebed22178f8
> > --- /dev/null
> > +++ b/drivers/firmware/qcom/qcom_pas.h
> > @@ -0,0 +1,53 @@
> > +/* 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);
>
> I think, some of them can be unwrapped to look cleaner..
>
> > +};
> > +
> > +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..ef7328ecfa47
> > --- /dev/null
> > +++ b/include/linux/firmware/qcom/qcom_pas.h
> > @@ -0,0 +1,41 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/*
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
>
>
> Should this not carry all copyright coming from qcom_scm.h
>
> > + */
> > +
> > +#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
--
-Mukesh Ojha
^ permalink raw reply
* Re: ath12k: handling of HE and EHT capabilities
From: Alexander Wilhelm @ 2026-03-13 7:45 UTC (permalink / raw)
To: Johannes Berg; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel
In-Reply-To: <abK42BnlaPi9J9YC@FUE-ALEWI-WINX>
On Thu, Mar 12, 2026 at 02:00:21PM +0100, Alexander Wilhelm wrote:
> On Thu, Mar 12, 2026 at 01:10:21PM +0100, Johannes Berg wrote:
> > Wait ...
> >
> > > > I don’t see this in the function. For example, the MAC capabilities are a
> > > > `u16 *` in CPU endianness, which is simply memcpy’d from the parsed
> > > > `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`,
> > > > as shown in the following code:
> > > >
> > > > printf("%s\t\tHE MAC Capabilities (0x", pre);
> > > > for (i = 0; i < 3; i++)
> > > > printf("%04x", mac_cap[i]);
> > > > printf("):\n");
> >
> > That's incorrect for sure. But iw code now actually reads
> >
> > printf("%s\t\tHE MAC Capabilities (0x", pre);
> > for (i = 0; i < 3; i++)
> > printf("%04x", le16toh(mac_cap[i]));
> > printf("):\n");
> >
> >
> > which is correct. HE PHY capabilities are printed as
> >
> > printf("%s\t\tHE PHY Capabilities: (0x", pre);
> > for (i = 0; i < 11; i++)
> > printf("%02x", ((__u8 *)phy_cap)[i + 1]);
> >
> > in my version of the code, and it seems to me the +1 is incorrect either
> > way?
> >
> > > printf("%s\t\tEHT MAC Capabilities (0x", pre);
> > > for (i = 0; i < 2; i++)
> > > printf("%02x", mac_cap[i]);
> >
> > This was also correct, not incorrect as I stated, since mac_cap is u8 *,
> > and EHT PHY capabilities are cast to u8 * first.
> >
> > Maybe your iw is just really old?
>
> Sorry, my fault. I'm using `OpenWrt v24.10.5` with `iw` version 6.9. The
> latest master has the `le16toh` implemented. With my `ath12k` fix the PHY
> capabilities and the respecitve descriptions are fine now. But I still
> cannot get MAC capabilities correct. I'll analyze it further.
Hi Johannes,
I finally have `ath12k` running with the correct capabilities. The latest
`iw` version also performs the byte swaps correctly, except for the HE MAC
capabilities output. There, each 2‑byte pair is swapped between big‑endian
and little‑endian platforms. I’m sending a patch to make this consistent
across all architectures. Thank you for the support.
Best regards
Alexander Wilhelm
^ permalink raw reply
* [PATCH] iw: util: use u8 type to print HE MAC capabilities
From: Alexander Wilhelm @ 2026-03-13 7:45 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
Currently the HE MAC capabilities are printed as u16 values. This causes
each two‑byte pair to be represented differently on big‑endian and
little‑endian platforms. Use u8 values instead to print these capabilities,
just like it is done for the HE PHY capabilities.
Signed-off-by: Alexander Wilhelm <alexander.wilhelm@westermo.com>
---
util.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/util.c b/util.c
index e6d42e5..11f8117 100644
--- a/util.c
+++ b/util.c
@@ -1304,8 +1304,8 @@ static void __print_he_capa(const __u16 *mac_cap,
#define PRINT_HE_PHY_CAP_MASK(...) PRINT_HE_CAP_MASK(phy_cap, __VA_ARGS__)
printf("%s\t\tHE MAC Capabilities (0x", pre);
- for (i = 0; i < 3; i++)
- printf("%04x", le16toh(mac_cap[i]));
+ for (i = 0; i < 6; i++)
+ printf("%02x", ((__u8 *)mac_cap)[i]);
printf("):\n");
PRINT_HE_MAC_CAP(0, 0, "+HTC HE Supported");
base-commit: 3dcef44ffd4a16a9eac49bd8016c445b14877294
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 03/15] firmware: qcom_scm: Migrate to generic PAS service
From: Mukesh Ojha @ 2026-03-13 7:56 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, 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
In-Reply-To: <20260312062756.694390-4-sumit.garg@kernel.org>
On Thu, Mar 12, 2026 at 11:57:44AM +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> With the availability of generic PAS service, let's add SCM calls as
> a backend to keep supporting legacy QTEE interfaces. The exported
> qcom_scm* wrappers will get dropped once all the client drivers get
> migrated as part of future patches.
>
> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> drivers/firmware/qcom/Kconfig | 1 +
> drivers/firmware/qcom/qcom_scm.c | 336 ++++++++++++++-----------------
> 2 files changed, 156 insertions(+), 181 deletions(-)
>
> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> index 8653639d06db..9a12ae2b639d 100644
> --- a/drivers/firmware/qcom/Kconfig
> +++ b/drivers/firmware/qcom/Kconfig
> @@ -15,6 +15,7 @@ config QCOM_PAS
> TEE bus based PAS service implementation.
>
> config QCOM_SCM
> + select QCOM_PAS
> select QCOM_TZMEM
> tristate
>
> diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
> index 8fbc96693a55..2d7937ae7c8f 100644
> --- a/drivers/firmware/qcom/qcom_scm.c
> +++ b/drivers/firmware/qcom/qcom_scm.c
> @@ -13,6 +13,7 @@
> #include <linux/dma-mapping.h>
> #include <linux/err.h>
> #include <linux/export.h>
> +#include <linux/firmware/qcom/qcom_pas.h>
> #include <linux/firmware/qcom/qcom_scm.h>
> #include <linux/firmware/qcom/qcom_tzmem.h>
> #include <linux/init.h>
> @@ -33,6 +34,7 @@
>
> #include <dt-bindings/interrupt-controller/arm-gic.h>
>
> +#include "qcom_pas.h"
> #include "qcom_scm.h"
> #include "qcom_tzmem.h"
>
> @@ -480,25 +482,6 @@ void qcom_scm_cpu_power_down(u32 flags)
> }
> EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down);
>
> -int qcom_scm_set_remote_state(u32 state, u32 id)
> -{
> - struct qcom_scm_desc desc = {
> - .svc = QCOM_SCM_SVC_BOOT,
> - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
> - .arginfo = QCOM_SCM_ARGS(2),
> - .args[0] = state,
> - .args[1] = id,
> - .owner = ARM_SMCCC_OWNER_SIP,
> - };
> - struct qcom_scm_res res;
> - int ret;
> -
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> -
> - return ret ? : res.result[0];
> -}
> -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
> -
> static int qcom_scm_disable_sdi(void)
> {
> int ret;
> @@ -571,26 +554,12 @@ static void qcom_scm_set_download_mode(u32 dload_mode)
> dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
> }
>
> -/**
> - * devm_qcom_scm_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
> - *
> - * Returns: The new PAS context, or ERR_PTR() on failure.
> - */
> struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
> u32 pas_id,
> phys_addr_t mem_phys,
> size_t mem_size)
> {
> - struct qcom_scm_pas_context *ctx;
> + struct qcom_pas_context *ctx;
Why this change..
>
> ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
> if (!ctx)
> @@ -601,11 +570,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev,
> ctx->mem_phys = mem_phys;
> ctx->mem_size = mem_size;
>
> - return ctx;
> + return (struct qcom_scm_pas_context *)ctx;
and this change as well ?
> }
> EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc);
>
> -static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
> +static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id,
> + dma_addr_t mdata_phys,
> struct qcom_scm_res *res)
> {
> struct qcom_scm_desc desc = {
> @@ -627,7 +597,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
>
> desc.args[1] = mdata_phys;
>
> - ret = qcom_scm_call(__scm->dev, &desc, res);
> + ret = qcom_scm_call(dev, &desc, res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -636,7 +606,8 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys,
> return ret;
> }
>
> -static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> +static int qcom_scm_pas_prep_and_init_image(struct device *dev,
> + struct qcom_pas_context *ctx,
> const void *metadata, size_t size)
> {
> struct qcom_scm_res res;
> @@ -651,7 +622,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> memcpy(mdata_buf, metadata, size);
> mdata_phys = qcom_tzmem_to_phys(mdata_buf);
>
> - ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res);
> + ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res);
> if (ret < 0)
> qcom_tzmem_free(mdata_buf);
> else
> @@ -660,25 +631,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx,
> return ret ? : res.result[0];
> }
>
> -/**
> - * qcom_scm_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_scm_pas_metadata_release() by the caller.
> - */
> -int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> - struct qcom_scm_pas_context *ctx)
> +static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id,
> + const void *metadata, size_t size,
> + struct qcom_pas_context *ctx)
Looks like alignment got wrong..
> {
> struct qcom_scm_res res;
> dma_addr_t mdata_phys;
> @@ -686,7 +641,8 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> int ret;
>
> if (ctx && ctx->use_tzmem)
> - return qcom_scm_pas_prep_and_init_image(ctx, metadata, size);
> + return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata,
> + size);
unwrap this..
>
> /*
> * During the scm call memory protection will be enabled for the meta
> @@ -700,16 +656,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> * If we pass a buffer that is already part of an SHM Bridge to this
> * call, it will fail.
> */
> - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys,
> - GFP_KERNEL);
> + mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL);
> if (!mdata_buf)
> return -ENOMEM;
>
> memcpy(mdata_buf, metadata, size);
>
> - ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res);
> + ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res);
> if (ret < 0 || !ctx) {
> - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys);
> + dma_free_coherent(dev, size, mdata_buf, mdata_phys);
> } else if (ctx) {
> ctx->ptr = mdata_buf;
> ctx->phys = mdata_phys;
> @@ -718,36 +673,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
>
> return ret ? : res.result[0];
> }
> -EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
>
> -/**
> - * qcom_scm_pas_metadata_release() - release metadata context
> - * @ctx: pas context
> - */
> -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
> +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size,
> + struct qcom_scm_pas_context *ctx)
> {
> - if (!ctx->ptr)
> - return;
> + return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size,
> + (struct qcom_pas_context *)ctx);
> +}
> +EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image);
>
> +static void __qcom_scm_pas_metadata_release(struct device *dev,
> + struct qcom_pas_context *ctx)
> +{
> if (ctx->use_tzmem)
> qcom_tzmem_free(ctx->ptr);
> else
> - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys);
> + dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys);
>
> ctx->ptr = NULL;
> }
> +
> +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx)
> +{
> + __qcom_scm_pas_metadata_release(__scm->dev,
> + (struct qcom_pas_context *)ctx);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release);
>
> -/**
> - * qcom_scm_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
> - *
> - * Returns 0 on success.
> - */
> -int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> +static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id,
> + phys_addr_t addr, phys_addr_t size)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -769,7 +723,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -777,9 +731,15 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
> +{
> + return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
>
> -static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> +static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id,
> + void *input_rt_tzm,
> size_t input_rt_size,
> size_t *output_rt_size)
> {
> @@ -814,7 +774,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> * with output_rt_tzm buffer with res.result[2] size however, It should not
> * be of unresonable size.
> */
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> if (!ret && res.result[2] > SZ_1G) {
> ret = -E2BIG;
> goto free_output_rt;
> @@ -831,51 +791,11 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
> return ret ? ERR_PTR(ret) : output_rt_tzm;
> }
>
> -/**
> - * qcom_scm_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 via the SMC call.
> - *
> - * 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_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
> - void *input_rt,
> - size_t input_rt_size,
> - size_t *output_rt_size)
> +static void *__qcom_scm_pas_get_rsc_table2(struct device *dev,
> + struct qcom_pas_context *ctx,
> + void *input_rt,
> + size_t input_rt_size,
> + size_t *output_rt_size)
> {
> struct resource_table empty_rsc = {};
> size_t size = SZ_16K;
> @@ -910,11 +830,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
>
> memcpy(input_rt_tzm, input_rt, input_rt_size);
>
> - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
> + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
> + input_rt_tzm,
> input_rt_size, &size);
> if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
> /* Try again with the size requested by the TZ */
> - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
> + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id,
> input_rt_tzm,
> input_rt_size,
> &size);
> @@ -945,16 +866,20 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
>
> return ret ? ERR_PTR(ret) : tbl_ptr;
> }
> +
> +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
> + void *input_rt,
> + size_t input_rt_size,
> + size_t *output_rt_size)
> +{
> + return __qcom_scm_pas_get_rsc_table2(__scm->dev,
Instead of using integar, we could use addition of more '_' to reflect
inner level functions..
> + (struct qcom_pas_context *)ctx,
> + input_rt, input_rt_size,
> + output_rt_size);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
>
> -/**
> - * qcom_scm_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_scm_pas_auth_and_reset(u32 pas_id)
> +static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -974,7 +899,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -982,28 +907,15 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_auth_and_reset(u32 pas_id)
> +{
> + return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset);
>
> -/**
> - * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
> - * remote processor
> - *
> - * @ctx: Context saved during call to qcom_scm_pas_context_init()
> - *
> - * 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_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> +static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev,
> + struct qcom_pas_context *ctx)
> {
> u64 handle;
> int ret;
> @@ -1014,7 +926,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> * memory region and then invokes a call to TrustZone to authenticate.
> */
> if (!ctx->use_tzmem)
> - return qcom_scm_pas_auth_and_reset(ctx->pas_id);
> + return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
>
> /*
> * When Linux runs @ EL2 Linux must create the shmbridge itself and then
> @@ -1024,20 +936,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> if (ret)
> return ret;
>
> - ret = qcom_scm_pas_auth_and_reset(ctx->pas_id);
> + ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id);
> qcom_tzmem_shm_bridge_delete(handle);
>
> return ret;
> }
> +
> +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx)
> +{
> + return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev,
> + (struct qcom_pas_context *)ctx);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset);
>
> -/**
> - * qcom_scm_pas_shutdown() - Shut down the remote processor
> - * @pas_id: peripheral authentication service id
> - *
> - * Returns 0 on success.
> - */
> -int qcom_scm_pas_shutdown(u32 pas_id)
> +static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state,
> + u32 pas_id)
> +{
> + struct qcom_scm_desc desc = {
> + .svc = QCOM_SCM_SVC_BOOT,
> + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE,
> + .arginfo = QCOM_SCM_ARGS(2),
> + .args[0] = state,
> + .args[1] = pas_id,
> + .owner = ARM_SMCCC_OWNER_SIP,
> + };
> + struct qcom_scm_res res;
> + int ret;
> +
> + ret = qcom_scm_call(dev, &desc, &res);
> +
> + return ret ? : res.result[0];
> +}
> +
> +int qcom_scm_set_remote_state(u32 state, u32 id)
> +{
> + return __qcom_scm_pas_set_remote_state(__scm->dev, state, id);
> +}
> +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state);
> +
> +static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -1057,7 +994,7 @@ int qcom_scm_pas_shutdown(u32 pas_id)
> if (ret)
> goto disable_clk;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
> qcom_scm_bw_disable();
>
> disable_clk:
> @@ -1065,16 +1002,14 @@ int qcom_scm_pas_shutdown(u32 pas_id)
>
> return ret ? : res.result[0];
> }
> +
> +int qcom_scm_pas_shutdown(u32 pas_id)
> +{
> + return __qcom_scm_pas_shutdown(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown);
>
> -/**
> - * qcom_scm_pas_supported() - Check if the peripheral authentication service is
> - * available for the given peripherial
> - * @pas_id: peripheral authentication service id
> - *
> - * Returns true if PAS is supported for this peripheral, otherwise false.
> - */
> -bool qcom_scm_pas_supported(u32 pas_id)
> +static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id)
> {
> int ret;
> struct qcom_scm_desc desc = {
> @@ -1086,16 +1021,49 @@ bool qcom_scm_pas_supported(u32 pas_id)
> };
> struct qcom_scm_res res;
>
> - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
> + if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL,
> QCOM_SCM_PIL_PAS_IS_SUPPORTED))
> return false;
>
> - ret = qcom_scm_call(__scm->dev, &desc, &res);
> + ret = qcom_scm_call(dev, &desc, &res);
>
> return ret ? false : !!res.result[0];
> }
> +
> +bool qcom_scm_pas_supported(u32 pas_id)
> +{
> + return __qcom_scm_pas_supported(__scm->dev, pas_id);
> +}
> EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
>
> +static struct qcom_pas_ops qcom_pas_ops_scm = {
> + .drv_name = "qcom_scm",
> + .supported = __qcom_scm_pas_supported,
> + .init_image = __qcom_scm_pas_init_image2,
> + .mem_setup = __qcom_scm_pas_mem_setup,
> + .get_rsc_table = __qcom_scm_pas_get_rsc_table2,
> + .auth_and_reset = __qcom_scm_pas_auth_and_reset,
> + .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset,
> + .set_remote_state = __qcom_scm_pas_set_remote_state,
> + .shutdown = __qcom_scm_pas_shutdown,
> + .metadata_release = __qcom_scm_pas_metadata_release,
> +};
> +
> +/**
> + * qcom_scm_is_pas_available() - Check if the peripheral authentication service
> + * is available via SCM or not
> + *
> + * Returns true if PAS is available, otherwise false.
> + */
> +static bool qcom_scm_is_pas_available(void)
> +{
> + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
> + QCOM_SCM_PIL_PAS_AUTH_AND_RESET))
> + return false;
> +
> + return true;
> +}
> +
> static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
> {
> struct qcom_scm_desc desc = {
> @@ -2782,6 +2750,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
>
> __get_convention();
>
> + if (qcom_scm_is_pas_available()) {
> + qcom_pas_ops_scm.dev = scm->dev;
> + qcom_pas_ops_register(&qcom_pas_ops_scm);
> + }
> +
> /*
> * If "download mode" is requested, from this point on warmboot
> * will cause the boot stages to enter download mode, unless
> @@ -2818,6 +2791,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev)
> {
> /* Clean shutdown, disable download mode to allow normal restart */
> qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP);
> + qcom_pas_ops_unregister();
> }
>
> static const struct of_device_id qcom_scm_dt_match[] = {
> --
> 2.51.0
>
nit: please double check the alignment due to name and 'static' addition
to the function..
--
-Mukesh Ojha
^ permalink raw reply
* Re: [PATCH v2 02/15] firmware: qcom: Add a generic PAS service
From: Mukesh Ojha @ 2026-03-13 7:59 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, 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
In-Reply-To: <20260312062756.694390-3-sumit.garg@kernel.org>
On Thu, Mar 12, 2026 at 11:57:43AM +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
> with S-EL2 and 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.
>
> 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 | 298 +++++++++++++++++++++++++
> drivers/firmware/qcom/qcom_pas.h | 53 +++++
> include/linux/firmware/qcom/qcom_pas.h | 41 ++++
> 5 files changed, 401 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..beb1bae55546
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.c
> @@ -0,0 +1,298 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
I know, this is new file but most of the documentation and some of the
function are rename to reflect pas service.
Should this carry original file copyright ? Not sure..
> +
> +#include <linux/device/devres.h>
> +#include <linux/firmware/qcom/qcom_pas.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +#include "qcom_pas.h"
> +
> +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 ops_ptr->init_image(ops_ptr->dev, pas_id,
> + metadata, size, ctx);
> +
> + return -ENODEV;
> +}
> +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 (!ctx || !ctx->ptr)
> + return;
> +
> + if (ops_ptr)
> + 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 ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
> + input_rt_size, output_rt_size);
> +
> + return ERR_PTR(-ENODEV);
> +}
> +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 ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
> +
> + return -ENODEV;
> +}
> +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()
> + *
> + * 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 ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->shutdown(ops_ptr->dev, pas_id);
> +
> + return -ENODEV;
> +}
> +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 ops_ptr->supported(ops_ptr->dev, pas_id);
> +
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_supported);
> +
> +/**
> + * qcom_pas_is_available() - Check for PAS service
> + *
> + * Return: true on success.
> + */
> +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);
> +
> +/**
> + * qcom_pas_ops_register() - Register PAS service ops
> + * @ops: PAS service ops pointer
> + */
> +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\n");
> +}
> +EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
> +
> +/**
> + * qcom_pas_ops_unregister() - Unregister PAS service ops
> + */
> +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 common 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..4ebed22178f8
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,53 @@
> +/* 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..ef7328ecfa47
> --- /dev/null
> +++ b/include/linux/firmware/qcom/qcom_pas.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * 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: [PATCH v3] wifi: ath9k: Obtain system GPIOS from descriptors
From: Andy Shevchenko @ 2026-03-13 8:19 UTC (permalink / raw)
To: Linus Walleij
Cc: Kalle Valo, Arnd Bergmann, Alban Bedel, Bartosz Golaszewski,
Toke Høiland-Jørgensen, Michał Kępień,
linux-wireless, brcm80211-dev-list.pdl, linux-gpio
In-Reply-To: <CAD++jLmerNFyjGXAH9n9MKNAkcTSy3swg53=PARMqsXKjT7R3w@mail.gmail.com>
On Thu, Mar 12, 2026 at 10:26:06PM +0100, Linus Walleij wrote:
> Hi Andy,
>
> (I'll obviously fix all the syntax issues for v4)
>
> On Thu, Mar 12, 2026 at 5:24 PM Andy Shevchenko
> <andriy.shevchenko@linux.intel.com> wrote:
>
> > Have you considered using software nodes instead?
>
> That's the big question. And also: have I considered adding device
> tree bindings to map to those look-ups, that would provide a way for
> new users of these devices to actually do the right thing. I thought of both!
>
> The big problem is that we don't have a handle on the device
> and it's name, because it comes from the device tree and
> could be named anything. Same thing with the GPIO controller.
>
> If we register lookups or software nodes in the GPIO driver we
> don't have a reference to the ath9k device or its name, and if we
> register it in the ath9k device, we don't have a handle on the
> GPIO controller or its name.
>
> All of these GPIOs "should have" had bindings and "should have"
> been in the device tree, but they are not, and I think some of those
> device trees are even outside of the Linux kernel so we can't really
> fix them either :( it's a mess, I'm just stirring the mud to try and
> make it a bit better by removing the global GPIO numbers.
Thanks for this elaboration! Since Bart is the person who wants to move
the lookup tables to software nodes, I leave that part to him. For time
being your patch looks good (after addressing style and minor issues I
pointed out), feel free to add
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
to the next version.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [syzbot ci] Re: misc chandef cleanups
From: syzbot ci @ 2026-03-13 8:45 UTC (permalink / raw)
To: arien.judge, johannes, lachlan.hodges, linux-wireless
Cc: syzbot, syzkaller-bugs
In-Reply-To: <20260312045804.362974-1-lachlan.hodges@morsemicro.com>
syzbot ci has tested the following series
[v3] misc chandef cleanups
https://lore.kernel.org/all/20260312045804.362974-1-lachlan.hodges@morsemicro.com
* [PATCH wireless-next v3 1/3] wifi: mac80211: don't use cfg80211_chandef_create() for default chandef
* [PATCH wireless-next v3 2/3] wifi: cfg80211: restrict cfg80211_chandef_create() to only HT-based bands
* [PATCH wireless-next v3 3/3] wifi: cfg80211: check non-S1G width with S1G chandef
and found the following issue:
WARNING in cfg80211_chandef_create
Full report is available here:
https://ci.syzbot.org/series/ce6fc7d6-d8d4-4d00-a746-db78cba13e47
***
WARNING in cfg80211_chandef_create
tree: torvalds
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
base: 80234b5ab240f52fa45d201e899e207b9265ef91
arch: amd64
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
config: https://ci.syzbot.org/builds/0a18d006-45de-4bb2-a6e2-2cbb788bd2f5/config
C repro: https://ci.syzbot.org/findings/c279480a-56a9-4a59-b533-0033d65eca62/c_repro
syz repro: https://ci.syzbot.org/findings/c279480a-56a9-4a59-b533-0033d65eca62/syz_repro
netlink: 8 bytes leftover after parsing attributes in process `syz.0.17'.
------------[ cut here ]------------
chan->band == NL80211_BAND_60GHZ || chan->band == NL80211_BAND_S1GHZ
WARNING: net/wireless/chan.c:35 at cfg80211_chandef_create+0x99/0x3d0 net/wireless/chan.c:34, CPU#1: syz.0.17/5951
Modules linked in:
CPU: 1 UID: 0 PID: 5951 Comm: syz.0.17 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:cfg80211_chandef_create+0x99/0x3d0 net/wireless/chan.c:34
Code: 8b 26 4c 89 e7 48 c7 c6 40 d6 e3 8f e8 a0 7d bd f6 49 83 fc 04 74 0d 41 83 fc 02 75 12 e8 0f 78 bd f6 eb 05 e8 08 78 bd f6 90 <0f> 0b 90 eb 05 e8 fd 77 bd f6 89 ef 48 c7 c6 60 d6 e3 8f e8 6f 7d
RSP: 0018:ffffc900054fef78 EFLAGS: 00010293
RAX: ffffffff8b0825a8 RBX: ffffc900054ff0e0 RCX: ffff888112b5ba80
RDX: 0000000000000000 RSI: ffffffff8fe3d640 RDI: 0000000000000004
RBP: 0000000000000002 R08: ffff888112b5ba80 R09: 0000000000000002
R10: 0000000000000004 R11: 0000000000000000 R12: 0000000000000004
R13: dffffc0000000000 R14: ffff888111c65998 R15: ffffc900054ff0e8
FS: 000055558dcaa500(0000) GS:ffff8882a9463000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f14d1588095 CR3: 000000010668a000 CR4: 00000000000006f0
Call Trace:
<TASK>
_nl80211_parse_chandef+0x438/0x1160 net/wireless/nl80211.c:3616
__nl80211_set_channel+0x1fe/0x850 net/wireless/nl80211.c:3736
nl80211_set_wiphy+0x116b/0x2fa0 net/wireless/nl80211.c:-1
genl_family_rcv_msg_doit+0x22a/0x330 net/netlink/genetlink.c:1114
genl_family_rcv_msg net/netlink/genetlink.c:1194 [inline]
genl_rcv_msg+0x61c/0x7a0 net/netlink/genetlink.c:1209
netlink_rcv_skb+0x232/0x4b0 net/netlink/af_netlink.c:2550
genl_rcv+0x28/0x40 net/netlink/genetlink.c:1218
netlink_unicast_kernel net/netlink/af_netlink.c:1318 [inline]
netlink_unicast+0x80f/0x9b0 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x813/0xb40 net/netlink/af_netlink.c:1894
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg net/socket.c:742 [inline]
____sys_sendmsg+0x972/0x9f0 net/socket.c:2592
___sys_sendmsg+0x2a5/0x360 net/socket.c:2646
__sys_sendmsg net/socket.c:2678 [inline]
__do_sys_sendmsg net/socket.c:2683 [inline]
__se_sys_sendmsg net/socket.c:2681 [inline]
__x64_sys_sendmsg+0x1bd/0x2a0 net/socket.c:2681
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f0ea239c799
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffe7ad20a38 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 00007f0ea2615fa0 RCX: 00007f0ea239c799
RDX: 0000000000000000 RSI: 0000200000000040 RDI: 0000000000000003
RBP: 00007f0ea2432bd9 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f0ea2615fac R14: 00007f0ea2615fa0 R15: 00007f0ea2615fa0
</TASK>
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
^ permalink raw reply
* Re: [syzbot ci] Re: misc chandef cleanups
From: Johannes Berg @ 2026-03-13 9:05 UTC (permalink / raw)
To: syzbot ci, arien.judge, lachlan.hodges, linux-wireless
Cc: syzbot, syzkaller-bugs
In-Reply-To: <69b3ce9d.050a0220.12d28.010d.GAE@google.com>
On Fri, 2026-03-13 at 01:45 -0700, syzbot ci wrote:
> syzbot ci has tested the following series
>
> [v3] misc chandef cleanups
> https://lore.kernel.org/all/20260312045804.362974-1-lachlan.hodges@morsemicro.com
> * [PATCH wireless-next v3 1/3] wifi: mac80211: don't use cfg80211_chandef_create() for default chandef
> * [PATCH wireless-next v3 2/3] wifi: cfg80211: restrict cfg80211_chandef_create() to only HT-based bands
> * [PATCH wireless-next v3 3/3] wifi: cfg80211: check non-S1G width with S1G chandef
>
> and found the following issue:
> WARNING in cfg80211_chandef_create
>
> Full report is available here:
> https://ci.syzbot.org/series/ce6fc7d6-d8d4-4d00-a746-db78cba13e47
>
> ***
>
> WARNING in cfg80211_chandef_create
D'oh, just after I apply it.
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3e867930e253..7314312ec567 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3634,8 +3634,6 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
case NL80211_CHAN_HT20:
case NL80211_CHAN_HT40PLUS:
case NL80211_CHAN_HT40MINUS:
- cfg80211_chandef_create(chandef, chandef->chan,
- chantype);
/* user input for center_freq is incorrect */
if (attrs[NL80211_ATTR_CENTER_FREQ1] &&
chandef->center_freq1 != nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1])) {
@@ -3652,6 +3650,11 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
"center frequency 2 can't be used");
return -EINVAL;
}
+ if (chandef->chan->band == NL80211_BAND_60GHZ ||
+ chandef->chan->band == NL80211_BAND_S1GHZ)
+ return -EINVAL;
+ cfg80211_chandef_create(chandef, chandef->chan,
+ chantype);
break;
default:
NL_SET_ERR_MSG_ATTR(extack,
I think?
johannes
^ permalink raw reply related
* [PATCH v2] wifi: mac80211: check tdls flag in ieee80211_tdls_oper
From: Deepanshu Kartikey @ 2026-03-13 9:24 UTC (permalink / raw)
To: johannes
Cc: linux-wireless, linux-kernel, Deepanshu Kartikey,
syzbot+56b6a844a4ea74487b7b
When NL80211_TDLS_ENABLE_LINK is called, the code only checks if the
station exists but not whether it is actually a TDLS station. This
allows the operation to proceed for non-TDLS stations, causing
unintended side effects like modifying channel context and HT
protection before failing.
Add a check for sta->sta.tdls early in the ENABLE_LINK case, before
any side effects occur, to ensure the operation is only allowed for
actual TDLS peers.
Reported-by: syzbot+56b6a844a4ea74487b7b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=56b6a844a4ea74487b7b
Tested-by: syzbot+56b6a844a4ea74487b7b@syzkaller.appspotmail.com
Suggested-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
v2: Instead of replacing WARN_ON_ONCE with tdls_peer address check,
add early check for sta->sta.tdls flag before any side effects
occur, as suggested by Johannes Berg.
---
net/mac80211/tdls.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842f..1dca2fae05a5 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -1449,7 +1449,7 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
}
sta = sta_info_get(sdata, peer);
- if (!sta)
+ if (!sta || !sta->sta.tdls)
return -ENOLINK;
iee80211_tdls_recalc_chanctx(sdata, sta);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] wifi: mac80211: fix WARN_ON_ONCE in ieee80211_tdls_oper
From: Deepanshu Kartikey @ 2026-03-13 9:25 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, linux-kernel, syzbot+56b6a844a4ea74487b7b
In-Reply-To: <b1c14b931bf62b114ca44c18f2724a601efa76f5.camel@sipsolutions.net>
On Fri, Mar 13, 2026 at 12:42 PM Johannes Berg
<johannes@sipsolutions.net> wrote:
> On Tue, 2026-03-10 at 21:30 +0530, Deepanshu Kartikey wrote:
> I think that check needs to be earlier, otherwise side effects happen
> (TDLS_PEER_AUTH flag).
>
> Also, I'm a bit confused, how is it possible the sta_info_get() worked,
> but there's no TDLS? Maybe really what it needs is
>
> sta = sta_info_get(sdata, peer);
> - if (!sta)
> + if (!sta || !sta->sta.tdls)
> return -ENOLINK;
>
> instead?
>
> johannes
Thanks for the clarification. I have sent the v2 patch.
Deepanshu
^ permalink raw reply
* Re: [syzbot ci] Re: misc chandef cleanups
From: Lachlan Hodges @ 2026-03-13 9:48 UTC (permalink / raw)
To: Johannes Berg
Cc: syzbot ci, arien.judge, linux-wireless, syzbot, syzkaller-bugs
In-Reply-To: <855780e1bece0de480b7fd3e1cf67c9f70129818.camel@sipsolutions.net>
On Fri, Mar 13, 2026 at 10:05:15AM +0100, Johannes Berg wrote:
> On Fri, 2026-03-13 at 01:45 -0700, syzbot ci wrote:
> > syzbot ci has tested the following series
> >
> > [v3] misc chandef cleanups
> > https://lore.kernel.org/all/20260312045804.362974-1-lachlan.hodges@morsemicro.com
> > * [PATCH wireless-next v3 1/3] wifi: mac80211: don't use cfg80211_chandef_create() for default chandef
> > * [PATCH wireless-next v3 2/3] wifi: cfg80211: restrict cfg80211_chandef_create() to only HT-based bands
> > * [PATCH wireless-next v3 3/3] wifi: cfg80211: check non-S1G width with S1G chandef
> >
> > and found the following issue:
> > WARNING in cfg80211_chandef_create
> >
> > Full report is available here:
> > https://ci.syzbot.org/series/ce6fc7d6-d8d4-4d00-a746-db78cba13e47
> >
> > ***
> >
> > WARNING in cfg80211_chandef_create
>
> D'oh, just after I apply it.
That didn't take long ^.^
> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 3e867930e253..7314312ec567 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -3634,8 +3634,6 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
> case NL80211_CHAN_HT20:
> case NL80211_CHAN_HT40PLUS:
> case NL80211_CHAN_HT40MINUS:
> - cfg80211_chandef_create(chandef, chandef->chan,
> - chantype);
> /* user input for center_freq is incorrect */
> if (attrs[NL80211_ATTR_CENTER_FREQ1] &&
> chandef->center_freq1 != nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1])) {
> @@ -3652,6 +3650,11 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
> "center frequency 2 can't be used");
> return -EINVAL;
> }
> + if (chandef->chan->band == NL80211_BAND_60GHZ ||
> + chandef->chan->band == NL80211_BAND_S1GHZ)
> + return -EINVAL;
> + cfg80211_chandef_create(chandef, chandef->chan,
> + chantype);
> break;
> default:
> NL_SET_ERR_MSG_ATTR(extack,
>
>
> I think?
I'm probably misunderstanding - but cfg80211_chandef_create() modifies
chandef->center_freq1 if you have a HT40+/- chantype wouldn't you
wanna do that before you validate against the CENTER_FREQ1 attribute?
Since in the generic init code above it sets cf1 to the control freq?
[...]
chandef->center_freq1 = KHZ_TO_MHZ(control_freq);
[...]
where it wouldn't match for HT40-/+ since im guessing the CF1 sent
down should be what it would be _after_ being set by
cfg80211_create_chandef() based on the chantype? Or am i missing
something?
lachlan
^ permalink raw reply
* [PATCH] wifi: ath12k: fix QCOM_RPROC_COMMON dependency
From: Arnd Bergmann @ 2026-03-13 9:48 UTC (permalink / raw)
To: Jeff Johnson
Cc: Arnd Bergmann, P Praneesh, Krzysztof Wilczyński,
Bartosz Golaszewski, Raj Kumar Bhagat, Johan Hovold,
Balamurugan S, linux-wireless, ath12k, linux-kernel
From: Arnd Bergmann <arnd@arndb.de>
The AHB driver depends on the remoteproc interface, which could
be in a loadable module when ath12k itself is built-in:
arm-linux-gnueabi-ld: drivers/net/wireless/ath/ath12k/ahb.o: in function `ath12k_ahb_probe':
ahb.c:(.text+0xb2c): undefined reference to `qcom_register_ssr_notifier'
arm-linux-gnueabi-ld: ahb.c:(.text+0x1000): undefined reference to `qcom_unregister_ssr_notifier'
arm-linux-gnueabi-ld: drivers/net/wireless/ath/ath12k/ahb.o: in function `ath12k_ahb_remove':
ahb.c:(.text+0x1146): undefined reference to `qcom_unregister_ssr_notifier'
Add a dependency to only allow the AHB portion to be enabled if either
remoteproc support is built-in or ath12k is itself a loadable module.
A better way to fix this would be to make the driver more modular and
ensure the AHB and PCI components are built into separate loadable modules
like it is done for the ATH10K driver, but that is a much larger rework
that someone else can do in the future.
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
drivers/net/wireless/ath/ath12k/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig
index 1ea1af1b8f6c..ed18e0624e03 100644
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -18,6 +18,7 @@ config ATH12K
config ATH12K_AHB
bool "QTI ath12k AHB support"
depends on ATH12K && REMOTEPROC
+ depends on QCOM_RPROC_COMMON=y || ATH12K=m
select QCOM_MDT_LOADER
select QCOM_SCM
help
--
2.39.5
^ permalink raw reply related
* Re: [PATCH 05/10 net-next v2] drivers: net: drop ipv6_stub usage and use direct function calls
From: Antonio Quartulli @ 2026-03-13 10:54 UTC (permalink / raw)
To: Fernando Fernandez Mancera, netdev
Cc: rbm, Jason Gunthorpe, Leon Romanovsky, Zhu Yanjun, Saeed Mahameed,
Tariq Toukan, Mark Bloch, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Boris Pismenny,
Ido Schimmel, Petr Machata, Simon Horman, Edward Cree,
Pablo Neira Ayuso, Harald Welte, Sabrina Dubroca, Oliver Neukum,
David Ahern, Jason A. Donenfeld, Stanislav Yakovlev,
Nikolay Aleksandrov, Parav Pandit, Edward Srouji, Vlad Dumitrescu,
Kees Cook, Jianbo Liu, Gal Pressman, Guillaume Nault,
Cosmin Ratiu, Carolina Jubran, Alexandre Cassen,
Stanislav Fomichev, open list:INFINIBAND SUBSYSTEM, open list,
open list:NETRONOME ETHERNET DRIVERS,
open list:SFC NETWORK DRIVER,
open list:GTP (GPRS Tunneling Protocol),
open list:USB CDC ETHERNET DRIVER,
open list:WIREGUARD SECURE NETWORK TUNNEL,
open list:INTEL PRO/WIRELESS 2100, 2200BG, 2915ABG NETWOR...,
open list:ETHERNET BRIDGE
In-Reply-To: <20260310153506.5181-6-fmancera@suse.de>
On 10/03/2026 16:34, Fernando Fernandez Mancera wrote:
> diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
> index 3716a1d82801..6dd11c71204b 100644
> --- a/drivers/net/ovpn/peer.c
> +++ b/drivers/net/ovpn/peer.c
> @@ -821,8 +821,7 @@ static struct in6_addr ovpn_nexthop_from_rt6(struct ovpn_priv *ovpn,
> .daddr = dest,
> };
>
> - entry = ipv6_stub->ipv6_dst_lookup_flow(dev_net(ovpn->dev), NULL, &fl,
> - NULL);
> + entry = ip6_dst_lookup_flow(dev_net(ovpn->dev), NULL, &fl, NULL);
> if (IS_ERR(entry)) {
> net_dbg_ratelimited("%s: no route to host %pI6c\n",
> netdev_name(ovpn->dev), &dest);
> diff --git a/drivers/net/ovpn/udp.c b/drivers/net/ovpn/udp.c
> index 272b535ecaad..059e896b4a2f 100644
> --- a/drivers/net/ovpn/udp.c
> +++ b/drivers/net/ovpn/udp.c
> @@ -14,7 +14,6 @@
> #include <net/addrconf.h>
> #include <net/dst_cache.h>
> #include <net/route.h>
> -#include <net/ipv6_stubs.h>
> #include <net/transp_v6.h>
> #include <net/udp.h>
> #include <net/udp_tunnel.h>
> @@ -251,7 +250,7 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
> dst_cache_reset(cache);
> }
>
> - dst = ipv6_stub->ipv6_dst_lookup_flow(sock_net(sk), sk, &fl, NULL);
> + dst = ip6_dst_lookup_flow(sock_net(sk), sk, &fl, NULL);
> if (IS_ERR(dst)) {
> ret = PTR_ERR(dst);
> net_dbg_ratelimited("%s: no route to host %pISpc: %d\n",
For ovpn:
Reviewed-by: Antonio Quartulli <antonio@openvpn.net>
Regards,
--
Antonio Quartulli
OpenVPN Inc.
^ permalink raw reply
* Re: [PATCH v2 04/15] firmware: qcom: Add a PAS TEE service
From: Mukesh Ojha @ 2026-03-13 11:07 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
konradybcio, 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
In-Reply-To: <20260312062756.694390-5-sumit.garg@kernel.org>
On Thu, Mar 12, 2026 at 11:57:45AM +0530, Sumit Garg wrote:
> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>
> Add support for Peripheral Authentication Service (PAS) driver based
> on TEE bus with OP-TEE providing the backend PAS service implementation.
>
> The TEE PAS service ABI is designed to be extensible with additional API
> as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future
> extensions of the PAS service needed while still maintaining backwards
> compatibility.
>
> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> ---
> drivers/firmware/qcom/Kconfig | 9 +
> drivers/firmware/qcom/Makefile | 1 +
> drivers/firmware/qcom/qcom_pas_tee.c | 477 +++++++++++++++++++++++++++
> 3 files changed, 487 insertions(+)
> create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c
>
> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> index 9a12ae2b639d..fff47abdaafd 100644
> --- a/drivers/firmware/qcom/Kconfig
> +++ b/drivers/firmware/qcom/Kconfig
> @@ -14,6 +14,15 @@ config QCOM_PAS
> backends plugged in whether it's an SCM implementation or a proper
> TEE bus based PAS service implementation.
>
> +config QCOM_PAS_TEE
> + tristate
> + select QCOM_PAS
> + depends on TEE
> + depends on !CPU_BIG_ENDIAN
> + help
> + Enable the generic Peripheral Authentication Service (PAS) provided
> + by the firmware TEE implementation as the backend.
> +
> config QCOM_SCM
> select QCOM_PAS
> select QCOM_TZMEM
> diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
> index dc5ab45f906a..48801d18f37b 100644
> --- a/drivers/firmware/qcom/Makefile
> +++ b/drivers/firmware/qcom/Makefile
> @@ -9,3 +9,4 @@ 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
> +obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o
> diff --git a/drivers/firmware/qcom/qcom_pas_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c
> new file mode 100644
> index 000000000000..7db9fd736369
> --- /dev/null
> +++ b/drivers/firmware/qcom/qcom_pas_tee.c
> @@ -0,0 +1,477 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/of.h>
> +#include <linux/firmware/qcom/qcom_pas.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/tee_drv.h>
> +#include <linux/uuid.h>
> +
> +#include "qcom_pas.h"
> +
> +/*
> + * Peripheral Authentication Service (PAS) supported.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + */
> +#define PTA_QCOM_PAS_IS_SUPPORTED 1
> +
> +/*
> + * PAS capabilities.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [out] params[1].value.a: PAS capability flags
> + */
> +#define PTA_QCOM_PAS_CAPABILITIES 2
> +
> +/*
> + * PAS image initialization.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [in] params[1].memref: Loadable firmware metadata
> + */
> +#define PTA_QCOM_PAS_INIT_IMAGE 3
> +
> +/*
> + * PAS memory setup.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [in] params[0].value.b: Relocatable firmware size
> + * [in] params[1].value.a: 32bit LSB relocatable firmware memory address
> + * [in] params[1].value.b: 32bit MSB relocatable firmware memory address
> + */
> +#define PTA_QCOM_PAS_MEM_SETUP 4
> +
> +/*
> + * PAS get resource table.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [inout] params[1].memref: Resource table config
> + */
> +#define PTA_QCOM_PAS_GET_RESOURCE_TABLE 5
> +
> +/*
> + * PAS image authentication and co-processor reset.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [in] params[0].value.b: Firmware size
> + * [in] params[1].value.a: 32bit LSB firmware memory address
> + * [in] params[1].value.b: 32bit MSB firmware memory address
> + * [in] params[2].memref: Optional fw memory space shared/lent
> + */
> +#define PTA_QCOM_PAS_AUTH_AND_RESET 6
> +
> +/*
> + * PAS co-processor set suspend/resume state.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + * [in] params[0].value.b: Co-processor state identifier
> + */
> +#define PTA_QCOM_PAS_SET_REMOTE_STATE 7
> +
> +/*
> + * PAS co-processor shutdown.
> + *
> + * [in] params[0].value.a: Unique 32bit remote processor identifier
> + */
> +#define PTA_QCOM_PAS_SHUTDOWN 8
> +
> +#define TEE_NUM_PARAMS 4
> +
> +/**
> + * struct qcom_pas_tee_private - PAS service private data
> + * @dev: PAS service device.
> + * @ctx: TEE context handler.
> + * @session_id: PAS TA session identifier.
> + */
> +struct qcom_pas_tee_private {
> + struct device *dev;
> + struct tee_context *ctx;
> + u32 session_id;
> +};
> +
> +static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_IS_SUPPORTED,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id
> + }
> + };
> + int ret;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS not supported, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id,
> + const void *metadata, size_t size,
> + struct qcom_pas_context *ctx)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_INIT_IMAGE,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id
> + },
> + [1] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
> + }
> + };
> + struct tee_shm *mdata_shm;
> + u8 *mdata_buf = NULL;
> + int ret;
> +
> + mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size);
> + if (IS_ERR(mdata_shm)) {
> + dev_err(dev, "mdata_shm allocation failed\n");
> + return PTR_ERR(mdata_shm);
> + }
> +
> + mdata_buf = tee_shm_get_va(mdata_shm, 0);
> + if (IS_ERR(mdata_buf)) {
> + dev_err(dev, "mdata_buf get VA failed\n");
> + tee_shm_free(mdata_shm);
> + return PTR_ERR(mdata_buf);
> + }
> + memcpy(mdata_buf, metadata, size);
> +
> + param[1].u.memref.shm = mdata_shm;
> + param[1].u.memref.size = size;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS init image failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + tee_shm_free(mdata_shm);
> + return -EINVAL;
> + }
> + ctx->ptr = (void *)mdata_shm;
> +
> + return 0;
> +}
> +
> +static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id,
> + phys_addr_t addr, phys_addr_t size)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_MEM_SETUP,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id,
> + .u.value.b = size,
> + },
> + [1] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = lower_32_bits(addr),
> + .u.value.b = upper_32_bits(addr),
> + }
> + };
> + int ret;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS mem setup failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T))
> +
> +static void *qcom_pas_tee_get_rsc_table(struct device *dev,
> + struct qcom_pas_context *ctx,
> + void *input_rt, size_t input_rt_size,
> + size_t *output_rt_size)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_GET_RESOURCE_TABLE,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = ctx->pas_id,
> + },
> + [1] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
> + .u.memref.size = input_rt_size,
> + }
> + };
> + void *rt_buf = NULL;
> + int ret;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
What is the purpose of this function ? looks like, this is for, how
much Linux need to allocate for output buffer ?
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS get RT failed, pas_id: %d, err: %x\n",
> + ctx->pas_id, inv_arg.ret);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (param[1].u.memref.size) {
> + struct tee_shm *rt_shm __free(shm_free) =
> + tee_shm_alloc_kernel_buf(data->ctx,
> + param[1].u.memref.size);
> + void *rt_shm_va;
> +
> + if (IS_ERR(rt_shm)) {
> + dev_err(dev, "rt_shm allocation failed\n");
> + return rt_shm;
> + }
> +
> + rt_shm_va = tee_shm_get_va(rt_shm, 0);
> + if (IS_ERR_OR_NULL(rt_shm_va)) {
> + dev_err(dev, "rt_shm get VA failed\n");
> + return ERR_PTR(-EINVAL);
> + }
> + memcpy(rt_shm_va, input_rt, input_rt_size);
> +
> + param[1].u.memref.shm = rt_shm;
Here, you are passing only one buffer for both input and output ?
Like, you are allocating of buffer of size returned from qtee which I
assume includes both input + output rt size and copying the input_rt
and calling invoke and in return you will get combine table in return ?
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS get RT failed, pas_id: %d, err: %x\n",
> + ctx->pas_id, inv_arg.ret);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + if (param[1].u.memref.size) {
> + *output_rt_size = param[1].u.memref.size;
> + rt_buf = kmalloc(param[1].u.memref.size, GFP_KERNEL);
> + if (!rt_buf)
> + return ERR_PTR(-ENOMEM);
> +
> + memcpy(rt_buf, rt_shm_va, *output_rt_size);
rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL);
https://lore.kernel.org/lkml/20260310140255.2520230-1-mukesh.ojha@oss.qualcomm.com/
> + }
> + }
> +
> + return rt_buf;
> +}
> +
> +static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id,
> + phys_addr_t mem_phys, size_t mem_size)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_AUTH_AND_RESET,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id,
> + .u.value.b = mem_size,
> + },
> + [1] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = lower_32_bits(mem_phys),
> + .u.value.b = upper_32_bits(mem_phys),
> + },
> + /* Reserved for fw memory space to be shared or lent */
> + [2] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
> + }
> + };
> + int ret;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS auth reset failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id)
> +{
> + return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0);
> +}
> +
> +static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev,
> + struct qcom_pas_context *ctx)
> +{
> + return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys,
> + ctx->mem_size);
> +}
> +
> +static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state,
> + u32 pas_id)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_SET_REMOTE_STATE,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id,
> + .u.value.b = state,
> + }
> + };
> + int ret;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS shutdown failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id)
> +{
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> + struct tee_ioctl_invoke_arg inv_arg = {
> + .func = PTA_QCOM_PAS_SHUTDOWN,
> + .session = data->session_id,
> + .num_params = TEE_NUM_PARAMS
> + };
> + struct tee_param param[4] = {
> + [0] = {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT,
> + .u.value.a = pas_id
> + }
> + };
> + int ret = 0;
> +
> + ret = tee_client_invoke_func(data->ctx, &inv_arg, param);
> + if (ret < 0 || inv_arg.ret != 0) {
> + dev_err(dev, "PAS shutdown failed, pas_id: %d, err: %x\n",
> + pas_id, inv_arg.ret);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void qcom_pas_tee_metadata_release(struct device *dev,
> + struct qcom_pas_context *ctx)
> +{
> + struct tee_shm *mdata_shm = ctx->ptr;
> +
> + tee_shm_free(mdata_shm);
> +}
> +
> +static struct qcom_pas_ops qcom_pas_ops_tee = {
> + .drv_name = "qcom-pas-tee",
> + .supported = qcom_pas_tee_supported,
> + .init_image = qcom_pas_tee_init_image,
> + .mem_setup = qcom_pas_tee_mem_setup,
> + .get_rsc_table = qcom_pas_tee_get_rsc_table,
> + .auth_and_reset = qcom_pas_tee_auth_and_reset,
> + .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset,
> + .set_remote_state = qcom_pas_tee_set_remote_state,
> + .shutdown = qcom_pas_tee_shutdown,
> + .metadata_release = qcom_pas_tee_metadata_release,
> +};
> +
> +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
> +{
> + return ver->impl_id == TEE_IMPL_ID_OPTEE;
> +}
> +
> +static int qcom_pas_tee_probe(struct tee_client_device *pas_dev)
> +{
> + struct device *dev = &pas_dev->dev;
> + struct qcom_pas_tee_private *data;
> + struct tee_ioctl_open_session_arg sess_arg = {
> + .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL
> + };
> + int ret, err = -ENODEV;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
> + if (IS_ERR(data->ctx))
> + return -ENODEV;
> +
> + export_uuid(sess_arg.uuid, &pas_dev->id.uuid);
> + ret = tee_client_open_session(data->ctx, &sess_arg, NULL);
> + if (ret < 0 || sess_arg.ret != 0) {
> + dev_err(dev, "tee_client_open_session failed, err: %x\n",
> + sess_arg.ret);
> + err = -EINVAL;
> + goto out_ctx;
> + }
> +
> + data->session_id = sess_arg.session;
> + dev_set_drvdata(dev, data);
> + qcom_pas_ops_tee.dev = dev;
> + qcom_pas_ops_register(&qcom_pas_ops_tee);
> +
> + return 0;
> +out_ctx:
> + tee_client_close_context(data->ctx);
> +
> + return err;
> +}
> +
> +static void qcom_pas_tee_remove(struct tee_client_device *pas_dev)
> +{
> + struct device *dev = &pas_dev->dev;
> + struct qcom_pas_tee_private *data = dev_get_drvdata(dev);
> +
> + qcom_pas_ops_unregister();
> + tee_client_close_session(data->ctx, data->session_id);
> + tee_client_close_context(data->ctx);
> +}
> +
> +static const struct tee_client_device_id qcom_pas_tee_id_table[] = {
> + {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784,
> + 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)},
> + {}
> +};
> +MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table);
> +
> +static struct tee_client_driver optee_pas_tee_driver = {
> + .probe = qcom_pas_tee_probe,
> + .remove = qcom_pas_tee_remove,
> + .id_table = qcom_pas_tee_id_table,
> + .driver = {
> + .name = "qcom-pas-tee",
> + },
> +};
> +
> +module_tee_client_driver(optee_pas_tee_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("TEE bus based Qualcomm PAS driver");
> --
> 2.51.0
>
--
-Mukesh Ojha
^ permalink raw reply
* [PATCH mt76] wifi: mt76: mt7915: set mt76 specific PS flag
From: David Bauer @ 2026-03-13 11:25 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek
mt76 tracks the PSM state of a sta internally with a wcid flag. TX to
such clients is skipped based on the presence of this flag.
This flag was not added to the PS state notify handler for MT7915 chips.
Without this flag, mt76 queues pending frames to the hardware,
accounting for airtime when a PSM notification is received while in a TX
iteration.
Set the PS flag for the STA WCID to prevent this from happening. TX gets
skipped in presence of this flag.
Signed-off-by: David Bauer <mail@david-bauer.net>
---
drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
index 51a52ed072eb4..0598bea29e498 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
@@ -421,6 +421,11 @@ static void mt7915_mcu_rx_ps_sync(struct mt7915_dev *dev, struct sk_buff *skb)
if (!sta)
goto out;
+ if (p->ps_bit)
+ set_bit(MT_WCID_FLAG_PS, &wcid->flags);
+ else
+ clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
+
ieee80211_sta_ps_transition_ni(sta, !!p->ps_bit);
out:
--
2.51.0
^ permalink raw reply related
* [PATCH ath-next v2] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
From: Nicolas Escande @ 2026-03-13 11:38 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless
On each WMI message received from the hardware, we alloc a temporary array
of WMI_TAG_MAX entries of type void *. This array is then populated with
pointers of parsed structs depending on the WMI type, and then freed. This
alloc can fail when memory pressure in the system is high enough.
Given the fact that it is scheduled in softirq with the system_bh_wq, we
should not be able to parse more than one WMI message per CPU at any time.
So instead lets move to a per cpu allocated array, that is reused across
calls: ath12K_wmi_tb that is global to the ath12K module. To alloc & free
we added two new module_init/exit functions for this module.
ath12k_wmi_tlv_parse_alloc() and ath12k_wmi_tlv_parse() are merged
together as it no longer allocs mem but returns the existing per-cpu one.
Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
---
Small note on the new ath12k_wmi_tb variable. It is located in core.c and
I did not exported it in core.h (nor is it exported with EXPORT_SYMBOL())
as it is only used in wmi.c and should not be used by sub modules like
ath12k_wifi7.
changes from v1:
- rebased on ath-next 27401c9b1432
- changed wording according to Jeff's comment
- moved alloc/cleanup to new module_init/exit functions in the
ath12k module as per Baochen's comment
changes from RFC:
- rebased on ath-next 8e0ab5b9adb7
- converted missing call sites ath12k_wmi_obss_color_collision_event()
& ath12k_wmi_pdev_temperature_event()
- changed alloc order & cleanup path in ath12k_core_alloc() as it seems
it confused people
- used sizeof(*tb) in ath12k_wmi_tlv_parse()
---
drivers/net/wireless/ath/ath12k/core.c | 22 +++
drivers/net/wireless/ath/ath12k/core.h | 1 +
drivers/net/wireless/ath/ath12k/wmi.c | 181 ++++++-------------------
3 files changed, 68 insertions(+), 136 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index c31c47fb5a73..c9a5baf41845 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -34,6 +34,8 @@ module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444);
MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode");
EXPORT_SYMBOL(ath12k_ftm_mode);
+void __percpu * ath12k_wmi_tb;
+
/* protected with ath12k_hw_group_mutex */
static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list);
@@ -2321,5 +2323,25 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size,
return NULL;
}
+static int ath12k_init(void)
+{
+ ath12k_wmi_tb = __alloc_percpu(WMI_TAG_MAX * sizeof(void *),
+ __alignof__(void *));
+ if (!ath12k_wmi_tb) {
+ pr_warn("Failed to alloc ath12k WMI tb\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void ath12k_exit(void)
+{
+ free_percpu(ath12k_wmi_tb);
+}
+
+module_init(ath12k_init);
+module_exit(ath12k_exit);
+
MODULE_DESCRIPTION("Driver support for Qualcomm Technologies WLAN devices");
MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 59c193b24764..02ee6c718621 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -19,6 +19,7 @@
#include <linux/average.h>
#include <linux/of.h>
#include <linux/rhashtable.h>
+#include <linux/percpu.h>
#include "qmi.h"
#include "htc.h"
#include "wmi.h"
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 8e13c3ec1cc7..7fae6c94a390 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -24,6 +24,8 @@
#include "p2p.h"
#include "testmode.h"
+extern void __percpu * ath12k_wmi_tb;
+
struct ath12k_wmi_svc_ready_parse {
bool wmi_svc_bitmap_done;
};
@@ -289,29 +291,19 @@ static int ath12k_wmi_tlv_iter_parse(struct ath12k_base *ab, u16 tag, u16 len,
return 0;
}
-static int ath12k_wmi_tlv_parse(struct ath12k_base *ar, const void **tb,
- const void *ptr, size_t len)
-{
- return ath12k_wmi_tlv_iter(ar, ptr, len, ath12k_wmi_tlv_iter_parse,
- (void *)tb);
-}
-
static const void **
-ath12k_wmi_tlv_parse_alloc(struct ath12k_base *ab,
- struct sk_buff *skb, gfp_t gfp)
+ath12k_wmi_tlv_parse(struct ath12k_base *ab, struct sk_buff *skb)
{
const void **tb;
int ret;
- tb = kzalloc_objs(*tb, WMI_TAG_MAX, gfp);
- if (!tb)
- return ERR_PTR(-ENOMEM);
+ tb = this_cpu_ptr(ath12k_wmi_tb);
+ memset(tb, 0, WMI_TAG_MAX * sizeof(*tb));
- ret = ath12k_wmi_tlv_parse(ab, tb, skb->data, skb->len);
- if (ret) {
- kfree(tb);
+ ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
+ ath12k_wmi_tlv_iter_parse, (void *)tb);
+ if (ret)
return ERR_PTR(ret);
- }
return tb;
}
@@ -3911,9 +3903,10 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk
const struct wmi_obss_color_collision_event *ev;
struct ath12k_link_vif *arvif;
u32 vdev_id, evt_type;
+ const void **tb;
u64 bitmap;
- const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
PTR_ERR(tb));
@@ -5714,7 +5707,7 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
const struct wmi_vdev_start_resp_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5724,13 +5717,11 @@ static int ath12k_pull_vdev_start_resp_tlv(struct ath12k_base *ab, struct sk_buf
ev = tb[WMI_TAG_VDEV_START_RESPONSE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch vdev start resp ev");
- kfree(tb);
return -EPROTO;
}
*vdev_rsp = *ev;
- kfree(tb);
return 0;
}
@@ -5809,7 +5800,7 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ath12k_dbg(ab, ATH12K_DBG_WMI, "processing regulatory ext channel list\n");
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -5819,7 +5810,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch reg chan list ext update ev\n");
- kfree(tb);
return -EPROTO;
}
@@ -5849,7 +5839,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
if (num_2g_reg_rules > MAX_REG_RULES || num_5g_reg_rules > MAX_REG_RULES) {
ath12k_warn(ab, "Num reg rules for 2G/5G exceeds max limit (num_2g_reg_rules: %d num_5g_reg_rules: %d max_rules: %d)\n",
num_2g_reg_rules, num_5g_reg_rules, MAX_REG_RULES);
- kfree(tb);
return -EINVAL;
}
@@ -5859,7 +5848,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) {
ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n",
i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES);
- kfree(tb);
return -EINVAL;
}
@@ -5884,14 +5872,12 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) {
ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n",
i);
- kfree(tb);
return -EINVAL;
}
}
if (!total_reg_rules) {
ath12k_warn(ab, "No reg rules available\n");
- kfree(tb);
return -EINVAL;
}
@@ -5993,7 +5979,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ext_wmi_reg_rule);
if (!reg_info->reg_rules_2g_ptr) {
- kfree(tb);
ath12k_warn(ab, "Unable to Allocate memory for 2g rules\n");
return -ENOMEM;
}
@@ -6027,7 +6012,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ext_wmi_reg_rule);
if (!reg_info->reg_rules_5g_ptr) {
- kfree(tb);
ath12k_warn(ab, "Unable to Allocate memory for 5g rules\n");
return -ENOMEM;
}
@@ -6046,7 +6030,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ext_wmi_reg_rule);
if (!reg_info->reg_rules_6g_ap_ptr[i]) {
- kfree(tb);
ath12k_warn(ab, "Unable to Allocate memory for 6g ap rules\n");
return -ENOMEM;
}
@@ -6061,7 +6044,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ext_wmi_reg_rule);
if (!reg_info->reg_rules_6g_client_ptr[j][i]) {
- kfree(tb);
ath12k_warn(ab, "Unable to Allocate memory for 6g client rules\n");
return -ENOMEM;
}
@@ -6096,7 +6078,6 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab,
ath12k_dbg(ab, ATH12K_DBG_WMI, "processed regulatory ext channel list\n");
- kfree(tb);
return 0;
}
@@ -6107,7 +6088,7 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
const struct wmi_peer_delete_resp_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6117,7 +6098,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
ev = tb[WMI_TAG_PEER_DELETE_RESP_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch peer delete resp ev");
- kfree(tb);
return -EPROTO;
}
@@ -6127,7 +6107,6 @@ static int ath12k_pull_peer_del_resp_ev(struct ath12k_base *ab, struct sk_buff *
ether_addr_copy(peer_del_resp->peer_macaddr.addr,
ev->peer_macaddr.addr);
- kfree(tb);
return 0;
}
@@ -6139,7 +6118,7 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
const struct wmi_vdev_delete_resp_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6149,13 +6128,11 @@ static int ath12k_pull_vdev_del_resp_ev(struct ath12k_base *ab,
ev = tb[WMI_TAG_VDEV_DELETE_RESP_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch vdev delete resp ev");
- kfree(tb);
return -EPROTO;
}
*vdev_id = le32_to_cpu(ev->vdev_id);
- kfree(tb);
return 0;
}
@@ -6167,7 +6144,7 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
const struct wmi_bcn_tx_status_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6177,14 +6154,12 @@ static int ath12k_pull_bcn_tx_status_ev(struct ath12k_base *ab,
ev = tb[WMI_TAG_OFFLOAD_BCN_TX_STATUS_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch bcn tx status ev");
- kfree(tb);
return -EPROTO;
}
*vdev_id = le32_to_cpu(ev->vdev_id);
*tx_status = le32_to_cpu(ev->tx_status);
- kfree(tb);
return 0;
}
@@ -6195,7 +6170,7 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
const struct wmi_vdev_stopped_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6205,13 +6180,11 @@ static int ath12k_pull_vdev_stopped_param_tlv(struct ath12k_base *ab, struct sk_
ev = tb[WMI_TAG_VDEV_STOPPED_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch vdev stop ev");
- kfree(tb);
return -EPROTO;
}
*vdev_id = le32_to_cpu(ev->vdev_id);
- kfree(tb);
return 0;
}
@@ -6350,7 +6323,7 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
const struct wmi_mgmt_tx_compl_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6360,7 +6333,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
ev = tb[WMI_TAG_MGMT_TX_COMPL_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch mgmt tx compl ev");
- kfree(tb);
return -EPROTO;
}
@@ -6370,7 +6342,6 @@ static int ath12k_pull_mgmt_tx_compl_param_tlv(struct ath12k_base *ab,
param->ppdu_id = ev->ppdu_id;
param->ack_rssi = ev->ack_rssi;
- kfree(tb);
return 0;
}
@@ -6533,7 +6504,7 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
const struct wmi_scan_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6543,7 +6514,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
ev = tb[WMI_TAG_SCAN_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch scan ev");
- kfree(tb);
return -EPROTO;
}
@@ -6555,7 +6525,6 @@ static int ath12k_pull_scan_ev(struct ath12k_base *ab, struct sk_buff *skb,
scan_evt_param->vdev_id = ev->vdev_id;
scan_evt_param->tsf_timestamp = ev->tsf_timestamp;
- kfree(tb);
return 0;
}
@@ -6566,7 +6535,7 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
const struct wmi_peer_sta_kickout_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6576,7 +6545,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
ev = tb[WMI_TAG_PEER_STA_KICKOUT_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch peer sta kickout ev");
- kfree(tb);
return -EPROTO;
}
@@ -6584,7 +6552,6 @@ static int ath12k_pull_peer_sta_kickout_ev(struct ath12k_base *ab, struct sk_buf
arg->reason = le32_to_cpu(ev->reason);
arg->rssi = le32_to_cpu(ev->rssi);
- kfree(tb);
return 0;
}
@@ -6595,7 +6562,7 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
const struct wmi_roam_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6605,7 +6572,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
ev = tb[WMI_TAG_ROAM_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch roam ev");
- kfree(tb);
return -EPROTO;
}
@@ -6613,7 +6579,6 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
roam_ev->reason = ev->reason;
roam_ev->rssi = ev->rssi;
- kfree(tb);
return 0;
}
@@ -6647,7 +6612,7 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
const struct wmi_chan_info_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6657,7 +6622,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
ev = tb[WMI_TAG_CHAN_INFO_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch chan info ev");
- kfree(tb);
return -EPROTO;
}
@@ -6674,7 +6638,6 @@ static int ath12k_pull_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
ch_info_ev->mac_clk_mhz = ev->mac_clk_mhz;
ch_info_ev->vdev_id = ev->vdev_id;
- kfree(tb);
return 0;
}
@@ -6686,7 +6649,7 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
const struct wmi_pdev_bss_chan_info_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6696,7 +6659,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
ev = tb[WMI_TAG_PDEV_BSS_CHAN_INFO_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch pdev bss chan info ev");
- kfree(tb);
return -EPROTO;
}
@@ -6714,7 +6676,6 @@ ath12k_pull_pdev_bss_chan_info_ev(struct ath12k_base *ab, struct sk_buff *skb,
bss_ch_info_ev->rx_bss_cycle_count_low = ev->rx_bss_cycle_count_low;
bss_ch_info_ev->rx_bss_cycle_count_high = ev->rx_bss_cycle_count_high;
- kfree(tb);
return 0;
}
@@ -6726,7 +6687,7 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
const struct wmi_vdev_install_key_compl_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6736,7 +6697,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
ev = tb[WMI_TAG_VDEV_INSTALL_KEY_COMPLETE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch vdev install key compl ev");
- kfree(tb);
return -EPROTO;
}
@@ -6746,7 +6706,6 @@ ath12k_pull_vdev_install_key_compl_ev(struct ath12k_base *ab, struct sk_buff *sk
arg->key_flags = le32_to_cpu(ev->key_flags);
arg->status = le32_to_cpu(ev->status);
- kfree(tb);
return 0;
}
@@ -6757,7 +6716,7 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
const struct wmi_peer_assoc_conf_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6767,14 +6726,12 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
ev = tb[WMI_TAG_PEER_ASSOC_CONF_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch peer assoc conf ev");
- kfree(tb);
return -EPROTO;
}
peer_assoc_conf->vdev_id = le32_to_cpu(ev->vdev_id);
peer_assoc_conf->macaddr = ev->peer_macaddr.addr;
- kfree(tb);
return 0;
}
@@ -6792,7 +6749,7 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
const void **tb;
int ret, i;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -6801,7 +6758,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT];
if (!ev) {
- kfree(tb);
ath12k_warn(ab, "failed to fetch 11d new cc ev");
return -EPROTO;
}
@@ -6814,8 +6770,6 @@ static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *s
ab->new_alpha2[0],
ab->new_alpha2[1]);
- kfree(tb);
-
for (i = 0; i < ab->num_radios; i++) {
pdev = &ab->pdevs[i];
ar = pdev->ar;
@@ -8567,7 +8521,7 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
const struct wmi_pdev_ctl_failsafe_chk_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8577,7 +8531,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch pdev ctl failsafe check ev");
- kfree(tb);
return;
}
@@ -8591,8 +8544,6 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
if (ev->ctl_failsafe_status != 0)
ath12k_warn(ab, "pdev ctl failsafe failure status %d",
ev->ctl_failsafe_status);
-
- kfree(tb);
}
static void
@@ -8664,7 +8615,7 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
const u32 *vdev_ids;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8676,7 +8627,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
if (!ev || !vdev_ids) {
ath12k_warn(ab, "failed to fetch pdev csa switch count ev");
- kfree(tb);
return;
}
@@ -8686,8 +8636,6 @@ ath12k_wmi_pdev_csa_switch_count_status_event(struct ath12k_base *ab,
ev->num_vdevs);
ath12k_wmi_process_csa_switch_count_event(ab, ev, vdev_ids);
-
- kfree(tb);
}
static void
@@ -8699,7 +8647,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
struct ath12k *ar;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8710,7 +8658,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
if (!ev) {
ath12k_warn(ab, "failed to fetch pdev dfs radar detected ev");
- kfree(tb);
return;
}
@@ -8749,8 +8696,6 @@ ath12k_wmi_pdev_dfs_radar_detected_event(struct ath12k_base *ab, struct sk_buff
exit:
rcu_read_unlock();
-
- kfree(tb);
}
static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
@@ -8761,7 +8706,7 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
int ret;
u16 length;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
@@ -8772,14 +8717,11 @@ static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id,
ev = tb[WMI_TAG_ARRAY_BYTE];
if (!ev) {
ath12k_warn(ab, "failed to fetch ftm msg\n");
- kfree(tb);
return;
}
length = skb->len - TLV_HDR_SIZE;
ath12k_tm_process_event(ab, cmd_id, ev, length);
- kfree(tb);
- tb = NULL;
}
static void
@@ -8792,7 +8734,7 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
int temp;
u32 pdev_id;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
return;
@@ -8801,15 +8743,12 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch pdev temp ev\n");
- kfree(tb);
return;
}
temp = a_sle32_to_cpu(ev->temp);
pdev_id = le32_to_cpu(ev->pdev_id);
- kfree(tb);
-
ath12k_dbg(ab, ATH12K_DBG_WMI,
"pdev temperature ev temp %d pdev_id %u\n",
temp, pdev_id);
@@ -8836,7 +8775,7 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
const struct wmi_fils_discovery_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab,
@@ -8848,15 +8787,12 @@ static void ath12k_fils_discovery_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_HOST_SWFDA_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch FILS discovery event\n");
- kfree(tb);
return;
}
ath12k_warn(ab,
"FILS discovery frame expected from host for vdev_id: %u, transmission scheduled at %u, next TBTT: %u\n",
ev->vdev_id, ev->fils_tt, ev->tbtt);
-
- kfree(tb);
}
static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
@@ -8866,7 +8802,7 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
const struct wmi_probe_resp_tx_status_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab,
@@ -8879,7 +8815,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
if (!ev) {
ath12k_warn(ab,
"failed to fetch probe response transmission status event");
- kfree(tb);
return;
}
@@ -8887,8 +8822,6 @@ static void ath12k_probe_resp_tx_status_event(struct ath12k_base *ab,
ath12k_warn(ab,
"Probe response transmission failed for vdev_id %u, status %u\n",
ev->vdev_id, ev->tx_status);
-
- kfree(tb);
}
static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
@@ -8900,7 +8833,7 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
struct ath12k *ar;
int ret, vdev_id;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse P2P NoA TLV: %d\n", ret);
@@ -8910,10 +8843,8 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_P2P_NOA_EVENT];
noa = tb[WMI_TAG_P2P_NOA_INFO];
- if (!ev || !noa) {
- ret = -EPROTO;
- goto out;
- }
+ if (!ev || !noa)
+ return -EPROTO;
vdev_id = __le32_to_cpu(ev->vdev_id);
@@ -8936,8 +8867,6 @@ static int ath12k_wmi_p2p_noa_event(struct ath12k_base *ab,
unlock:
rcu_read_unlock();
-out:
- kfree(tb);
return ret;
}
@@ -8948,7 +8877,7 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
const void **tb;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -8956,10 +8885,8 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
}
ev = tb[WMI_TAG_RFKILL_EVENT];
- if (!ev) {
- kfree(tb);
+ if (!ev)
return;
- }
ath12k_dbg(ab, ATH12K_DBG_MAC,
"wmi tlv rfkill state change gpio %d type %d radio_state %d\n",
@@ -8972,7 +8899,6 @@ static void ath12k_rfkill_state_change_event(struct ath12k_base *ab,
spin_unlock_bh(&ab->base_lock);
queue_work(ab->workqueue, &ab->rfkill_work);
- kfree(tb);
}
static void
@@ -8988,7 +8914,7 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
const struct wmi_twt_enable_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse wmi twt enable status event tlv: %d\n",
@@ -8999,15 +8925,12 @@ static void ath12k_wmi_twt_enable_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_TWT_ENABLE_COMPLETE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch twt enable wmi event\n");
- goto exit;
+ return;
}
ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt enable event pdev id %u status %u\n",
le32_to_cpu(ev->pdev_id),
le32_to_cpu(ev->status));
-
-exit:
- kfree(tb);
}
static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
@@ -9017,7 +8940,7 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
const struct wmi_twt_disable_event *ev;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse wmi twt disable status event tlv: %d\n",
@@ -9028,15 +8951,12 @@ static void ath12k_wmi_twt_disable_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_TWT_DISABLE_COMPLETE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch twt disable wmi event\n");
- goto exit;
+ return;
}
ath12k_dbg(ab, ATH12K_DBG_MAC, "wmi twt disable event pdev id %d status %u\n",
le32_to_cpu(ev->pdev_id),
le32_to_cpu(ev->status));
-
-exit:
- kfree(tb);
}
static int ath12k_wmi_wow_wakeup_host_parse(struct ath12k_base *ab,
@@ -9109,7 +9029,7 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
const void **tb;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
@@ -9119,7 +9039,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
ev = tb[WMI_TAG_GTK_OFFLOAD_STATUS_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch gtk offload status ev");
- kfree(tb);
return;
}
@@ -9129,7 +9048,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
rcu_read_unlock();
ath12k_warn(ab, "failed to get arvif for vdev_id:%d\n",
le32_to_cpu(ev->vdev_id));
- kfree(tb);
return;
}
@@ -9145,8 +9063,6 @@ static void ath12k_wmi_gtk_offload_status_event(struct ath12k_base *ab,
(void *)&replay_ctr_be, GFP_ATOMIC);
rcu_read_unlock();
-
- kfree(tb);
}
static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
@@ -9158,7 +9074,7 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
const void **tb;
int ret, i;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse mlo setup complete event tlv: %d\n",
@@ -9169,7 +9085,6 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
ev = tb[WMI_TAG_MLO_SETUP_COMPLETE_EVENT];
if (!ev) {
ath12k_warn(ab, "failed to fetch mlo setup complete event\n");
- kfree(tb);
return;
}
@@ -9188,14 +9103,11 @@ static void ath12k_wmi_event_mlo_setup_complete(struct ath12k_base *ab,
if (!ar) {
ath12k_warn(ab, "invalid pdev_id %d status %u in setup complete event\n",
ev->pdev_id, ev->status);
- goto out;
+ return;
}
ar->mlo_setup_status = le32_to_cpu(ev->status);
complete(&ar->mlo_setup_done);
-
-out:
- kfree(tb);
}
static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
@@ -9205,7 +9117,7 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
const void **tb;
int ret;
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ tb = ath12k_wmi_tlv_parse(ab, skb);
if (IS_ERR(tb)) {
ret = PTR_ERR(tb);
ath12k_warn(ab, "failed to parse teardown complete event tlv: %d\n", ret);
@@ -9215,11 +9127,8 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab,
ev = tb[WMI_TAG_MLO_TEARDOWN_COMPLETE];
if (!ev) {
ath12k_warn(ab, "failed to fetch teardown complete event\n");
- kfree(tb);
return;
}
-
- kfree(tb);
}
#ifdef CONFIG_ATH12K_DEBUGFS
--
2.53.0
^ permalink raw reply related
* Re: [syzbot ci] Re: misc chandef cleanups
From: Johannes Berg @ 2026-03-13 11:41 UTC (permalink / raw)
To: Lachlan Hodges
Cc: syzbot ci, arien.judge, linux-wireless, syzbot, syzkaller-bugs
In-Reply-To: <sezz36jv4edclmbtrcbdwzyb5goxp2tcfneoi56ounzuled42v@o6bajqutzp33>
On Fri, 2026-03-13 at 20:48 +1100, Lachlan Hodges wrote:
>
> > diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> > index 3e867930e253..7314312ec567 100644
> > --- a/net/wireless/nl80211.c
> > +++ b/net/wireless/nl80211.c
> > @@ -3634,8 +3634,6 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
> > case NL80211_CHAN_HT20:
> > case NL80211_CHAN_HT40PLUS:
> > case NL80211_CHAN_HT40MINUS:
> > - cfg80211_chandef_create(chandef, chandef->chan,
> > - chantype);
> > /* user input for center_freq is incorrect */
> > if (attrs[NL80211_ATTR_CENTER_FREQ1] &&
> > chandef->center_freq1 != nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1])) {
> > @@ -3652,6 +3650,11 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
> > "center frequency 2 can't be used");
> > return -EINVAL;
> > }
> > + if (chandef->chan->band == NL80211_BAND_60GHZ ||
> > + chandef->chan->band == NL80211_BAND_S1GHZ)
> > + return -EINVAL;
> > + cfg80211_chandef_create(chandef, chandef->chan,
> > + chantype);
> > break;
> > default:
> > NL_SET_ERR_MSG_ATTR(extack,
> >
> >
> > I think?
>
> I'm probably misunderstanding - but cfg80211_chandef_create() modifies
> chandef->center_freq1 if you have a HT40+/- chantype wouldn't you
> wanna do that before you validate against the CENTER_FREQ1 attribute?
Oh, yeah, oops.
> Since in the generic init code above it sets cf1 to the control freq?
>
> [...]
> chandef->center_freq1 = KHZ_TO_MHZ(control_freq);
> [...]
>
> where it wouldn't match for HT40-/+ since im guessing the CF1 sent
> down should be what it would be _after_ being set by
> cfg80211_create_chandef() based on the chantype? Or am i missing
> something?
No, I just didn't think about it. I moved it because I thought I'd do
this differently, but this validation can just come first anyway.
johannes
^ permalink raw reply
* [PATCH wireless-next] wifi: nl80211: reject S1G/60G with HT chantype
From: Johannes Berg @ 2026-03-13 12:01 UTC (permalink / raw)
To: linux-wireless; +Cc: Lachlan Hodges, Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This configuration doesn't make sense, neither S1G nor
60G have 20 or 40 MHz channel width. Reject it to not
run into the new cfg80211_chandef_create() warning.
Fixes: 92d77e06e73c ("wifi: cfg80211: restrict cfg80211_chandef_create() to only HT-based bands")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
net/wireless/nl80211.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 3e867930e253..d2ef13ab1a20 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -3634,6 +3634,9 @@ static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
case NL80211_CHAN_HT20:
case NL80211_CHAN_HT40PLUS:
case NL80211_CHAN_HT40MINUS:
+ if (chandef->chan->band == NL80211_BAND_60GHZ ||
+ chandef->chan->band == NL80211_BAND_S1GHZ)
+ return -EINVAL;
cfg80211_chandef_create(chandef, chandef->chan,
chantype);
/* user input for center_freq is incorrect */
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 0/4] wifi: rtl8xxxu: implement AP mode for 8188EU
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
To: Jes.Sorensen, rtl8821cerfe2
Cc: linux-wireless, linux-kernel, Georg Müller
This series tries to implement AP mode for Realtek RTL8188EU chips.
This is not final. I still have issues connecting to the AP, searching for some
input on what may be the reason or how to further debug issues.
Patch 1 could be picked independently as it shouldn't change anything for the
non-AP mode.
---
Changes in v2:
- add patch to move dynamic_tx_rpt_timing_counter from ra_info to priv
- convert ra_info to a dynamic array
- update max report mac id after station add/remove
---
Georg Müller (4):
wifi: rtl8xxxu: move dynamic_tx_rpt_timing_counter from ra_info to
priv
wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
wifi: rtl8xxxu: update max report mac id on station add / remove for
8188e chips
wifi: rtl8xxxu: Enable AP mode for RTL8188EU
drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 22 +++++-----
drivers/net/wireless/realtek/rtl8xxxu/core.c | 41 ++++++++++++++++---
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 5 ++-
3 files changed, 50 insertions(+), 18 deletions(-)
--
2.53.0
^ permalink raw reply
* [RFC PATCH v2 1/4] wifi: rtl8xxxu: move dynamic_tx_rpt_timing_counter from ra_info to priv
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
To: Jes.Sorensen, rtl8821cerfe2
Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313131849.3148013-1-georgmueller@gmx.net>
dynamic_tx_rpt_timing_counter is not per mac_id, but used across all
mac_ids.
---
drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 8 ++++----
drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index 766a7a7c7d28..de2837a91bbe 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1550,14 +1550,14 @@ static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
}
if (ra->decision_rate == ra->pre_rate)
- ra->dynamic_tx_rpt_timing_counter++;
+ priv->dynamic_tx_rpt_timing_counter++;
else
- ra->dynamic_tx_rpt_timing_counter = 0;
+ priv->dynamic_tx_rpt_timing_counter = 0;
- if (ra->dynamic_tx_rpt_timing_counter >= 4) {
+ if (priv->dynamic_tx_rpt_timing_counter >= 4) {
/* Rate didn't change 4 times, extend RPT timing */
rtl8188e_set_tx_rpt_timing(ra, INCREASE_TIMING);
- ra->dynamic_tx_rpt_timing_counter = 0;
+ priv->dynamic_tx_rpt_timing_counter = 0;
}
ra->pre_rate = ra->decision_rate;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 9fb2583ffffc..4a744b5c1aec 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1756,7 +1756,6 @@ struct rtl8xxxu_ra_info {
u16 drop;
u16 rpt_time;
u16 pre_min_rpt_time;
- u8 dynamic_tx_rpt_timing_counter;
u8 ra_waiting_counter;
u8 ra_pending_counter;
u8 ra_drop_after_down;
@@ -1917,6 +1916,7 @@ struct rtl8xxxu_priv {
struct rtl8xxxu_ra_report ra_report;
struct rtl8xxxu_cfo_tracking cfo_tracking;
struct rtl8xxxu_ra_info ra_info;
+ u8 dynamic_tx_rpt_timing_counter; // 8188e specific
bool led_registered;
char led_name[32];
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 2/4] wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
To: Jes.Sorensen, rtl8821cerfe2
Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313131849.3148013-1-georgmueller@gmx.net>
convert member ra_info to an array with one entry per mac id. This
allows having different rate control settings per connected station
in ap mode.
The max_macid_num is conservative. The old driver used 32 [1], some
other sources said 64 [2]. I have not enough adapters to test any of the
higher values. Given the usage of this chipset in nano dongles, I think
the 16 might be high enough.
[1] https://github.com/lwfinger/rtl8188eu/blob/f5d1c8df2e2d8b217ea0113bf2cf3e37df8cb716/include/sta_info.h#L28
[2] https://lore.kernel.org/linux-wireless/27e83382-4c84-1841-c428-d1e5143ea20c@gmail.com/
Signed-off-by: Georg Müller <georgmueller@gmx.net>
---
drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 12 ++++++------
drivers/net/wireless/realtek/rtl8xxxu/core.c | 19 ++++++++++++++-----
.../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 3 ++-
3 files changed, 22 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index de2837a91bbe..607ca62194fc 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1468,9 +1468,8 @@ static void rtl8188e_reset_ra_counter(struct rtl8xxxu_ra_info *ra)
ra->nsc_down = (n_threshold_high[rate_id] + n_threshold_low[rate_id]) >> 1;
}
-static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
+static void rtl8188e_rate_decision(struct rtl8xxxu_priv *priv, struct rtl8xxxu_ra_info *ra)
{
- struct rtl8xxxu_priv *priv = container_of(ra, struct rtl8xxxu_priv, ra_info);
const u8 *retry_penalty_idx_0;
const u8 *retry_penalty_idx_1;
const u8 *retry_penalty_up_idx;
@@ -1669,7 +1668,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
struct device *dev = &priv->udev->dev;
- struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+ struct rtl8xxxu_ra_info *ra;
u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
@@ -1688,6 +1687,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
for (macid = 0; macid < items; macid++) {
valid = false;
+ ra = &priv->ra_info[macid];
if (macid < 64)
valid = macid_valid & BIT(macid);
@@ -1704,7 +1704,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
if (ra->total > 0) {
if (ra->ra_stage < 5)
- rtl8188e_rate_decision(ra);
+ rtl8188e_rate_decision(priv, ra);
else if (ra->ra_stage == 5)
rtl8188e_power_training_try_state(ra);
else /* ra->ra_stage == 6 */
@@ -1781,7 +1781,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
u32 ramask, u8 rateid, int sgi, int txbw_40mhz,
u8 macid)
{
- struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+ struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
ra->rate_id = rateid;
ra->rate_mask = ramask;
@@ -1792,7 +1792,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
static void rtl8188e_ra_set_rssi(struct rtl8xxxu_priv *priv, u8 macid, u8 rssi)
{
- priv->ra_info.rssi_sta_ra = rssi;
+ priv->ra_info[macid].rssi_sta_ra = rssi;
}
void rtl8188e_ra_info_init_all(struct rtl8xxxu_ra_info *ra)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 794187d28caa..ea4dcca9d22a 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -3921,6 +3921,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
struct device *dev = &priv->udev->dev;
struct rtl8xxxu_fileops *fops = priv->fops;
bool macpower;
+ u16 mac_id;
int ret;
u8 val8;
u16 val16;
@@ -4393,9 +4394,16 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
priv->cfo_tracking.crystal_cap = priv->default_crystal_cap;
}
- if (priv->rtl_chip == RTL8188E)
- rtl8188e_ra_info_init_all(&priv->ra_info);
-
+ if (priv->rtl_chip == RTL8188E) {
+ priv->ra_info = kmalloc_array(RTL8188E_MAX_MAC_ID_NUM, sizeof(*priv->ra_info));
+ if (!priv->ra_info) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ for (mac_id = 0; mac_id < RTL8188E_MAX_MAC_ID_NUM; mac_id++) {
+ rtl8188e_ra_info_init_all(&priv->ra_info[mac_id]);
+ }
+ }
set_bit(RTL8XXXU_BC_MC_MACID, priv->mac_id_map);
set_bit(RTL8XXXU_BC_MC_MACID1, priv->mac_id_map);
@@ -5338,7 +5346,7 @@ rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
{
struct rtl8xxxu_priv *priv = hw->priv;
struct device *dev = &priv->udev->dev;
- struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+ struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
u8 *qc = ieee80211_get_qos_ctl(hdr);
u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
u32 rate = 0;
@@ -7895,6 +7903,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
err_set_intfdata:
usb_set_intfdata(interface, NULL);
+ kfree(priv->ra_info);
kfree(priv->fw_data);
mutex_destroy(&priv->usb_buf_mutex);
mutex_destroy(&priv->syson_indirect_access_mutex);
@@ -7924,7 +7933,7 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface)
usb_set_intfdata(interface, NULL);
dev_info(&priv->udev->dev, "disconnecting\n");
-
+ kfree(priv->ra_info);
kfree(priv->fw_data);
mutex_destroy(&priv->usb_buf_mutex);
mutex_destroy(&priv->syson_indirect_access_mutex);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 4a744b5c1aec..9ce820ff4793 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -76,6 +76,7 @@
#define RTL_FW_PAGE_SIZE 4096
#define RTL8XXXU_FIRMWARE_POLL_MAX 1000
+#define RTL8188E_MAX_MAC_ID_NUM 16
#define RTL8723A_CHANNEL_GROUPS 3
#define RTL8723A_MAX_RF_PATHS 2
#define RTL8723B_CHANNEL_GROUPS 6
@@ -1915,7 +1916,7 @@ struct rtl8xxxu_priv {
struct rtl8xxxu_btcoex bt_coex;
struct rtl8xxxu_ra_report ra_report;
struct rtl8xxxu_cfo_tracking cfo_tracking;
- struct rtl8xxxu_ra_info ra_info;
+ struct rtl8xxxu_ra_info *ra_info;
u8 dynamic_tx_rpt_timing_counter; // 8188e specific
bool led_registered;
--
2.53.0
^ 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