* RE: rtw89: RTL8852BE P2P-device iftype and STA+P2P interface combination
From: Ping-Ke Shih @ 2026-06-30 0:50 UTC (permalink / raw)
To: Doug Brewer; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <CAG0V13Qfp+eVCR6NjQJydN-fL8tus_EyJCWGQqQxTkKA9ZXBFQ@mail.gmail.com>
Doug Brewer <brewer.doug@gmail.com> wrote:
> On Mon, Jun 29, 2026 at 10:10 AM Ping-Ke Shih <pkshih@realtek.com> wrote:
> >
> > Doug Brewer wrote:
> > > Hi,
> > >
> > > I'm experimenting with Wi-Fi Display (Miracast sink) concurrent with an
> > > STA connection on an RTL8852BE (PCIe) using the mainline rtw89 driver
> > > (kernel 6.18.37).
> > >
> > > iw phy reports:
> > > Supported interface modes:
> > > * managed, AP, P2P-client, P2P-GO
> > > (no P2P-device)
> > > interface combinations are not supported
> > >
> > > In practice this blocks the standard P2P flow: there is no P2P-device
> > > iftype for a dedicated discovery context, and no advertised interface
> > > combination for managed + P2P-client coexistence.
> > >
> > > My questions:
> > > 1. is P2P-device iftype support planned for rtw89 on RTL885x? Is there a
> > > known technical blocker, or is it simply not yet implemented?
> >
> > We are planning to add P2P-device iftype. It needs to consider the cases of
> > channel context, conditions of power save, and etc. It will take some time.
> >
> > I think it would be okay that you use STA interface to do P2P negotiation,
> > and then create P2P-client or P2P-GO iftype then.
> >
> > > 2. would advertising a managed + P2P-client interface combination
> > > (single channel) be feasible on the current rtw89?
> >
> > This is a SCC which is supported.
> >
> > > 3. is MCC (#channels > 1) on the roadmap, or considered out of scope?
> >
> > Current support MCC as well. However, we are cooking new firmware to support
> > hw_scan with two operation channels -- which doesn't matter if you don't need
> > to do scan when MCC is operating.
>
> Thanks for the suggestion. I tried using the STA interface for P2P
> negotiation, and wanted to share what I found.
>
> With the STA connected (2.4GHz ch11) and an active p2p_connect, a
> wpa_supplicant -dd trace shows GO negotiation getting fairly far:
>
> Peer's GO-NEG Request is received
> send the GO-NEG Response on ch11, peer ACKs it (TX ack=1)
> State goes GO_NEG -> CONNECT
> then time out waiting for the GO-NEG Confirm, status=-1
>
> I select ch11 as the P2P operating channel (same as STA, SCC), while
> the peer's operating preference is 5GHz ch149. It looks like after we TX
> the Response, the radio doesn't stay on ch11 to listen for the Confirm,
> so the frame is missed -- presumably because the single radio is serving
> the STA connection.
So, peer doesn't stay ch11 to complete he negotiation, right?
What is the peer device you are using? Can you setup another RTL8852BE?
I suggest running simple scenario first to dig cause.
1. two peers make P2P group without any STA connection
2. RTL8852BE with a STA connection, and peer without connection.
>
> Aalso tested with the STA on 5GHz (ch149); the result is the same
> GO-NEG Confirm timeout.
>
> Is this the channel-context issue that P2P-device iftype will address?
> And with the current driver, is there any way to keep the P2P listen
> context on the operating channel during GO negotiation while STA is up?
Before P2P negotiation completion, there is only one channel context.
The second interface (GC or GO) is created when the P2P role is decided
by P2P negotiation.
You need to check supplicant log about channels on both peers. I think
remain-on-channel is the method supplicant switch channel to send
negotiation frames and to stay on listen channel.
>
> (FWIW, passing an explicit freq= to p2p_connect is rejected with FAIL,
> whether or not it matches the STA channel.)
Not sure why. In our side, it seems work.
I'd share a pair of wpa_supplicant .conf and wpa_cli commands we are testing
for reference.
Peer 1:
ctrl_interface=/var/run/wpa_supplicant
network={
ssid="ap_x"
key_mgmt=NONE
}
update_config=1
device_name=my_p2p
manufacturer=Realtek
model_name=RTW_STA
model_number=WLAN_CU
serial_number=12345
device_type=1-0050F204-1
os_version=01020300
config_methods=virtual_display virtual_push_button keypad
p2p_listen_reg_class=81
p2p_listen_channel=1
p2p_oper_reg_class=81
p2p_oper_channel=1
p2p_no_group_iface=1
wpa_supplicant -i wlan0 -c p2p.conf
wpa_cli
> p2p_find
> p2p_connect $SUT_MAC_ADDR pbc (freq=xxxx go_intent=15)
Peer 2:
ctrl_interface=/var/run/wpa_supplicant
update_config=1
device_name=my_p2p
manufacturer=Realtek
model_name=RTW_STA
model_number=WLAN_CU
serial_number=12345
device_type=1-0050F204-1
os_version=01020300
config_methods=virtual_display virtual_push_button keypad
p2p_listen_reg_class=81
p2p_listen_channel=1
p2p_oper_reg_class=81
p2p_oper_channel=1
p2p_no_group_iface=1
wpa_supplicant -i wlan0 -c p2p.conf
wpa_cli
> p2p_find
> p2p_connect $DUT_MAC_ADDR pbc (freq=xxxx go_intent=1)
Regards
Ping-Ke
^ permalink raw reply
* Re: [PATCH ath-next 3/3] wifi: ath12k: allocate HOST_DDR and BDF regions after Q6 RO region
From: Jeff Johnson @ 2026-06-30 0:36 UTC (permalink / raw)
To: Aaradhana Sahu, ath12k; +Cc: linux-wireless
In-Reply-To: <20260611033314.2330-4-aaradhana.sahu@oss.qualcomm.com>
On 6/10/2026 8:33 PM, Aaradhana Sahu wrote:
> @@ -2781,9 +2781,12 @@ static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab)
> goto out;
>
> avail_rmem_size = resource_size(&res);
> - if (chunk->type == BDF_MEM_REGION_TYPE) {
> + if (chunk->type == BDF_MEM_REGION_TYPE ||
> + chunk->type == HOST_DDR_REGION_TYPE) {
> avail_rmem_size -= ab->hw_params->bdf_addr_offset;
> - res.start += ab->hw_params->bdf_addr_offset;
> + avail_rmem_size -= offset;
> + res.start += ab->hw_params->bdf_addr_offset + offset;
> + offset += chunk->size;
avail_rmem_size is size_t (unsigned).
If bdf_addr_offset + offset >= resource_size(&res) (e.g., DT
misconfiguration), the subtraction silently wraps to a huge value, the
avail_rmem_size < chunk->size guard passes incorrectly, and ioremap proceeds
out-of-bounds.
So seems there should be a test to sanitize the DT values.
> }
>
> if (avail_rmem_size < chunk->size) {
^ permalink raw reply
* Re: [PATCH ath-next 1/3] wifi: ath12k: switch to name-based reserved memory lookup
From: Jeff Johnson @ 2026-06-30 0:30 UTC (permalink / raw)
To: Aaradhana Sahu, ath12k; +Cc: linux-wireless
In-Reply-To: <20260611033314.2330-2-aaradhana.sahu@oss.qualcomm.com>
On 6/10/2026 8:33 PM, Aaradhana Sahu wrote:
> The driver currently retrieves reserved memory regions using index-based
> lookup, which depends on the ordering of reserved-memory nodes in the
> device tree. Since different platforms define these regions in varying
> orders and combinations, this approach is not compatible and can result
> in incorrect memory region access.
>
> Switch to looking up memory regions by name instead of index so it does
> not depend on node order.
>
> Use names already defined in qcom,ipq5332-wifi.yaml, so there are no
> backward compatibility issues.
>
> Tested-on: IPQ5332 hw1.0 AHB WLAN.WBE.1.6-01275-QCAHKSWPL_SILICONZ-1
>
> Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath12k/ahb.c | 18 ++++++------
> drivers/net/wireless/ath/ath12k/core.c | 25 -----------------
> drivers/net/wireless/ath/ath12k/core.h | 2 --
> drivers/net/wireless/ath/ath12k/qmi.c | 38 +++++++++++++-------------
> 4 files changed, 29 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c
> index 30733a244454..695b605a92fd 100644
> --- a/drivers/net/wireless/ath/ath12k/ahb.c
> +++ b/drivers/net/wireless/ath/ath12k/ahb.c
> @@ -15,6 +15,7 @@
> #include "ahb.h"
> #include "debug.h"
> #include "hif.h"
> +#include <linux/of_reserved_mem.h>
system includes should come before the local includes. I'd fix this in my tree
except there is another issue in the 3/3 patch so I'll want a v2 for the
entire series.
^ permalink raw reply
* RE: [PATCH rtw-next v3] wifi: rtw89: check return values in rtw89_ops_start_ap()
From: Ping-Ke Shih @ 2026-06-30 0:27 UTC (permalink / raw)
To: Dmitry Morgun
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
lvc-project@linuxtesting.org
In-Reply-To: <20260629094452.8709-1-d.morgun@ispras.ru>
Dmitry Morgun <d.morgun@ispras.ru> wrote:
> Subject: [PATCH rtw-next v3] wifi: rtw89: check return values in rtw89_ops_start_ap()
Patch is fine. But I don't remember why this is v3. Can you point out
v1 and v2?
> Several functions called in rtw89_ops_start_ap() may fail to allocate
> skb or fail to send H2C command to firmware, returning -ENOMEM or an
> error code. Their return values are ignored, so subsequent commands
> are executed with incorrect state.
>
> Check the return values and propagate errors.
>
> Found by Linux Verification Center (linuxtesting.org) with SVACE.
>
> Fixes: a52e4f2ce0f5 ("wifi: rtw89: implement ieee80211_ops::start_ap and stop_ap")
> Signed-off-by: Dmitry Morgun <d.morgun@ispras.ru>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
^ permalink raw reply
* Re: [PATCH 1/1] wifi: ath12k: support calibration-variant from device tree
From: Andrew LaMarche @ 2026-06-29 18:21 UTC (permalink / raw)
To: Ernest Van Hoecke
Cc: Jeff Johnson, Jeff Johnson, linux-wireless, ath12k, linux-kernel
In-Reply-To: <rh6uz6bqetobowyrist7hhytdu32v2azvuambenyzofur3tjyj@wghfgwtmjj53>
I will prepare v2 this week.
> On Jun 29, 2026, at 4:21 AM, Ernest Van Hoecke <ernestvanhoecke@gmail.com> wrote:
>
> On Mon, Jun 15, 2026 at 03:36:50PM +0200, Ernest Van Hoecke wrote:
>> On Wed, May 27, 2026 at 05:01:51PM -0400, Andrew LaMarche wrote:
>>> Ack on the deprecation of qcom,ath12k-calibration-variant in favor of the
>>> generic qcom,calibration-variant.
>>>
>>> However, drivers/net/wireless/ath/ath12k/core.c still misses the logic to
>>> actually load in the BDF, which this patch also accomplishes. I don’t see that
>>> in the Qualcomm-authored series you linked. Perhaps a v2 to address this?
>>>
>>> Andrew
>>>
>>>> On May 27, 2026, at 3:52 PM, Jeff Johnson <jeff.johnson@oss.qualcomm.com> wrote:
>>>>
>>>> On 5/27/2026 9:12 AM, Andrew LaMarche wrote:
>>>>> Hi,
>>>>>
>>>>> A kind ping here. I’m not sure why this functionality is missing in the first place, but it is needed for loading caldata from the device tree.
>>>>
>>>> Your patch duplicates functionality in the Qualcomm authored series:
>>>> https://msgid.link/20250228184214.337119-1-quic_rajkbhag@quicinc.com
>>>>
>>>> And note the upstream device bindings for ath10k and ath11k only support the
>>>> generic binding qcom,calibration-variant.
>>>>
>>>> There are no longer any generation-specific bindings, see:
>>>> https://msgid.link/20250225-b-wifi-qcom-calibration-variant-v1-0-3b2aa3f89c53@linaro.org
>>>>
>>>> /jeff
>>>
>>
>> Hi Jeff,
>> Hi Andrew,
>>
>> We also need this functionality, and as Andrew noted, the patch series
>> from Qualcomm does not actually address this.
>>
>> I believe some confusion arose because the patch series mentions the
>> "qcom,calibration-variant" DT property, but it does nothing to implement
>> this in the driver. In that series it only exists within a schema file:
>> Documentation/devicetree/bindings/net/wireless/qcom,ipq5332-wifi.yaml
>>
>> Our need for this property was previously discussed here, and for now I
>> don't see a way forward without it:
>> https://lore.kernel.org/all/77vowy4ax4cl6dlc45i2q3fjmwn3q676wqghq267tmbix7773b@27h5t66mflur/
>>
>> Thanks a lot for your work here.
>>
>> Best regards,
>> Ernest
>>
>
> Hi Andrew,
>
> Are you planning to send a v2 of this?
>
> If not, I'm considering sending a version of this with the
> qcom,calibration-variant binding to move this forward.
>
> Thanks for your work here!
>
> Kind regards,
> Ernest
>
^ permalink raw reply
* mt76 newly rejects formerly working MAC from EPROM
From: Tobias Klausmann @ 2026-06-29 18:02 UTC (permalink / raw)
To: linux-mediatek; +Cc: linux-wireless, Felix Fietkau
Hi!
As of 7.2-rc1, my mediatek USB wifi dongle no longer works the way it
used to. I now get an error about its MAC being wrong:
usbcore: registered new interface driver mt7921u
mt7921u 8-1:1.0: HW/SW Version: 0x8a108a10, Build Time: 20260224110909a
mt7921u 8-1:1.0: WM Firmware Version: ____010000, Build Time: 20260224110949
mt7921u 8-1:1.0: Invalid MAC address, using random address c2:4d:ae:23:29:38
mt7921u 8-1:1.0 wlxc24dae232938: renamed from wlan0
This naturally breaks my udev rules, interface naming etc etc.
So I bisected it, and this is the commit that started it:
commit 31ee1582717e220cc5a3fa8f3940d5693c5c9169 (HEAD)
Author: Rosen Penev <rosenp@gmail.com>
Date: Sun Apr 26 22:17:46 2026 -0700
wifi: mt76: fix of_get_mac_address error handling
Check return value instead of is_valid_ether_addr. The latter is handled
by the former.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Link: https://patch.msgid.link/20260427051746.954704-1-rosenp@gmail.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index afdb73661866e..b99d7452800fd 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -181,7 +181,7 @@ mt76_eeprom_override(struct mt76_phy *phy)
if (err == -EPROBE_DEFER)
return err;
- if (!is_valid_ether_addr(phy->macaddr)) {
+ if (err) {
eth_random_addr(phy->macaddr);
dev_info(dev->dev,
"Invalid MAC address, using random address %pM\n",
SO I thought that maybe my device had had a broken MAC all this time,
and I just didn't notice. I changed the code a bit to emit the erroneous
MAC and the value of err, which resulted in:
usbcore: registered new interface driver mt7921u
mt7921u 8-1:1.0: HW/SW Version: 0x8a108a10, Build Time: 20260224110909a
mt7921u 8-1:1.0: WM Firmware Version: ____010000, Build Time: 20260224110949
mt7921u 8-1:1.0: Invalid MAC address e0:e1:a9:38:96:29 err: -19
mt7921u 8-1:1.0: Using random address 3a:ca:1d:51:83:f2
mt7921u 8-1:1.0 wlx3aca1d5183f2: renamed from wlan0
To me, e0:e1:a9:38:96:29 looks like a perfectly valid MAC, so at a
minimum, the current code's wording is misleading: it assumes the only
way of_get_mac_address() can ever return a nonzero value is if the MAC
is invalid. However, looking at its code, I only see one nonzero return
statement, which returns ENODEV, which seems at odds with "broken MAC".
Either way, I am looking for a way to get a stable MAC again, either by
fixing this commit (if it is indeed a bug) or fixing my hardware (if the
bug is there.
Best,
Tobias Klausmann
^ permalink raw reply related
* Re: [PATCH v11] PCI: Add device-specific reset for Qualcomm devices
From: Manivannan Sadhasivam @ 2026-06-29 16:01 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez
Cc: bhelgaas, alex, jjohnson, linux-pci, linux-wireless, ath11k,
ath12k, mhi, linux-kernel
In-Reply-To: <20260626055023.197470-1-jtornosm@redhat.com>
On Fri, Jun 26, 2026 at 07:50:23AM +0200, Jose Ignacio Tornos Martinez wrote:
> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
> lack working reset methods for VFIO passthrough scenarios. These devices
> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
> broken bus reset.
>
> The problem manifests in VFIO passthrough scenarios:
>
> - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
> Normal VM operation works fine, including clean shutdown/reboot.
> However, when the VM terminates uncleanly (crash, force-off), VFIO
> attempts to reset the device before it can be assigned to another VM.
> Without a working reset method, the device remains in an undefined state,
> preventing reuse.
>
> - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
> on first VM assignment without proper reset capability.
>
> Add device-specific reset methods using BAR-space hardware reset registers
> that exist in these devices:
>
> - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
> ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
> ath11k_mhi_set_mhictrl_reset()):
> - Write/clear reset bit at offset 0x3008
> - Wait for PCIe link recovery (up to 5 seconds)
> - Clear MHI controller SYSERR status at offset 0x38
>
> - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
> driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
> - Write reset request to offset 0xb0
> - Wait 2 seconds for reset completion
>
> These are true hardware reset mechanisms (not power management or firmware
> error recovery), providing proper device reset for VFIO scenarios.
>
> Testing was performed on desktop platforms with M.2 WiFi and modem cards
> using M.2-to-PCIe adapters, including extensive force-reset cycling to
> verify stability.
>
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
Couple of comments below, including one bug due to my previous suggestion. Once
those are addressed, feel free to add:
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
> ---
> v11: Address Manivannan Sadhasivam feedback:
> - Remove unused QUALCOMM_WIFI_MHISTATUS define
> - Use PCI_ERROR_RESPONSE instead of 0xffffffff
> - Sort device IDs in ascending order (0x0308, 0x1103, 0x1107)
> v10: https://lore.kernel.org/all/20260623183115.1585273-1-jtornosm@redhat.com/
>
> drivers/pci/quirks.c | 117 +++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 117 insertions(+)
>
> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
> index 431c021d7414..0de606366200 100644
> --- a/drivers/pci/quirks.c
> +++ b/drivers/pci/quirks.c
> @@ -4240,6 +4240,120 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
> return 0;
> }
>
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
> +#define QUALCOMM_WIFI_MHICTRL 0x38
> +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
Qcom calls these WiFi devices as WLAN devices. So I'd prefer to use this term
all around even for function names.
> +
> +/*
> + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
> + * registers.
> + */
> +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
> +{
> + bool link_recovered = false;
> + unsigned long timeout;
> + void __iomem *bar;
> + u32 val;
> + u16 cmd;
> +
> + if (probe)
> + return 0;
> +
> + if (pdev->current_state != PCI_D0)
> + return -EINVAL;
> +
> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
> +
> + bar = pci_iomap(pdev, 0, 0);
> + if (!bar) {
> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
> + return -ENODEV;
> + }
> +
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + val |= QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + val &= ~QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V;
> + iowrite32(val, bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> +
> + msleep(10);
> +
> + timeout = jiffies + msecs_to_jiffies(5000);
> + while (time_before(jiffies, timeout)) {
> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
> + if (val != PCI_ERROR_RESPONSE) {
As Sashiko also pointed out, this will always evaluate to true due to
PCI_ERROR_RESPONSE being ~0ULL. Please use:
if (!PCI_POSSIBLE_ERROR(val))
Sorry for the wrong suggestion earlier.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH] wifi: ath9k: avoid device access after async firmware request
From: Yousef Alhouseen @ 2026-06-29 13:56 UTC (permalink / raw)
To: Toke Høiland-Jørgensen
Cc: linux-wireless, linux-kernel, stable, syzbot+cb7ed9d85261445a0201
In-Reply-To: <87jyrhd6c1.fsf@toke.dk>
Thanks for the pointer. I missed the pending patch; please drop mine.
Thanks,
Yousef
On Mon, 29 Jun 2026 11:49:34 +0200, "Toke Høiland-Jørgensen"
<toke@toke.dk> wrote:
> Yousef Alhouseen <alhouseenyousef@gmail.com> writes:
>
> > request_firmware_nowait() may invoke the callback before the requesting
> > context resumes. When a firmware lookup fails, the callback starts the
> > next fallback request. That nested request can exhaust the fallback list,
> > complete fw_done, and let disconnect free hif_dev before the parent request
> > returns.
> >
> > The parent then dereferences hif_dev only to print a successful-request
> > message. Remove that post-request access so completion cannot leave an
> > older callback using the freed device state.
> >
> > Fixes: e904cf6fe230 ("ath9k_htc: introduce support for different fw versions")
> > Reported-by: syzbot+cb7ed9d85261445a0201@syzkaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=cb7ed9d85261445a0201
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
>
> An identical patch was already submitted and is currently pending:
> https://patchwork.kernel.org/project/linux-wireless/patch/20260605153210.20471-1-1020691186@qq.com/
>
> -Toke
^ permalink raw reply
* Re: [PATCH 1/2] wifi: iwlwifi: enable MFP_CAPABLE in FIPS mode
From: Johannes Berg @ 2026-06-29 13:02 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez
Cc: emmanuel.grumbach, linux-kernel, linux-wireless,
miriam.rachel.korenblit
In-Reply-To: <20260629124535.599631-1-jtornosm@redhat.com>
Hi,
> Since we have some customers requesting this, I'd appreciate some
> clarification on what specifically cannot be done so I can better
> understand the firmware requirements.
The firmware handles various action frames that are robust action
frames, so their protection must be checked on RX and they must be
protected on TX. For example, CSA (spectrum management) action frames,
or Block-Ack action frames.
> Our testing shows MFP working with software crypto in FIPS mode
> (connection succeeds, "MFP: yes" confirmed, firmware reports SEC_ENC_ERR
> 0x707 indicating it's not handling the frames). Indeed it was working like
> this before your commit.
Sure, it *works*, but it's not secure nor fast.
> Could you help me to understand:
> - What breaks when MFP_CAPABLE is enabled in FIPS mode?
> - Is there a specific scenario (beyond WoWLAN/beacon protection) where
> firmware must handle MFP frames?
See above, I think.
> - Is the issue that it appears to work but fails in certain conditions?
>
> If possible, I would like to get information at least to justify the
> reasons why FIPS cannot be used with WiFi Intel cards (or if there is
> some way/approach to make it work).
I don't think there's any good/secure way of doing this.
johannes
^ permalink raw reply
* [PATCH v2 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add support for the Bluetooth controller found in the IPQ5018 SoC.
This driver implements firmware loading and the transport layer between
the HCI core and the Bluetooth controller.
The firmware is loaded by the host into the dedicated reserved memory
carveout and authenticated by TrustZone. A Secure Channel Manager (SCM)
call safely brings the peripheral core out of reset.
A shared memory ring buffer topology handles runtime data frame
transport between the host APSS and the controller.
An outgoing APCS IPC bit and an incoming GIC interrupt handle host/guest
signaling.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/bluetooth/Kconfig | 11 +
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btqcomipc.c | 1052 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1064 insertions(+)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..c9785f43c87c 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -413,6 +413,17 @@ config BT_MTKUART
Say Y here to compile support for MediaTek Bluetooth UART devices
into the kernel or say M to compile it as module (btmtkuart).
+config BT_QCOMIPC
+ tristate "Qualcomm IPQ5018 IPC based HCI support"
+ select BT_QCA
+ help
+ Qualcomm IPQ5018 IPC based HCI driver.
+ This driver is used to load firmware and bridge HCI data onto shared
+ memory between the host and the Bluetooth controller.
+
+ Say Y here to compile support for HCI over Qualcomm IPC into the
+ kernel or say M to compile as a module.
+
config BT_QCOMSMD
tristate "Qualcomm SMD based HCI support"
depends on RPMSG || (COMPILE_TEST && RPMSG=n)
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index e6b1c1180d1d..05f19047bed0 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_MRVL) += btmrvl.o
obj-$(CONFIG_BT_MRVL_SDIO) += btmrvl_sdio.o
obj-$(CONFIG_BT_MTKSDIO) += btmtksdio.o
obj-$(CONFIG_BT_MTKUART) += btmtkuart.o
+obj-$(CONFIG_BT_QCOMIPC) += btqcomipc.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
new file mode 100644
index 000000000000..16bc59e4edae
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,1052 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/elf.h>
+#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/mdt_loader.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btqca.h"
+
+/** Message header format.
+ *
+ * ----------------------------------------------------------------
+ * BitPos | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 - 0 |
+ * ---------------------------------------------------------------
+ * Field | long_msg |ACK | RFU | pkt_type | cmd |
+ * ----------------------------------------------------------------
+ *
+ * - long_msg : If set, indicates that the payload is larger than the
+ * IPC_MSG_PLD_SZ. The payload instead contains a pointer to the
+ * long message buffer in the shared BTSS memory space.
+ *
+ * - ACK : Set if sending ACK if required by sending acknowledegement
+ * to sender i.e. send an ack IPC interrupt if set.
+ *
+ * - RFU : Reserved for future use.
+ *
+ * - pkt_type : IPC Packet Type
+ *
+ * - cmd : Contains unique command ID
+ */
+
+#define IPC_MSG_HDR_SZ 4
+#define IPC_MSG_PLD_SZ 40
+#define IPC_TOTAL_MSG_SZ (IPC_MSG_HDR_SZ + IPC_MSG_PLD_SZ)
+
+/* Message Header */
+#define IPC_HDR_LONG_MSG BIT(15)
+#define IPC_HDR_REQ_ACK BIT(14)
+#define IPC_HDR_PKT_TYPE_MASK GENMASK(9, 8)
+#define IPC_HDR_PKT_TYPE_CUST 0
+#define IPC_HDR_PKT_TYPE_HCI 1
+#define IPC_HDR_PKT_TYPE_AUDIO 2
+#define IPC_HDR_PKT_TYPE_RFU 3
+#define IPC_HDR_CMD_MASK GENMASK(7, 0)
+
+#define IPC_CMD_STOP 1
+#define IPC_CMD_SWITCH_TO_UART 2
+#define IPC_CMD_PREPARE_DUMP 3
+#define IPC_CMD_COLLECT_DUMP 4
+#define IPC_CMD_START 5
+
+#define IPC_TX_QSIZE 32
+
+#define TO_APPS_ADDR(a) (desc->mem_region + (int)(uintptr_t)a)
+#define TO_BT_ADDR(a) (a - desc->mem_region)
+#define IPC_LBUF_SZ(w, x, y, z) (((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z)
+
+#define GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b)
+
+#define GET_RX_INDEX_FROM_BUF(x, y) ((x - desc->rx_ctxt->lring_buf) / y)
+
+#define GET_TX_INDEX_FROM_BUF(x, y) ((x - desc->tx_ctxt->lring_buf) / y)
+
+#define IS_RX_MEM_NON_CONTIGIOUS(buf, len, sz) \
+ ((buf + len) > (desc->rx_ctxt->lring_buf + \
+ (sz * desc->rx_ctxt->lmsg_buf_cnt)))
+
+#define BTSS_PAS_ID 0xc
+
+#define BTSS_PWR_STATE_ACTIVE 0x0
+#define BTSS_PWR_STATE_ECO 0x4
+#define BTSS_PWR_CTRL_DELAY_MS 50
+
+struct long_msg_info {
+ __le16 smsg_free_cnt;
+ __le16 lmsg_free_cnt;
+ u8 ridx;
+ u8 widx;
+} __packed;
+
+struct ipc_aux_ptr {
+ __le32 len;
+ __le32 buf;
+} __packed;
+
+struct ring_buffer {
+ __le16 msg_hdr;
+ __le16 len;
+ union {
+ u8 smsg_data[IPC_MSG_PLD_SZ];
+ __le32 lmsg_data;
+ } payload;
+} __packed;
+
+struct ring_buffer_info {
+ __le32 rbuf;
+ u8 ring_buf_cnt;
+ u8 ridx;
+ u8 widx;
+ u8 tidx;
+ __le32 next;
+} __packed;
+
+struct context_info {
+ __le16 total_size;
+ u8 lmsg_buf_cnt;
+ u8 smsg_buf_cnt;
+ struct ring_buffer_info sring_buf_info;
+ __le32 sring_buf;
+ __le32 lring_buf;
+ __le32 reserved;
+} __packed;
+
+struct qcom_btss {
+ struct device *dev;
+ struct hci_dev *hdev;
+
+ struct regmap *regmap;
+ u32 offset;
+ u32 bit;
+ int irq;
+
+ const char *firmware;
+ void *mem_region;
+ phys_addr_t mem_phys;
+ phys_addr_t mem_reloc;
+ size_t mem_size;
+
+ struct sk_buff_head tx_q;
+ struct workqueue_struct *wq;
+ struct work_struct work;
+ wait_queue_head_t wait_q;
+ spinlock_t lock;
+
+ struct context_info *tx_ctxt;
+ struct context_info *rx_ctxt;
+ struct long_msg_info lmsg_ctxt;
+
+ bool running;
+};
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb);
+
+static void *btss_alloc_lmsg(struct qcom_btss *desc, u32 len,
+ struct ipc_aux_ptr *aux_ptr, bool *is_lbuf_full)
+{
+ struct device *dev = desc->dev;
+ u8 idx, blks, blks_consumed;
+ void *ret_ptr;
+ u32 lsz;
+
+ if (desc->tx_ctxt->lring_buf == 0) {
+ dev_err(dev, "no long message buffer initialized\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf, lmsg_buf_cnt);
+ blks = GET_NO_OF_BLOCKS(len, lsz);
+
+ if (!desc->lmsg_ctxt.lmsg_free_cnt ||
+ (blks > desc->lmsg_ctxt.lmsg_free_cnt))
+ return ERR_PTR(-EAGAIN);
+
+ idx = desc->lmsg_ctxt.widx;
+
+ if ((desc->lmsg_ctxt.widx + blks) > desc->tx_ctxt->lmsg_buf_cnt) {
+ blks_consumed = desc->tx_ctxt->lmsg_buf_cnt - idx;
+ aux_ptr->len = len - (blks_consumed * lsz);
+ aux_ptr->buf = desc->tx_ctxt->lring_buf;
+ }
+
+ desc->lmsg_ctxt.widx = (desc->lmsg_ctxt.widx + blks) %
+ desc->tx_ctxt->lmsg_buf_cnt;
+
+ desc->lmsg_ctxt.lmsg_free_cnt -= blks;
+
+ if (desc->lmsg_ctxt.lmsg_free_cnt <=
+ ((desc->tx_ctxt->lmsg_buf_cnt * 20) / 100))
+ *is_lbuf_full = true;
+
+ ret_ptr = TO_APPS_ADDR(desc->tx_ctxt->lring_buf) + (idx * lsz);
+
+ return ret_ptr;
+}
+
+static struct ring_buffer_info *btss_get_tx_rbuf(struct qcom_btss *desc,
+ bool *is_sbuf_full)
+{
+ u8 idx;
+ struct ring_buffer_info *rinfo;
+
+ for (rinfo = &(desc->tx_ctxt->sring_buf_info); rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ idx = (rinfo->widx + 1) % (desc->tx_ctxt->smsg_buf_cnt);
+
+ if (idx != rinfo->tidx) {
+ desc->lmsg_ctxt.smsg_free_cnt--;
+
+ if (desc->lmsg_ctxt.smsg_free_cnt <=
+ ((desc->tx_ctxt->smsg_buf_cnt * 20) / 100))
+ *is_sbuf_full = true;
+
+ return rinfo;
+ }
+ }
+
+ return ERR_PTR(-EAGAIN);
+}
+
+static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
+ struct sk_buff *skb)
+{
+ struct hci_dev *hdev = desc->hdev;
+ struct ring_buffer_info *rinfo;
+ struct ipc_aux_ptr aux_ptr;
+ struct ring_buffer *rbuf;
+ bool is_lbuf_full = false;
+ bool is_sbuf_full = false;
+ u16 hdr = msg_hdr;
+ void *ptr_buf;
+ u32 len;
+
+ /* Account for HCI packet type as it's not included in the skb payload */
+ len = (skb) ? skb->len + 1 : 0;
+ memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+ if (len > IPC_MSG_PLD_SZ) {
+ hdr |= IPC_HDR_LONG_MSG;
+
+ ptr_buf = btss_alloc_lmsg(desc, len,
+ &aux_ptr, &is_lbuf_full);
+ if (IS_ERR(ptr_buf)) {
+ bt_dev_err(hdev, "TX long buffers full");
+ hdev->stat.err_tx++;
+ return PTR_ERR(ptr_buf);
+ }
+ }
+
+ rinfo = btss_get_tx_rbuf(desc, &is_sbuf_full);
+ if (IS_ERR(rinfo)) {
+ bt_dev_err(hdev, "TX short buffers full");
+ hdev->stat.err_tx++;
+ return PTR_ERR(rinfo);
+ }
+
+ rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[rinfo->widx];
+
+ if (!skb)
+ goto complete_tx;
+
+ if (len > IPC_MSG_PLD_SZ)
+ rbuf->payload.lmsg_data = cpu_to_le32(TO_BT_ADDR(ptr_buf));
+ else
+ ptr_buf = rbuf->payload.smsg_data;
+
+ /* if it's a short message, the aux len and buf are NULL */
+ memcpy_toio(ptr_buf, &hci_skb_pkt_type(skb), 1);
+ memcpy_toio((u8 *)ptr_buf + 1, skb->data, skb->len - aux_ptr.len);
+ if (aux_ptr.buf) {
+ memcpy_toio(TO_APPS_ADDR(aux_ptr.buf),
+ (skb->data + (skb->len - aux_ptr.len)),
+ aux_ptr.len);
+ }
+
+ /*
+ * if free buffer count is low, send ACK request to signal to the
+ * firmware to process and free up queued buffers in the TX ring.
+ */
+ if (is_sbuf_full || is_lbuf_full)
+ hdr |= IPC_HDR_REQ_ACK;
+
+complete_tx:
+ rbuf->msg_hdr = cpu_to_le16(hdr);
+ rbuf->len = cpu_to_le16(len);
+
+ rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+ regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+ return 0;
+}
+
+static void btss_process_tx_queue(struct qcom_btss *desc)
+{
+ struct sk_buff *skb;
+ u16 hdr;
+ int ret;
+
+ while ((skb = skb_dequeue(&desc->tx_q))) {
+ hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+
+ ret = btss_send(desc, hdr, skb);
+ if (ret) {
+ bt_dev_err(desc->hdev, "Failed to send message");
+ skb_queue_head(&desc->tx_q, skb);
+ break;
+ }
+
+ btqcomipc_update_stats(desc->hdev, skb);
+ kfree_skb(skb);
+ }
+}
+
+static void btss_free_lmsg(struct qcom_btss *desc, u32 lmsg, u16 len)
+{
+ u8 idx;
+ u8 blks;
+ u32 lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf,
+ lmsg_buf_cnt);
+
+ idx = GET_TX_INDEX_FROM_BUF(lmsg, lsz);
+
+ if (idx != desc->lmsg_ctxt.ridx)
+ return;
+
+ blks = GET_NO_OF_BLOCKS(len, lsz);
+
+ desc->lmsg_ctxt.ridx = (desc->lmsg_ctxt.ridx + blks) %
+ desc->tx_ctxt->lmsg_buf_cnt;
+
+ desc->lmsg_ctxt.lmsg_free_cnt += blks;
+}
+
+static int btss_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
+{
+ u16 msg_hdr = 0;
+ int ret;
+
+ switch (cmd) {
+ case IPC_CMD_STOP:
+ spin_unlock(&desc->lock);
+ ret = qcom_scm_pas_set_bluetooth_power_mode(BTSS_PAS_ID,
+ BTSS_PWR_STATE_ECO);
+ spin_lock(&desc->lock);
+ if (ret && ret != -EOPNOTSUPP) {
+ bt_dev_err(desc->hdev,
+ "Failed to apply BTSS power-save mode: %d",
+ ret);
+ return ret;
+ }
+
+ WRITE_ONCE(desc->running, false);
+
+ msg_hdr |= cmd;
+ ret = btss_send(desc, msg_hdr, NULL);
+ if (ret)
+ bt_dev_err(desc->hdev,
+ "Failed to send control message");
+ break;
+ case IPC_CMD_START:
+ spin_unlock(&desc->lock);
+ ret = qcom_scm_pas_set_bluetooth_power_mode(BTSS_PAS_ID,
+ BTSS_PWR_STATE_ACTIVE);
+ spin_lock(&desc->lock);
+ if (ret && ret != -EOPNOTSUPP) {
+ bt_dev_err(desc->hdev,
+ "Failed to apply BTSS active power mode: %d",
+ ret);
+ return ret;
+ }
+
+ desc->tx_ctxt = (struct context_info *)((void *)desc->rx_ctxt +
+ le16_to_cpu(desc->rx_ctxt->total_size));
+ desc->lmsg_ctxt.widx = 0;
+ desc->lmsg_ctxt.ridx = 0;
+ desc->lmsg_ctxt.smsg_free_cnt = desc->tx_ctxt->smsg_buf_cnt;
+ desc->lmsg_ctxt.lmsg_free_cnt = desc->tx_ctxt->lmsg_buf_cnt;
+ WRITE_ONCE(desc->running, true);
+ wake_up(&desc->wait_q);
+ break;
+ default:
+ bt_dev_err(desc->hdev, "Unsupported CMD ID: %u", cmd);
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static inline int btss_recv_hci_frame(struct qcom_btss *desc, const u8 *data,
+ size_t len)
+{
+ unsigned char pkt_type;
+ struct sk_buff *skb;
+ size_t pkt_len;
+
+ if (len < 1)
+ return -EPROTO;
+
+ pkt_type = data[0];
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ {
+ if (len < 1 + HCI_EVENT_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *)(data + 1);
+
+ pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+ break;
+ }
+ case HCI_COMMAND_PKT: {
+ if (len < 1 + HCI_COMMAND_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_command_hdr *hdr = (struct hci_command_hdr *)(data + 1);
+
+ pkt_len = HCI_COMMAND_HDR_SIZE + le16_to_cpu(hdr->plen);
+ break;
+ }
+ case HCI_ACLDATA_PKT:
+ {
+ if (len < 1 + HCI_ACL_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *)(data + 1);
+
+ pkt_len = HCI_ACL_HDR_SIZE + le16_to_cpu(hdr->dlen);
+ break;
+ }
+ case HCI_SCODATA_PKT:
+ {
+ if (len < 1 + HCI_SCO_HDR_SIZE)
+ return -EILSEQ;
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *)(data + 1);
+
+ pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+ break;
+ }
+ default:
+ return -EPROTO;
+ }
+
+ if (pkt_len + 1 > len)
+ return -EINVAL;
+
+ skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+ if (!skb) {
+ desc->hdev->stat.err_rx++;
+ return -ENOMEM;
+ }
+
+ skb->dev = (void *)desc->hdev;
+ hci_skb_pkt_type(skb) = pkt_type;
+ skb_put_data(skb, data + 1, pkt_len);
+
+ hci_recv_frame(desc->hdev, skb);
+ desc->hdev->stat.byte_rx += pkt_len;
+
+ return 0;
+}
+
+static inline int btss_process_rx(struct qcom_btss *desc,
+ struct ring_buffer_info *rinfo,
+ bool *ack, u8 *rx_count)
+{
+ u8 ridx, lbuf_idx, blks_consumed, pkt_type, cmd;
+ struct ipc_aux_ptr aux_ptr;
+ struct ring_buffer *rbuf;
+ uint8_t *rxbuf = NULL;
+ unsigned char *buf;
+ u32 lsz;
+ int ret;
+
+ ridx = rinfo->ridx;
+
+ while (ridx != rinfo->widx) {
+ memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+ rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[ridx];
+
+ if (rbuf->msg_hdr & IPC_HDR_LONG_MSG) {
+ rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data);
+ lsz = IPC_LBUF_SZ(desc->rx_ctxt, total_size, lring_buf,
+ lmsg_buf_cnt);
+
+ if (IS_RX_MEM_NON_CONTIGIOUS(rbuf->payload.lmsg_data,
+ rbuf->len, lsz)) {
+ lbuf_idx = GET_RX_INDEX_FROM_BUF(
+ rbuf->payload.lmsg_data, lsz);
+
+ blks_consumed = desc->rx_ctxt->lmsg_buf_cnt -
+ lbuf_idx;
+ aux_ptr.len = rbuf->len - (blks_consumed * lsz);
+ aux_ptr.buf = desc->rx_ctxt->lring_buf;
+ }
+ } else {
+ rxbuf = rbuf->payload.smsg_data;
+ }
+
+ *ack = (rbuf->msg_hdr & IPC_HDR_REQ_ACK);
+
+ pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, rbuf->msg_hdr);
+
+ switch (pkt_type) {
+ case IPC_HDR_PKT_TYPE_HCI:
+ buf = kmalloc(rbuf->len, GFP_ATOMIC);
+ if (!buf) {
+ rinfo->ridx = ridx;
+ return -ENOMEM;
+ }
+
+ memcpy_fromio(buf, rxbuf, rbuf->len - aux_ptr.len);
+
+ if (aux_ptr.buf)
+ memcpy_fromio(buf + (rbuf->len - aux_ptr.len),
+ TO_APPS_ADDR(aux_ptr.buf),
+ aux_ptr.len);
+
+ ret = btss_recv_hci_frame(desc, buf, rbuf->len);
+ if (ret)
+ bt_dev_err(desc->hdev,
+ "Failed to process HCI frame: %d",
+ ret);
+ kfree(buf);
+ break;
+ case IPC_HDR_PKT_TYPE_CUST:
+ cmd = FIELD_GET(IPC_HDR_CMD_MASK, rbuf->msg_hdr);
+ ret = btss_recv_cust_frame(desc, cmd);
+ if (ret)
+ bt_dev_warn(desc->hdev,
+ "Failed to process custom frame: %d",
+ ret);
+ break;
+ default:
+ break;
+ }
+
+ ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+
+ if (rx_count)
+ (*rx_count)++;
+
+ rinfo->ridx = ridx;
+ }
+
+ return 0;
+}
+
+static void btss_process_ack(struct qcom_btss *desc)
+{
+ struct ring_buffer_info *rinfo;
+ struct ring_buffer *rbuf;
+ u8 tidx;
+
+ for (rinfo = &desc->tx_ctxt->sring_buf_info; rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ tidx = rinfo->tidx;
+ rbuf = (struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf);
+
+ while (tidx != rinfo->ridx) {
+ if (rbuf[tidx].msg_hdr & IPC_HDR_LONG_MSG) {
+ btss_free_lmsg(desc,
+ rbuf[tidx].payload.lmsg_data,
+ rbuf[tidx].len);
+ }
+
+ tidx = (tidx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+ desc->lmsg_ctxt.smsg_free_cnt++;
+ }
+
+ rinfo->tidx = tidx;
+
+ btss_process_tx_queue(desc);
+ }
+}
+
+static void btss_worker(struct work_struct *work)
+{
+ struct qcom_btss *desc = container_of(work, struct qcom_btss, work);
+ struct ring_buffer_info *rinfo;
+ bool ack = false;
+ u32 offset;
+ int ret;
+
+ spin_lock(&desc->lock);
+
+ if (unlikely(!READ_ONCE(desc->running))) {
+ /*
+ * FW sets offset of RX context info at the start of the memory
+ * region upon boot
+ */
+ offset = readl(desc->mem_region);
+ dev_dbg(desc->dev, "offset after firmware boot: 0x%08x\n",
+ offset);
+ desc->rx_ctxt = (struct context_info *)(desc->mem_region + offset);
+ } else {
+ btss_process_ack(desc);
+ }
+
+ for (rinfo = &(desc->rx_ctxt->sring_buf_info);
+ rinfo != NULL;
+ rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+ ret = btss_process_rx(desc, rinfo, &ack,
+ &desc->rx_ctxt->smsg_buf_cnt);
+ if (ret) {
+ bt_dev_err(desc->hdev,
+ "Failed to process peer msgs: %d", ret);
+ goto spin_unlock;
+ }
+ }
+
+ if (ack)
+ regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+spin_unlock:
+ spin_unlock(&desc->lock);
+}
+
+static irqreturn_t btss_irq_handler(int irq, void *data)
+{
+ struct qcom_btss *desc = data;
+
+ queue_work(desc->wq, &desc->work);
+
+ return IRQ_HANDLED;
+}
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u8 pkt_type = hci_skb_pkt_type(skb);
+
+ hdev->stat.byte_tx += skb->len;
+ switch (pkt_type) {
+ case HCI_COMMAND_PKT:
+ hdev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ hdev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ hdev->stat.sco_tx++;
+ break;
+ default:
+ break;
+ }
+}
+
+static int btcomqipc_firmware_load(struct qcom_btss *desc)
+{
+ const struct elf32_phdr *phdrs;
+ const struct firmware *seg_fw;
+ const struct elf32_phdr *phdr;
+ const struct elf32_hdr *ehdr;
+ const struct firmware *fw;
+ int i, ret;
+
+ ret = request_firmware(&fw, desc->firmware, desc->dev);
+ if (ret) {
+ dev_err(desc->dev, "Failed to request firmware: %d\n",
+ ret);
+ return ret;
+ }
+
+ ehdr = (const struct elf32_hdr *)fw->data;
+ phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+ ret = qcom_mdt_pas_init(desc->dev, fw, desc->firmware,
+ BTSS_PAS_ID, desc->mem_phys, NULL);
+ if (ret) {
+ dev_err(desc->dev, "PAS init failed: %d\n", ret);
+ goto release_fw;
+ }
+
+ for (i = 0; i < ehdr->e_phnum; i++) {
+ char *seg_name __free(kfree) = kstrdup(desc->firmware,
+ GFP_KERNEL);
+ if (!seg_name) {
+ ret = -ENOMEM;
+ goto release_fw;
+ }
+
+ phdr = &phdrs[i];
+
+ /* Only process valid loadable data segments */
+ if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
+ continue;
+
+ if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
+ dev_err(desc->dev,
+ "Segment data exceeds the reserved memory area!\n");
+ goto release_fw;
+ }
+
+ /* Check if firmware is split across multiple segment files */
+ if (phdr->p_offset > fw->size ||
+ phdr->p_offset + phdr->p_filesz > fw->size) {
+ sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);
+ ret = request_firmware(&seg_fw, seg_name,
+ desc->dev);
+ if (ret) {
+ dev_err(desc->dev,
+ "Could not find split segment binary: %s\n",
+ seg_name);
+ goto release_fw;
+ }
+
+ /*
+ * Use the virtual instead of the physical address as
+ * the offset
+ */
+ memcpy_toio(desc->mem_region + phdr->p_vaddr,
+ seg_fw->data, phdr->p_filesz);
+
+ release_firmware(seg_fw);
+ } else {
+ memcpy_toio(desc->mem_region + phdr->p_vaddr,
+ fw->data + phdr->p_offset, phdr->p_filesz);
+ }
+ }
+
+release_fw:
+ release_firmware(fw);
+ return ret;
+}
+
+static int btqcomipc_open(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+ int ret;
+
+ if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
+ bt_dev_err(hdev,
+ "PAS is not available for peripheral: 0x%x",
+ BTSS_PAS_ID);
+ return -ENODEV;
+ }
+
+ ret = btcomqipc_firmware_load(desc);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to load firmware: %d", ret);
+ return ret;
+ }
+
+ /* Boot firmware */
+ ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to boot firmware: %d", ret);
+ return ret;
+ }
+
+ msleep(BTSS_PWR_CTRL_DELAY_MS);
+ ret = wait_event_timeout(desc->wait_q, READ_ONCE(desc->running),
+ msecs_to_jiffies(1000));
+
+ if (!ret) {
+ bt_dev_err(hdev, "Timeout waiting for BTSS start");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int btqcomipc_close(struct hci_dev *hdev)
+{
+ int ret;
+
+ ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
+ return ret;
+ }
+
+ msleep(BTSS_PWR_CTRL_DELAY_MS);
+
+ return 0;
+}
+
+static int btqcomipc_setup(struct hci_dev *hdev)
+{
+ struct qca_btsoc_version ver;
+ int ret;
+
+ /*
+ * Enable controller to do both LE scan and BR/EDR inquiry
+ * simultaneously.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+ /*
+ * Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute
+ * setup for every hci up.
+ */
+ hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+ ret = qca_read_soc_version(hdev, &ver, QCA_IPQ5018);
+ if (ret)
+ return -EINVAL;
+
+ ret = qca_uart_setup(hdev, 0, QCA_IPQ5018, ver, NULL);
+ if (ret) {
+ bt_dev_err(hdev, "Failed to setup UART: %d\n", ret);
+ return ret;
+ }
+
+ bt_dev_info(hdev, "QCA Build Info: %s", hdev->fw_info);
+
+ /* Obtain and set BD address from NVMEM cell */
+ hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+ hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+
+ return 0;
+}
+
+static int btqcomipc_send(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ u16 hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+ int ret;
+
+ if (unlikely(!READ_ONCE(desc->running))) {
+ bt_dev_err(hdev,
+ "BTSS not initialized, failed to send message");
+ ret = -ENODEV;
+ goto free_skb;
+ }
+
+ ret = btss_send(desc, hdr, skb);
+ if (ret) {
+ if (ret == -EAGAIN) {
+ if (skb_queue_len(&desc->tx_q) >= IPC_TX_QSIZE) {
+ bt_dev_err(hdev,
+ "TX queue full, dropping message");
+ hdev->stat.err_tx++;
+ ret = -ENOBUFS;
+ } else {
+ skb_queue_tail(&desc->tx_q, skb);
+ return 0;
+ }
+ } else {
+ bt_dev_err(hdev, "Failed to send message: %d", ret);
+ hdev->stat.err_tx++;
+ }
+ }
+
+ btqcomipc_update_stats(desc->hdev, skb);
+
+free_skb:
+ kfree_skb(skb);
+
+ return ret;
+}
+
+static int btqcomipc_flush(struct hci_dev *hdev)
+{
+ struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+ skb_queue_purge(&desc->tx_q);
+ return 0;
+}
+
+static int btqcomipc_init(struct qcom_btss *desc)
+{
+ struct device *dev = desc->dev;
+ int ret;
+
+ init_waitqueue_head(&desc->wait_q);
+ spin_lock_init(&desc->lock);
+ skb_queue_head_init(&desc->tx_q);
+
+ desc->wq = create_singlethread_workqueue("btss_wq");
+ if (!desc->wq) {
+ dev_err(dev, "Failed to initialize workqueue\n");
+ return -EAGAIN;
+ }
+
+ INIT_WORK(&desc->work, btss_worker);
+
+ ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "btss_irq", desc);
+
+ if (ret)
+ dev_err(dev, "error registering irq[%d] ret = %d\n",
+ desc->irq, ret);
+
+ return ret;
+}
+
+static void btqcomipc_deinit(struct qcom_btss *desc)
+{
+ disable_irq(desc->irq);
+ if (desc->wq != NULL) {
+ flush_workqueue(desc->wq);
+ skb_queue_purge(&desc->tx_q);
+ destroy_workqueue(desc->wq);
+ desc->wq = NULL;
+ }
+}
+
+static int btqcomipc_alloc_memory_region(struct qcom_btss *desc)
+{
+ struct device *dev = desc->dev;
+ struct resource res;
+ int ret;
+
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (ret) {
+ dev_err(dev, "unable to acquire memory-region resource\n");
+ return ret;
+ }
+
+ desc->mem_phys = res.start;
+ desc->mem_reloc = res.start;
+ desc->mem_size = resource_size(&res);
+ desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+ if (!desc->mem_region) {
+ dev_err(dev, "unable to map memory region: %pR\n", &res);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int btqcomipc_probe(struct platform_device *pdev)
+{
+ struct reset_control *btss_reset;
+ struct device *dev = &pdev->dev;
+ struct qcom_btss *desc;
+ struct hci_dev *hdev;
+ unsigned int args[2];
+ struct clk *lpo_clk;
+ int ret;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->dev = dev;
+
+ ret = of_property_read_string(dev->of_node, "firmware-name",
+ &desc->firmware);
+ if (ret < 0)
+ return ret;
+
+ ret = btqcomipc_alloc_memory_region(desc);
+ if (ret)
+ return ret;
+
+ desc->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "qcom,ipc",
+ 2, args);
+ if (IS_ERR(desc->regmap))
+ return PTR_ERR(desc->regmap);
+
+ desc->offset = args[0];
+ desc->bit = args[1];
+
+ lpo_clk = devm_clk_get_enabled(dev, "lpo");
+ if (IS_ERR(lpo_clk))
+ return dev_err_probe(dev, PTR_ERR(lpo_clk),
+ "Failed to get lpo clock\n");
+
+ btss_reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+ if (IS_ERR_OR_NULL(btss_reset))
+ return dev_err_probe(dev, PTR_ERR(btss_reset),
+ "unable to deassert reset\n");
+
+ desc->irq = platform_get_irq(pdev, 0);
+ if (desc->irq < 0)
+ return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
+
+ ret = btqcomipc_init(desc);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to initialize\n");
+
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ btqcomipc_deinit(desc);
+ return -ENOMEM;
+ }
+
+ hci_set_drvdata(hdev, desc);
+ desc->hdev = hdev;
+ SET_HCIDEV_DEV(hdev, &pdev->dev);
+ hdev->bus = HCI_IPC;
+
+ hdev->open = btqcomipc_open;
+ hdev->close = btqcomipc_close;
+ hdev->setup = btqcomipc_setup;
+ hdev->send = btqcomipc_send;
+ hdev->flush = btqcomipc_flush;
+ hdev->set_bdaddr = qca_set_bdaddr;
+
+ ret = hci_register_dev(hdev);
+ if (ret < 0) {
+ btqcomipc_deinit(desc);
+ hci_free_dev(hdev);
+ return dev_err_probe(dev, -EBUSY, "Failed to register hdev\n");
+ }
+
+ platform_set_drvdata(pdev, desc);
+
+ return 0;
+}
+
+static void btqcomipc_remove(struct platform_device *pdev)
+{
+ struct qcom_btss *desc = platform_get_drvdata(pdev);
+
+ if (!desc)
+ return;
+
+ btqcomipc_deinit(desc);
+
+ if (desc->hdev) {
+ hci_unregister_dev(desc->hdev);
+ hci_free_dev(desc->hdev);
+ }
+}
+
+static const struct of_device_id btqcomipc_of_match[] = {
+ { .compatible = "qcom,ipq5018-bt" },
+ { /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, btqcomipc_of_match);
+
+static struct platform_driver btqcomipc_driver = {
+ .probe = btqcomipc_probe,
+ .remove = btqcomipc_remove,
+ .driver = {
+ .name = "btqcomipc",
+ .of_match_table = btqcomipc_of_match,
+ },
+};
+
+module_platform_driver(btqcomipc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Bluetooth IPC Driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH v2 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add maintainers entry for Qualcomm IPQ5018 Bluetooth driver.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0b9d7c8276ac..60f7251d1a16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22289,6 +22289,13 @@ S: Maintained
F: Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml
F: drivers/regulator/vqmmc-ipq4019-regulator.c
+QUALCOMM IPQ5018 BLUETOOTH DRIVER
+M: George Moussalem <george.moussalem@outlook.com>
+L: linux-bluetooth@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
+F: drivers/bluetooth/btqcomipc.c
+
QUALCOMM IRIS VIDEO ACCELERATOR DRIVER
M: Vikash Garodia <vikash.garodia@oss.qualcomm.com>
M: Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>
--
2.53.0
^ permalink raw reply related
* [PATCH v2 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add nodes for the reserved memory carveout and Bluetooth.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..65a47ba7d3a3 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -17,6 +17,23 @@ / {
#address-cells = <2>;
#size-cells = <2>;
+ bluetooth: bluetooth {
+ compatible = "qcom,ipq5018-bt";
+
+ firmware-name = "qca/bt_fw_patch.mbn";
+
+ clocks = <&gcc GCC_BTSS_LPO_CLK>;
+ clock-names = "lpo";
+ resets = <&gcc GCC_BTSS_BCR>;
+
+ qcom,ipc = <&apcs_glb 8 23>;
+ interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+ memory-region = <&btss_region>;
+
+ status = "disabled";
+ };
+
clocks {
gephy_rx_clk: gephy-rx-clk {
compatible = "fixed-clock";
@@ -136,6 +153,11 @@ reserved-memory {
#size-cells = <2>;
ranges;
+ btss_region: bluetooth@7000000 {
+ reg = <0x0 0x07000000 0x0 0x58000>;
+ no-map;
+ };
+
bootloader@4a800000 {
reg = <0x0 0x4a800000 0x0 0x200000>;
no-map;
@@ -647,7 +669,8 @@ watchdog: watchdog@b017000 {
apcs_glb: mailbox@b111000 {
compatible = "qcom,ipq5018-apcs-apps-global",
- "qcom,ipq6018-apcs-apps-global";
+ "qcom,ipq6018-apcs-apps-global",
+ "syscon";
reg = <0x0b111000 0x1000>;
#clock-cells = <1>;
clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;
--
2.53.0
^ permalink raw reply related
* [PATCH v2 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Document the Qualcomm IPQ5018 Bluetooth controller.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
.../bindings/net/bluetooth/qcom,ipq5018-bt.yaml | 86 ++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
new file mode 100644
index 000000000000..2119162994e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ5018 Bluetooth
+
+maintainers:
+ - George Moussalem <george.moussalem@outlook.com>
+
+properties:
+ compatible:
+ enum:
+ - qcom,ipq5018-bt
+
+ clocks:
+ items:
+ - description: Bluetooth Subsystem low power oscillator clock
+
+ clock-names:
+ items:
+ - const: lpo
+
+ firmware-name:
+ maxItems: 1
+
+ memory-region:
+ items:
+ - description: |
+ Reserved memory carveout for firmware loading and runtime data
+ transport between the host and the Bluetooth controller.
+
+ interrupts:
+ maxItems: 1
+
+ qcom,ipc:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ items:
+ - items:
+ - description: syscon node representing the APCS registers
+ - description: offset to the register within the syscon
+ - description: IPC bit within the register
+ description: |
+ The outgoing IPC bit used for signaling the Bluetooth controller of a host
+ event or for sending an ACK if the remote processor expects it.
+
+ resets:
+ items:
+ - description: Bluetooth Subsystem reset
+
+required:
+ - compatible
+ - clocks
+ - clock-names
+ - firmware-name
+ - interrupts
+ - qcom,ipc
+ - resets
+
+allOf:
+ - $ref: bluetooth-controller.yaml#
+ - $ref: qcom,bluetooth-common.yaml
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+ bluetooth {
+ compatible = "qcom,ipq5018-bt";
+
+ firmware-name = "qca/bt_fw_patch.mbn";
+
+ clocks = <&gcc GCC_BTSS_LPO_CLK>;
+ clock-names = "lpo";
+ resets = <&gcc GCC_BTSS_BCR>;
+
+ qcom,ipc = <&apcs_glb 8 23>;
+ interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+ memory-region = <&btss_region>;
+ };
--
2.53.0
^ permalink raw reply related
* [PATCH v2 2/6] Bluetooth: btqca: Add IPQ5018 support
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
Add the IPQ5018 SoC type and support for loading its firmware.
The firmware tested has been taken from GPL sources of various router
boards. Firmware files needed are:
- qca/bt_fw_patch.mbn
- qca/mpnv10.bin
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/bluetooth/btqca.c | 16 ++++++++++++++++
drivers/bluetooth/btqca.h | 3 +++
2 files changed, 19 insertions(+)
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 04ebe290bc78..e136e91976cf 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -380,6 +380,9 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
break;
case TLV_TYPE_NVM:
+ if (soc_type == QCA_IPQ5018)
+ break;
+
if (fw_size < sizeof(struct tlv_type_hdr))
return -EINVAL;
@@ -794,6 +797,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
else
rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
+ if (soc_type == QCA_IPQ5018)
+ goto download_nvm;
+
if (soc_type == QCA_WCN6750)
qca_send_patch_config_cmd(hdev);
@@ -881,6 +887,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
qca_read_fw_board_id(hdev, &boardid);
+download_nvm:
/* Download NVM configuration */
config.type = TLV_TYPE_NVM;
if (firmware_name) {
@@ -939,6 +946,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
"hmtnv", soc_type, ver, rom_ver, boardid);
break;
+ case QCA_IPQ5018:
+ snprintf(config.fwname, sizeof(config.fwname),
+ "qca/mpnv%02x.bin", rom_ver);
+ break;
default:
snprintf(config.fwname, sizeof(config.fwname),
"qca/nvm_%08x.bin", soc_ver);
@@ -958,6 +969,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
return err;
}
+ if (soc_type == QCA_IPQ5018)
+ msleep(NVM_READY_DELAY_MS);
+
switch (soc_type) {
case QCA_QCA2066:
case QCA_QCA6390:
@@ -965,6 +979,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_IPQ5018:
err = qca_disable_soc_logging(hdev);
if (err < 0)
return err;
@@ -1001,6 +1016,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
case QCA_WCN6750:
case QCA_WCN6855:
case QCA_WCN7850:
+ case QCA_IPQ5018:
/* get fw build info */
err = qca_read_fw_build_info(hdev);
if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 8f3c1b1c77b3..343cd62d1137 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -54,6 +54,8 @@
#define QCA_HSP_GF_SOC_ID 0x1200
#define QCA_HSP_GF_SOC_MASK 0x0000ff00
+#define NVM_READY_DELAY_MS 1500
+
enum qca_baudrate {
QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600,
@@ -158,6 +160,7 @@ enum qca_btsoc_type {
QCA_WCN6750,
QCA_WCN6855,
QCA_WCN7850,
+ QCA_IPQ5018,
};
#if IS_ENABLED(CONFIG_BT_QCA)
--
2.53.0
^ permalink raw reply related
* [PATCH v2 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
In-Reply-To: <20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com>
From: George Moussalem <george.moussalem@outlook.com>
The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
modes which are required to be configured through a Secure Channel
Manager (SCM) call to TrustZone. However, not all Trusted Execution
Environment (QSEE) images support this call, so first check if the call
is available.
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
drivers/firmware/qcom/qcom_scm.c | 49 ++++++++++++++++++++++++++++++++++
drivers/firmware/qcom/qcom_scm.h | 1 +
include/linux/firmware/qcom/qcom_scm.h | 1 +
3 files changed, 51 insertions(+)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b601a4b89db..e26f54e5033b 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -1094,6 +1094,55 @@ bool qcom_scm_pas_supported(u32 pas_id)
}
EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
+static int __qcom_scm_pas_set_bluetooth_power_mode(u32 pas_id, u32 val)
+{
+ struct qcom_scm_desc desc = {
+ .svc = QCOM_SCM_SVC_PIL,
+ .cmd = QCOM_SCM_PIL_PAS_BT_PWR_MODE,
+ .arginfo = QCOM_SCM_ARGS(2),
+ .args[0] = pas_id,
+ .args[1] = val,
+ .owner = ARM_SMCCC_OWNER_SIP,
+ };
+ struct qcom_scm_res res;
+ int ret;
+
+ ret = qcom_scm_clk_enable();
+ if (ret)
+ return ret;
+
+ ret = qcom_scm_bw_enable();
+ if (ret)
+ goto disable_clk;
+
+ ret = qcom_scm_call(__scm->dev, &desc, &res);
+ qcom_scm_bw_disable();
+
+disable_clk:
+ qcom_scm_clk_disable();
+
+ return ret ? : res.result[0];
+}
+
+/**
+ * qcom_scm_pas_set_bluetooth_power_mode() - Configure power optimization mode
+ * for the Bluetooth subsystem (BTSS)
+ * @pas_id: peripheral authentication service id
+ * @val: 0x0 for normal operation, 0x4 for ECO mode
+ *
+ * Return: 0 on success, negative errno on failure.
+ * Returns -EOPNOTSUPP if the firmware configuration call is unavailable.
+ */
+int qcom_scm_pas_set_bluetooth_power_mode(u32 pas_id, u32 val)
+{
+ if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+ QCOM_SCM_PIL_PAS_BT_PWR_MODE))
+ return -EOPNOTSUPP;
+
+ return __qcom_scm_pas_set_bluetooth_power_mode(pas_id, val);
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_set_bluetooth_power_mode);
+
static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
{
struct qcom_scm_desc desc = {
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index caab80a73e17..5579df5a2aca 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
+#define QCOM_SCM_PIL_PAS_BT_PWR_MODE 0x21
#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
#define QCOM_SCM_SVC_IO 0x05
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index 5747bd191bf1..76de4b69580b 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -93,6 +93,7 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
size_t *output_rt_size);
int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
+int qcom_scm_pas_set_bluetooth_power_mode(u32 pas_id, u32 val);
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 0/6] Add support for IPQ5018 Bluetooth
From: George Moussalem via B4 Relay @ 2026-06-29 13:01 UTC (permalink / raw)
To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
Mathieu Poirier, Philipp Zabel
Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
George Moussalem
Hello,
This patch series introduces Bluetooth support for IPQ5018.
Bluetooth firmware is loaded by the host into a dedicated reserved
memory carveout and authenticated by TrustZone. A Secure Channel Manager
(SCM) call safely brings the peripheral core out of reset.
A shared memory ring buffer topology handles runtime data frame
transport between the host APSS and the Bluetooth controller.
An outgoing APCS IPC bit and an incoming GIC interrupt handle
host/guest signaling.
This series has been tested and verified on various IPQ5018 router
boards utilizing firmware extracted from GPL distributions, using both
mdt and mbn file formats.
[ 14.781511] Bluetooth: hci0: QCA Product ID :0x00000016
[ 14.781583] Bluetooth: hci0: QCA SOC Version :0x20180100
[ 14.785926] Bluetooth: hci0: QCA ROM Version :0x00000100
[ 14.791546] Bluetooth: hci0: QCA Patch Version:0x00003ded
[ 14.796698] Bluetooth: hci0: QCA controller version 0x01000100
[ 14.802217] Bluetooth: hci0: QCA Downloading qca/mpnv10.bin
[ 16.393850] Bluetooth: hci0: QCA Build Info: BTFW.MAPLE.1.0.0-00102-MPL_ROM_PATCHZ-1
Best regards,
George Moussalem
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
Changes in v2:
- removed unused struct member btss_reset from m0_btss struct.
- used devm_reset_control_get_exclusive_deasserted to acquire and
deassert btss_reset in one call. No need to explicitly assert upon
unbind since devres will handle it.
- As per Bjorn's comments, collapsed the rproc and BT driver into one
which now handles firmware loading and transport.
- As per further review comments and above change, squashed the two
devicetree bindings into one to represent the Bluetooth controller
- Added new SCM call to drive Bluetooth power modes. Based on testing on
more router boards, this is required during start/stop sequences to
avoid the controller hanging. This is only supported on relatively
newer QSEE versions, so the SCM call is checked for availability
before use.
- Refactored resource lifecycle management in the driver so that IRQ and
work queue are managed at the driver level (probe/remove) while FW
loading and powering on/off the controller is handled upon hdev
open/close.
- Consolidated TX send functions for custom IPC and HCI frames into one
and solved a potential NULL pointer dereference issue under TX
pressure.
- Replace code to load and initialize firmware metadate by existing
qcom_mdt_pas_init function.
- Solved an off byone calculation error in the RX parser.
- Added entry to MAINTAINERS file
- Link to v1: https://lore.kernel.org/r/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com
---
George Moussalem (6):
dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
Bluetooth: btqca: Add IPQ5018 support
firmware: qcom: scm: Add support for setting Bluetooth power modes
Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver
.../bindings/net/bluetooth/qcom,ipq5018-bt.yaml | 86 ++
MAINTAINERS | 7 +
arch/arm64/boot/dts/qcom/ipq5018.dtsi | 25 +-
drivers/bluetooth/Kconfig | 11 +
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btqca.c | 16 +
drivers/bluetooth/btqca.h | 3 +
drivers/bluetooth/btqcomipc.c | 1052 ++++++++++++++++++++
drivers/firmware/qcom/qcom_scm.c | 49 +
drivers/firmware/qcom/qcom_scm.h | 1 +
include/linux/firmware/qcom/qcom_scm.h | 1 +
11 files changed, 1251 insertions(+), 1 deletion(-)
---
base-commit: 4d1ab324fcb7d20df5a071edb0304461846fdc12
change-id: 20260625-ipq5018-bluetooth-06ff66c9d753
prerequisite-message-id: <20260612-block-as-nvmem-v5-0-95e0b30fff90@oss.qualcomm.com>
prerequisite-patch-id: 6ce8686c1683f468d86b4502f5ec9d19c392a382
prerequisite-patch-id: e362f7fcbacff716b7ef720e6780786a7d88c013
prerequisite-patch-id: 9168930e40551e842c8171d5433a6f39ad4b78a4
prerequisite-patch-id: 64fecfbd1e085d7d2ab0ae23295ca34ec8e14c5e
prerequisite-patch-id: 566804aaa690ee9aa285d0fd75fd16d94fbadebf
prerequisite-patch-id: dc18bec338f54b3051f4523f9d1d3c0566a20ccd
prerequisite-patch-id: b6b3eb46429936ab49423d295433daf47981db0f
prerequisite-patch-id: 75caa99e3bbcdf41b6462b9f5f703bea1d4a65fa
prerequisite-patch-id: 35e9968f482f78ca233eb0306d9c5fdbff093175
Best regards,
--
George Moussalem <george.moussalem@outlook.com>
^ permalink raw reply
* Re: [PATCH 1/2] wifi: iwlwifi: enable MFP_CAPABLE in FIPS mode
From: Jose Ignacio Tornos Martinez @ 2026-06-29 12:45 UTC (permalink / raw)
To: johannes
Cc: emmanuel.grumbach, jtornosm, linux-kernel, linux-wireless,
miriam.rachel.korenblit
In-Reply-To: <297578c2a1293523a28896f30031db16ccd26bd0.camel@sipsolutions.net>
Hi Johannes,
Thank you for the quick response.
Since we have some customers requesting this, I'd appreciate some
clarification on what specifically cannot be done so I can better
understand the firmware requirements.
Our testing shows MFP working with software crypto in FIPS mode
(connection succeeds, "MFP: yes" confirmed, firmware reports SEC_ENC_ERR
0x707 indicating it's not handling the frames). Indeed it was working like
this before your commit.
But clearly I'm missing something important about the architecture.
Could you help me to understand:
- What breaks when MFP_CAPABLE is enabled in FIPS mode?
- Is there a specific scenario (beyond WoWLAN/beacon protection) where
firmware must handle MFP frames?
- Is the issue that it appears to work but fails in certain conditions?
If possible, I would like to get information at least to justify the
reasons why FIPS cannot be used with WiFi Intel cards (or if there is
some way/approach to make it work).
Thanks
Best regards
Jose Ignacio
^ permalink raw reply
* Re: [PATCH 1/2] wifi: iwlwifi: enable MFP_CAPABLE in FIPS mode
From: Johannes Berg @ 2026-06-29 12:16 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, linux-wireless
Cc: miriam.rachel.korenblit, emmanuel.grumbach, linux-kernel
In-Reply-To: <20260629121213.597038-2-jtornosm@redhat.com>
On Mon, 2026-06-29 at 14:12 +0200, Jose Ignacio Tornos Martinez wrote:
> Re-enable MFP_CAPABLE flag in FIPS mode for iwlwifi to allow Management
> Frame Protection (802.11w) to work with mac80211 software crypto.
>
> Commit 0636800c8ee1 ("wifi: iwlwifi: disable certain features for
> fips_enabled") disabled MFP_CAPABLE when fips_enabled=1.
> I think the original concern about "some frames need to be handled in
> firmware" applies to firmware-offloaded features like WoWLAN and beacon
> protection, which remain correctly disabled by the commented commit.
No, you think wrong, this cannot be done.
johannes
^ permalink raw reply
* [PATCH 2/2] wifi: iwlwifi: reduce encryption error message to debug level in FIPS mode
From: Jose Ignacio Tornos Martinez @ 2026-06-29 12:12 UTC (permalink / raw)
To: linux-wireless
Cc: miriam.rachel.korenblit, johannes.berg, emmanuel.grumbach,
linux-kernel, Jose Ignacio Tornos Martinez
In-Reply-To: <20260629121213.597038-1-jtornosm@redhat.com>
Reduce and modify in FIPS mode the warning message:
"iwlwifi 0000:07:00.0: Unhandled alg: 0x707"
from IWL_WARN to IWL_DEBUG_RX when fips_enabled is true and status is
RX_MPDU_RES_STATUS_SEC_ENC_ERR (0x707), as this is expected behavior
in FIPS mode and not an error.
The current IWL_WARN message misleadingly suggests an error when this
is actually the intended behavior - firmware crypto disabled, software
crypto handling MFP frames using FIPS-approved AES-CMAC/GMAC algorithms.
These messages appear infrequently (shortly after association when first
IGTK-protected frames arrive) and are already rate-limited, so reducing
them to debug level in FIPS mode removes the false impression of a
problem while still providing useful information for debugging.
This prevents false-positive "Unhandled alg" warnings from appearing
in normal FIPS mode operation with Management Frame Protection enabled,
while preserving warnings for actual unexpected conditions.
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 7f0b4f5daa21..3d6760d5c500 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -6,6 +6,7 @@
*/
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
+#include <linux/fips.h>
#include "iwl-trans.h"
#include "mvm.h"
#include "fw-api.h"
@@ -494,6 +495,16 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
return 0;
case RX_MPDU_RES_STATUS_SEC_CMAC_GMAC_ENC:
break;
+ case RX_MPDU_RES_STATUS_SEC_ENC_ERR:
+ if (fips_enabled) {
+ /* Expected in FIPS mode - firmware has no keys,
+ * mac80211 software crypto will handle it
+ */
+ IWL_DEBUG_RX(mvm, "FIPS mode: Firmware cannot decrypt, status: 0x%x\n",
+ status);
+ break;
+ }
+ fallthrough;
default:
/*
* Sometimes we can get frames that were not decrypted
--
2.54.0
^ permalink raw reply related
* [PATCH 1/2] wifi: iwlwifi: enable MFP_CAPABLE in FIPS mode
From: Jose Ignacio Tornos Martinez @ 2026-06-29 12:12 UTC (permalink / raw)
To: linux-wireless
Cc: miriam.rachel.korenblit, johannes.berg, emmanuel.grumbach,
linux-kernel, Jose Ignacio Tornos Martinez
In-Reply-To: <20260629121213.597038-1-jtornosm@redhat.com>
Re-enable MFP_CAPABLE flag in FIPS mode for iwlwifi to allow Management
Frame Protection (802.11w) to work with mac80211 software crypto.
Commit 0636800c8ee1 ("wifi: iwlwifi: disable certain features for
fips_enabled") disabled MFP_CAPABLE when fips_enabled=1.
I think the original concern about "some frames need to be handled in
firmware" applies to firmware-offloaded features like WoWLAN and beacon
protection, which remain correctly disabled by the commented commit.
For normal STA mode operation, management frames are processed in software.
And MFP can function in FIPS mode for normal STA operation when mac80211
software crypto handles IGTK encryption/decryption using FIPS-approved
AES-CMAC/GMAC algorithms.
Other major WiFi drivers (ath11k, rtlwifi, mt76, ...) set MFP_CAPABLE
unconditionally, suggesting this approach is viable for FIPS mode operation
with software crypto.
After re-enabling, testing on Intel WiFi 6E AX210 with fips=1 shows:
- IGTK ciphers (CMAC, GMAC-128, GMAC-256) are properly advertised
- WPA3-SAE connections with MFP required succeed
- iw station dump confirms "MFP: yes"
Firmware logs "Unhandled alg: 0x707" (SEC_ENC_ERR) during operation,
confirming that firmware does not have the keys and frames are being
handled by software crypto as expected.
Fixes: 0636800c8ee1 ("wifi: iwlwifi: disable certain features for fips_enabled")
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 1ec9807e4827..b0aea73c5784 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -462,8 +462,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IWL_ERR(mvm,
"iwlmvm doesn't allow to disable BT Coex, check bt_coex_active module parameter\n");
- if (!fips_enabled)
- ieee80211_hw_set(hw, MFP_CAPABLE);
+ ieee80211_hw_set(hw, MFP_CAPABLE);
mvm->ciphers[hw->wiphy->n_cipher_suites] = WLAN_CIPHER_SUITE_AES_CMAC;
hw->wiphy->n_cipher_suites++;
--
2.54.0
^ permalink raw reply related
* [PATCH 0/2] wifi: iwlwifi: Fix WiFi operation in FIPS mode
From: Jose Ignacio Tornos Martinez @ 2026-06-29 12:12 UTC (permalink / raw)
To: linux-wireless
Cc: miriam.rachel.korenblit, johannes.berg, emmanuel.grumbach,
linux-kernel, Jose Ignacio Tornos Martinez
This series re-enables WiFi connectivity in FIPS mode for iwlwifi by
allowing Management Frame Protection (MFP/802.11w) to work with mac80211
software crypto.
Currently, iwlwifi connections fail in FIPS mode because commit
0636800c8ee1 (("wifi: iwlwifi: disable certain features for fips_enabled")
disabled MFP_CAPABLE when fips_enabled=1. The testing shows that MFP works
correctly in FIPS mode when mac80211 software crypto handles IGTK encryption
/decryption using FIPS-approved AES-CMAC/GMAC algorithms.
I think the original concern that "MFP since some frames need to be handled
in firmware" can be avoided for normal STA operation. The firmware-offload
requirement appears to apply specifically to features like WoWLAN and beacon
protection, which remain correctly disabled. AFAIK, for standard client
connections, management frames are processed only in software and mac80211
provides complete IGTK handling.
Patch 1 re-enables MFP_CAPABLE for iwlwifi to allow basic WiFi client
operation in FIPS mode. Patch 2 reduces a misleading warning message that
appears during normal FIPS operation to debug level - the warning actually
confirms that frames are not processed by firmware and are correctly handled
by software crypto instead.
Testing on Intel WiFi 6E AX210 with fips=1:
Before:
# cat /proc/sys/crypto/fips_enabled
1
# iw phy phy0 info | grep -A 10 "Cipher"
Supported Ciphers:
* CCMP-128 (00-0f-ac:4)
* CCMP-256 (00-0f-ac:10)
* GCMP-128 (00-0f-ac:8)
* GCMP-256 (00-0f-ac:9)
# nmcli connection up SSID
Error: Connection activation failed
After:
# cat /proc/sys/crypto/fips_enabled
1
# iw phy phy0 info | grep -A 10 "Cipher"
Supported Ciphers:
* CCMP-128 (00-0f-ac:4)
* CCMP-256 (00-0f-ac:10)
* GCMP-128 (00-0f-ac:8)
* GCMP-256 (00-0f-ac:9)
* CMAC (00-0f-ac:6)
* CMAC-256 (00-0f-ac:13)
* GMAC-128 (00-0f-ac:11)
* GMAC-256 (00-0f-ac:12)
# nmcli connection up SSID
Connection successfully activated
# iw dev wlp7s0 station dump | grep MFP
MFP: yes
Jose Ignacio Tornos Martinez (2):
wifi: iwlwifi: enable MFP_CAPABLE in FIPS mode
wifi: iwlwifi: reduce encryption error message to debug level in FIPS mode
--
2.47.1
^ permalink raw reply
* [PATCH] wifi: mwifiex: bound uAP association event IEs to the event buffer
From: HE WEI (ギカク) @ 2026-06-29 12:03 UTC (permalink / raw)
To: Brian Norris, Francesco Dolcini
Cc: Miri Korenblit, Johannes Berg, Kalle Valo, Kees Cook,
linux-wireless, linux-kernel, HE WEI (ギカク)
mwifiex_process_uap_event() handles EVENT_UAP_STA_ASSOC by exposing the
(re)association request IEs that the firmware copies into the event:
sinfo->assoc_req_ies = &event->data[len];
len = (u8 *)sinfo->assoc_req_ies - (u8 *)&event->frame_control;
sinfo->assoc_req_ies_len = le16_to_cpu(event->len) - (u16)len;
event->len is supplied by the device firmware and is never validated,
and the subtraction is unchecked. assoc_req_ies points into
adapter->event_body[MAX_EVENT_SIZE], a fixed-size array embedded in the
kmalloc()'d struct mwifiex_adapter.
On the ap_11n_enabled path mwifiex_set_sta_ht_cap() walks these IEs with
cfg80211_find_ie(), whose for_each_element() loop dereferences each
element header. A firmware-reported event->len larger than the bytes
actually received makes assoc_req_ies_len describe IEs that extend past
event_body, so the walk reads out of the adapter slab object -- a
slab-out-of-bounds read (KASAN: slab-out-of-bounds in cfg80211_find_ie).
An event->len smaller than the header instead makes the int subtraction
negative, which wraps to a huge size_t when stored in assoc_req_ies_len.
The same length is handed to cfg80211_new_sta(), so a more modest
over-claim can also copy stale event_body bytes into the
NL80211_CMD_NEW_STATION notification.
A malicious or malfunctioning mwifiex device (USB/SDIO/PCIe) can deliver
such an event while the interface is in AP/uAP mode.
Validate event->len before use: reject a length that underflows the
header or that would place the IEs outside the event_body[] buffer the
event was copied into. The bound is against event_body[MAX_EVENT_SIZE]
rather than the actually-received length because the transports store the
event differently (USB and SDIO leave the 4-byte event header in
event_skb, PCIe strips it via skb_pull), whereas event_body is the single
fixed buffer all of them copy the event into. This is the event-path
analogue of the receive-path bounds checks added in commit 119585281617
("wifi: mwifiex: Fix OOB and integer underflow when rx packets").
Fixes: e568634ae7ac ("mwifiex: add AP event handling framework")
Signed-off-by: HE WEI (ギカク) <skyexpoc@gmail.com>
---
.../net/wireless/marvell/mwifiex/uap_event.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c
index 679fdae0f001..adca7da29f0f 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_event.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c
@@ -126,6 +126,24 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv)
sinfo->assoc_req_ies = &event->data[len];
len = (u8 *)sinfo->assoc_req_ies -
(u8 *)&event->frame_control;
+
+ /*
+ * event->len is reported by the device firmware and is not
+ * otherwise validated. Reject a length that underflows the
+ * header, or that would place the association request IEs
+ * outside the fixed-size event_body[] buffer the event was
+ * copied into; otherwise the IE walk in
+ * mwifiex_set_sta_ht_cap() reads past event_body and out
+ * of the adapter slab object.
+ */
+ if (le16_to_cpu(event->len) < len ||
+ (u8 *)&event->frame_control + le16_to_cpu(event->len) >
+ adapter->event_body + MAX_EVENT_SIZE) {
+ mwifiex_dbg(adapter, ERROR,
+ "invalid STA assoc event length\n");
+ kfree(sinfo);
+ return -1;
+ }
sinfo->assoc_req_ies_len =
le16_to_cpu(event->len) - (u16)len;
}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] wifi: ath9k: avoid device access after async firmware request
From: Toke Høiland-Jørgensen @ 2026-06-29 9:49 UTC (permalink / raw)
To: Yousef Alhouseen
Cc: linux-wireless, linux-kernel, stable, syzbot+cb7ed9d85261445a0201,
Yousef Alhouseen
In-Reply-To: <20260628001350.20997-1-alhouseenyousef@gmail.com>
Yousef Alhouseen <alhouseenyousef@gmail.com> writes:
> request_firmware_nowait() may invoke the callback before the requesting
> context resumes. When a firmware lookup fails, the callback starts the
> next fallback request. That nested request can exhaust the fallback list,
> complete fw_done, and let disconnect free hif_dev before the parent request
> returns.
>
> The parent then dereferences hif_dev only to print a successful-request
> message. Remove that post-request access so completion cannot leave an
> older callback using the freed device state.
>
> Fixes: e904cf6fe230 ("ath9k_htc: introduce support for different fw versions")
> Reported-by: syzbot+cb7ed9d85261445a0201@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=cb7ed9d85261445a0201
> Cc: stable@vger.kernel.org
> Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com>
An identical patch was already submitted and is currently pending:
https://patchwork.kernel.org/project/linux-wireless/patch/20260605153210.20471-1-1020691186@qq.com/
-Toke
^ permalink raw reply
* [PATCH rtw-next v3] wifi: rtw89: check return values in rtw89_ops_start_ap()
From: Dmitry Morgun @ 2026-06-29 9:44 UTC (permalink / raw)
To: Ping-Ke Shih; +Cc: Dmitry Morgun, linux-wireless, linux-kernel, lvc-project
Several functions called in rtw89_ops_start_ap() may fail to allocate
skb or fail to send H2C command to firmware, returning -ENOMEM or an
error code. Their return values are ignored, so subsequent commands
are executed with incorrect state.
Check the return values and propagate errors.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Fixes: a52e4f2ce0f5 ("wifi: rtw89: implement ieee80211_ops::start_ap and stop_ap")
Signed-off-by: Dmitry Morgun <d.morgun@ispras.ru>
---
drivers/net/wireless/realtek/rtw89/mac80211.c | 35 ++++++++++++++++---
1 file changed, 30 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 501c3af1d..cd4076a17 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -811,11 +811,36 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw,
ether_addr_copy(rtwvif_link->bssid, link_conf->bssid);
rtw89_cam_bssid_changed(rtwdev, rtwvif_link);
- rtw89_mac_port_update(rtwdev, rtwvif_link);
- rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL);
- rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE);
- rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true);
- rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE);
+ ret = rtw89_mac_port_update(rtwdev, rtwvif_link);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to update mac port\n");
+ return ret;
+ }
+
+ ret = rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cmac table\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_h2c_role_maintain(rtwdev, rtwvif_link, NULL, RTW89_ROLE_TYPE_CHANGE);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c role info\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c join info\n");
+ return ret;
+ }
+
+ ret = rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, NULL, RTW89_ROLE_TYPE_CHANGE);
+ if (ret) {
+ rtw89_warn(rtwdev, "failed to send h2c cam\n");
+ return ret;
+ }
+
rtw89_chip_rfk_channel(rtwdev, rtwvif_link);
if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) {
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v10] Add device-specific reset for Qualcomm devices
From: Baochen Qiang @ 2026-06-29 9:44 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Jose Ignacio Tornos Martinez, bhelgaas, alex, jjohnson, linux-pci,
linux-wireless, ath11k, ath12k, mhi, linux-kernel
In-Reply-To: <l3pcelhxn46p4smkkg5yob5enf4d4garp2p6macopnq5h2gwyf@zbbn65k4tz2q>
On 6/25/2026 9:03 PM, Manivannan Sadhasivam wrote:
> On Wed, Jun 24, 2026 at 03:47:12PM +0800, Baochen Qiang wrote:
>>
>>
>> On 6/24/2026 2:31 AM, Jose Ignacio Tornos Martinez wrote:
>>> Some Qualcomm PCIe devices (WCN6855/WCN7850 WiFi cards, SDX62/SDX65 modems)
>>> lack working reset methods for VFIO passthrough scenarios. These devices
>>> have no FLR capability, advertise NoSoftRst+ (blocking PM reset), and have
>>> broken bus reset.
>>>
>>> The problem manifests in VFIO passthrough scenarios:
>>>
>>> - WCN6855 (17cb:1103) and WCN7850 (17cb:1107) WiFi devices:
>>> Normal VM operation works fine, including clean shutdown/reboot.
>>> However, when the VM terminates uncleanly (crash, force-off), VFIO
>>> attempts to reset the device before it can be assigned to another VM.
>>> Without a working reset method, the device remains in an undefined state,
>>> preventing reuse.
>>>
>>> - SDX62/SDX65 (17cb:0308) 5G modems: Never successfully initialize even
>>> on first VM assignment without proper reset capability.
>>>
>>> Add device-specific reset methods using BAR-space hardware reset registers
>>> that exist in these devices:
>>>
>>> - WCN6855/WCN7850 WiFi devices use SoC global reset via BAR0 (sequence from
>>> ath11k/ath12k driver: ath11k_pci_soc_global_reset(), ath11k_pci_sw_reset(),
>>> ath11k_mhi_set_mhictrl_reset()):
>>> - Write/clear reset bit at offset 0x3008
>>> - Wait for PCIe link recovery (up to 5 seconds)
>>> - Clear MHI controller SYSERR status at offset 0x38
>>>
>>> - SDX62/SDX65 modem devices use MHI SoC reset via BAR0 (sequence from MHI
>>> driver: mhi_soc_reset(), mhi_pci_reset_prepare()):
>>> - Write reset request to offset 0xb0
>>> - Wait 2 seconds for reset completion
>>>
>>> These are true hardware reset mechanisms (not power management or firmware
>>> error recovery), providing proper device reset for VFIO scenarios.
>>>
>>> Testing was performed on desktop platforms with M.2 WiFi and modem cards
>>> using M.2-to-PCIe adapters, including extensive force-reset cycling to
>>> verify stability.
>>>
>>> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
>>> ---
>>> v10:
>>> - Complete redesign based on maintainer feedback (Manivannan Sadhasivam,
>>> Alex Williamson): use actual hardware reset registers from
>>> device drivers instead of D3hot power cycling
>>> v9: https://lore.kernel.org/all/20260612142638.1243895-1-jtornosm@redhat.com/
>>>
>>> drivers/pci/quirks.c | 118 +++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 118 insertions(+)
>>>
>>> diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
>>> index 431c021d7414..8ad3f214e520 100644
>>> --- a/drivers/pci/quirks.c
>>> +++ b/drivers/pci/quirks.c
>>> @@ -4240,6 +4240,121 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
>>> return 0;
>>> }
>>>
>>> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET 0x3008
>>> +#define QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET_V BIT(0)
>>> +#define QUALCOMM_WIFI_MHISTATUS 0x48
>>> +#define QUALCOMM_WIFI_MHICTRL 0x38
>>> +#define QUALCOMM_WIFI_MHICTRL_RESET_MASK 0x2
>>> +
>>> +/*
>>> + * Qualcomm WiFi device-specific reset using SoC global reset via BAR0
>>> + * registers.
>>> + */
>>> +static int reset_qualcomm_wifi(struct pci_dev *pdev, bool probe)
>>> +{
>>> + bool link_recovered = false;
>>> + unsigned long timeout;
>>> + void __iomem *bar;
>>> + u32 val;
>>> + u16 cmd;
>>> +
>>> + if (probe)
>>> + return 0;
>>> +
>>> + if (pdev->current_state != PCI_D0)
>>> + return -EINVAL;
>>> +
>>> + pci_read_config_word(pdev, PCI_COMMAND, &cmd);
>>> + pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
>>> +
>>> + bar = pci_iomap(pdev, 0, 0);
>>> + if (!bar) {
>>> + pci_write_config_word(pdev, PCI_COMMAND, cmd);
>>> + return -ENODEV;
>>> + }
>>> +
>>> + val = ioread32(bar + QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET);
>>
>> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET is beyond the first 4K bar area hence requires MHI
>> wakeup before accessing, see [1]. the wakeup callback for WCN6855 is
>> ath11k_pci_bus_wake_up() which calls mhi_device_get_sync(). Not sure how this can be done
>> here. Maybe Mani can provide some hints?
>>
>
> I don't think the device needs to be waken up before
> QUALCOMM_WIFI_PCIE_SOC_GLOBAL_RESET. ath11k driver wakes up the device for
> accessing the MHI interface I believe. Since this callback is not touching MHI,
> there is no need to wakeup the device, AFAIK.
MHI register space is constitute with 3 segments, with mhi offset always being 0, bhi
offset 0x100 and bhie offset 0x224 (the latter two offsets are for WCN7850, but I think
also apply to other devices). That said the whole MHI register space does not go beyond
the first 4K region, hence wakeup is not for accessing MHI interface.
>
> - Mani
>
^ permalink raw reply
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