Linux wireless drivers development
 help / color / mirror / Atom feed
* RE: [PATCH wireless-next v3 14/15] wifi: cfg80211: add LTF keyseed support for secure ranging
From: Stern, Avraham @ 2026-03-15  8:22 UTC (permalink / raw)
  To: Peddolla Harshavardhan Reddy, johannes@sipsolutions.net
  Cc: linux-wireless@vger.kernel.org, kavita.kavita@oss.qualcomm.com
In-Reply-To: <20260305160712.1263829-15-peddolla.reddy@oss.qualcomm.com>



> Sent: Thursday, March 5, 2026 6:07 PM
> To: johannes@sipsolutions.net
> Cc: linux-wireless@vger.kernel.org; kavita.kavita@oss.qualcomm.com
> Subject: [PATCH wireless-next v3 14/15] wifi: cfg80211: add LTF keyseed support for secure ranging

> Currently there is no way to install an LTF key seed that can be used in non-trigger-based (NTB) and trigger-based (TB) FTM ranging to protect NDP frames. Without this, drivers cannot enable PHY-layer security for peer measurement sessions, leaving ranging measurements vulnerable to eavesdropping and manipulation.

Installing keys when there is no station (PASN keys) is something new (not supported by mac80211, is it?). so currently even installing the TK from PASN is not clearly defined?

  
>  struct key_params {
>  	const u8 *key;
> @@ -839,6 +841,8 @@ struct key_params {
>  	u16 vlan_id;
>  	u32 cipher;
>  	enum nl80211_key_mode mode;
> +	const u8 *ltf_keyseed;
> +	size_t ltf_keyseed_len;
 
Since the mode is NL80211_KEY_LTF_SEED, can use key and key_len, like any other key.
On the other hand, LTF key seed is not really a key... maybe add the KDK (from which the LTF key seed is derived) instead?
 
> + * @NL80211_KEY_LTF_SEED: LTF key seed is used by the driver to generate
> + *	secure LTF keys used in case of peer measurement request with FTM
> + *	request type as either %NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED
> + *	or %NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED, secure LTF key seeds will
> + *	help enable PHY security in peer measurement session. The corresponding
> + *	keys need to be configured beforehand to ensure peer measurement
> + *	session is secure. Only valid if %NL80211_EXT_FEATURE_SET_KEY_LTF_SEED

"the corresponding keys" - refers to the TK?
"beforehand" - before installing the LTF key seed or before the measurement?
This is not clear. 


---------------------------------------------------------------------
A member of the Intel Corporation group of companies

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


^ permalink raw reply

* [PATCH wireless v2] wifi: iwlwifi: mld: stop TX during firmware restart
From: Sheroz Juraev @ 2026-03-15  8:12 UTC (permalink / raw)
  To: linux-wireless; +Cc: stable, Sheroz Juraev
In-Reply-To: <20260303104217.180715-1-goodmartiandev@gmail.com>

When iwlwifi firmware crashes (e.g., NMI_INTERRUPT_UNKNOWN on Intel
BE201/Wi-Fi 7), iwl_mld_nic_error() sets mld->fw_status.in_hw_restart
to true. However, iwl_mld_tx_from_txq() does not check this flag before
dequeuing frames from mac80211 and pushing them to the transport layer.

Since the firmware is dead, iwl_trans_tx() returns -EIO for each frame,
which then gets freed immediately. Under high-throughput conditions
(e.g., Tailscale UDP traffic or active SSH sessions), this creates a
tight dequeue-send-fail-free loop that wastes CPU cycles and generates
rapid skb allocation churn, leading to memory pressure from slab
fragmentation.

The RX path already has this guard (iwl_mld_rx_mpdu checks
in_hw_restart at rx.c:1906), and so does the TXQ allocation worker
(iwl_mld_add_txqs_wk at tx.c:156). Add the same guard to
iwl_mld_tx_from_txq() to stop all TX during firmware restart.

Frames left in mac80211's TXQs are naturally drained after restart
completes, when queue reallocation triggers iwl_mld_tx_from_txq()
via iwl_mld_add_txq_list(), or when new upper-layer traffic invokes
wake_tx_queue.

Tested on ASUS Zenbook 14 UX3405CA with Intel BE201 (Wi-Fi 7) on
kernel 6.19.5 where the firmware crashes approximately every 10-15
minutes under Tailscale traffic.

Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Cc: stable@vger.kernel.org
Signed-off-by: Sheroz Juraev <goodmartiandev@gmail.com>
---
v2: add target tree name to subject, drop cover letter (single patch)
---
 drivers/net/wireless/intel/iwlwifi/mld/tx.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
index 3b4b575aa..c5eb36652 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c
@@ -959,6 +959,16 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
 	struct sk_buff *skb = NULL;
 	u8 zero_addr[ETH_ALEN] = {};

+	/*
+	 * Don't transmit during firmware restart. The firmware is dead,
+	 * so iwl_trans_tx() would return -EIO for each frame. Avoid the
+	 * overhead of dequeuing from mac80211 only to immediately free
+	 * the skbs, and the potential memory pressure from rapid skb
+	 * allocation churn during high-throughput restart scenarios.
+	 */
+	if (unlikely(mld->fw_status.in_hw_restart))
+		return;
+
 	/*
 	 * No need for threads to be pending here, they can leave the first
 	 * taker all the work.
--
2.47.2


^ permalink raw reply related

* Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
From: LB F @ 2026-03-15  0:24 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CALdGYqSq416qqqZ7t+wG5fir9NWfi3578+brdaj05q-42Gj14w@mail.gmail.com>

Oleksandr Havrylov <goainwo@gmail.com> wrote:
> After extended testing with your DMI patch applied, the hard freeze is
> gone. However, with ASPM disabled but LPS Deep still active, I observe
> periodic h2c timeouts during idle which cause occasional WiFi throughput
> drops and Bluetooth audio stuttering. When I additionally set
> disable_lps_deep=Y, all symptoms disappear completely. This confirms
> that combining the ASPM quirk with dynamic LPS_DEEP_MODE_NONE would be
> the complete fix. Ready to test an updated patch if you decide to
> include this.

Hi Ping-Ke,

While monitoring logs with the current patch applied, I noticed two
things that might be useful.

First, the following message appears each time the driver loads:

  rtw88_8821ce 0000:13:00.0: can't disable ASPM; OS doesn't have ASPM control

This suggests the BIOS retains control over ASPM and prevents any
OS-level override via pci_disable_link_state(). The system remains
stable regardless, which confirms that the rtw_pci_disable_aspm flag
approach in your patch is the correct and effective method here.

Second, during normal operation I observe this warning periodically:

  WARNING: net/mac80211/rx.c:5491 at ieee80211_rx_list+0x177/0x1020 [mac80211]

This is the same location that appeared in the call trace just before
the hard freeze. You mentioned earlier that malformed RX data reaching
mac80211 could be a factor. I'm not sure if this warning is related,
but I wanted to flag it in case it is useful for your RX validation
investigation.

No h2c timeouts or firmware errors have been observed. The system
remains fully stable.

Best regards,
Oleksandr Havrylov

^ permalink raw reply

* [PATCH] qtnfmac: use alloc_netdev macro for single queue devices
From: Roi L @ 2026-03-14 16:08 UTC (permalink / raw)
  To: imitsyanko; +Cc: linux-wireless, Roi L

alloc_netdev is a macro for single queue devices, so there's no need to
call alloc_netdev_mqs with a single tx/rx queue.

Signed-off-by: Roi L <roeilev321_@outlook.com>
---
 drivers/net/wireless/quantenna/qtnfmac/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/quantenna/qtnfmac/core.c b/drivers/net/wireless/quantenna/qtnfmac/core.c
index 0c106709ae29..fece048f7ca0 100644
--- a/drivers/net/wireless/quantenna/qtnfmac/core.c
+++ b/drivers/net/wireless/quantenna/qtnfmac/core.c
@@ -452,8 +452,8 @@ int qtnf_core_net_attach(struct qtnf_wmac *mac, struct qtnf_vif *vif,
 	void *qdev_vif;
 	int ret;
 
-	dev = alloc_netdev_mqs(sizeof(struct qtnf_vif *), name,
-			       name_assign_type, ether_setup, 1, 1);
+	dev = alloc_netdev(sizeof(struct qtnf_vif *), name,
+			   name_assign_type, ether_setup);
 	if (!dev)
 		return -ENOMEM;
 
-- 
2.53.0


^ permalink raw reply related

* Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
From: LB F @ 2026-03-14 12:39 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CALdGYqRYVxGbg+qRNUvRNr9V4f2YVZ7p=amwq1ktdmZVkwxjQg@mail.gmail.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:
> I'd adopt your suggestion (dynamic LPS_DEEP_MODE_NONE) if the test
> is positive.

Hi Ping-Ke,

Following your suggestion, I performed an additional experiment to
validate the dynamic LPS_DEEP_MODE_NONE idea. Please treat this
purely as a field test report -- I am not a kernel developer, and the
implementation below is certainly not upstream-quality. I am sharing
it only in the hope that it helps you design a proper solution.

What I did:

I extended your DMI quirk in pci.c with an additional capability flag
for LPS Deep mode. The only file touched was pci.c (your patch) --
main.c was left completely unmodified.

The changes to your patch are as follows:

  /* 1. Extended the capabilities enum */
  enum rtw88_quirk_dis_pci_caps {
          QUIRK_DIS_PCI_CAP_ASPM,
          QUIRK_DIS_PCI_CAP_LPS_DEEP,  /* test addition */
  };

  /* 2. Extended disable_pci_caps() callback */
  static int disable_pci_caps(const struct dmi_system_id *dmi)
  {
          uintptr_t dis_caps = (uintptr_t)dmi->driver_data;

          if (dis_caps & BIT(QUIRK_DIS_PCI_CAP_ASPM))
                  rtw_pci_disable_aspm = true;

          if (dis_caps & BIT(QUIRK_DIS_PCI_CAP_LPS_DEEP))
                  rtw_disable_lps_deep_mode = true;

          return 1;
  }

  /* 3. Both flags set for the HP P3S95EA#ACB entry */
  .driver_data = (void *)(BIT(QUIRK_DIS_PCI_CAP_ASPM) |
                          BIT(QUIRK_DIS_PCI_CAP_LPS_DEEP)),

I am aware that setting rtw_disable_lps_deep_mode from pci.c is
architecturally impure -- it is a global flag that would affect all
rtw88 devices in a hypothetical multi-adapter system. A proper
per-device solution (e.g. a flag inside struct rtw_dev set during
probe) would be cleaner. I simply used the existing global as the
most straightforward way to validate the concept.

Verification:

Confirmed no rtw88-related entries exist in /etc/modprobe.d/,
/lib/modprobe.d/, or /run/modprobe.d/, ruling out any external
parameter injection.

After loading the patched modules, the following was confirmed via
sysfs:

  /sys/module/rtw88_core/parameters/disable_lps_deep_mode = Y
  /sys/module/rtw88_pci/parameters/disable_aspm = Y

This confirms the DMI quirk is the sole source of both values.

Results (10-minute idle observation, battery power, wifi.powersave=3):

  With your ASPM patch alone (LPS Deep still active):
    - periodic "failed to send h2c command" bursts observed
    - occasional WiFi throughput drops and Bluetooth audio stuttering

  With ASPM patch + LPS Deep disabled via the quirk:
    - h2c=0, lps=0 across the entire observation window
    - WiFi throughput stable, Bluetooth audio uninterrupted

The result confirms that disabling LPS Deep Mode in addition to ASPM
completely eliminates the remaining firmware timeout loop on this
platform.

I hope this experiment is useful as a data point. Please feel free to
discard the implementation and design a proper solution -- I am ready
to test any updated patch you send.

Best regards,
Oleksandr Havrylov

^ permalink raw reply

* Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
From: LB F @ 2026-03-14 10:52 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CALdGYqQ5K0iuxjjX4TwNLi9Km5O+YL3Y9r6Bwfk9BaiuV3BHPA@mail.gmail.com>

After extended testing with your DMI patch applied, the hard freeze is
gone. However, with ASPM disabled but LPS Deep still active, I observe
periodic h2c timeouts during idle which cause occasional WiFi
throughput drops and Bluetooth audio stuttering. When I additionally
set disable_lps_deep=Y, all symptoms disappear completely. This
confirms that combining the ASPM quirk with dynamic LPS_DEEP_MODE_NONE
would be the complete fix. Ready to test an updated patch if you
decide to include this.

^ permalink raw reply

* [PATCH wireless] wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
From: Felix Fietkau @ 2026-03-14  6:54 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes

ieee80211_tx_prepare_skb() has three error paths, but only two of them
free the skb. The first error path (ieee80211_tx_prepare() returning
TX_DROP) does not free it, while invoke_tx_handlers() failure and the
fragmentation check both do.

Add kfree_skb() to the first error path so all three are consistent,
and remove the now-redundant frees in callers (ath9k, mt76,
mac80211_hwsim) to avoid double-free.

Document the skb ownership guarantee in the function's kdoc.

Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
 drivers/net/wireless/ath/ath9k/channel.c      | 6 ++----
 drivers/net/wireless/mediatek/mt76/scan.c     | 4 +---
 drivers/net/wireless/virtual/mac80211_hwsim.c | 1 -
 include/net/mac80211.h                        | 4 +++-
 net/mac80211/tx.c                             | 4 +++-
 5 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 121e51ce1bc0..8b27d8cc086a 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1006,7 +1006,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
 	skb_set_queue_mapping(skb, IEEE80211_AC_VO);
 
 	if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
-		goto error;
+		return;
 
 	txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
 	if (ath_tx_start(sc->hw, skb, &txctl))
@@ -1119,10 +1119,8 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
 
 		skb->priority = 7;
 		skb_set_queue_mapping(skb, IEEE80211_AC_VO);
-		if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
-			dev_kfree_skb_any(skb);
+		if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta))
 			return false;
-		}
 		break;
 	default:
 		return false;
diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c
index ff9176cdee3d..63b0447e55c1 100644
--- a/drivers/net/wireless/mediatek/mt76/scan.c
+++ b/drivers/net/wireless/mediatek/mt76/scan.c
@@ -63,10 +63,8 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid)
 
 	rcu_read_lock();
 
-	if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) {
-		ieee80211_free_txskb(phy->hw, skb);
+	if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL))
 		goto out;
-	}
 
 	info = IEEE80211_SKB_CB(skb);
 	if (req->no_cck)
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 82adcc848189..721775d1426a 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -3157,7 +3157,6 @@ static void hw_scan_work(struct work_struct *work)
 						      hwsim->tmp_chan->band,
 						      NULL)) {
 				rcu_read_unlock();
-				kfree_skb(probe);
 				continue;
 			}
 
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 310546d4fca6..afe0bf29a9df 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7411,7 +7411,9 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
  * @band: the band to transmit on
  * @sta: optional pointer to get the station to send the frame to
  *
- * Return: %true if the skb was prepared, %false otherwise
+ * Return: %true if the skb was prepared, %false otherwise.
+ * On failure, the skb is freed by this function; callers must not
+ * free it again.
  *
  * Note: must be called under RCU lock
  */
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 04a3ea9beae5..d4527b19b48e 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1896,8 +1896,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
 	struct ieee80211_tx_data tx;
 	struct sk_buff *skb2;
 
-	if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+	if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+		kfree_skb(skb);
 		return false;
+	}
 
 	info->band = band;
 	info->control.vif = vif;
-- 
2.51.0


^ permalink raw reply related

* Re: [PATCH] dt-bindings: net: wireless: marvell,sd8787: Relax length constraints
From: Rob Herring (Arm) @ 2026-03-14  0:08 UTC (permalink / raw)
  To: Fabio Estevam
  Cc: briannorris, conor+dt, linux-wireless, devicetree, johannes,
	krzk+dt, kvalo, linux-kernel, Frank.Li
In-Reply-To: <20260311194531.70441-1-festevam@gmail.com>


On Wed, 11 Mar 2026 16:45:31 -0300, Fabio Estevam wrote:
> Commit 31ed9d9d71ec ("ARM: dts: rockchip: Limit WiFi TX power on
> rk3288-veyron-jerry") added calibration data for the rk3288-veyron-jerry
> platform. The commit message explicitly notes that "the length can vary
> between hw versions", as documented in the original text binding.
> 
> The current YAML schema enforces fixed maximum lengths for calibration
> data arrays, which causes dtbs_check warnings for rk3288-veyron-jerry.dts.
> 
> Relax the constraints for the two properties that have
> variable-length data in this platform by adding minItems based on the
> actual data used in the downstream kernel:
> 
> - marvell,caldata-txpwrlimit-2g: 508 bytes (from rk3288-veyron-jerry.dts)
> - marvell,caldata-txpwrlimit-5g-sub2: 744 bytes (from
> rk3288-veyron-jerry.dts)
> 
> The original maxItems values are preserved as upper bounds to maintain
> validation for other platforms while accommodating this specific
> hardware variant.
> 
> Fixes: 25f855413885 ("dt-bindings: net: wireless: convert marvel-8xxx.txt to yaml format")
> Signed-off-by: Fabio Estevam <festevam@gmail.com>
> ---
>  .../devicetree/bindings/net/wireless/marvell,sd8787.yaml        | 2 ++
>  1 file changed, 2 insertions(+)
> 

Acked-by: Rob Herring (Arm) <robh@kernel.org>


^ permalink raw reply

* Re: [RFC PATCH v3 0/4] wifi: rtl8xxxu: implement AP mode for 8188EU
From: Bitterblue Smith @ 2026-03-13 23:14 UTC (permalink / raw)
  To: Georg Müller, Jes.Sorensen; +Cc: linux-wireless, linux-kernel
In-Reply-To: <20260313135321.3196688-1-georgmueller@gmx.net>

On 13/03/2026 15:53, Georg Müller wrote:
> This series tries to implement AP mode for Realtek RTL8188EU chips.
> 
> This is not final. I still have issues connecting to the AP, searching for some
> input on what may be the reason or how to further debug issues.
> 

What are the issues? Were they happening with your original patch
which only added supports_ap and max_macid_num?

Another thing still missing is the macid in the TX descriptor
(rtl8xxxu_fill_txdesc_v3).

> Patch 1 could be picked independently as it shouldn't change anything for the
> non-AP mode.
> 
> ---
> Changes in v3:
> - fix compile errors caused by testing on machine with older kernel
> Changes in v2:
> - add patch to move dynamic_tx_rpt_timing_counter from ra_info to priv
> - convert ra_info to a dynamic array
> - update max report mac id after station add/remove
> 
> ---
> Georg Müller (4):
>   wifi: rtl8xxxu: move dynamic_tx_rpt_timing_counter from ra_info to
>     priv
>   wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
>   wifi: rtl8xxxu: update max report mac id on station add / remove for
>     8188e chips
>   wifi: rtl8xxxu: Enable AP mode for RTL8188EU
> 
>  drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 22 +++++-----
>  drivers/net/wireless/realtek/rtl8xxxu/core.c  | 41 ++++++++++++++++---
>  .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  5 ++-
>  3 files changed, 50 insertions(+), 18 deletions(-)
> 


^ permalink raw reply

* [PATCH v4] wifi: ath9k: Obtain system GPIOS from descriptors
From: Linus Walleij @ 2026-03-13 21:53 UTC (permalink / raw)
  To: Kalle Valo, Andy Shevchenko, Arnd Bergmann, Alban Bedel,
	Bartosz Golaszewski, Toke Høiland-Jørgensen,
	Michał Kępień
  Cc: linux-wireless, brcm80211-dev-list.pdl, linux-gpio, Linus Walleij,
	Linus Walleij

The ath9k has an odd use of system-wide GPIOs: if the chip
does not have internal GPIO capability, it will try to obtain a
GPIO line from the system GPIO controller:

  if (BIT(gpio) & ah->caps.gpio_mask)
        ath9k_hw_gpio_cfg_wmac(...);
  else if (AR_SREV_SOC(ah))
        ath9k_hw_gpio_cfg_soc(ah, gpio, out, label);

Where ath9k_hw_gpio_cfg_soc() will attempt to issue
gpio_request_one() passing the local GPIO number of the controller
(0..31) to gpio_request_one().

This is somewhat peculiar and possibly even dangerous: there is
nowadays no guarantee of the numbering of these system-wide
GPIOs, and assuming that GPIO 0..31 as used by ath9k would
correspond to GPIOs 0..31 on the system as a whole seems a bit
wild.

Register all 32 GPIOs at index 0..31 directly in the ATH79K
GPIO driver and associate with the NULL device (making them
widely available) if and only if we are probing ATH79K wifi
from the AHB bus (used for SoCs). We obtain these offsets from
the NULL device if necessary.

These GPIOs should ideally be defined in the device tree
instead, but we have no control over that for the legacy
code path.

Testcompiled with the ath79 defconfig.

Reported-by: Michał Kępień <kernel@kempniu.pl>
Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
This patch set is a long standing attempt to get rid of the global
GPIO numbers from the ath9k Wireless driver.

Maybe Kalle can merge this to the Wireless tree if we agree on this
hack solution.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
Changes in v4:
- Fix review comments from Andy.
- Collect ACKs.
- Link to v3: https://lore.kernel.org/r/20260312-descriptors-wireless-v3-1-5230e0870c31@kernel.org

Changes in v3:
- Rebased on kernel v7.0-rc1
- Fix up issues as pointed out by Michał Kępień
- Link to v2: https://lore.kernel.org/r/20240423-descriptors-wireless-v2-1-6d1d03b30bfa@linaro.org

Changes in v2:
- Define all the descriptors directly in the ATH79K
  GPIO driver in case the driver want to request them directly.
- Link to v1: https://lore.kernel.org/r/20240131-descriptors-wireless-v1-0-e1c7c5d68746@linaro.org
---
 drivers/gpio/gpio-ath79.c           | 56 ++++++++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath9k/hw.c | 33 ++++++++++++++--------
 drivers/net/wireless/ath/ath9k/hw.h |  3 +-
 3 files changed, 79 insertions(+), 13 deletions(-)

diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c
index 2ad9f6ac6636..7c10fa282ad2 100644
--- a/drivers/gpio/gpio-ath79.c
+++ b/drivers/gpio/gpio-ath79.c
@@ -11,6 +11,7 @@
 #include <linux/device.h>
 #include <linux/gpio/driver.h>
 #include <linux/gpio/generic.h>
+#include <linux/gpio/machine.h> /* For WLAN GPIOs */
 #include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/mod_devicetable.h>
@@ -214,6 +215,55 @@ static const struct of_device_id ath79_gpio_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
 
+#if IS_ENABLED(CONFIG_ATH9K_AHB)
+/*
+ * This registers all of the ath79k GPIOs as descriptors to be picked
+ * directly from the ATH79K wifi driver if the two are jitted together
+ * in the same SoC.
+ */
+#define ATH79K_WIFI_DESCS 32
+static int ath79_gpio_register_wifi_descriptors(struct device *dev,
+						const char *label)
+{
+	struct gpiod_lookup_table *lookup;
+	int i;
+
+	/* Create a gpiod lookup using gpiochip-local offsets + 1 for NULL */
+	lookup = devm_kzalloc(dev,
+			      struct_size(lookup, table, ATH79K_WIFI_DESCS + 1),
+			      GFP_KERNEL);
+	if (!lookup)
+		return -ENOMEM;
+
+	/*
+	 * Ugly system-wide lookup for the NULL device: we know this
+	 * is already NULL but explicitly assign it here for people to
+	 * know what is going on. (Yes this is an ugly legacy hack, live
+	 * with it.)
+	 */
+	lookup->dev_id = NULL;
+
+	for (i = 0; i < ATH79K_WIFI_DESCS; i++) {
+		lookup->table[i] =
+			/*
+			 * Set the HW offset on the chip and the lookup
+			 * index to the same value, so looking up index 0
+			 * will get HW offset 0, index 1 HW offset 1 etc.
+			 */
+			GPIO_LOOKUP_IDX(label, i, "ath9k", i, GPIO_ACTIVE_HIGH);
+	}
+
+	gpiod_add_lookup_table(lookup);
+
+	return 0;
+}
+#else
+static int ath79_gpio_register_wifi_descriptors(struct device *dev,
+						const char *label)
+{
+}
+#endif
+
 static int ath79_gpio_probe(struct platform_device *pdev)
 {
 	struct gpio_generic_chip_config config;
@@ -276,7 +326,11 @@ static int ath79_gpio_probe(struct platform_device *pdev)
 		girq->handler = handle_simple_irq;
 	}
 
-	return devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
+	err = devm_gpiochip_add_data(dev, &ctrl->chip.gc, ctrl);
+	if (err)
+		return err;
+
+	return ath79_gpio_register_wifi_descriptors(dev, ctrl->chip.gc.label);
 }
 
 static struct platform_driver ath79_gpio_driver = {
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index a45351afcf6e..05c95e67a853 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -21,7 +21,7 @@
 #include <linux/time.h>
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/unaligned.h>
 
 #include "hw.h"
@@ -2719,19 +2719,28 @@ static void ath9k_hw_gpio_cfg_output_mux(struct ath_hw *ah, u32 gpio, u32 type)
 static void ath9k_hw_gpio_cfg_soc(struct ath_hw *ah, u32 gpio, bool out,
 				  const char *label)
 {
+	enum gpiod_flags flags = out ? GPIOD_OUT_LOW : GPIOD_IN;
+	struct gpio_desc *gpiod;
 	int err;
 
-	if (ah->caps.gpio_requested & BIT(gpio))
+	if (ah->gpiods[gpio])
 		return;
 
-	err = devm_gpio_request_one(ah->dev, gpio, out ? GPIOF_OUT_INIT_LOW : GPIOF_IN, label);
+	/*
+	 * Obtains a system specific GPIO descriptor from another GPIO controller.
+	 * Ideally this should come from the device tree, this is a legacy code
+	 * path.
+	 */
+	gpiod = gpiod_get_index(NULL, "ath9k", gpio, flags);
+	err = PTR_ERR_OR_ZERO(gpiod);
 	if (err) {
 		ath_err(ath9k_hw_common(ah), "request GPIO%d failed:%d\n",
 			gpio, err);
 		return;
 	}
 
-	ah->caps.gpio_requested |= BIT(gpio);
+	gpiod_set_consumer_name(gpiod, label);
+	ah->gpiods[gpio] = gpiod;
 }
 
 static void ath9k_hw_gpio_cfg_wmac(struct ath_hw *ah, u32 gpio, bool out,
@@ -2791,10 +2800,12 @@ void ath9k_hw_gpio_free(struct ath_hw *ah, u32 gpio)
 	if (!AR_SREV_SOC(ah))
 		return;
 
-	WARN_ON(gpio >= ah->caps.num_gpio_pins);
+	if (ah->gpiods[gpio]) {
+		gpiod_put(ah->gpiods[gpio]);
+		ah->gpiods[gpio] = NULL;
+	}
 
-	if (ah->caps.gpio_requested & BIT(gpio))
-		ah->caps.gpio_requested &= ~BIT(gpio);
+	WARN_ON(gpio >= ah->caps.num_gpio_pins);
 }
 EXPORT_SYMBOL(ath9k_hw_gpio_free);
 
@@ -2822,8 +2833,8 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
 			val = REG_READ(ah, AR_GPIO_IN(ah)) & BIT(gpio);
 		else
 			val = MS_REG_READ(AR, gpio);
-	} else if (BIT(gpio) & ah->caps.gpio_requested) {
-		val = gpio_get_value(gpio) & BIT(gpio);
+	} else if (ah->gpiods[gpio]) {
+		val = gpiod_get_value(ah->gpiods[gpio]);
 	} else {
 		WARN_ON(1);
 	}
@@ -2846,8 +2857,8 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
 			AR7010_GPIO_OUT : AR_GPIO_IN_OUT(ah);
 
 		REG_RMW(ah, out_addr, val << gpio, BIT(gpio));
-	} else if (BIT(gpio) & ah->caps.gpio_requested) {
-		gpio_set_value(gpio, val);
+	} else if (ah->gpiods[gpio]) {
+		gpiod_set_value(ah->gpiods[gpio], val);
 	} else {
 		WARN_ON(1);
 	}
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index eaa07d6dbde0..d9d2f64c5570 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -19,6 +19,7 @@
 
 #include <linux/if_ether.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/firmware.h>
 
@@ -302,7 +303,6 @@ struct ath9k_hw_capabilities {
 	u8 max_rxchains;
 	u8 num_gpio_pins;
 	u32 gpio_mask;
-	u32 gpio_requested;
 	u8 rx_hp_qdepth;
 	u8 rx_lp_qdepth;
 	u8 rx_status_len;
@@ -783,6 +783,7 @@ struct ath_hw {
 	struct ath9k_hw_capabilities caps;
 	struct ath9k_channel channels[ATH9K_NUM_CHANNELS];
 	struct ath9k_channel *curchan;
+	struct gpio_desc *gpiods[32];
 
 	union {
 		struct ar5416_eeprom_def def;

---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20240122-descriptors-wireless-b8da95dcab35

Best regards,
-- 
Linus Walleij <linusw@kernel.org>


^ permalink raw reply related

* [PATCH 2/2] wifi: libertas: don't kill URBs in interrupt context
From: Heitor Alves de Siqueira @ 2026-03-13 21:27 UTC (permalink / raw)
  To: Johannes Berg, Szymon Wilczek
  Cc: linux-wireless, libertas-dev, linux-kernel, kernel-dev,
	syzbot+74afbb6355826ffc2239
In-Reply-To: <20260313-libertas-usb-anchors-v1-0-915afbe988d7@igalia.com>

Serialization for the TX path was enforced by calling
usb_kill_urb()/usb_kill_anchored_urbs(), to prevent transmission before
a previous URB was completed. usb_tx_block() can be called from
interrupt context (e.g. in the HCD giveback path), so we can't always
use it to kill in-flight URBs.

Prevent sleeping during interrupt context by checking the tx_submitted
anchor for existing URBs. We now return -EBUSY, to indicate there's
a pending request.

Reported-by: syzbot+74afbb6355826ffc2239@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=74afbb6355826ffc2239
Fixes: d66676e6ca96 ("wifi: libertas: fix WARNING in usb_tx_block")
Signed-off-by: Heitor Alves de Siqueira <halves@igalia.com>
---
 drivers/net/wireless/marvell/libertas/if_usb.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index 11cd1422f46a..d3b9f7619a1a 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
@@ -429,7 +429,12 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb
 		goto tx_ret;
 	}
 
-	usb_kill_anchored_urbs(&cardp->tx_submitted);
+	/* check if there are pending URBs */
+	if (!usb_anchor_empty(&cardp->tx_submitted)) {
+		lbs_deb_usbd(&cardp->udev->dev, "%s failed: pending URB\n", __func__);
+		ret = -EBUSY;
+		goto tx_ret;
+	}
 
 	usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
 			  usb_sndbulkpipe(cardp->udev,

-- 
2.53.0


^ permalink raw reply related

* [PATCH 0/2] wifi: libertas: add USB anchors for TX/RX paths
From: Heitor Alves de Siqueira @ 2026-03-13 21:27 UTC (permalink / raw)
  To: Johannes Berg, Szymon Wilczek
  Cc: linux-wireless, libertas-dev, linux-kernel, kernel-dev,
	syzbot+74afbb6355826ffc2239

This series fixes a sleep-in-interrupt-context bug reported by Syzbot in
the Marvell libertas driver. Previous in-flight URBs were killed with
usb_kill_urb(), which shouldn't be called from interrupt context as it
might sleep.

The first patch adds USB anchors for the TX and RX paths, so we have a
way to track any in-flight URBs. Existing functions now use the anchors
instead of handling URBs directly.

The second patch fixes the reported bug by checking the TX anchor for
any in-flight URBs before sending out new ones. We now return -EBUSY
instead of killing the pending URB.

Signed-off-by: Heitor Alves de Siqueira <halves@igalia.com>
---
Heitor Alves de Siqueira (2):
      wifi: libertas: use USB anchors for tracking in-flight URBs
      wifi: libertas: don't kill URBs in interrupt context

 drivers/net/wireless/marvell/libertas/if_usb.c | 32 ++++++++++++++++++--------
 drivers/net/wireless/marvell/libertas/if_usb.h |  3 +++
 2 files changed, 25 insertions(+), 10 deletions(-)
---
base-commit: 80234b5ab240f52fa45d201e899e207b9265ef91
change-id: 20260313-libertas-usb-anchors-71dd8442dd42

Best regards,
-- 
Heitor Alves de Siqueira <halves@igalia.com>


^ permalink raw reply

* [PATCH 1/2] wifi: libertas: use USB anchors for tracking in-flight URBs
From: Heitor Alves de Siqueira @ 2026-03-13 21:27 UTC (permalink / raw)
  To: Johannes Berg, Szymon Wilczek
  Cc: linux-wireless, libertas-dev, linux-kernel, kernel-dev
In-Reply-To: <20260313-libertas-usb-anchors-v1-0-915afbe988d7@igalia.com>

The libertas driver currently handles URB lifecycles manually, which
makes it non-trivial to check if specific URBs are pending or not. Add
anchors for TX/RX URBs, and use those to track in-flight requests.

Signed-off-by: Heitor Alves de Siqueira <halves@igalia.com>
---
 drivers/net/wireless/marvell/libertas/if_usb.c | 27 ++++++++++++++++----------
 drivers/net/wireless/marvell/libertas/if_usb.h |  3 +++
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c
index 8a6bf1365cfa..11cd1422f46a 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas/if_usb.c
@@ -114,8 +114,8 @@ static void if_usb_write_bulk_callback(struct urb *urb)
 static void if_usb_free(struct if_usb_card *cardp)
 {
 	/* Unlink tx & rx urb */
-	usb_kill_urb(cardp->tx_urb);
-	usb_kill_urb(cardp->rx_urb);
+	usb_kill_anchored_urbs(&cardp->tx_submitted);
+	usb_kill_anchored_urbs(&cardp->rx_submitted);
 
 	usb_free_urb(cardp->tx_urb);
 	cardp->tx_urb = NULL;
@@ -221,6 +221,9 @@ static int if_usb_probe(struct usb_interface *intf,
 		     udev->descriptor.bDeviceSubClass,
 		     udev->descriptor.bDeviceProtocol);
 
+	init_usb_anchor(&cardp->rx_submitted);
+	init_usb_anchor(&cardp->tx_submitted);
+
 	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
 		endpoint = &iface_desc->endpoint[i].desc;
 		if (usb_endpoint_is_bulk_in(endpoint)) {
@@ -426,7 +429,7 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb
 		goto tx_ret;
 	}
 
-	usb_kill_urb(cardp->tx_urb);
+	usb_kill_anchored_urbs(&cardp->tx_submitted);
 
 	usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
 			  usb_sndbulkpipe(cardp->udev,
@@ -435,8 +438,10 @@ static int usb_tx_block(struct if_usb_card *cardp, uint8_t *payload, uint16_t nb
 
 	cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
 
+	usb_anchor_urb(cardp->tx_urb, &cardp->tx_submitted);
 	if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
 		lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
+		usb_unanchor_urb(cardp->tx_urb);
 	} else {
 		lbs_deb_usb2(&cardp->udev->dev, "usb_submit_urb success\n");
 		ret = 0;
@@ -467,8 +472,10 @@ static int __if_usb_submit_rx_urb(struct if_usb_card *cardp,
 			  cardp);
 
 	lbs_deb_usb2(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
+	usb_anchor_urb(cardp->rx_urb, &cardp->rx_submitted);
 	if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
 		lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
+		usb_unanchor_urb(cardp->rx_urb);
 		kfree_skb(skb);
 		cardp->rx_skb = NULL;
 		ret = -1;
@@ -838,8 +845,8 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
 	}
 
 	/* Cancel any pending usb business */
-	usb_kill_urb(cardp->rx_urb);
-	usb_kill_urb(cardp->tx_urb);
+	usb_kill_anchored_urbs(&cardp->rx_submitted);
+	usb_kill_anchored_urbs(&cardp->tx_submitted);
 
 	cardp->fwlastblksent = 0;
 	cardp->fwdnldover = 0;
@@ -869,8 +876,8 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
 	if (cardp->bootcmdresp == BOOT_CMD_RESP_NOT_SUPPORTED) {
 		/* Return to normal operation */
 		ret = -EOPNOTSUPP;
-		usb_kill_urb(cardp->rx_urb);
-		usb_kill_urb(cardp->tx_urb);
+		usb_kill_anchored_urbs(&cardp->rx_submitted);
+		usb_kill_anchored_urbs(&cardp->tx_submitted);
 		if (if_usb_submit_rx_urb(cardp) < 0)
 			ret = -EIO;
 		goto done;
@@ -900,7 +907,7 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret,
 	wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
 
 	timer_delete_sync(&cardp->fw_timeout);
-	usb_kill_urb(cardp->rx_urb);
+	usb_kill_anchored_urbs(&cardp->rx_submitted);
 
 	if (!cardp->fwdnldover) {
 		pr_info("failed to load fw, resetting device!\n");
@@ -960,8 +967,8 @@ static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
 		goto out;
 
 	/* Unlink tx & rx urb */
-	usb_kill_urb(cardp->tx_urb);
-	usb_kill_urb(cardp->rx_urb);
+	usb_kill_anchored_urbs(&cardp->tx_submitted);
+	usb_kill_anchored_urbs(&cardp->rx_submitted);
 
  out:
 	return ret;
diff --git a/drivers/net/wireless/marvell/libertas/if_usb.h b/drivers/net/wireless/marvell/libertas/if_usb.h
index 7d0daeb33c3f..a0cd36197c2b 100644
--- a/drivers/net/wireless/marvell/libertas/if_usb.h
+++ b/drivers/net/wireless/marvell/libertas/if_usb.h
@@ -48,6 +48,9 @@ struct if_usb_card {
 	struct urb *rx_urb, *tx_urb;
 	struct lbs_private *priv;
 
+	struct usb_anchor rx_submitted;
+	struct usb_anchor tx_submitted;
+
 	struct sk_buff *rx_skb;
 
 	uint8_t ep_in;

-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH 00/10] carl9170: 802.11n compliance and driver improvements
From: Christian Lamparter @ 2026-03-13 21:14 UTC (permalink / raw)
  To: Masi Osmani; +Cc: linux-wireless, ath9k-devel
In-Reply-To: <AM7PPF5613FA0B6D188CBDBAFC0CE13247A9444A@AM7PPF5613FA0B6.EURP251.PROD.OUTLOOK.COM>

On 3/12/26 11:37 AM, Masi Osmani wrote:
> The carl9170 driver for Atheros AR9170-based USB WiFi adapters has been
> effectively unmaintained since 2016.  While the hardware shipped as
> Draft-N certified, several 802.11n capabilities were never advertised
> to mac80211, diagnostic counters were left as TODO stubs, and some
> hardware features were never wired up.

Ok, if you want to take a shot at this. Sure why not.

> This series addresses these gaps in 10 independent patches, ordered
> from simple HT capability flags to more involved PHY programming:
> 
> Patches 1-3: HT capability corrections
>    - Enable SGI_20 (was only SGI_40)
>    - Advertise RX STBC (1 spatial stream)
>    - Document the SMPS handler (replacing bare TODO)
> 
> Patches 4-5: Diagnostic counters
>    - Wire up the RX dropped frame counter
>    - Track PHY errors via debugfs
> 
> Patch 6: TX power calibration
>    - Replace hardcoded 18 dBm with per-channel EEPROM values
> 
> Patch 7: Recovery hardening
>    - Add exponential backoff to prevent restart storms
> 
> Patch 8: Antenna diversity
>    - Enable fast antenna diversity for 2-chain devices
> 
> Patch 9: DFS radar detection
>    - Program radar registers, call ieee80211_radar_detected()
> 
> Patch 10: Runtime IQ calibration
>    - Periodic I/Q recalibration via existing stat_work timer
> 
> All patches are individually compile-tested and have been verified
> on real hardware (AVM Fritz!WLAN USB Stick N, AR9001U) running
> kernel 6.18.12.  Each patch applies and compiles independently on
> top of the previous ones.


Interesting, do you know what PHY it's really based on? My information was
that it's a AR9160 and as you said Draft-N (that had issues with Radar) and
not a AR92xx-PHY.

And features like the fast channel switching, Radar etc. were never tested/qualified
and that's why they got implemented or as in the case of fast channel switching
had to be removed later. (See the post from Chen about carl9170)

Maybe @QCA. Is Chen still there and can advise which features are OK
and which might not?

Also if you have the knowledge/time: Do you think you could figure out why
the 802.11e (QOS) doesn't work with aggregation (see: __carl9170_get_queue)?
This bugs me to this day.

(Also there's a hack around the package filtering, that is breaking
the device a little bit... But it was necessary for the Intel 4965 Wifis
(AR9170_MAC_RX_CTRL_PASS_TO_HOST see carl9170_set_operating_mode()) .
This is also an unsolved mystery.

Cheers,
Christian

^ permalink raw reply

* Re: [PATCH v2 2/3] remoteproc: qcom_wcnss_iris: add support for WCN3610
From: Dmitry Baryshkov @ 2026-03-13 19:27 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Kerigan Creighton, linux-wireless, loic.poulain, wcn36xx,
	andersson, mathieu.poirier, linux-remoteproc, linux-arm-msm, robh,
	krzk+dt, conor+dt, devicetree, linux-kernel
In-Reply-To: <20260305-crouching-sceptical-spoonbill-fe75fb@quoll>

On Thu, Mar 05, 2026 at 09:06:30AM +0100, Krzysztof Kozlowski wrote:
> On Wed, Mar 04, 2026 at 06:32:52PM -0600, Kerigan Creighton wrote:
> > Add a qcom,wcn3610 compatible string.
> > The WCN3610 shares the same register configuration as the
> > WCN3620, so its configuration is being reused.
> > 
> > Signed-off-by: Kerigan Creighton <kerigancreighton@gmail.com>
> > Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> > ---
> > Changes in v2:
> >  - Move remoteproc compatible string addition to the middle of 
> >    the patch set.
> >  - Add Reviewed-by Dmitry (thanks!)
> > ---
> >  drivers/remoteproc/qcom_wcnss_iris.c | 1 +
> >  1 file changed, 1 insertion(+)
> > 
> > diff --git a/drivers/remoteproc/qcom_wcnss_iris.c b/drivers/remoteproc/qcom_wcnss_iris.c
> > index 2b89b4db6c..e58b59355f 100644
> > --- a/drivers/remoteproc/qcom_wcnss_iris.c
> > +++ b/drivers/remoteproc/qcom_wcnss_iris.c
> > @@ -95,6 +95,7 @@ void qcom_iris_disable(struct qcom_iris *iris)
> >  }
> >  
> >  static const struct of_device_id iris_of_match[] = {
> > +	{ .compatible = "qcom,wcn3610", .data = &wcn3620_data },
> 
> So compatible with wcn3620? Why are you adding it in such case? Drop the
> change and express compatibility or explain lack of it in the bindings
> patch.

I'd say, keep the compatible. It's used in the next patch. But yes, it
might need some epxlanation.

> 
> Best regards,
> Krzysztof
> 

-- 
With best wishes
Dmitry

^ permalink raw reply

* Re: [PATCH 02/61] btrfs: Prefer IS_ERR_OR_NULL over manual NULL check
From: David Sterba @ 2026-03-13 19:22 UTC (permalink / raw)
  To: Philipp Hahn
  Cc: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Chris Mason, David Sterba
In-Reply-To: <20260310-b4-is_err_or_null-v1-2-bd63b656022d@avm.de>

On Tue, Mar 10, 2026 at 12:48:28PM +0100, Philipp Hahn wrote:
> Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
> check.
> 
> IS_ERR_OR_NULL() already uses likely(!ptr) internally. checkpatch does
> not like nesting it:
> > WARNING: nested (un)?likely() calls, IS_ERR_OR_NULL already uses
> > unlikely() internally
> Remove the explicit use of likely().
> 
> Change generated with coccinelle.
> 
> To: Chris Mason <clm@fb.com>
> To: David Sterba <dsterba@suse.com>
> Cc: linux-btrfs@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Signed-off-by: Philipp Hahn <phahn-oss@avm.de>

Added to for-next, we seem to be using IS_ERR_OR_NULL() already in a
few other places so this is makes sense for consistency. Thanks.

^ permalink raw reply

* [RFC PATCH v3 4/4] wifi: rtl8xxxu: Enable AP mode for RTL8188EU
From: Georg Müller @ 2026-03-13 13:53 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313135321.3196688-1-georgmueller@gmx.net>

Allow devices with this driver to be used as a wireless access point.

Signed-off-by: Georg Müller <georgmueller@gmx.net>
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index 607ca62194fc..67fd77944d67 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1867,6 +1867,8 @@ struct rtl8xxxu_fileops rtl8188eu_fops = {
 	.init_reg_pkt_life_time = 1,
 	.gen2_thermal_meter = 1,
 	.max_sec_cam_num = 32,
+	.supports_ap = 1,
+	.max_macid_num = RTL8188E_MAX_MAC_ID_NUM,
 	.adda_1t_init = 0x0b1b25a0,
 	.adda_1t_path_on = 0x0bdb25a0,
 	/*
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v3 3/4] wifi: rtl8xxxu: update max report mac id on station add / remove for 8188e chips
From: Georg Müller @ 2026-03-13 13:53 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313135321.3196688-1-georgmueller@gmx.net>

---
 drivers/net/wireless/realtek/rtl8xxxu/core.c | 23 +++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 5ad23c5c9305..15fc4843edb2 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -3884,6 +3884,15 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv)
 	rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
 }
 
+static u8 rtl8xxxu_max_acquired_macid(struct rtl8xxxu_priv *priv)
+{
+	u8 macid;
+
+	macid = find_last_bit(priv->mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
+
+	return macid;
+}
+
 static u8 rtl8xxxu_acquire_macid(struct rtl8xxxu_priv *priv)
 {
 	u8 macid;
@@ -7499,6 +7508,7 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
 	struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv;
 	struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
 	struct rtl8xxxu_priv *priv = hw->priv;
+	u8 max_mac_id;
 
 	mutex_lock(&priv->sta_mutex);
 	ewma_rssi_init(&sta_info->avg_rssi);
@@ -7510,6 +7520,11 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
 			return -ENOSPC;
 		}
 
+		if (priv->rtl_chip == RTL8188E) {
+			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
+			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
+		}
+
 		rtl8xxxu_refresh_rate_mask(priv, 0, sta, true);
 		priv->fops->report_connect(priv, sta_info->macid, H2C_MACID_ROLE_STA, true);
 	} else {
@@ -7535,10 +7550,16 @@ static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw,
 {
 	struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv;
 	struct rtl8xxxu_priv *priv = hw->priv;
+	u8 max_mac_id;
 
 	mutex_lock(&priv->sta_mutex);
-	if (vif->type == NL80211_IFTYPE_AP)
+	if (vif->type == NL80211_IFTYPE_AP) {
 		rtl8xxxu_release_macid(priv, sta_info->macid);
+		if (priv->rtl_chip == RTL8188E) {
+			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
+			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
+		}
+	}
 	mutex_unlock(&priv->sta_mutex);
 
 	return 0;
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v3 2/4] wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
From: Georg Müller @ 2026-03-13 13:53 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313135321.3196688-1-georgmueller@gmx.net>

convert member ra_info to an array with one entry per mac id. This
allows having different rate control settings per connected station
in ap mode.

The max_macid_num is conservative. The old driver used 32 [1], some
other sources said 64 [2]. I have not enough adapters to test any of the
higher values. Given the usage of this chipset in nano dongles, I think
the 16 might be high enough.

[1] https://github.com/lwfinger/rtl8188eu/blob/f5d1c8df2e2d8b217ea0113bf2cf3e37df8cb716/include/sta_info.h#L28
[2] https://lore.kernel.org/linux-wireless/27e83382-4c84-1841-c428-d1e5143ea20c@gmail.com/

Signed-off-by: Georg Müller <georgmueller@gmx.net>
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 12 ++++++------
 drivers/net/wireless/realtek/rtl8xxxu/core.c  | 19 ++++++++++++++-----
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  3 ++-
 3 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index de2837a91bbe..607ca62194fc 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1468,9 +1468,8 @@ static void rtl8188e_reset_ra_counter(struct rtl8xxxu_ra_info *ra)
 	ra->nsc_down = (n_threshold_high[rate_id] + n_threshold_low[rate_id]) >> 1;
 }
 
-static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
+static void rtl8188e_rate_decision(struct rtl8xxxu_priv *priv, struct rtl8xxxu_ra_info *ra)
 {
-	struct rtl8xxxu_priv *priv = container_of(ra, struct rtl8xxxu_priv, ra_info);
 	const u8 *retry_penalty_idx_0;
 	const u8 *retry_penalty_idx_1;
 	const u8 *retry_penalty_up_idx;
@@ -1669,7 +1668,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 	u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
 	struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
 	struct device *dev = &priv->udev->dev;
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra;
 	u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
 	u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
 	u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
@@ -1688,6 +1687,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 
 	for (macid = 0; macid < items; macid++) {
 		valid = false;
+		ra = &priv->ra_info[macid];
 
 		if (macid < 64)
 			valid = macid_valid & BIT(macid);
@@ -1704,7 +1704,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 
 			if (ra->total > 0) {
 				if (ra->ra_stage < 5)
-					rtl8188e_rate_decision(ra);
+					rtl8188e_rate_decision(priv, ra);
 				else if (ra->ra_stage == 5)
 					rtl8188e_power_training_try_state(ra);
 				else /* ra->ra_stage == 6 */
@@ -1781,7 +1781,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
 			  u32 ramask, u8 rateid, int sgi, int txbw_40mhz,
 			  u8 macid)
 {
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
 
 	ra->rate_id = rateid;
 	ra->rate_mask = ramask;
@@ -1792,7 +1792,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
 
 static void rtl8188e_ra_set_rssi(struct rtl8xxxu_priv *priv, u8 macid, u8 rssi)
 {
-	priv->ra_info.rssi_sta_ra = rssi;
+	priv->ra_info[macid].rssi_sta_ra = rssi;
 }
 
 void rtl8188e_ra_info_init_all(struct rtl8xxxu_ra_info *ra)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 794187d28caa..5ad23c5c9305 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -3921,6 +3921,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 	struct device *dev = &priv->udev->dev;
 	struct rtl8xxxu_fileops *fops = priv->fops;
 	bool macpower;
+	u16 mac_id;
 	int ret;
 	u8 val8;
 	u16 val16;
@@ -4393,9 +4394,16 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 		priv->cfo_tracking.crystal_cap = priv->default_crystal_cap;
 	}
 
-	if (priv->rtl_chip == RTL8188E)
-		rtl8188e_ra_info_init_all(&priv->ra_info);
-
+	if (priv->rtl_chip == RTL8188E)	{
+		priv->ra_info = kmalloc_array(RTL8188E_MAX_MAC_ID_NUM, sizeof(*priv->ra_info), GFP_KERNEL);
+		if (!priv->ra_info) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+		for (mac_id = 0; mac_id < RTL8188E_MAX_MAC_ID_NUM; mac_id++) {
+			rtl8188e_ra_info_init_all(&priv->ra_info[mac_id]);
+		}
+	}
 	set_bit(RTL8XXXU_BC_MC_MACID, priv->mac_id_map);
 	set_bit(RTL8XXXU_BC_MC_MACID1, priv->mac_id_map);
 
@@ -5338,7 +5346,7 @@ rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 {
 	struct rtl8xxxu_priv *priv = hw->priv;
 	struct device *dev = &priv->udev->dev;
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
 	u8 *qc = ieee80211_get_qos_ctl(hdr);
 	u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
 	u32 rate = 0;
@@ -7895,6 +7903,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
 err_set_intfdata:
 	usb_set_intfdata(interface, NULL);
 
+	kfree(priv->ra_info);
 	kfree(priv->fw_data);
 	mutex_destroy(&priv->usb_buf_mutex);
 	mutex_destroy(&priv->syson_indirect_access_mutex);
@@ -7924,7 +7933,7 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface)
 	usb_set_intfdata(interface, NULL);
 
 	dev_info(&priv->udev->dev, "disconnecting\n");
-
+	kfree(priv->ra_info);
 	kfree(priv->fw_data);
 	mutex_destroy(&priv->usb_buf_mutex);
 	mutex_destroy(&priv->syson_indirect_access_mutex);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 4a744b5c1aec..9ce820ff4793 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -76,6 +76,7 @@
 #define RTL_FW_PAGE_SIZE		4096
 #define RTL8XXXU_FIRMWARE_POLL_MAX	1000
 
+#define RTL8188E_MAX_MAC_ID_NUM		16
 #define RTL8723A_CHANNEL_GROUPS		3
 #define RTL8723A_MAX_RF_PATHS		2
 #define RTL8723B_CHANNEL_GROUPS		6
@@ -1915,7 +1916,7 @@ struct rtl8xxxu_priv {
 	struct rtl8xxxu_btcoex bt_coex;
 	struct rtl8xxxu_ra_report ra_report;
 	struct rtl8xxxu_cfo_tracking cfo_tracking;
-	struct rtl8xxxu_ra_info ra_info;
+	struct rtl8xxxu_ra_info *ra_info;
 	u8 dynamic_tx_rpt_timing_counter; // 8188e specific
 
 	bool led_registered;
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v3 1/4] wifi: rtl8xxxu: move dynamic_tx_rpt_timing_counter from ra_info to priv
From: Georg Müller @ 2026-03-13 13:53 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313135321.3196688-1-georgmueller@gmx.net>

dynamic_tx_rpt_timing_counter is not per mac_id, but used across all
mac_ids.
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c    | 8 ++++----
 drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index 766a7a7c7d28..de2837a91bbe 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1550,14 +1550,14 @@ static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
 	}
 
 	if (ra->decision_rate == ra->pre_rate)
-		ra->dynamic_tx_rpt_timing_counter++;
+		priv->dynamic_tx_rpt_timing_counter++;
 	else
-		ra->dynamic_tx_rpt_timing_counter = 0;
+		priv->dynamic_tx_rpt_timing_counter = 0;
 
-	if (ra->dynamic_tx_rpt_timing_counter >= 4) {
+	if (priv->dynamic_tx_rpt_timing_counter >= 4) {
 		/* Rate didn't change 4 times, extend RPT timing */
 		rtl8188e_set_tx_rpt_timing(ra, INCREASE_TIMING);
-		ra->dynamic_tx_rpt_timing_counter = 0;
+		priv->dynamic_tx_rpt_timing_counter = 0;
 	}
 
 	ra->pre_rate = ra->decision_rate;
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 9fb2583ffffc..4a744b5c1aec 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -1756,7 +1756,6 @@ struct rtl8xxxu_ra_info {
 	u16 drop;
 	u16 rpt_time;
 	u16 pre_min_rpt_time;
-	u8 dynamic_tx_rpt_timing_counter;
 	u8 ra_waiting_counter;
 	u8 ra_pending_counter;
 	u8 ra_drop_after_down;
@@ -1917,6 +1916,7 @@ struct rtl8xxxu_priv {
 	struct rtl8xxxu_ra_report ra_report;
 	struct rtl8xxxu_cfo_tracking cfo_tracking;
 	struct rtl8xxxu_ra_info ra_info;
+	u8 dynamic_tx_rpt_timing_counter; // 8188e specific
 
 	bool led_registered;
 	char led_name[32];
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v3 0/4] wifi: rtl8xxxu: implement AP mode for 8188EU
From: Georg Müller @ 2026-03-13 13:53 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller

This series tries to implement AP mode for Realtek RTL8188EU chips.

This is not final. I still have issues connecting to the AP, searching for some
input on what may be the reason or how to further debug issues.

Patch 1 could be picked independently as it shouldn't change anything for the
non-AP mode.

---
Changes in v3:
- fix compile errors caused by testing on machine with older kernel
Changes in v2:
- add patch to move dynamic_tx_rpt_timing_counter from ra_info to priv
- convert ra_info to a dynamic array
- update max report mac id after station add/remove

---
Georg Müller (4):
  wifi: rtl8xxxu: move dynamic_tx_rpt_timing_counter from ra_info to
    priv
  wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
  wifi: rtl8xxxu: update max report mac id on station add / remove for
    8188e chips
  wifi: rtl8xxxu: Enable AP mode for RTL8188EU

 drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 22 +++++-----
 drivers/net/wireless/realtek/rtl8xxxu/core.c  | 41 ++++++++++++++++---
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  5 ++-
 3 files changed, 50 insertions(+), 18 deletions(-)

-- 
2.53.0


^ permalink raw reply

* Re: [RFC PATCH v2 3/4] wifi: rtl8xxxu: update max report mac id on station add / remove for 8188e chips
From: Georg Müller @ 2026-03-13 13:26 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2; +Cc: linux-wireless, linux-kernel
In-Reply-To: <20260313131849.3148013-4-georgmueller@gmx.net>


Am 13.03.26 um 14:18 schrieb Georg Müller:
> ---
>   drivers/net/wireless/realtek/rtl8xxxu/core.c | 22 +++++++++++++++++++-
>   1 file changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
> index ea4dcca9d22a..6d97bb212f75 100644
> --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
> +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
> @@ -3884,6 +3884,15 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv)
>   	rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
>   }
>   
> +static u8 rtl8xxxu_max_acquired_macid(struct rtl8xxxu_priv *priv)
> +{
> +	u8 macid;
> +
> +	macid = find_last_bit(priv->mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
> +
> +	return macid;
> +}
> +
>   static u8 rtl8xxxu_acquire_macid(struct rtl8xxxu_priv *priv)
>   {
>   	u8 macid;
> @@ -7499,6 +7508,7 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
>   	struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv;
>   	struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
>   	struct rtl8xxxu_priv *priv = hw->priv;
> +	u8 max_mac_id;
>   
>   	mutex_lock(&priv->sta_mutex);
>   	ewma_rssi_init(&sta_info->avg_rssi);
> @@ -7510,6 +7520,11 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
>   			return -ENOSPC;
>   		}
>   
> +		if (priv->rtl_chip == RTL8188E) {
> +			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
> +			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
> +		}
> +
>   		rtl8xxxu_refresh_rate_mask(priv, 0, sta, true);
>   		priv->fops->report_connect(priv, sta_info->macid, H2C_MACID_ROLE_STA, true);
>   	} else {
> @@ -7537,8 +7552,13 @@ static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw,
>   	struct rtl8xxxu_priv *priv = hw->priv;

sorry: declaration "u8 max_mac_id;" is missing here. This was lost when copying back from my test VM.

>   	mutex_lock(&priv->sta_mutex);
> -	if (vif->type == NL80211_IFTYPE_AP)
> +	if (vif->type == NL80211_IFTYPE_AP) {
>   		rtl8xxxu_release_macid(priv, sta_info->macid);
> +		if (priv->rtl_chip == RTL8188E) {
> +			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
> +			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
> +		}
> +	}
>   	mutex_unlock(&priv->sta_mutex);
>   
>   	return 0;


^ permalink raw reply

* [RFC PATCH v2 4/4] wifi: rtl8xxxu: Enable AP mode for RTL8188EU
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313131849.3148013-1-georgmueller@gmx.net>

Allow devices with this driver to be used as a wireless access point.

Signed-off-by: Georg Müller <georgmueller@gmx.net>
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index 607ca62194fc..67fd77944d67 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1867,6 +1867,8 @@ struct rtl8xxxu_fileops rtl8188eu_fops = {
 	.init_reg_pkt_life_time = 1,
 	.gen2_thermal_meter = 1,
 	.max_sec_cam_num = 32,
+	.supports_ap = 1,
+	.max_macid_num = RTL8188E_MAX_MAC_ID_NUM,
 	.adda_1t_init = 0x0b1b25a0,
 	.adda_1t_path_on = 0x0bdb25a0,
 	/*
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 3/4] wifi: rtl8xxxu: update max report mac id on station add / remove for 8188e chips
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313131849.3148013-1-georgmueller@gmx.net>

---
 drivers/net/wireless/realtek/rtl8xxxu/core.c | 22 +++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index ea4dcca9d22a..6d97bb212f75 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -3884,6 +3884,15 @@ void rtl8xxxu_init_burst(struct rtl8xxxu_priv *priv)
 	rtl8xxxu_write8(priv, REG_RSV_CTRL, val8);
 }
 
+static u8 rtl8xxxu_max_acquired_macid(struct rtl8xxxu_priv *priv)
+{
+	u8 macid;
+
+	macid = find_last_bit(priv->mac_id_map, RTL8XXXU_MAX_MAC_ID_NUM);
+
+	return macid;
+}
+
 static u8 rtl8xxxu_acquire_macid(struct rtl8xxxu_priv *priv)
 {
 	u8 macid;
@@ -7499,6 +7508,7 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
 	struct rtl8xxxu_sta_info *sta_info = (struct rtl8xxxu_sta_info *)sta->drv_priv;
 	struct rtl8xxxu_vif *rtlvif = (struct rtl8xxxu_vif *)vif->drv_priv;
 	struct rtl8xxxu_priv *priv = hw->priv;
+	u8 max_mac_id;
 
 	mutex_lock(&priv->sta_mutex);
 	ewma_rssi_init(&sta_info->avg_rssi);
@@ -7510,6 +7520,11 @@ static int rtl8xxxu_sta_add(struct ieee80211_hw *hw,
 			return -ENOSPC;
 		}
 
+		if (priv->rtl_chip == RTL8188E) {
+			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
+			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
+		}
+
 		rtl8xxxu_refresh_rate_mask(priv, 0, sta, true);
 		priv->fops->report_connect(priv, sta_info->macid, H2C_MACID_ROLE_STA, true);
 	} else {
@@ -7537,8 +7552,13 @@ static int rtl8xxxu_sta_remove(struct ieee80211_hw *hw,
 	struct rtl8xxxu_priv *priv = hw->priv;
 
 	mutex_lock(&priv->sta_mutex);
-	if (vif->type == NL80211_IFTYPE_AP)
+	if (vif->type == NL80211_IFTYPE_AP) {
 		rtl8xxxu_release_macid(priv, sta_info->macid);
+		if (priv->rtl_chip == RTL8188E) {
+			max_mac_id = rtl8xxxu_max_acquired_macid(priv);
+			rtl8xxxu_write8(priv, REG_TX_REPORT_CTRL + 1, max_mac_id + 1);
+		}
+	}
 	mutex_unlock(&priv->sta_mutex);
 
 	return 0;
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 2/4] wifi: rtl8xxxu: handle rate control for 8188e a per mac_id
From: Georg Müller @ 2026-03-13 13:18 UTC (permalink / raw)
  To: Jes.Sorensen, rtl8821cerfe2
  Cc: linux-wireless, linux-kernel, Georg Müller
In-Reply-To: <20260313131849.3148013-1-georgmueller@gmx.net>

convert member ra_info to an array with one entry per mac id. This
allows having different rate control settings per connected station
in ap mode.

The max_macid_num is conservative. The old driver used 32 [1], some
other sources said 64 [2]. I have not enough adapters to test any of the
higher values. Given the usage of this chipset in nano dongles, I think
the 16 might be high enough.

[1] https://github.com/lwfinger/rtl8188eu/blob/f5d1c8df2e2d8b217ea0113bf2cf3e37df8cb716/include/sta_info.h#L28
[2] https://lore.kernel.org/linux-wireless/27e83382-4c84-1841-c428-d1e5143ea20c@gmail.com/

Signed-off-by: Georg Müller <georgmueller@gmx.net>
---
 drivers/net/wireless/realtek/rtl8xxxu/8188e.c | 12 ++++++------
 drivers/net/wireless/realtek/rtl8xxxu/core.c  | 19 ++++++++++++++-----
 .../net/wireless/realtek/rtl8xxxu/rtl8xxxu.h  |  3 ++-
 3 files changed, 22 insertions(+), 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
index de2837a91bbe..607ca62194fc 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/8188e.c
@@ -1468,9 +1468,8 @@ static void rtl8188e_reset_ra_counter(struct rtl8xxxu_ra_info *ra)
 	ra->nsc_down = (n_threshold_high[rate_id] + n_threshold_low[rate_id]) >> 1;
 }
 
-static void rtl8188e_rate_decision(struct rtl8xxxu_ra_info *ra)
+static void rtl8188e_rate_decision(struct rtl8xxxu_priv *priv, struct rtl8xxxu_ra_info *ra)
 {
-	struct rtl8xxxu_priv *priv = container_of(ra, struct rtl8xxxu_priv, ra_info);
 	const u8 *retry_penalty_idx_0;
 	const u8 *retry_penalty_idx_1;
 	const u8 *retry_penalty_up_idx;
@@ -1669,7 +1668,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 	u32 *_rx_desc = (u32 *)(skb->data - sizeof(struct rtl8xxxu_rxdesc16));
 	struct rtl8xxxu_rxdesc16 *rx_desc = (struct rtl8xxxu_rxdesc16 *)_rx_desc;
 	struct device *dev = &priv->udev->dev;
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra;
 	u32 tx_rpt_len = rx_desc->pktlen & 0x3ff;
 	u32 items = tx_rpt_len / TX_RPT2_ITEM_SIZE;
 	u64 macid_valid = ((u64)_rx_desc[5] << 32) | _rx_desc[4];
@@ -1688,6 +1687,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 
 	for (macid = 0; macid < items; macid++) {
 		valid = false;
+		ra = &priv->ra_info[macid];
 
 		if (macid < 64)
 			valid = macid_valid & BIT(macid);
@@ -1704,7 +1704,7 @@ void rtl8188e_handle_ra_tx_report2(struct rtl8xxxu_priv *priv, struct sk_buff *s
 
 			if (ra->total > 0) {
 				if (ra->ra_stage < 5)
-					rtl8188e_rate_decision(ra);
+					rtl8188e_rate_decision(priv, ra);
 				else if (ra->ra_stage == 5)
 					rtl8188e_power_training_try_state(ra);
 				else /* ra->ra_stage == 6 */
@@ -1781,7 +1781,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
 			  u32 ramask, u8 rateid, int sgi, int txbw_40mhz,
 			  u8 macid)
 {
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
 
 	ra->rate_id = rateid;
 	ra->rate_mask = ramask;
@@ -1792,7 +1792,7 @@ rtl8188e_update_rate_mask(struct rtl8xxxu_priv *priv,
 
 static void rtl8188e_ra_set_rssi(struct rtl8xxxu_priv *priv, u8 macid, u8 rssi)
 {
-	priv->ra_info.rssi_sta_ra = rssi;
+	priv->ra_info[macid].rssi_sta_ra = rssi;
 }
 
 void rtl8188e_ra_info_init_all(struct rtl8xxxu_ra_info *ra)
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c
index 794187d28caa..ea4dcca9d22a 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c
@@ -3921,6 +3921,7 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 	struct device *dev = &priv->udev->dev;
 	struct rtl8xxxu_fileops *fops = priv->fops;
 	bool macpower;
+	u16 mac_id;
 	int ret;
 	u8 val8;
 	u16 val16;
@@ -4393,9 +4394,16 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw)
 		priv->cfo_tracking.crystal_cap = priv->default_crystal_cap;
 	}
 
-	if (priv->rtl_chip == RTL8188E)
-		rtl8188e_ra_info_init_all(&priv->ra_info);
-
+	if (priv->rtl_chip == RTL8188E)	{
+		priv->ra_info = kmalloc_array(RTL8188E_MAX_MAC_ID_NUM, sizeof(*priv->ra_info));
+		if (!priv->ra_info) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+		for (mac_id = 0; mac_id < RTL8188E_MAX_MAC_ID_NUM; mac_id++) {
+			rtl8188e_ra_info_init_all(&priv->ra_info[mac_id]);
+		}
+	}
 	set_bit(RTL8XXXU_BC_MC_MACID, priv->mac_id_map);
 	set_bit(RTL8XXXU_BC_MC_MACID1, priv->mac_id_map);
 
@@ -5338,7 +5346,7 @@ rtl8xxxu_fill_txdesc_v3(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr,
 {
 	struct rtl8xxxu_priv *priv = hw->priv;
 	struct device *dev = &priv->udev->dev;
-	struct rtl8xxxu_ra_info *ra = &priv->ra_info;
+	struct rtl8xxxu_ra_info *ra = &priv->ra_info[macid];
 	u8 *qc = ieee80211_get_qos_ctl(hdr);
 	u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
 	u32 rate = 0;
@@ -7895,6 +7903,7 @@ static int rtl8xxxu_probe(struct usb_interface *interface,
 err_set_intfdata:
 	usb_set_intfdata(interface, NULL);
 
+	kfree(priv->ra_info);
 	kfree(priv->fw_data);
 	mutex_destroy(&priv->usb_buf_mutex);
 	mutex_destroy(&priv->syson_indirect_access_mutex);
@@ -7924,7 +7933,7 @@ static void rtl8xxxu_disconnect(struct usb_interface *interface)
 	usb_set_intfdata(interface, NULL);
 
 	dev_info(&priv->udev->dev, "disconnecting\n");
-
+	kfree(priv->ra_info);
 	kfree(priv->fw_data);
 	mutex_destroy(&priv->usb_buf_mutex);
 	mutex_destroy(&priv->syson_indirect_access_mutex);
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
index 4a744b5c1aec..9ce820ff4793 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h
@@ -76,6 +76,7 @@
 #define RTL_FW_PAGE_SIZE		4096
 #define RTL8XXXU_FIRMWARE_POLL_MAX	1000
 
+#define RTL8188E_MAX_MAC_ID_NUM		16
 #define RTL8723A_CHANNEL_GROUPS		3
 #define RTL8723A_MAX_RF_PATHS		2
 #define RTL8723B_CHANNEL_GROUPS		6
@@ -1915,7 +1916,7 @@ struct rtl8xxxu_priv {
 	struct rtl8xxxu_btcoex bt_coex;
 	struct rtl8xxxu_ra_report ra_report;
 	struct rtl8xxxu_cfo_tracking cfo_tracking;
-	struct rtl8xxxu_ra_info ra_info;
+	struct rtl8xxxu_ra_info *ra_info;
 	u8 dynamic_tx_rpt_timing_counter; // 8188e specific
 
 	bool led_registered;
-- 
2.53.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox