* [PATCH 1/2] ath10k: skip quiet mode for WCN3990 to prevent firmware crash
From: Malte Schababerle @ 2026-03-22 12:48 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath10k, Malte Schababerle
In-Reply-To: <20260322124822.230492-1-m.schababerle@gmail.com>
WCN3990 firmware (WLAN.HL.3.2) crashes deterministically when the
quiet mode WMI command is sent during ath10k_start(). The crash occurs
at PC=0xb0008e20 in wlanmdsp.mbn, ~17ms after the subsequent
vdev_create command, and cascades into a full modem crash.
Commit 53884577fbcef ("ath10k: skip sending quiet mode cmd for
WCN3990") addressed this for HL2.0 firmware by gating quiet mode on
WMI_SERVICE_THERM_THROT. HL2.0 did not advertise the service bit, so
the guard was effective. However, newer WCN3990 firmware (HL3.2)
erroneously advertises WMI_SERVICE_THERM_THROT via its TLV service
map despite still being unable to handle the quiet mode command.
Skip quiet mode unconditionally for WCN3990 using QCA_REV_WCN3990()
instead of relying on the service bit.
Tested on OnePlus 7T (SM8150/WCN3990) with WLAN.HL.3.2.0.c2-00006
and WLAN.HL.3.2.0.c2-00011 (both crash without patch, both work with):
- wlan0 comes up without crash
- WiFi scanning works
- NetworkManager recognizes the interface
Fixes: 53884577fbcef ("ath10k: skip sending quiet mode cmd for WCN3990")
Signed-off-by: Malte Schababerle <m.schababerle@gmail.com>
---
drivers/net/wireless/ath/ath10k/thermal.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c
index 8b15ec07b1071..33f299f414710 100644
--- a/drivers/net/wireless/ath/ath10k/thermal.c
+++ b/drivers/net/wireless/ath/ath10k/thermal.c
@@ -136,6 +136,16 @@ void ath10k_thermal_set_throttling(struct ath10k *ar)
if (!ar->wmi.ops->gen_pdev_set_quiet_mode)
return;
+ /* WCN3990 firmware crashes on quiet mode despite advertising support.
+ * See also commit 53884577fbcef ("ath10k: skip sending quiet mode
+ * cmd for WCN3990").
+ */
+ if (QCA_REV_WCN3990(ar)) {
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
+ "skip quiet mode for WCN3990 (known crash trigger)\n");
+ return;
+ }
+
if (ar->state != ATH10K_STATE_ON)
return;
--
2.47.3
^ permalink raw reply related
* [PATCH 0/2] ath10k: WCN3990 firmware workarounds for WLAN.HL.3.2
From: Malte Schababerle @ 2026-03-22 12:48 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath10k, Malte Schababerle
Two firmware workarounds for WCN3990 (QCA6174 integrated variant on
Qualcomm SM8150/SDM845 platforms) running WLAN.HL.3.2 firmware.
Both issues are firmware bugs where WCN3990 advertises capabilities it
cannot handle correctly. Tested on OnePlus 7T (SM8150) with
WLAN.HL.3.2.0.c2-00006 and WLAN.HL.3.2.0.c2-00011.
Patch 1 fixes a deterministic firmware crash caused by the quiet mode
WMI command. The existing guard from commit 53884577fbcef relied on
THERM_THROT not being advertised, but HL3.2 now advertises it despite
still crashing on the command.
Patch 2 works around a bug where active scan does not tune the radio
on 5GHz non-DFS channels, making 5GHz networks invisible. Forcing
passive scan mode restores 5GHz discovery.
Malte Schababerle (2):
ath10k: skip quiet mode for WCN3990 to prevent firmware crash
ath10k: force passive scan on 5GHz for WCN3990
drivers/net/wireless/ath/ath10k/mac.c | 8 ++++++++
drivers/net/wireless/ath/ath10k/thermal.c | 10 ++++++++++
2 files changed, 18 insertions(+)
--
2.47.3
^ permalink raw reply
* RE: [PATCH 0/1] wifi: iwlwifi: mld: fix TSO segmentation explosion causing UAF
From: Korenblit, Miriam Rachel @ 2026-03-22 12:29 UTC (permalink / raw)
To: Cole Leavitt, greearb@candelatech.com
Cc: johannes@sipsolutions.net, linux-wireless@vger.kernel.org
In-Reply-To: <20260218144723.31699-1-cole@unwrap.rs>
> -----Original Message-----
> From: Cole Leavitt <cole@unwrap.rs>
> Sent: Wednesday, February 18, 2026 4:47 PM
> To: greearb@candelatech.com
> Cc: johannes@sipsolutions.net; linux-wireless@vger.kernel.org; Korenblit, Miriam
> Rachel <miriam.rachel.korenblit@intel.com>; Cole Leavitt <cole@unwrap.rs>
> Subject: [PATCH 0/1] wifi: iwlwifi: mld: fix TSO segmentation explosion causing
> UAF
>
> Ben,
>
> I've been digging into the use-after-free crash you reported on your
> BE200 running the MLD driver (tcp_shifted_skb refcount underflow, followed by
> NULL deref in tcp_rack_detect_loss). I think I found the root cause -- it's a
> missing guard in the MLD TSO segmentation path that lets num_subframes=0
> reach skb_gso_segment(), producing the 32k+ segment explosion you're seeing.
>
> Here's the full chain:
>
> 1) mld/tlc.c:790 -- when firmware's TLC notification disables AMSDU for
> a TID (bit not set in amsdu_enabled), the MLD driver sets:
>
> link_sta->agg.max_tid_amsdu_len[i] = 1;
>
> This sentinel value 1 means "AMSDU disabled on this TID".
>
> 2) mld/tx.c:836-837 -- the TSO path checks:
>
> max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid];
> if (!max_tid_amsdu_len) // <-- only catches zero, not 1
> return iwl_tx_tso_segment(skb, 1, ...);
>
> Value 1 passes this check.
>
> 3) mld/tx.c:847 -- the division produces zero:
>
> num_subframes = (1 + 2) / (1534 + 2) = 0
>
> Any max_tid_amsdu_len below ~1534 (one subframe) produces 0 here.
>
> 4) iwl-utils.c:27 -- gso_size is set to zero:
>
> skb_shinfo(skb)->gso_size = num_subframes * mss = 0 * 1460 = 0
>
> 5) iwl-utils.c:30 -- skb_gso_segment() with gso_size=0 creates 32001+
> tiny segments, which is the error you're seeing:
>
> "skbuff: ERROR: Found more than 32000 packets in skb_segment"
> "iwl-mvm-tx-tso-segment, list gso-segment list is huge: 32001"
>
> 6) mld/tx.c:912-936 -- the loop queues ~1024 of those segments to the
> TX ring before it fills up, then purges the rest. This creates a
> massive burst of tiny frames that stress the BA completion path.
>
> The MVM driver is immune because it checks mvmsta->amsdu_enabled (a
> separate bitmap) at tx.c:912 and tx.c:936 BEFORE ever reaching the
> num_subframes calculation. MLD has no equivalent -- it relies solely on
> max_tid_amsdu_len, and the sentinel value 1 slips through.
>
> This explains all your observations:
> - 6.18 regression: BE200 moved from MVM (has guard) to MLD (no guard)
> - AP-specific: the problem AP causes firmware to disable AMSDU for the
> active TID (other APs enable it, so max_tid_amsdu_len gets a proper
> value from iwl_mld_get_amsdu_size_of_tid())
> - 28min gap between TSO explosion and UAF: the ~1024 micro-frame burst
> creates massive alloc/free churn in the skb slab, which can corrupt
> TCP retransmit queue entries allocated from the same cache
> - No firmware error: firmware is fine, the bug is purely in MLD's TSO
> parameter calculation
>
> The fix (in patch 1/1) adds a guard after the num_subframes calculation -- if it's
> zero, fall back to single-subframe TSO (num_subframes=1), which correctly sets
> gso_size=mss. This matches what MVM effectively does via its amsdu_enabled
> checks.
>
> Could you test this against the problem AP? Two things that would help confirm
> the theory:
>
> 1) Before applying the fix, add this debug print to see the actual
> max_tid_amsdu_len value with the problem AP:
>
> // In iwl_mld_tx_tso_segment(), after line 847
> if (!num_subframes)
> pr_warn_once("iwlmld: num_subframes=0, max_tid_amsdu_len=%u "
> "subf_len=%u mss=%u\n",
> max_tid_amsdu_len, subf_len, mss);
>
> 2) After applying the fix, run against the problem AP for 1+ day and
> check if both the TSO explosion AND the UAF are gone.
>
> I also noticed a few secondary defense-in-depth regressions in MLD's TX
> completion path vs MVM:
>
> - MLD's iwl_mld_tx_reclaim_txq() has no per-TID reclaim tracking
> (MVM has tid_data->next_reclaimed and validates tid_data->txq_id)
> - The transport-level reclaim_lock prevents direct double-free, but
> MLD is missing MVM's extra safety checks
>
> These are probably not directly causing your crash, but worth noting.
>
> Cole Leavitt (1):
> wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is
> disabled
>
> drivers/net/wireless/intel/iwlwifi/mld/tx.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> --
> 2.52.0
Thank you for the clear analysis!
Miri
^ permalink raw reply
* RE: [PATCH 1/1] wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is disabled
From: Korenblit, Miriam Rachel @ 2026-03-22 12:28 UTC (permalink / raw)
To: Cole Leavitt, greearb@candelatech.com
Cc: johannes@sipsolutions.net, linux-wireless@vger.kernel.org
In-Reply-To: <20260218144723.31699-2-cole@unwrap.rs>
> -----Original Message-----
> From: Cole Leavitt <cole@unwrap.rs>
> Sent: Wednesday, February 18, 2026 4:47 PM
> To: greearb@candelatech.com
> Cc: johannes@sipsolutions.net; linux-wireless@vger.kernel.org; Korenblit, Miriam
> Rachel <miriam.rachel.korenblit@intel.com>; Cole Leavitt <cole@unwrap.rs>
> Subject: [PATCH 1/1] wifi: iwlwifi: mld: fix TSO segmentation explosion when
> AMSDU is disabled
>
> When the TLC notification disables AMSDU for a TID, the MLD driver sets
> max_tid_amsdu_len to the sentinel value 1. The TSO segmentation path in
> iwl_mld_tx_tso_segment() checks for zero but not for this sentinel, allowing it to
> reach the num_subframes calculation:
>
> num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad)
> = (1 + 2) / (1534 + 2) = 0
>
> This zero propagates to iwl_tx_tso_segment() which sets:
>
> gso_size = num_subframes * mss = 0
>
> Calling skb_gso_segment() with gso_size=0 creates over 32000 tiny segments
> from a single GSO skb. This floods the TX ring with ~1024 micro-frames (the rest
> are purged), creating a massive burst of TX completion events that can lead to
> memory corruption and a subsequent use-after-free in TCP's retransmit queue
> (refcount underflow in tcp_shifted_skb, NULL deref in tcp_rack_detect_loss).
>
> The MVM driver is immune because it checks mvmsta->amsdu_enabled before
> reaching the num_subframes calculation. The MLD driver has no equivalent
> bitmap check and relies solely on max_tid_amsdu_len, which does not catch the
> sentinel value.
>
> Fix this by falling back to single-subframe TSO (num_subframes=1) when the
> AMSDU length limit is too small to fit even one subframe.
>
> Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
> Signed-off-by: Cole Leavitt <cole@unwrap.rs>
> ---
> drivers/net/wireless/intel/iwlwifi/mld/tx.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> index fbb672f4d8c7..1d47254a4148 100644
> --- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
> @@ -846,6 +846,17 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld,
> struct sk_buff *skb,
> */
> num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad);
>
> + /* If the AMSDU length limit is too small to fit even a single
> + * subframe (e.g. max_tid_amsdu_len is the sentinel value 1 set by
> + * the TLC notification when AMSDU is disabled for this TID), fall
> + * back to non-AMSDU TSO segmentation. Without this guard,
> + * num_subframes=0 causes gso_size=0 in iwl_tx_tso_segment(),
> + * which makes skb_gso_segment() produce tens of thousands of
> + * 1-byte segments, overloading the TX ring and completion path.
> + */
Having 0 subframes doesn't make sense and wouldn't have happen if not the bug...
I would have check if AMSDU is disabled for the TID in question by correcting the check to " max_tid_amsdu_len == 1"
Then, we can even warn on if (!num_subframe), to avoid more bugs like this in the future...
Also, I'd make sure that link_sta->agg.max_tid_amsdu_len can't be 0 (there is only some error case that needs to be adjusted).
> + if (!num_subframes)
> + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
> +
> if (sta->max_amsdu_subframes &&
> num_subframes > sta->max_amsdu_subframes)
> num_subframes = sta->max_amsdu_subframes;
> --
> 2.52.0
^ permalink raw reply
* Re: [PATCH 01/11] wifi: mt76: connac: use is_connac2() to replace is_mt7921() checks
From: Philip Müller @ 2026-03-22 8:04 UTC (permalink / raw)
To: Sean Wang
Cc: nbd, lorenzo.bianconi, linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <CAGp9Lzr85cNvveAzpb6SaB2TofY06SGcVRuUq_xTWpURuc1AxA@mail.gmail.com>
On 2/21/26 21:19, Sean Wang wrote:
> Thanks for the catch I’ll update this in the next version.
Hi Sean, thx. Do you have already an updated version of this driver and
a rough idea when all of this might reach wireless-next branch?
--
Best, Philip
^ permalink raw reply
* Re: [PATCH wireless-next v2 00/34] wifi: inffmac: introducing a driver for Infineon's new generation chipsets
From: Marek Vasut @ 2026-03-21 16:24 UTC (permalink / raw)
To: Gokul Sivakumar
Cc: linux-wireless, Johannes Berg, Arend van Spriel,
wlan-kernel-dev-list
In-Reply-To: <aaGraKzdMcyb6QmW@ISCN5CG14747PP.infineon.com>
On 2/27/26 3:34 PM, Gokul Sivakumar wrote:
> On 02/27, Marek Vasut wrote:
>> On 1/16/26 5:33 PM, Gokul Sivakumar wrote:
>>> On 01/15, Marek Vasut wrote:
>>>> On 1/14/26 9:12 AM, Gokul Sivakumar wrote:
>>>>> On 01/14, Marek Vasut wrote:
>>>>>> On 1/13/26 9:33 PM, Gokul Sivakumar wrote:
>>>>>>> Infineon(Cypress) is introducing a new INFFMAC (WLAN FULLMAC) Linux driver
>>>>>>> specifically for its new-generation AIROC family of Wi-Fi Connectivity
>>>>>>> Processor (CP) chipsets (CYW5591x), Wi-Fi + Bluetooth combo chipsets
>>>>>>> (CYW5557x, CYW5551x, CYW5591x, CYW43022), and also for all future chipsets.
>>>>>> Support for the CYW55572 can be easily added into the existing brcmfmac
>>>>>> driver, I already posted a patch over a year ago [1], but it was blocked
>>>>>> by an off-list email.
>>>>>
>>>>>> Frankly, I do not see any good reason why the brcmfmac driver shouldn't
>>>>>> be extended when it is clearly easily doable. Adding new fork of the
>>>>>> brcmfmac would only increase maintenance burden and prevent bugfixes
>>>>>> from reaching the brcmfmac.
>>>>>>
>>>>>> [1] https://lore.kernel.org/all/20240909203133.74777-2-marex@denx.de/
>>>>>
>>>>> There are multiple reasons behind Infineon's proposal for this new INFFMAC
>>>>> driver for its new-generation chips. Sharing a few here, and more info
>>>>> is available in the v1 cover-letter [1]. For Example, the CYW5591x family
>>>>> chipsets that is currently supported in this INFFMAC driver has a unique
>>>>> Connected-MCU / Connectivtiy Processor (CP) Architecture [2], primarly
>>>>> intended for IoT applications and it is completely different from any of
>>>>> the legacy Broadcom architecture chipsets of Infineon supported currently
>>>>> in upstream BRCMFMAC.
>>>>
>>>> This does not prevent them from being integrated in brcmfmac like many
>>>> of the other already supported chips, there seems to be no technical
>>>> reason here.
>>>
>>> I hope you would have got a chance to look into the elaborate info provided
>>> in v1 cover-letter about the challenges that we faced with that approach.
>>> In Infineon's new architecture CYW5591x family of chipsets coming with an
>>> onboard FLASH Memory, it does not follow the traditional Device firmware
>>> download sequence used by any of the legacy Broadcom chipsets and it would
>>> be appropriate to use a dedicated driver for Infineon's chipsets which were
>>> developed and intended mainly for IoT use-cases.
>>>
>>> Here it requires a Boot firmware download, and a host handshake with the
>>> downloaded RAM boot firmware, followed by a CP Firmware image Download and
>>> validation. Sharing here the sequence for coldboot image download to FLASH.
>>>
>>> Host Driver Device
>>> ___________ ____________
>>> | |
>>> | Power UP and execute CP Firmware
>>> | from FLASH if available
>>> Attempt to enable SDIO F2 |
>>> if fails, FLASH is empty, |
>>> Need to Download Firmware |
>>> | |
>>> Wait for |
>>> DFU_CP_D2H_MSG_BL_READY |
>>> |<<======== DFU_CP_D2H_MSG_BL_READY <<============|
>>> | |
>>> Fetch Boot Firmware |
>>> from Filesystem |
>>> |======>> DFU_CP_H2D_MSG_BOOT_FWLOAD_START =====>>|
>>> | |
>>> |======>> Transfer Boot Firmware over SDIO =====>>|
>>> | in multiple chunks |
>>> | |
>>> |======>> DFU_CP_H2D_MSG_BOOT_FWLOAD_DONE ======>>|
>>> | Execute the downloaded
>>> | RAM Bootloader
>>> Wait for |
>>> DFU_CP_D2H_MSG_BOOTFW_READY |
>>> |<<========= DFU_CP_D2H_MSG_BOOTFW_READY <<=======|
>>> | |
>>> Fetch CP Firmware |
>>> from Filesystem |
>>> |=======>> Transfer CP Firmware over SDIO ======>>|
>>> | in multiple chunks |
>>> | Download CP Firmware
>>> | to FLASH Memory
>>> | |
>>> |=======>> DFU_CP_H2D_MSG_BOOTFW_DATA_LOADED ====>|
>>> | |
>>> |=======>> DFU_CP_D2H_MSG_FW_VALIDAT_START ======>|
>>> | |
>>> Wait for Validate the CP Firmware
>>> DFU_CP_D2H_MSG_FW_VALIDAT_DONE |
>>> |<<======= DFU_CP_D2H_MSG_FW_VALIDAT_DONE <<======|
>>> | |
>>> | Execute the CP Firmware
>>> Proceed with |
>>> ctrl cmds to Device |
>>> | |
>>>
>>> Here there is no need to redownload the Device Firmware from the Host
>>> filesystem everyime the Host or Device goes through a power cycle. Also the
>>> device is capable of fully operating in an offloaded mode even when the
>>> host is in suspended state or even if fully powered off. This is critical
>>> to save power for extended durations in IoT use cases which are often
>>> battery operated.
>>
>> This seems like a technical detail of the firmware loading, which can be
>> handled by the driver ?
>
> If this is the base of your argument, then theoritically any vendor driver can
> be added with the functionality to manage any other WLAN chip from another vendor.
> A single monolithic wireless driver could potentially manage every vendor's chips,
> without the need for any hardware specific abstraction. But, clearly this is not
> how it is currently in wireless subsystem, because of multiple compelling reasons.
No, my argument is, that whatever firmware loading mechanism can be
easily separated from the operation of the driver.
> And given that Infineon's new architecture CYW5591x family of chipsets are completely
> new, tailor made inhouse for IoT use-cases, with a different chipset architecture,
> different bootflow, etc. it is not practical to add it into another vendor's driver
> source which has a different flow for managing its devices.
It seems all the development even for the new chips was done on top of
brcmfmac [1] , and this driver seems like a copy-and-rename of brcmfmac.
[1] https://github.com/Infineon/ifx-wireless-drivers/
>>>>> The CYW5591x family chipsets has dedicated MCU Core
>>>>> in addition to the WLAN core and an onboard FLASH memory for the Firmware.
>>>>
>>>> It seems all brcmfmac devices have a cortex-M core in them since a long
>>>> time.
>>>
>>> We were intending to mention that CYW5591x Cortex-M33 core is dedicated to
>>> function as a Co-Processor to the Host CPU/MCU and offload multiple wifi
>>> and network operations if configured by the user before host goes to sleep.
>>> You can refer the chipset block diagram in CYW55913/2/1 Product Brief [1]
>>> The CYW5591x SoC Backplane interconnects are also different from any of the
>>> legacy Broadcom chipsets.
>>
>> Maybe the driver can be forked when it turns out this functionality
>> cannot be easily added into the existing driver ?
>
> Our response remains the same, as given for your exact same question few weeks
> back in this thread. We shared our explantion with the rationale behind our proposal.
Writing pages of text won't get you too far, preparing a patchset on top
of brcmfmac will be a good start, and it would show willingness to
cooperate with upstream.
>>>>> And with respect to the support for the new-generation CYW5557x family of
>>>>> Secure chipsets, that requires a Secure boot handshake with the Bootloader.
>>>>
>>>> It seems like the TRX firmware loading is trivial to add, and parts of
>>>> it are already in the Linux kernel for other brcm components. It seems
>>>> this TRX was used since old broadcom MIPS SoCs.
>>>
>>> No, your understanding needs a correction. Infineon's TRXSE and TRXS Device
>>> Bootloader handshake and Firmware formats are quite different from the old
>>> Broadcom TRX formats that you are mentioning here. Being two individual
>>> WLAN vendors, the chipset internals, firmware formats, Device Bootloader
>>> handshake cannot be expected to be the same. Yet another reason why it is
>>> not practically feasible to use upstream BRCMFMAC for the new-generation
>>> chipsets and new firmware releases from Infineon.
>>
>> Surely it is possible to do something like
>>
>> if (wlan_id_needs_trxse)
>> load_trxse();
>> else
>> load_old_firmware_style();
>>
>> ?
>>
>> It seems this was already done in the two patches I submitted about a
>> year ago, so this is clearly doable.
>
> Along with TRXSE and TRXS firmwares, Infineon also uses a TRX format firmware,
> which is also different with the TRX format of Broadcom's firmware. So still
> need to differentiate between TRX firmwares because the WLAN vendor is different.
> This is not a question about whether it is about doable, more than that, whether
> it is practical.
It is a firmware loader, a piece of code that is well isolated.
>>>>> Even if the enumeration and firmware download support for the CYW55572 is
>>>>> somehow retro-fitted into the existing upstream BRCMFMAC driver, there are
>>>>> multiple other features and aspects of the Driver-Device communication that
>>>>> are unique to each WLAN vendor, which are not always practically feasible
>>>>> to support in the same upstream driver.
>>>>
>>>> Why ?
>>>
>>> As mentioned before, various aspects of the Driver-Device communication are
>>> unique to each WLAN vendor. And it is not practically feasible to confine
>>> them into a common upstream driver, also not sustainable to maintain it that
>>> way througout the lifetime of the hardware.
>>
>> The patches I submitted showed this was feasible, so I am not buying
>> that argument. Can you give that a try instead ?
>
> Our argument is on practical feasibility of doing so and its implications throughout
> the lifetime of this new-generation device, when later tring to add other features and
> when Infineon specific Driver-Device communiction flow evolve even more. This is quite
> different from feasbility of making brcmfmac just detect the chip and load the firmware.
> It is not sustainable to confine the support for a chips from multiple WLAN vendors
> into the same upstream Linux driver.
Apparently it is, and it was already done by infineon themselves, see
[1] above.
In fact, extending and maintaining existing driver is a much better way
to start with Linux kernel upstreaming than dumping a 50 kLoC duplicate
of existing driver onto the list.
Even if the initial support is not complete, it can be extended over
time, that is normal. And it is much easier to upstream support by
gradually improving/extending the already upstream code than dump a
large chunk of code all at once and expect it to get applied.
> Yes, we are aware of the couple patches that you submitted for upstream review. Before
> that, initially those two patches were first released by Infineon downstream, only for
> specific end use-cases and not actually intended for upstream.
>
>>>>> Because currently BRCMFMAC driver
>>>>> has a shared ownership with more than 3 WLAN vendor organizations and this
>>>>> approach has its limitations.
>>>>
>>>> Yes, this means it is necessary to cooperate and coordinate with other
>>>> people, on the mailing list.
>>>
>>> Infineon is committed to coordinate with other vendors in managing upstream
>>> BRCMFMAC driver and continuing the support for its legacy chipsets that
>>> still follows the legacy Broadcom architecture. Like for example, we have
>>> added support for CYW54591-PCIe in upstream BRCMFMAC few months back [2],
>>> because it has many things in common with other legacy Broadcom chips.
>>> But the same does not applies for all the new-generation Infineon chipsets.
>>>
>>>>> For Example, the version of the PCIe and SDIO
>>>>> BUS specific shared info in Device Memory is expected same from chipsets
>>>>> from all vendors. There would be a complex need to maintain vendor specifc
>>>>> as well as BUS specific Shared info version, vendor specific BUS Protocol
>>>>> layers, vendor specific firmware event header OUIs (currently always expects
>>>>> BRCM OUI even from other vendor chips) and even more.
>>>>
>>>> This sounds like code refactoring is necessary.
>>>>
>>>>> Confining different architecture chips from different WLAN vendors into the
>>>>> same legacy BRCMFMAC driver codebase, may not be sustainable and could
>>>>> potentially increase the maintainence effort and codebase complexity.
>>>>> And not practically feasible to continue splitting this driver with more
>>>>> vendor specific checks in the longer run. Since being different vendors,
>>>>> each will only naturally diverge even more in the future with their chipset
>>>>> Architecture, Driver-Device communication flow, etc. Infineon will continue
>>>>> to support its legacy chipsets, already supported in the upstream BRCMFMAC.
>>>> Maybe all the extra functionality can be added later, and the driver can
>>>> be forked later, when it becomes clear that refactoring is not an option
>>>> and it is becoming too difficult to maintain ?
>>>
>>> Refactoring the existing upstream BRCMFMAC driver for the new-architecture
>>> chipsets developed by Infineon is not a practical option now because these
>>> devices are fairly new. In the lifetime of these devices, the functionality
>>> of the Devices and Driver interaction are only going to evolve even more.
>>> Eventually, it would become more cumbersome to maintain as well as to avoid
>>> regressions for different architecture chips from different WLAN vendors
>>> if same upstream driver is used.
>>>
>>> We strongly believe that this is an appropriate time for thinking about a
>>> proposal to proceed with a dedicated driver for the new-generation chips
>>> developed individually by Infineon.
>>>
>>>> So far, it seems the current generation chips can be easily added to
>>>> brcmfmac, even if the feature set would be limited. Adding them would
>>>
>>> We are not in the same page here. As mentioned earlier and as you might
>>> know, there is infact much more to it than adding enumeration support for a
>>> chip in a driver. Certainly not easy when eventually Infineon is required
>>> to enable the end users with the actual capabilities of the chipset, also
>>> Infineon has the responsibility to ensure driver compatibility with all the
>>> newly shipped Device firmwares.
>>>
>>>> allow the maintainers to review such a smaller patchset and get at least
>>>> some hardware support in, step by step, instead of this mega-patchset.
>>>
>>> We understood that review bandwidth is limited and have taken the feedback
>>> received on v1 very seriously, so multiple features were already stripped
>>> off in v2. Willing to skip even more functionality in the upcoming versions
>>> and also ready to provide more information as needed to make the INFFMAC
>>> driver review as much easy as possible.
>>
>> So, can you try and implement minimal TRXSE firmware loading and support
>> for the CYW555xx for brcmfmac as an example, so it would be clear
>> whether or not it can be added into the brcmfmac driver ?
>
> No, as we mentioned earlier, just adding the enumeration & FW loading support
> for the chipset in the brcmfmac driver and claiming that the device support is
> available in the upstream brcmfmac driver would never be enough.
Clearly it is, and the rest can be extended, cf. [1] above.
> Further, Infineon
> has the responsibility to eventually add the actual features and capabilities of
> this new-generation chip in the same upstream driver, so that the end users can
> utilize the chipset to its fullest capability. It would not be practical to do this
> later for the new generation Infineon chips, once the DEV ID is added into brcmfmac.
> Because chipsets from multiple WLAN vendors are currently being managed by the same
> driver, continuing to add more vendor specific checks for every driver functionality
> is not sustainable.
Again, gradually improving upstream code is the way to go.
> The dedicated vendor WLAN drivers existing below the common cfg80211 & mac80211 layers
> in the linux WLAN stack, helps to maintain the Device Hardware specific abstraction,
> by taking care of the unique Device specific operation, Driver-Device Communication flow.
> Confining multiple WLAN Vendor chipsets in same upstream driver, would only weaken the
> abstraction even more.
>
> We would like to emphasize that Infineon objective is not to be forever tied to the
> existing common BRCMFMAC driver for its new generation chipsets, only because the
> then Cypress (now Infineon) acquired few legacy chipsets (supported in brcmfmac) from
> Broadcom nearly a decade ago in 2016.
>
>> Oh ... and I also noticed, that infineon recently fully rewrote the
>> history of the firmware repository [1], both the master branch history
>> and the tags now point to different commit SHAs.
>>
>> [1] https://github.com/Infineon/ifx-linux-firmware
>
> Ok, we will notify the respective folks internally.
It seems the problem is still not solved, the repository history was
rewritten and nobody seems to care.
I also tested this inffmac patchset with CYW55513 and not even scanning
seems to work.
^ permalink raw reply
* Re: [PATCH 16/16] carl9170: cmd: downgrade transient register I/O errors to wiphy_dbg
From: Christian Lamparter @ 2026-03-21 22:41 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B67FB95CB5305CEF9DAA209441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/17/26 12:06 PM, Masi Osmani wrote:
> Register read/write failures during deauth/teardown transitions are
> harmless — mac80211 tries to read survey stats or write slot_time
> while the firmware is in a transitional state. The command times
> out with -EIO but the adapter recovers and re-authenticates normally.
>
> Downgrade both "writing reg ... failed" and "reading regs failed"
> from wiphy_err to wiphy_dbg to reduce dmesg noise. The errors are
> still visible with dynamic debug enabled for investigation.
Sure
Acked-By: Christian Lamparter <chunkeey@gmail.com>
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/cmd.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c
> --- a/drivers/net/wireless/ath/carl9170/cmd.c
> +++ b/drivers/net/wireless/ath/carl9170/cmd.c
> @@ -52,7 +52,7 @@
> (u8 *) buf, 0, NULL);
> if (err) {
> if (net_ratelimit()) {
> - wiphy_err(ar->hw->wiphy, "writing reg %#x "
> + wiphy_dbg(ar->hw->wiphy, "writing reg %#x "
> "(val %#x) failed (%d)\n", reg, val, err);
> }
> }
> @@ -78,7 +78,7 @@
> 4 * nregs, (u8 *)res);
> if (err) {
> if (net_ratelimit()) {
> - wiphy_err(ar->hw->wiphy, "reading regs failed (%d)\n",
> + wiphy_dbg(ar->hw->wiphy, "reading regs failed (%d)\n",
> err);
> }
> return err;
^ permalink raw reply
* Re: [PATCH 13/16] carl9170: rx: gate data frame delivery on STARTED state
From: Johannes Berg @ 2026-03-21 22:37 UTC (permalink / raw)
To: Christian Lamparter, Masi Osmani; +Cc: linux-wireless
In-Reply-To: <17fe2464-e8a1-4f2b-a024-a78bcf460bcd@gmail.com>
On Sat, 2026-03-21 at 23:34 +0100, Christian Lamparter wrote:
> On 3/17/26 12:06 PM, Masi Osmani wrote:
> > Do not deliver data frames to mac80211 unless the device is fully
> > started. After carl9170_op_stop() the driver state drops to IDLE,
> > but the USB RX path can still receive frames from the hardware.
> > Without this gate, ieee80211_rx() may reference station data that
> > sta_info_destroy_part2() is concurrently freeing during interface
> > teardown, causing a use-after-free kernel panic.
> >
> > The race occurs when ieee80211_handle_reconfig_failure() clears
> > IN_DRIVER flags without stopping the hardware: cfg80211 then tears
> > down interfaces via ieee80211_do_stop() which calls sta_info_flush()
> > while the driver's RX path still delivers frames. This was observed
> > when carl9170 firmware deadlocks during a restart attempt and
> > ieee80211_reconfig() fails at drv_add_interface().
> >
> > The gate is placed in carl9170_rx_untie_data() just before the
> > ieee80211_rx() call, not in the USB layer, because firmware command
> > responses (including CARL9170_RSP_BOOT needed for firmware upload)
> > must still flow through the shared RX path via
> > carl9170_handle_command_response() which returns before reaching
> > this point.
> >
> > Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> > ---
> > drivers/net/wireless/ath/carl9170/rx.c | 8 ++++++++
> > 1 file changed, 8 insertions(+)
> >
> > diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> > index 683343013..19c6bd418 100644
> > --- a/drivers/net/wireless/ath/carl9170/rx.c
> > +++ b/drivers/net/wireless/ath/carl9170/rx.c
> > @@ -676,6 +676,14 @@ static int carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len,
> >
> > carl9170_ba_check(ar, buf, len);
> >
> > + /*
> > + * Do not deliver data frames to mac80211 unless the device is
> > + * fully started. After carl9170_op_stop() the state drops to
> > + * IDLE, preventing a use-after-free when sta_info_destroy_part2()
> > + * races with ieee80211_rx() during interface teardown.
> > + */
>
> If what you write is true for an up-to-date kernel, this needs to be addressed in mac80211.
> Under no circumstance should mac80211 behave that way... for any driver, in any case.
>
> Can you please post the panics/errors/warnings?
Yeah, this whole use-after-free is super fishy since we have RCU for it
all. Masi proposed a patch to mac80211 with similar strange reasoning
which I rejected, and never provided the errors that were happening ...
johannes
^ permalink raw reply
* Re: [PATCH 13/16] carl9170: rx: gate data frame delivery on STARTED state
From: Christian Lamparter @ 2026-03-21 22:34 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B6ADFF8016B03CAA1A9DEB9441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/17/26 12:06 PM, Masi Osmani wrote:
> Do not deliver data frames to mac80211 unless the device is fully
> started. After carl9170_op_stop() the driver state drops to IDLE,
> but the USB RX path can still receive frames from the hardware.
> Without this gate, ieee80211_rx() may reference station data that
> sta_info_destroy_part2() is concurrently freeing during interface
> teardown, causing a use-after-free kernel panic.
>
> The race occurs when ieee80211_handle_reconfig_failure() clears
> IN_DRIVER flags without stopping the hardware: cfg80211 then tears
> down interfaces via ieee80211_do_stop() which calls sta_info_flush()
> while the driver's RX path still delivers frames. This was observed
> when carl9170 firmware deadlocks during a restart attempt and
> ieee80211_reconfig() fails at drv_add_interface().
>
> The gate is placed in carl9170_rx_untie_data() just before the
> ieee80211_rx() call, not in the USB layer, because firmware command
> responses (including CARL9170_RSP_BOOT needed for firmware upload)
> must still flow through the shared RX path via
> carl9170_handle_command_response() which returns before reaching
> this point.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/rx.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index 683343013..19c6bd418 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -676,6 +676,14 @@ static int carl9170_handle_mpdu(struct ar9170 *ar, u8 *buf, int len,
>
> carl9170_ba_check(ar, buf, len);
>
> + /*
> + * Do not deliver data frames to mac80211 unless the device is
> + * fully started. After carl9170_op_stop() the state drops to
> + * IDLE, preventing a use-after-free when sta_info_destroy_part2()
> + * races with ieee80211_rx() during interface teardown.
> + */
If what you write is true for an up-to-date kernel, this needs to be addressed in mac80211.
Under no circumstance should mac80211 behave that way... for any driver, in any case.
Can you please post the panics/errors/warnings?
> + if (!IS_STARTED(ar))
> + return 0;
> skb = carl9170_rx_copy_data(buf, len);
> if (!skb)
> return -ENOMEM;
^ permalink raw reply
* Re: [PATCH 14/16] carl9170: main: guard op_config and bss_info_changed against non-STARTED state
From: Christian Lamparter @ 2026-03-21 22:28 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B6FDA6759CDCBE2421A8039441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/17/26 12:06 PM, Masi Osmani wrote:
> When wavemon or NetworkManager trigger rapid USB unbind/rebind
> recovery cycles, each deregistration produces these -EIO errors.
> Over 30+ cycles this exhausts mac80211 resources and causes a
> kernel panic.
Resources exhaustion? Kernel Panic? This does sound like an embedded device!
Have you checked for leaks? ( https://docs.kernel.org/dev-tools/kmemleak.html )
Can you please post such a panic? You could be looking at as of yet undiscovered,
bonafide bug.... that needs squashing.
Regards,
Christian
^ permalink raw reply
* Re: [PATCH 3/3] carlfw: disable buggy PSM to prevent USB command timeouts
From: Christian Lamparter @ 2026-03-21 22:23 UTC (permalink / raw)
To: iamdevnull; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B6CAD9AE8898D13A62F8CF9441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/17/26 10:11 AM, iamdevnull wrote:
> From: Masi Osmani <mas-i@hotmail.de>
>
> The carl9170 firmware power save implementation causes the SH-2
> processor to stop responding to USB commands after entering PS mode.
> Powering down the ADDAC and synthesizer via rf_psm() makes the
> device miss host command responses, triggering -ETIMEDOUT (-110)
> on the host every 45-135 seconds during normal operation.
>
> The kernel.org driver documentation confirms:
> "Power Save Mode, It's implemented but buggy"
>
> Three changes:
> - fw.c: remove CARL9170FW_PSM and CARL9170FW_FIXED_5GHZ_PSM from
> firmware capability bitmask so the driver never enables PS
> - rf.c: rf_psm() early return — never power down ADDAC/synthesizer
> - hostif.c: accept but ignore CARL9170_CMD_PSM commands gracefully
>
> With PSM disabled, the adapter stays fully responsive on USB.
> Tested: 0 crashes in 180s (previously every 45-135s). The host
> cannot force PS on even via iw set power_save on since the
> firmware no longer advertises the capability.
I have an idea here: Can you please add a new
config option (i.e. CARL9170FW_POWERSAVEMANAGEMENT),
let it depend on CARL9170FW_BROKEN_FEATURES (default n)
and then #ifdef / #endif around the feature flags and
make a rf_psm() stub when that feature isn't set?
Thanks
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> carlfw/src/fw.c | 6 ++++--
> carlfw/src/hostif.c | 4 ++--
> carlfw/src/rf.c | 7 +++++++
> 3 files changed, 13 insertions(+), 4 deletions(-)
>
> --- a/carlfw/src/fw.c 2026-03-16 23:38:46.184137155 +0100
> +++ b/carlfw/src/fw.c 2026-03-16 23:38:59.714232929 +0100
> @@ -48,8 +48,10 @@ const struct carl9170_firmware_descripto
> #endif /* CONFIG_CARL9170FW_USB_DOWN_STREAM */
> #ifdef CONFIG_CARL9170FW_RADIO_FUNCTIONS
> BIT(CARL9170FW_COMMAND_PHY) |
> - BIT(CARL9170FW_PSM) |
> - BIT(CARL9170FW_FIXED_5GHZ_PSM) |
> + /*
> + * PSM capability removed — firmware
> + * PS causes USB command timeouts.
> + */
> #endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
> #ifdef CONFIG_CARL9170FW_SECURITY_ENGINE
> BIT(CARL9170FW_COMMAND_CAM) |
> --- a/carlfw/src/rf.c 2026-03-16 23:38:46.188101929 +0100
> +++ b/carlfw/src/rf.c 2026-03-16 23:39:12.970421845 +0100
> @@ -237,6 +237,13 @@ void rf_psm(void)
> {
> u32 bank3;
>
> + /*
> + * PSM disabled — powering down ADDAC/synthesizer causes the
> + * SH-2 to miss USB command responses, triggering host-side
> + * -ETIMEDOUT and device crash. Always stay awake.
> + */
> + return;
> +
> if (fw.phy.psm.state == CARL9170_PSM_SOFTWARE) {
> /* not enabled by the driver */
> return;
> --- a/carlfw/src/hostif.c 2026-03-16 23:38:46.192102245 +0100
> +++ b/carlfw/src/hostif.c 2026-03-16 23:39:27.262628301 +0100
> @@ -285,9 +285,9 @@ void handle_cmd(struct carl9170_rsp *res
> break;
>
> case CARL9170_CMD_PSM:
> + /* PSM commands accepted but ignored — PS is disabled
> + * to prevent USB command timeout crashes. */
> resp->hdr.len = 0;
> - fw.phy.psm.state = le32_to_cpu(cmd->psm.state);
> - rf_psm();
Hmm this removal of rf_psm() seems to be a leftover then.. since you
added the return; in the function too?
> break;
> #endif /* CONFIG_CARL9170FW_RADIO_FUNCTIONS */
>
^ permalink raw reply
* Re: [PATCH 2/3] carlfw: wlanrx: batch RX frame upload triggers
From: Christian Lamparter @ 2026-03-21 22:11 UTC (permalink / raw)
To: iamdevnull, Christian Lamparter; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B6DF41F140DFB83D1B53649441A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/17/26 10:11 AM, iamdevnull wrote:
> From: Masi Osmani <mas-i@hotmail.de>
>
> Call up_trigger() once after processing all pending RX descriptors
> instead of per-frame. The PTA DMA transfers all queued descriptors
> in a single USB transaction, reducing interrupt overhead on the host
> by up to N (where N = frames per RX burst).
>
> On a busy 2.4 GHz channel with 10+ APs visible, this reduces USB
> interrupt rate during scan sweeps from ~200/s to ~30/s.
Interesting. Are you willing to share what PC/device you connected
your Fritz!WLAN N adapter to?
Because yes, I tried this before and I didn't see such a big difference.
This was back with my AMD Athlon X2, Core2Duo T7200 and i7 2630qm, It
became irrelevant with the i7 4770 (which I still have... but with a
new 2018-ish MB).
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> carlfw/src/wlanrx.c | 12 ++++++++++--
> 1 file changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/carlfw/src/wlanrx.c b/carlfw/src/wlanrx.c
> index 1234567..abcdefg 100644
> --- a/carlfw/src/wlanrx.c
> +++ b/carlfw/src/wlanrx.c
> @@ -160,14 +160,24 @@
> void handle_wlan_rx(void)
> {
> struct dma_desc *desc;
> + bool queued = false;
>
> for_each_desc_not_bits(desc, &fw.wlan.rx_queue, AR9170_OWN_BITS_HW) {
> if (!(wlan_rx_filter(desc) & fw.wlan.rx_filter)) {
> dma_put(&fw.pta.up_queue, desc);
> - up_trigger();
> + queued = true;
> } else {
> dma_reclaim(&fw.wlan.rx_queue, desc);
> wlan_trigger(AR9170_DMA_TRIGGER_RXQ);
> }
> }
> +
> + /*
> + * Trigger USB upload once for the entire batch rather than
> + * per frame. The PTA DMA will transfer all queued descriptors
> + * in a single USB transaction, reducing interrupt overhead on
> + * the host by up to N (where N = frames per RX burst).
> + */
> + if (queued)
> + up_trigger();
> }
^ permalink raw reply
* Re: [PATCH 11/12] carl9170: skip cross-band channel changes during software scan
From: Christian Lamparter @ 2026-03-21 21:58 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, Johannes Berg
In-Reply-To: <AM7PPF5613FA0B68A6825857195D5EF51B59443A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/15/26 11:56 PM, Masi Osmani wrote:
> The carl9170 relies on mac80211 software scanning because it does not
> implement a hw_scan callback. During a scan, mac80211 iterates all
> supported channels across both bands, calling carl9170_op_config()
> with IEEE80211_CONF_CHANGE_CHANNEL for each one.
>
> Every channel change triggers a full baseband cold reset, RF bank
> re-initialisation and AGC calibration via the firmware RF_INIT command
> with a 200 ms timeout. Cross-band switches (2.4 GHz <-> 5 GHz) are
> especially expensive and error-prone: the AGC calibration frequently
> times out (firmware returns error code 2), leaving the PHY in a
> degraded state. Subsequent channel changes -- even within the same
> band -- then also fail, and after three consecutive failures the
> driver restarts the device, causing a multi-second connectivity gap.
>
> When the adapter is associated on a specific band, scanning channels
> on the other band produces no useful roaming candidates for the
> current BSS. Add sw_scan_start/sw_scan_complete callbacks to track
> the scanning state and skip cross-band channel changes while a
> software scan is in progress. Intentional cross-band association
> changes (e.g. roaming from 2.4 GHz to 5 GHz on a dual-band SSID)
> are not affected because they occur outside the scanning window.
>
> Tested on Fritz\!WLAN N (AR9170) with 2.4 GHz association and
> concurrent full-band scans: no channel change failures, no device
> restarts, no PHY corruption.
Dealing with hardware misbehaving... Yeah!
I always suspected that the reason for this misbehaving are clock glitches.
This is because the whole device gets reclocked when switching between 2.4GHz (44/88MHz)
and 5GHz (40/80MHz) band and also HT40 (88/80MHz) vs HT20/Legacy(40/44MHz) too).
I do see the point here and I can confirm that to this day, this causes the
described annoyances.
@Johannes: Is this "stay within the band" something the driver should do,
or could this be moved up to mac80211/cfg80211?
(The way I did it was with wpa_supplicant since it allows one to specifiy
the scanning frequencies with scan_freq ... or even limit the frequencies
with freq_list. That said, this doesn't work great with NetworkManager
and the likes)
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/main.c | 42 +++++++++++++++++++
> 2 files changed, 43 insertions(+)
>
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h 2026-03-15 23:51:23.598565789 +0100
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h 2026-03-15 23:51:39.769123563 +0100
> @@ -333,6 +333,7 @@ struct ar9170 {
> /* PHY */
> struct ieee80211_channel *channel;
> unsigned int num_channels;
> + bool scanning;
> int noise[4];
> unsigned int chan_fail;
> unsigned int total_chan_fail;
> --- a/drivers/net/wireless/ath/carl9170/main.c 2026-03-15 23:51:23.597355728 +0100
> +++ b/drivers/net/wireless/ath/carl9170/main.c 2026-03-15 23:52:02.845563524 +0100
> @@ -916,6 +916,33 @@ static int carl9170_op_config(struct iee
> enum nl80211_channel_type channel_type =
> cfg80211_get_chandef_type(&hw->conf.chandef);
>
> + /*
> + * Skip cross-band channel changes during software scan.
> + *
> + * mac80211 sw_scan iterates all channels including the
> + * other band. Each channel change requires a full BB
> + * cold reset and AGC calibration via the firmware RF_INIT
> + * command (200 ms timeout). Cross-band switches
> + * frequently cause AGC calibration timeouts (firmware
> + * returns error 2), leaving the PHY in a degraded state
> + * that cascades into failures on subsequent intra-band
> + * channel changes and ultimately triggers a device
> + * restart after three consecutive failures.
> + *
> + * When associated, scanning the other band yields no
> + * useful roaming candidates for the current BSS. Skip
> + * the channel change so mac80211 advances to the next
> + * scan channel harmlessly.
> + */
> + if (ar->scanning && ar->channel &&
> + hw->conf.chandef.chan->band != ar->channel->band) {
> + wiphy_dbg(ar->hw->wiphy,
> + "skip cross-band scan: %d MHz -> %d MHz\n",
> + ar->channel->center_freq,
> + hw->conf.chandef.chan->center_freq);
> + goto out;
> + }
> +
> /* adjust slot time for 5 GHz */
> err = carl9170_set_slot_time(ar);
> if (err)
> @@ -954,6 +981,27 @@ out:
> return err;
> }
>
> +static void carl9170_op_sw_scan_start(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif,
> + const u8 *mac_addr)
> +{
> + struct ar9170 *ar = hw->priv;
> +
> + mutex_lock(&ar->mutex);
> + ar->scanning = true;
> + mutex_unlock(&ar->mutex);
> +}
> +
> +static void carl9170_op_sw_scan_complete(struct ieee80211_hw *hw,
> + struct ieee80211_vif *vif)
> +{
> + struct ar9170 *ar = hw->priv;
> +
> + mutex_lock(&ar->mutex);
> + ar->scanning = false;
> + mutex_unlock(&ar->mutex);
> +}
> +
> static u64 carl9170_op_prepare_multicast(struct ieee80211_hw *hw,
> struct netdev_hw_addr_list *mc_list)
> {
> @@ -1723,6 +1771,8 @@ static const struct ieee80211_ops carl91
> .add_interface = carl9170_op_add_interface,
> .remove_interface = carl9170_op_remove_interface,
> .config = carl9170_op_config,
> + .sw_scan_start = carl9170_op_sw_scan_start,
> + .sw_scan_complete = carl9170_op_sw_scan_complete,
> .prepare_multicast = carl9170_op_prepare_multicast,
> .configure_filter = carl9170_op_configure_filter,
> .conf_tx = carl9170_op_conf_tx,
^ permalink raw reply
* Re: [PATCH 12/12] carl9170: rx: handle zeroed PLCP CCK rate as corrupted frame
From: Christian Lamparter @ 2026-03-21 21:42 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless
In-Reply-To: <AM7PPF5613FA0B686F41D14D2742825BE219443A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/15/26 11:56 PM, Masi Osmani wrote:
> The firmware occasionally delivers frames tagged as CCK modulation
> with a zeroed PLCP rate byte (plcp[0] == 0x00). This typically
> happens after PHY state degradation from a failed channel change or
> from RF noise on weak signals.
And the firmware just "delivers" the frame that it gets from the hardware.
Apart from filtering (if enabled) it doesn't/shouldn't edit it.
> Currently these frames fall through to the default case and produce
> a rate-limited wiphy_err log:
>
> ieee80211 phy3: invalid plcp cck rate (0).
>
> The frame is garbage regardless of the log level. Handle plcp[0]
> == 0x00 as a dedicated case: increment the rx_dropped counter
> (visible via debugfs) and return -EINVAL silently. Downgrade the
> remaining default case log from wiphy_err to wiphy_dbg so that
> genuinely unexpected PLCP values can still be investigated via
> dynamic debug without polluting normal dmesg output.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/rx.c | 10 +++++++++-
> 1 file changed, 9 insertions(+), 1 deletion(-)
>
> --- a/drivers/net/wireless/ath/carl9170/rx.c 2026-03-15 23:51:23.599698582 +0100
> +++ b/drivers/net/wireless/ath/carl9170/rx.c 2026-03-15 23:52:21.041912498 +0100
> @@ -372,9 +372,18 @@ static int carl9170_rx_mac_status(struct
> case AR9170_RX_PHY_RATE_CCK_11M:
> status->rate_idx = 3;
> break;
> + case 0x00:
> + /*
> + * Zeroed PLCP rate byte: the firmware delivered a
> + * corrupted frame, typically after PHY degradation
> + * from a failed channel change or from RF noise on
> + * weak signals. Drop silently.
> + */
> + ar->rx_dropped++;
> + return -EINVAL;
I don't think we can really tell? From what I know the antenna must have pick up
this frame that way already. Is there some insight in how the hardware works?
Because from what I can gleam this "head->plcp[0]" seems to be pretty much the raw
value from what the PHY produced.
Note: This all might be because AR9170_MAC_RX_CTRL_PASS_TO_HOST is enabled.
> default:
> if (net_ratelimit()) {
> - wiphy_err(ar->hw->wiphy, "invalid plcp cck "
> + wiphy_dbg(ar->hw->wiphy, "invalid plcp cck "
> "rate (%x).\n", head->plcp[0]);
> }
Downgrading this to debug? Yes, that I'm totally fine with.
^ permalink raw reply
* Re: [PATCH 10/10] carl9170: phy: add periodic runtime IQ calibration
From: Christian Lamparter @ 2026-03-21 21:25 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6567EFD98F8FD6FE6511E9444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Add periodic runtime I/Q calibration triggered from the existing
> survey statistics work handler (carl9170_stat_work). The AR9170
> hardware performs initial IQ calibration during channel setup, but
> I/Q imbalance drifts with temperature over time, degrading EVM
> and increasing packet error rate.
>
> The new carl9170_run_iq_calibration() function sets the DO_IQCAL
> bit in PHY_TIMING_CTRL4 for both chains, which triggers the
> hardware to re-measure I/Q imbalance and update the correction
> coefficients automatically. This is a non-blocking operation --
> the hardware runs the calibration in the background without
> interrupting normal traffic.
>
> The ath9k driver performs similar periodic calibration via its
> longcal timer (every 30s). carl9170_stat_work runs at a
> comparable interval, making it a natural trigger point.
This is interesting.
I checked OTUS and unlike the antenna diversity these definitions, the IQCAL are present in
HalPlus/OTUS_FB50/hpani.h:
| #define AR_PHY_TIMING_CTRL4 0x1C5920 /* timing control */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF 0x01F /* Mask for kcos_theta-1 for q correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF_S 0 /* shift for Q_COFF */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF 0x7E0 /* Mask for sin_theta for i correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF_S 5 /* Shift for sin_theta for i correction */
| #define AR_PHY_TIMING_CTRL4_IQCORR_ENABLE 0x800 /* enable IQ correction */
| #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX 0xF000 /* Mask for max number of samples (logarithmic) */
| #define AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX_S 12 /* Shift for max number of samples */
| #define AR_PHY_TIMING_CTRL4_DO_IQCAL 0x10000 /* perform IQ calibration */
There's not a 100% match with what ath9k ar9002 is doing though. It looks like ar9002 can do more
than just IQCAL (as the matching value for AR_PHY_TIMING_CTRL4_DO_IQCAL is called AR_PHY_TIMING_CTRL4_DO_CAL.
And you need to set a separate AR_PHY_CALMODE to tell it what of the available CALs (IQ, GAIN, DC)
There's a chance that there's a benifit, if this is working.
One question about this though:
In HalPlus/OTUS_FB50/hpani.h:
|
| /* PHY IQ calibration results */
| #define AR_PHY_IQCAL_RES_PWR_MEAS_I 0x1C5C10 /* power measurement for I */
| #define AR_PHY_IQCAL_RES_PWR_MEAS_Q 0x1C5C14 /* power measurement for Q */
| #define AR_PHY_IQCAL_RES_IQ_CORR_MEAS 0x1C5C18 /* IQ correlation measurement */
This makes it look like we need to read back the results after the calibration
and put them into AR_PHY_TIMING_CTRL4's IQCORR_Q_Q and IQCORR_Q_I ... At least that's
what ath9k with the ar9002_calib seems to be doing in ar9002_hw_iqcalibrate().
In other words: Do you have some credible information that just setting
AR9170_PHY_TIMING_CTRL4_DO_IQCAL is enough?
Cheers,
Christian
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/main.c | 2 ++
> drivers/net/wireless/ath/carl9170/phy.c | 36 ++++++++++++++++++++
> 3 files changed, 39 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index 2eedb2f..0175f8e 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -605,6 +605,7 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> enum nl80211_channel_type bw);
> int carl9170_get_noisefloor(struct ar9170 *ar);
> void carl9170_update_channel_maxpower(struct ar9170 *ar);
> +int carl9170_run_iq_calibration(struct ar9170 *ar);
>
> /* FW */
> int carl9170_parse_firmware(struct ar9170 *ar);
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index ebf9fa9..50c0922 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -910,6 +910,8 @@ static void carl9170_stat_work(struct work_struct *work)
>
> mutex_lock(&ar->mutex);
> err = carl9170_update_survey(ar, false, true);
> + if (!err)
> + carl9170_run_iq_calibration(ar);
> mutex_unlock(&ar->mutex);
>
> if (err)
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index c294df7..b145e9e 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1637,6 +1637,42 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
> }
> }
>
> +int carl9170_run_iq_calibration(struct ar9170 *ar)
> +{
> + u32 val;
> + int err;
> +
> + if (!ar->channel)
> + return 0;
> +
> + /*
> + * Trigger runtime IQ calibration. The hardware measures
> + * I/Q imbalance and updates the correction coefficients
> + * automatically when DO_IQCAL is set. We trigger on both
> + * chains and re-enable the IQ correction afterwards.
> + */
> + err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), &val);
> + if (err)
> + return err;
> +
> + val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
> + err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(0), val);
> + if (err)
> + return err;
> +
> + /* chain 2 */
> + err = carl9170_read_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), &val);
> + if (err)
> + return err;
> +
> + val |= AR9170_PHY_TIMING_CTRL4_DO_IQCAL;
> + err = carl9170_write_reg(ar, AR9170_PHY_REG_TIMING_CTRL4(2), val);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> static int carl9170_set_radar_detection(struct ar9170 *ar,
> struct ieee80211_channel *channel)
> {
^ permalink raw reply
* Re: [PATCH 07/10] carl9170: main: add exponential restart backoff
From: Christian Lamparter @ 2026-03-21 20:42 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6C3933233D4272D36007C9444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> When the AR9170 enters a bad state (firmware errors, command
> timeouts, TX queue stalls), the driver can trigger rapid-fire
> restarts that prevent the device from ever stabilizing.
>
> Add exponential backoff to carl9170_restart(): if a restart
> request arrives before the current backoff window has elapsed,
> the request is throttled. The backoff starts at 500 ms and
> doubles on each restart, capping at 30 seconds. A successful
> restart resets the backoff to zero.
If this helps? It would be great if the device could get to "cool down"
instead of the restart request being throttled, after all something must
have happend for the restart request to be issued.
>
> Additionally, use named constants for the firmware error
> threshold (CARL9170_FW_ERROR_THRESHOLD) instead of a magic
> number, and add a window-based counting approach to avoid
> accumulating sporadic errors over long uptimes.
I guess you are refering with "window-based counting" to the
CARL9170_FW_ERROR_WINDOW_MS ? But is it used anywhere?
Maybe part of the patch is missing, or is there another patch?
Otherwise, I would be inclined to test this out (in other words: ack it).
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 2 ++
> drivers/net/wireless/ath/carl9170/main.c | 27 ++++++++++++++++++++
> drivers/net/wireless/ath/carl9170/rx.c | 10 +++++++-
> 3 files changed, 38 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index a2ffa62..2eedb2f 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -301,6 +301,8 @@ struct ar9170 {
> bool needs_full_reset;
> bool force_usb_reset;
> atomic_t pending_restarts;
> + unsigned long last_restart_jiffies;
> + unsigned int restart_backoff_ms;
>
> /* interface mode settings */
> struct list_head vif_list;
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index dcedcb1..ebf9fa9 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -492,6 +492,7 @@ static void carl9170_restart_work(struct work_struct *work)
> if (!err && !ar->force_usb_reset) {
> ar->restart_counter++;
> atomic_set(&ar->pending_restarts, 0);
> + ar->restart_backoff_ms = 0;
>
> ieee80211_restart_hw(ar->hw);
> } else {
> @@ -505,6 +506,9 @@ static void carl9170_restart_work(struct work_struct *work)
> }
> }
>
> +#define CARL9170_RESTART_BACKOFF_INIT_MS 500
> +#define CARL9170_RESTART_BACKOFF_MAX_MS 30000
> +
> void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
> {
> carl9170_set_state_when(ar, CARL9170_STARTED, CARL9170_IDLE);
> @@ -519,6 +523,29 @@ void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r)
> return;
> }
>
> + /*
> + * Exponential backoff: if restarts are happening too frequently,
> + * increase the delay before accepting the next one. This prevents
> + * restart storms when the device is in a bad state.
> + */
> + if (ar->last_restart_jiffies &&
> + time_before(jiffies, ar->last_restart_jiffies +
> + msecs_to_jiffies(ar->restart_backoff_ms))) {
> + dev_warn(&ar->udev->dev,
> + "restart (%d) throttled (backoff %u ms)\n",
> + r, ar->restart_backoff_ms);
> + atomic_dec(&ar->pending_restarts);
> + return;
> + }
> +
> + ar->last_restart_jiffies = jiffies;
> + if (ar->restart_backoff_ms == 0)
> + ar->restart_backoff_ms = CARL9170_RESTART_BACKOFF_INIT_MS;
> + else
> + ar->restart_backoff_ms = min(ar->restart_backoff_ms * 2,
> + (unsigned int)
> + CARL9170_RESTART_BACKOFF_MAX_MS);
> +
> ieee80211_stop_queues(ar->hw);
>
> dev_err(&ar->udev->dev, "restart device (%d)\n", r);
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index 414d499..bb909b5 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -46,6 +46,14 @@
> #include "hw.h"
> #include "cmd.h"
>
> +/*
> + * Time window for firmware error counting. Sporadic errors are
> + * normal over long uptimes; only a burst of errors within a short
> + * window warrants a restart.
> + */
> +#define CARL9170_FW_ERROR_WINDOW_MS 10000
This CARL9170_FW_ERROR_WINDOW_MS isn't used anywhere?
> +#define CARL9170_FW_ERROR_THRESHOLD 3
> +
> static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
> {
> bool restart = false;
> @@ -54,7 +62,7 @@ static void carl9170_dbg_message(struct ar9170 *ar, const char *buf, u32 len)
> if (len > 3) {
> if (memcmp(buf, CARL9170_ERR_MAGIC, 3) == 0) {
> ar->fw.err_counter++;
> - if (ar->fw.err_counter > 3) {
> + if (ar->fw.err_counter >= CARL9170_FW_ERROR_THRESHOLD) {
> then vs >= now. You could update CARL9170_FW_ERROR_THRESHOLD to 4 to match the "previous" behavior.
But no, this is neither here nor there.
> restart = true;
> reason = CARL9170_RR_TOO_MANY_FIRMWARE_ERRORS;
> }
^ permalink raw reply
* Re: [PATCH 05/10] carl9170: rx: track PHY errors via debugfs
From: Christian Lamparter @ 2026-03-21 20:29 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6B42814E38096301FE9429444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Count PHY errors reported by the hardware in the RX status and
> expose the counter through debugfs as rx_phy_errors. Previously,
> PHY errors from ar9170_rx_phystatus were silently ignored (marked
> with a TODO comment).
>
> The counter helps diagnose RF environment issues (interference,
> multipath, low SNR) without requiring monitor mode or additional
> tooling.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
Sure!
Acked-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/debug.c | 2 ++
> drivers/net/wireless/ath/carl9170/rx.c | 4 +++-
> 3 files changed, 6 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index ba29b4a..eaac859 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -386,6 +386,7 @@ struct ar9170 {
> unsigned int tx_ack_failures;
> unsigned int tx_fcs_errors;
> unsigned int rx_dropped;
> + unsigned int rx_phy_errors;
>
> /* EEPROM */
> struct ar9170_eeprom eeprom;
> diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c
> index 2d73456..0498df2 100644
> --- a/drivers/net/wireless/ath/carl9170/debug.c
> +++ b/drivers/net/wireless/ath/carl9170/debug.c
> @@ -794,6 +794,7 @@ DEBUGFS_READONLY_FILE(tx_janitor_last_run, 64, "last run:%d ms ago",
> DEBUGFS_READONLY_FILE(tx_dropped, 20, "%d", ar->tx_dropped);
>
> DEBUGFS_READONLY_FILE(rx_dropped, 20, "%d", ar->rx_dropped);
> +DEBUGFS_READONLY_FILE(rx_phy_errors, 20, "%d", ar->rx_phy_errors);
>
> DEBUGFS_READONLY_FILE(sniffer_enabled, 20, "%d", ar->sniffer_enabled);
> DEBUGFS_READONLY_FILE(rx_software_decryption, 20, "%d",
> @@ -830,6 +831,7 @@ void carl9170_debugfs_register(struct ar9170 *ar)
> DEBUGFS_ADD(tx_ampdu_list_len);
>
> DEBUGFS_ADD(rx_dropped);
> + DEBUGFS_ADD(rx_phy_errors);
> DEBUGFS_ADD(sniffer_enabled);
> DEBUGFS_ADD(rx_software_decryption);
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index c664014..414d499 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -455,7 +455,9 @@ static void carl9170_rx_phy_status(struct ar9170 *ar,
> if (phy->rssi[i] & 0x80)
> phy->rssi[i] = ((~phy->rssi[i] & 0x7f) + 1) & 0x7f;
>
> - /* TODO: we could do something with phy_errors */
> + if (phy->phy_err)
> + ar->rx_phy_errors++;
> +
> status->signal = ar->noise[0] + phy->rssi_combined;
> }
>
^ permalink raw reply
* Re: [PATCH 09/10] carl9170: fw: enable DFS radar detection
From: Christian Lamparter @ 2026-03-21 20:11 UTC (permalink / raw)
To: Masi Osmani, Christian Lamparter; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6E8DE143A385080A72F139444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Enable DFS (Dynamic Frequency Selection) radar detection on the
> AR9170. The hardware has radar detection registers (RADAR_0,
> RADAR_1, RADAR_EXT) and the firmware already sends
> CARL9170_RSP_RADAR events, but the driver never programmed the
> detection parameters and only logged a "please report" message.
>
> Changes:
> - Program radar detection pulse parameters in phy.c when the
> current channel has IEEE80211_CHAN_RADAR set. Values are
> based on ath9k defaults for FCC/ETSI compliance.
Which values did you use? The best I can find are in ar5008_phy.c
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/ar5008_phy.c?h=v7.0-rc4#n1268>
| conf->fir_power = -33;
| conf->radar_rssi = 20;
| conf->pulse_height = 10;
| conf->pulse_rssi = 15;
| conf->pulse_inband = 15;
| conf->pulse_maxlen = 255;
| conf->pulse_inband_step = 12;
| conf->radar_inband = 8;
the one in the patch look a little bit different.
Can I ask, how did you tune them? Do you have a setup to mimic
those air-port or weather radars? Or are you living close to one?
> - Advertise radar_detect_widths in the interface combination
> (fw.c) for 20 MHz noHT, 20 MHz HT, and 40 MHz HT.
> - Replace the old "please report" message with a call to
> ieee80211_radar_detected() so mac80211 can trigger the
> proper DFS state machine (channel switch / CAC).
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/fw.c | 3 ++
> drivers/net/wireless/ath/carl9170/phy.c | 45 +++++++++++++++++++++++++
> drivers/net/wireless/ath/carl9170/rx.c | 7 ++--
> 3 files changed, 50 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c
> index 419f553..a730593 100644
> --- a/drivers/net/wireless/ath/carl9170/fw.c
> +++ b/drivers/net/wireless/ath/carl9170/fw.c
> @@ -215,6 +215,9 @@ static void carl9170_fw_set_if_combinations(struct ar9170 *ar,
> ar->if_combs[0].max_interfaces = ar->fw.vif_num;
> ar->if_combs[0].limits = ar->if_comb_limits;
> ar->if_combs[0].n_limits = ARRAY_SIZE(ar->if_comb_limits);
> + ar->if_combs[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
> + BIT(NL80211_CHAN_WIDTH_20) |
> + BIT(NL80211_CHAN_WIDTH_40);
>
> ar->hw->wiphy->iface_combinations = ar->if_combs;
> ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ar->if_combs);
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index bcd9066..c294df7 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1637,6 +1637,47 @@ void carl9170_update_channel_maxpower(struct ar9170 *ar)
> }
> }
>
> +static int carl9170_set_radar_detection(struct ar9170 *ar,
> + struct ieee80211_channel *channel)
> +{
> + bool enable = channel->flags & IEEE80211_CHAN_RADAR;
> +
> + carl9170_regwrite_begin(ar);
> +
> + if (enable) {
> + /*
> + * Configure radar detection pulse parameters.
> + * Values based on ath9k's defaults for FCC/ETSI.
> + */
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_0,
> + AR9170_PHY_RADAR_0_ENA |
> + AR9170_PHY_RADAR_0_FFT_ENA |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_INBAND, 5) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_PRSSI, 1) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_HEIGHT, 6) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_RRSSI, 12) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_0_FIRPWR, 33));
> +
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_1,
> + AR9170_PHY_RADAR_1_MAX_RRSSI |
> + AR9170_PHY_RADAR_1_BLOCK_CHECK |
> + AR9170_PHY_RADAR_1_RELSTEP_CHECK |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_RELSTEP_THRESH, 8) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_RELPWR_THRESH, 12) |
> + SET_CONSTVAL(AR9170_PHY_RADAR_1_MAXLEN, 255));
> +
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT,
> + AR9170_PHY_RADAR_EXT_ENA);
This seems to be based on ar5008_hw_set_radar_params except that:
| if (conf->ext_channel)
| REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
| else
| REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA);
you always set the EXT_ENA bit?
> + } else {
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_0, 0);
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_1, 0);
> + carl9170_regwrite(AR9170_PHY_REG_RADAR_EXT, 0);
> + }
> +
> + carl9170_regwrite_finish();
> + return carl9170_regwrite_result();
> +}
> +
> int carl9170_get_noisefloor(struct ar9170 *ar)
> {
> static const u32 phy_regs[] = {
> @@ -1739,6 +1780,10 @@ int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> if (err)
> return err;
>
> + err = carl9170_set_radar_detection(ar, channel);
> + if (err)
> + return err;
> +
> tmp = AR9170_PHY_TURBO_FC_SINGLE_HT_LTF1 |
> AR9170_PHY_TURBO_FC_HT_EN;
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index bb909b5..1fe727c 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -259,11 +259,8 @@ void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len)
> break;
>
> case CARL9170_RSP_RADAR:
> - if (!net_ratelimit())
> - break;
> -
> - wiphy_info(ar->hw->wiphy, "FW: RADAR! Please report this "
> - "incident to linux-wireless@vger.kernel.org !\n");
> + wiphy_info(ar->hw->wiphy, "FW: radar pulse detected\n");
> + ieee80211_radar_detected(ar->hw, NULL);
> break;
>
> case CARL9170_RSP_GPIO:
^ permalink raw reply
* Re: [PATCH 08/10] carl9170: phy: enable antenna diversity for 2-chain devices
From: Christian Lamparter @ 2026-03-21 19:53 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6344DB5885359FAFC80D69444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Enable fast antenna diversity on devices with two RX chains
> (rx_mask == 3). This programs the MULTICHAIN_GAIN_CTL register
> to configure main/alternate LNA settings and enables the
> CCK fast antenna diversity bit.
>
> The AR9170 hardware has antenna diversity registers inherited
> from the AR9285/AR9287 family, but the carl9170 driver never
> programmed them. For dual-chain devices this improves receive
> performance in multipath environments by allowing the baseband
> to quickly switch between antenna paths.
Oh, no. It does program them! It's part of the ar5416_phy_init array in phy.c.
So. From what I remember, this was the reason why I copied the definitions
over from the ath9k driver to carl9170. Because this register must be important
if they are part of the init values. But I don't know if these definitions
are the same for AR9170 and AR9285. That's why the AR9285 is there.
As for ar5416_phy_init values. They came from the original OTUS driver.
Hope this helps.
If possible, this feature should be governed by something like the experimental
module parameter?
>
> The diversity configuration mirrors what ath9k uses for the
> AR9285 single-chip design:
> - Main LNA: LNA1
> - Alt LNA: LNA1+LNA2 (combined)
> - Gain table: table 0 for both paths
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/phy.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index 290c336..bcd9066 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -536,6 +536,31 @@ static int carl9170_init_phy_from_eeprom(struct ar9170 *ar,
> carl9170_regwrite(AR9170_PHY_REG_RX_CHAINMASK, ar->eeprom.rx_mask);
> carl9170_regwrite(AR9170_PHY_REG_CAL_CHAINMASK, ar->eeprom.rx_mask);
>
> + /*
> + * Enable fast antenna diversity for 2-chain devices.
> + * Configure main/alt LNA with both chains for best
> + * multipath performance.
> + */
> + if (ar->eeprom.rx_mask == 3) {
> + val = carl9170_def_val(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL,
> + is_2ghz, is_40mhz);
> + val |= AR9170_PHY_9285_ANT_DIV_CTL;
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_LNACONF, val,
> + AR9170_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_LNACONF, val,
> + AR9170_PHY_9285_ANT_DIV_LNA1);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_ALT_GAINTB, val,
> + AR9170_PHY_9285_ANT_DIV_GAINTB_0);
> + SET_VAL(AR9170_PHY_9285_ANT_DIV_MAIN_GAINTB, val,
> + AR9170_PHY_9285_ANT_DIV_GAINTB_0);
> + carl9170_regwrite(AR9170_PHY_REG_MULTICHAIN_GAIN_CTL, val);
> +
> + val = carl9170_def_val(AR9170_PHY_REG_CCK_DETECT,
> + is_2ghz, is_40mhz);
> + val |= AR9170_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV;
> + carl9170_regwrite(AR9170_PHY_REG_CCK_DETECT, val);
> + }
> +
> carl9170_regwrite_finish();
> return carl9170_regwrite_result();
> }
^ permalink raw reply
* Re: [PATCH 06/10] carl9170: phy: populate per-channel TX power from EEPROM
From: Christian Lamparter @ 2026-03-21 19:24 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6E8EAEDB73BAB87748A8C9444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:38 AM, Masi Osmani wrote:
> Replace the hardcoded max_power = 18 dBm (marked XXX) in the
> channel definitions with actual per-channel values derived from
> the EEPROM calibration target power tables.
>
> The new carl9170_get_max_tgt_power() function interpolates the
> maximum target power for a given frequency from the EEPROM's
> legacy OFDM and CCK target power tables, using the same frequency
> encoding and interpolation helpers already used by the power
> calibration code. carl9170_update_channel_maxpower() iterates
> all registered channels and updates their max_power fields.
Why the need for interpolation here? Don't you just need to look
for the max(ctpl[idx].power, previous_value) within the band?
I'm not aware of any high-powered AR9170 devices. Were/are there any?
Cheers,
Christian
>
> This is called during EEPROM parsing, so mac80211 and userspace
> see correct per-channel power limits from the start. The CHAN
> macro default of 18 dBm remains as a safe fallback for channels
> where EEPROM data is missing.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/carl9170.h | 1 +
> drivers/net/wireless/ath/carl9170/main.c | 4 +-
> drivers/net/wireless/ath/carl9170/phy.c | 88 ++++++++++++++++++++
> 3 files changed, 92 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h
> index eaac859..a2ffa62 100644
> --- a/drivers/net/wireless/ath/carl9170/carl9170.h
> +++ b/drivers/net/wireless/ath/carl9170/carl9170.h
> @@ -602,6 +602,7 @@ int carl9170_led_set_state(struct ar9170 *ar, const u32 led_state);
> int carl9170_set_channel(struct ar9170 *ar, struct ieee80211_channel *channel,
> enum nl80211_channel_type bw);
> int carl9170_get_noisefloor(struct ar9170 *ar);
> +void carl9170_update_channel_maxpower(struct ar9170 *ar);
>
> /* FW */
> int carl9170_parse_firmware(struct ar9170 *ar);
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index d75688c..dcedcb1 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -89,7 +89,7 @@ struct ieee80211_rate __carl9170_ratetable[] = {
> #define CHAN(_freq, _idx) { \
> .center_freq = (_freq), \
> .hw_value = (_idx), \
> - .max_power = 18, /* XXX */ \
> + .max_power = 18, \
> }
>
> static struct ieee80211_channel carl9170_2ghz_chantable[] = {
> @@ -1930,6 +1930,8 @@ static int carl9170_parse_eeprom(struct ar9170 *ar)
> if (!bands)
> return -EINVAL;
>
> + carl9170_update_channel_maxpower(ar);
> +
> ar->survey = devm_kcalloc(&ar->udev->dev, chans,
> sizeof(struct survey_info), GFP_KERNEL);
> if (!ar->survey)
> diff --git a/drivers/net/wireless/ath/carl9170/phy.c b/drivers/net/wireless/ath/carl9170/phy.c
> index 34d9fd7..290c336 100644
> --- a/drivers/net/wireless/ath/carl9170/phy.c
> +++ b/drivers/net/wireless/ath/carl9170/phy.c
> @@ -1524,6 +1524,94 @@ static void carl9170_set_power_cal(struct ar9170 *ar, u32 freq,
> carl9170_calc_ctl(ar, freq, bw);
> }
>
> +static u8 carl9170_get_max_tgt_power(struct ar9170 *ar, u32 freq)
> +{
> + struct ar9170_calibration_target_power_legacy *ctpl;
> + int ntargets, idx, n, i;
> + u8 f, max_power = 0;
> + u8 pwr_freqs[AR5416_MAX_NUM_TGT_PWRS];
> +
> + if (freq < 3000)
> + f = freq - 2300;
> + else
> + f = (freq - 4800) / 5;
> +
> + /* check legacy target powers (OFDM for 2G, 5G leg) */
> + for (i = 0; i < 2; i++) {
> + switch (i) {
> + case 0:
> + if (freq >= 3000) {
> + ctpl = &ar->eeprom.cal_tgt_pwr_5G[0];
> + ntargets = AR5416_NUM_5G_TARGET_PWRS;
> + } else {
> + ctpl = &ar->eeprom.cal_tgt_pwr_2G_ofdm[0];
> + ntargets = AR5416_NUM_2G_OFDM_TARGET_PWRS;
> + }
> + break;
> + case 1:
> + if (freq < 3000) {
> + ctpl = &ar->eeprom.cal_tgt_pwr_2G_cck[0];
> + ntargets = AR5416_NUM_2G_CCK_TARGET_PWRS;
> + } else {
> + continue;
> + }
> + break;
> + default:
> + continue;
> + }
> +
> + for (n = 0; n < ntargets; n++) {
> + if (ctpl[n].freq == 0xff)
> + break;
> + pwr_freqs[n] = ctpl[n].freq;
> + }
> + ntargets = n;
> + if (ntargets < 2)
> + continue;
> +
> + idx = carl9170_find_freq_idx(ntargets, pwr_freqs, f);
> + for (n = 0; n < 4; n++) {
> + u8 pwr;
> +
> + pwr = carl9170_interpolate_u8(f,
> + ctpl[idx + 0].freq,
> + ctpl[idx + 0].power[n],
> + ctpl[idx + 1].freq,
> + ctpl[idx + 1].power[n]);
> + max_power = max(max_power, pwr);
> + }
> + }
> +
> + /* target power is in half-dBm, max_power is in dBm */
> + return max_power / 2;
> +}
> +
> +void carl9170_update_channel_maxpower(struct ar9170 *ar)
> +{
> + struct ieee80211_supported_band *band;
> + int i;
> +
> + band = ar->hw->wiphy->bands[NL80211_BAND_2GHZ];
> + if (band) {
> + for (i = 0; i < band->n_channels; i++) {
> + u8 pwr = carl9170_get_max_tgt_power(ar,
> + band->channels[i].center_freq);
> + if (pwr)
> + band->channels[i].max_power = pwr;
> + }
> + }
> +
> + band = ar->hw->wiphy->bands[NL80211_BAND_5GHZ];
> + if (band) {
> + for (i = 0; i < band->n_channels; i++) {
> + u8 pwr = carl9170_get_max_tgt_power(ar,
> + band->channels[i].center_freq);
> + if (pwr)
> + band->channels[i].max_power = pwr;
> + }
> + }
> +}
> +
> int carl9170_get_noisefloor(struct ar9170 *ar)
> {
> static const u32 phy_regs[] = {
^ permalink raw reply
* Re: [PATCH 04/10] carl9170: rx: wire up dropped frame counter
From: Christian Lamparter @ 2026-03-21 19:03 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6AC8119FB64246940D3899444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:37 AM, Masi Osmani wrote:
> Increment ar->rx_dropped when frames with unrecognized error
> codes are dropped in carl9170_rx_mac_status(). The counter
> was already defined in the ar9170 struct and exported via
> debugfs, but the actual increment was missing -- only a TODO
> comment was in place.
>
> This provides visibility into receive-path frame drops through
> the existing debugfs interface.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/rx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c
> index 6833430..c664014 100644
> --- a/drivers/net/wireless/ath/carl9170/rx.c
> +++ b/drivers/net/wireless/ath/carl9170/rx.c
> @@ -340,7 +340,7 @@ static int carl9170_rx_mac_status(struct ar9170 *ar,
>
> /* drop any other error frames */
> if (unlikely(error)) {
> - /* TODO: update netdevice's RX dropped/errors statistics */
> + ar->rx_dropped++;
No, that would cause it to be counted twice.
This is because in the parent function carl9170_rx_untie_data() (yes, carl9170_rx_mac_status() is only
called from there.) already has the "ar->rx_dropped++;" in the "drop:" error code path.
^ permalink raw reply
* Re: [PATCH 03/10] carl9170: mac80211: document spatial multiplexing power save handler
From: Christian Lamparter @ 2026-03-21 18:57 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6B76223A73FFC756A19A99444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:37 AM, Masi Osmani wrote:
> Replace the bare TODO comment in the SMPS configuration handler
> with documentation explaining why the driver accepts but does not
> act on SMPS mode changes.
>
> The AR9170 advertises SM_PS disabled (both chains always active)
> in its HT capabilities. While mac80211 may still send SMPS
> configuration requests, implementing static or dynamic SMPS would
> require firmware support for per-chain enable/disable that the
> AR9170 firmware (v1.9.9) does not provide.
>
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
Sure, this patch is reasonable.
Acked-by: Christian Lamparter <chunkeey@gmail.com>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index 6324b38..d75688c 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -910,7 +910,13 @@ static int carl9170_op_config(struct ieee80211_hw *hw, int radio_idx, u32 change
> }
>
> if (changed & IEEE80211_CONF_CHANGE_SMPS) {
> - /* TODO */
> + /*
> + * We advertise SM_PS disabled (all chains active).
> + * mac80211 may still request mode changes, which we
> + * accept but only support OFF (both chains active).
> + * Static/dynamic SMPS would require firmware support
> + * for chain control that the AR9170 does not provide.
> + */
> err = 0;
> }
>
^ permalink raw reply
* Re: [PATCH 02/10] carl9170: mac80211: advertise RX STBC capability
From: Christian Lamparter @ 2026-03-21 18:47 UTC (permalink / raw)
To: Masi Osmani, Christian Lamparter; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6ACC91029B589B12E12639444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:37 AM, Masi Osmani wrote:
> The AR9170 baseband supports receiving Space-Time Block Coded
> streams (1 spatial stream). The capability was not advertised
> in the HT capabilities, causing peers to never use STBC when
> transmitting to us. Enabling RX STBC improves receive diversity
> gain and link reliability, especially in multipath environments.
>
> The ath9k driver for the same chipset family advertises RX STBC
> based on the number of RX chains. With 2 RX chains, the AR9170
> can decode 1 STBC spatial stream.
No, not that I can tell.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/common-init.c?h=v7.0-rc4#n204
| if (AR_SREV_9280_20_OR_LATER(ah)) {
| if (max_streams >= 2)
| ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
| ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
| }
the ath9k driver supports this for generation after the AR9160, the AR9280
The commit message is wrong again. If you are sure this is working though,
please rewrite the commit message and put this feature after a experimental
module parameter (like for the STBC patch you proposed).
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index aa7e481..6324b38 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -156,7 +156,8 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
> IEEE80211_HT_CAP_SGI_40 | \
> IEEE80211_HT_CAP_SGI_20 | \
> IEEE80211_HT_CAP_DSSSCCK40 | \
> - IEEE80211_HT_CAP_SM_PS, \
> + IEEE80211_HT_CAP_SM_PS | \
> + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \
> .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
> .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, \
> .mcs = { \
^ permalink raw reply
* Re: [PATCH 01/10] carl9170: mac80211: enable Short Guard Interval for 20 MHz
From: Christian Lamparter @ 2026-03-21 18:41 UTC (permalink / raw)
To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B662B9909BAEB554565F499444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>
On 3/12/26 11:37 AM, Masi Osmani wrote:
> The AR9170 hardware supports Short Guard Interval (400ns) for both
> 20 MHz and 40 MHz channel widths. SGI_40 was already advertised in
> the HT capabilities, but SGI_20 was missing. This reduces the OFDM
> symbol duration from 800ns to 400ns on 20 MHz channels, increasing
> the maximum PHY rate from 130 Mbps to 144.4 Mbps (MCS 15, 2SS).
>
> ath9k (the PCI sibling for the same AR9xxx chipset family) has
> always advertised both SGI_20 and SGI_40.
>
Are you really, really sure about that?
Because based on what's in ath9k:
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/hw.c?h=v7.0-rc4#n2622>
| if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
| pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
Whereas AR_SREV_9287_11_OR_LATER and AR_SREV_9271 are defined in reg.h @
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/net/wireless/ath/ath9k/reg.h?h=v7.0-rc4#n861>
|#define AR_SREV_9287_11_OR_LATER(_ah) \
| (((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9287))
| [...]
|#define AR_SREV_9271(_ah) \
| (((_ah))->hw_version.macVersion == AR_SREV_VERSION_9271)
What's strange here is that this isn't a PHY check, but MAC check...
And the AR9170 uses a modified ZyDAS MAC which is completely different
than what any of the ath9k siblings uses.
So based on that: No. I can't ACK that. The feature might work or not,
but that commit message is wrong.
That said, if you say have been successfully been using this and rewrite the
commit message to not include wrong information and add a module_parameter
like experimental that enables it, this would be OK!
Cheers,
Christian
> Signed-off-by: Masi Osmani <mas-i@hotmail.de>
> ---
> drivers/net/wireless/ath/carl9170/main.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c
> index a7a9345..aa7e481 100644
> --- a/drivers/net/wireless/ath/carl9170/main.c
> +++ b/drivers/net/wireless/ath/carl9170/main.c
> @@ -154,6 +154,7 @@ static struct ieee80211_channel carl9170_5ghz_chantable[] = {
> .cap = IEEE80211_HT_CAP_MAX_AMSDU | \
> IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \
> IEEE80211_HT_CAP_SGI_40 | \
> + IEEE80211_HT_CAP_SGI_20 | \
> IEEE80211_HT_CAP_DSSSCCK40 | \
> IEEE80211_HT_CAP_SM_PS, \
> .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \
^ permalink raw reply
* [PATCH iwlwifi-next 15/15] wifi: iwlwifi: mld: set RX_FLAG_RADIOTAP_TLV_AT_END generically
From: Miri Korenblit @ 2026-03-21 17:29 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260321172922.3938740-1-miriam.rachel.korenblit@intel.com>
From: Johannes Berg <johannes.berg@intel.com>
Instead of setting this flag in the iwl_mld_radiotap_put_tlv()
users, and not even all of them, set it inside the function.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 6f40d6e47083..a2e586c6ea67 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include <net/mac80211.h>
@@ -791,6 +791,9 @@ static void *
iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len)
{
struct ieee80211_radiotap_tlv *tlv;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+
+ rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
tlv = skb_put(skb, sizeof(*tlv));
tlv->type = cpu_to_le16(type);
@@ -1234,8 +1237,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len);
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
-
switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) {
case 0:
if (he_type == RATE_MCS_HE_TYPE_TRIG) {
@@ -1329,7 +1330,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
struct sk_buff *skb)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(mld->monitor.cur_aid);
@@ -1353,8 +1353,6 @@ static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, &mld->monitor.cur_aid,
sizeof(mld->monitor.cur_aid));
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
#endif
@@ -1362,7 +1360,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
struct sk_buff *skb,
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
{
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_vendor_content *radiotap;
const u16 vendor_data_len = sizeof(*ntfy);
@@ -1382,8 +1379,6 @@ static void iwl_mld_add_rtap_sniffer_phy_data(struct iwl_mld *mld,
/* fill the data now */
memcpy(radiotap->data, ntfy, vendor_data_len);
-
- rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END;
}
static void
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox