* [PATCH wireless-next v4 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-28 12:30 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This is based on Draft P802.11bn_D1.2, but that's still very
incomplete, so don't handle a number of things and make some
local decisions such as using 40 bits for MAC capabilities
and 8 bits for PHY capabilities.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v4:
- update to D1.2, including DBE in UHR capabilities
- fold in suggestions from Pablo
---
include/linux/ieee80211-uhr.h | 215 ++++++++++++++++++++++++++++++++++
include/linux/ieee80211.h | 33 +++++-
2 files changed, 246 insertions(+), 2 deletions(-)
create mode 100644 include/linux/ieee80211-uhr.h
diff --git a/include/linux/ieee80211-uhr.h b/include/linux/ieee80211-uhr.h
new file mode 100644
index 000000000000..06556867b004
--- /dev/null
+++ b/include/linux/ieee80211-uhr.h
@@ -0,0 +1,215 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * IEEE 802.11 UHR definitions
+ *
+ * Copyright (c) 2025-2026 Intel Corporation
+ */
+#ifndef LINUX_IEEE80211_UHR_H
+#define LINUX_IEEE80211_UHR_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define IEEE80211_UHR_OPER_PARAMS_DPS_ENA 0x0001
+#define IEEE80211_UHR_OPER_PARAMS_NPCA_ENA 0x0002
+#define IEEE80211_UHR_OPER_PARAMS_DBE_ENA 0x0004
+#define IEEE80211_UHR_OPER_PARAMS_PEDCA_ENA 0x0008
+
+struct ieee80211_uhr_oper {
+ __le16 params;
+ u8 basic_mcs_nss_set[4];
+ u8 variable[];
+} __packed;
+
+#define IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS 0x0000000F
+#define IEEE80211_UHR_NPCA_PARAMS_MIN_DUR_THRESH 0x000000F0
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_DELAY 0x00003F00
+#define IEEE80211_UHR_NPCA_PARAMS_SWITCH_BACK_DELAY 0x000FC000
+#define IEEE80211_UHR_NPCA_PARAMS_INIT_QSRC 0x00300000
+#define IEEE80211_UHR_NPCA_PARAMS_MOPLEN 0x00400000
+#define IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES 0x00800000
+
+struct ieee80211_uhr_npca_info {
+ __le32 params;
+ __le16 dis_subch_bmap[];
+} __packed;
+
+static inline bool ieee80211_uhr_oper_size_ok(const u8 *data, u8 len,
+ bool beacon)
+{
+ const struct ieee80211_uhr_oper *oper = (const void *)data;
+ u8 needed = sizeof(*oper);
+
+ if (len < needed)
+ return false;
+
+ /* nothing else present in beacons */
+ if (beacon)
+ return true;
+
+ /* FIXME: DPS, DBE, P-EDCA (consider order, also relative to NPCA) */
+
+ if (oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)) {
+ const struct ieee80211_uhr_npca_info *npca =
+ (const void *)oper->variable;
+
+ needed += sizeof(*npca);
+
+ if (len < needed)
+ return false;
+
+ if (npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES))
+ needed += sizeof(npca->dis_subch_bmap[0]);
+ }
+
+ return len >= needed;
+}
+
+/*
+ * Note: cannot call this on the element coming from a beacon,
+ * must ensure ieee80211_uhr_oper_size_ok(..., false) first
+ */
+static inline const struct ieee80211_uhr_npca_info *
+ieee80211_uhr_npca_info(const struct ieee80211_uhr_oper *oper)
+{
+ if (!(oper->params & cpu_to_le16(IEEE80211_UHR_OPER_PARAMS_NPCA_ENA)))
+ return NULL;
+
+ /* FIXME: DPS */
+
+ return (const void *)oper->variable;
+}
+
+static inline const __le16 *
+ieee80211_uhr_npca_dis_subch_bitmap(const struct ieee80211_uhr_oper *oper)
+{
+ const struct ieee80211_uhr_npca_info *npca;
+
+ npca = ieee80211_uhr_npca_info(oper);
+ if (!npca)
+ return NULL;
+ if (!(npca->params & cpu_to_le32(IEEE80211_UHR_NPCA_PARAMS_DIS_SUBCH_BMAP_PRES)))
+ return NULL;
+ return npca->dis_subch_bmap;
+}
+
+#define IEEE80211_UHR_MAC_CAP0_DPS_SUPP 0x01
+#define IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP 0x02
+#define IEEE80211_UHR_MAC_CAP0_DPS_AP_STATIC_HCM_SUPP 0x04
+#define IEEE80211_UHR_MAC_CAP0_NPCA_SUPP 0x10
+#define IEEE80211_UHR_MAC_CAP0_ENH_BSR_SUPP 0x20
+#define IEEE80211_UHR_MAC_CAP0_ADD_MAP_TID_SUPP 0x40
+#define IEEE80211_UHR_MAC_CAP0_EOTSP_SUPP 0x80
+
+#define IEEE80211_UHR_MAC_CAP1_DSO_SUPP 0x01
+#define IEEE80211_UHR_MAC_CAP1_PEDCA_SUPP 0x02
+#define IEEE80211_UHR_MAC_CAP1_DBE_SUPP 0x04
+#define IEEE80211_UHR_MAC_CAP1_UL_LLI_SUPP 0x08
+#define IEEE80211_UHR_MAC_CAP1_P2P_LLI_SUPP 0x10
+#define IEEE80211_UHR_MAC_CAP1_PUO_SUPP 0x20
+#define IEEE80211_UHR_MAC_CAP1_AP_PUO_SUPP 0x40
+#define IEEE80211_UHR_MAC_CAP1_DUO_SUPP 0x80
+
+#define IEEE80211_UHR_MAC_CAP2_OMC_UL_MU_DIS_RX_SUPP 0x01
+#define IEEE80211_UHR_MAC_CAP2_AOM_SUPP 0x02
+#define IEEE80211_UHR_MAC_CAP2_IFCS_LOC_SUPP 0x04
+#define IEEE80211_UHR_MAC_CAP2_UHR_TRS_SUPP 0x08
+#define IEEE80211_UHR_MAC_CAP2_TXSPG_SUPP 0x10
+#define IEEE80211_UHR_MAC_CAP2_TXOP_RET_IN_TXSPG 0x20
+#define IEEE80211_UHR_MAC_CAP2_UHR_OM_PU_TO_LOW 0xC0
+
+#define IEEE80211_UHR_MAC_CAP3_UHR_OM_PU_TO_HIGH 0x03
+#define IEEE80211_UHR_MAC_CAP3_PARAM_UPD_ADV_NOTIF_INTV 0x1C
+#define IEEE80211_UHR_MAC_CAP3_UPD_IND_TIM_INTV_LOW 0xE0
+
+#define IEEE80211_UHR_MAC_CAP4_UPD_IND_TIM_INTV_HIGH 0x03
+#define IEEE80211_UHR_MAC_CAP4_BOUNDED_ESS 0x04
+#define IEEE80211_UHR_MAC_CAP4_BTM_ASSURANCE 0x08
+#define IEEE80211_UHR_MAC_CAP4_CO_BF_SUPP 0x10
+
+#define IEEE80211_UHR_MAC_CAP_DBE_MAX_BW 0x07
+#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES 0x08
+#define IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES 0x10
+
+struct ieee80211_uhr_capa_mac {
+ u8 mac_cap[5];
+} __packed;
+
+struct ieee80211_uhr_capa {
+ struct ieee80211_uhr_capa_mac mac;
+ /* DBE, PHY capabilities */
+ u8 variable[];
+} __packed;
+
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_LE80 0x01
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_LE80 0x02
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_160 0x04
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_160 0x08
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_SND_NDP_320 0x10
+#define IEEE80211_UHR_PHY_CAP_MAX_NSS_RX_DL_MU_320 0x20
+#define IEEE80211_UHR_PHY_CAP_ELR_RX 0x40
+#define IEEE80211_UHR_PHY_CAP_ELR_TX 0x80
+
+struct ieee80211_uhr_capa_phy {
+ u8 cap;
+} __packed;
+
+static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len)
+{
+ const struct ieee80211_uhr_capa *cap = (const void *)data;
+ size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_capa_phy);
+
+ if (len < needed)
+ return false;
+
+ if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
+ u8 dbe;
+
+ needed += 1;
+ if (len < needed)
+ return false;
+
+ dbe = cap->variable[0];
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
+ needed += 24;
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+ needed += 24;
+ }
+
+ return len >= needed;
+}
+
+static inline const struct ieee80211_uhr_capa_phy *
+ieee80211_uhr_phy_cap(const struct ieee80211_uhr_capa *cap)
+{
+ u8 offs = 0;
+
+ if (cap->mac.mac_cap[1] & IEEE80211_UHR_MAC_CAP1_DBE_SUPP) {
+ u8 dbe = cap->variable[0];
+
+ offs += 1;
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_160_PRES)
+ offs += 24;
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+ offs += 24;
+ }
+
+ return (const void *)&cap->variable[offs];
+}
+
+#define IEEE80211_SMD_INFO_CAPA_DL_DATA_FWD 0x01
+#define IEEE80211_SMD_INFO_CAPA_MAX_NUM_PREP 0x0E
+#define IEEE80211_SMD_INFO_CAPA_TYPE 0x10
+#define IEEE80211_SMD_INFO_CAPA_PTK_PER_AP_MLD 0x20
+
+struct ieee80211_smd_info {
+ u8 id[ETH_ALEN];
+ u8 capa;
+ __le16 timeout;
+} __packed;
+
+#endif /* LINUX_IEEE80211_UHR_H */
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index fbde215c25aa..82d797be95b9 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -9,7 +9,7 @@
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2013 - 2014 Intel Mobile Communications GmbH
* Copyright (c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright (c) 2018 - 2025 Intel Corporation
+ * Copyright (c) 2018 - 2026 Intel Corporation
*/
#ifndef LINUX_IEEE80211_H
@@ -1200,8 +1200,9 @@ struct ieee80211_mgmt {
#define BSS_MEMBERSHIP_SELECTOR_SAE_H2E 123
#define BSS_MEMBERSHIP_SELECTOR_HE_PHY 122
#define BSS_MEMBERSHIP_SELECTOR_EHT_PHY 121
+#define BSS_MEMBERSHIP_SELECTOR_UHR_PHY 120
-#define BSS_MEMBERSHIP_SELECTOR_MIN BSS_MEMBERSHIP_SELECTOR_EHT_PHY
+#define BSS_MEMBERSHIP_SELECTOR_MIN BSS_MEMBERSHIP_SELECTOR_UHR_PHY
/* mgmt header + 1 byte category code */
#define IEEE80211_MIN_ACTION_SIZE offsetof(struct ieee80211_mgmt, u.action.u)
@@ -1802,6 +1803,15 @@ enum ieee80211_eid_ext {
WLAN_EID_EXT_BANDWIDTH_INDICATION = 135,
WLAN_EID_EXT_KNOWN_STA_IDENTIFCATION = 136,
WLAN_EID_EXT_NON_AP_STA_REG_CON = 137,
+ WLAN_EID_EXT_UHR_OPER = 151,
+ WLAN_EID_EXT_UHR_CAPA = 152,
+ WLAN_EID_EXT_MACP = 153,
+ WLAN_EID_EXT_SMD = 154,
+ WLAN_EID_EXT_BSS_SMD_TRANS_PARAMS = 155,
+ WLAN_EID_EXT_CHAN_USAGE = 156,
+ WLAN_EID_EXT_UHR_MODE_CHG = 157,
+ WLAN_EID_EXT_UHR_PARAM_UPD = 158,
+ WLAN_EID_EXT_TXPI = 159,
};
/* Action category code */
@@ -2745,6 +2755,22 @@ static inline bool for_each_element_completed(const struct element *element,
#define WLAN_RSNX_CAPA_PROTECTED_TWT BIT(4)
#define WLAN_RSNX_CAPA_SAE_H2E BIT(5)
+/* EBPCC = Enhanced BSS Parameter Change Count */
+#define IEEE80211_ENH_CRIT_UPD_EBPCC 0x0F
+#define IEEE80211_ENH_CRIT_UPD_TYPE 0x70
+#define IEEE80211_ENH_CRIT_UPD_TYPE_NO_UHR 0
+#define IEEE80211_ENH_CRIT_UPD_TYPE_UHR 1
+#define IEEE80211_ENH_CRIT_UPD_ALL 0x80
+
+/**
+ * struct ieee80211_enh_crit_upd - enhanced critical update (UHR)
+ * @v: value of the enhanced critical update data,
+ * see %IEEE80211_ENH_CRIT_UPD_* to parse the bits
+ */
+struct ieee80211_enh_crit_upd {
+ u8 v;
+} __packed;
+
/*
* reduced neighbor report, based on Draft P802.11ax_D6.1,
* section 9.4.2.170 and accepted contributions.
@@ -2763,6 +2789,7 @@ static inline bool for_each_element_completed(const struct element *element,
#define IEEE80211_RNR_TBTT_PARAMS_COLOC_ESS 0x10
#define IEEE80211_RNR_TBTT_PARAMS_PROBE_ACTIVE 0x20
#define IEEE80211_RNR_TBTT_PARAMS_COLOC_AP 0x40
+#define IEEE80211_RNR_TBTT_PARAMS_SAME_SMD 0x80
#define IEEE80211_RNR_TBTT_PARAMS_PSD_NO_LIMIT 127
#define IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED -128
@@ -2815,12 +2842,14 @@ struct ieee80211_tbtt_info_ge_11 {
u8 bss_params;
s8 psd_20;
struct ieee80211_rnr_mld_params mld_params;
+ struct ieee80211_enh_crit_upd enh_crit_upd;
} __packed;
#include "ieee80211-ht.h"
#include "ieee80211-vht.h"
#include "ieee80211-he.h"
#include "ieee80211-eht.h"
+#include "ieee80211-uhr.h"
#include "ieee80211-mesh.h"
#include "ieee80211-s1g.h"
#include "ieee80211-p2p.h"
--
2.52.0
^ permalink raw reply related
* Re: [PATCH wireless-next v3] wifi: mac80211: Add eMLSR/eMLMR action frame parsing support
From: Johannes Berg @ 2026-01-28 11:50 UTC (permalink / raw)
To: Lorenzo Bianconi, Ryder Lee, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: linux-wireless, Felix Fietkau, Shayne Chen, Christian Marangi,
linux-mediatek, linux-arm-kernel
In-Reply-To: <20260128-mac80211-emlsr-v3-1-473b0c1d9cc4@kernel.org>
I probably missed this the last time already:
> + if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) ||
> + (control & IEEE80211_EML_CTRL_EMLMR_MODE)) {
> + eml_params.link_bitmap = get_unaligned_le16(ptr);
> + if (eml_params.link_bitmap &&
Surely 0 is an invalid bitmap? To disable it, you send a frame without
IEEE80211_EML_CTRL_EMLSR_MODE or IEEE80211_EML_CTRL_EMLMR_MODE set, so I
don't see how an empty bitmap would be valid?
> + !(eml_params.link_bitmap & sdata->vif.active_links))
That's the wrong check, I think? It only checks that you have any
overlap, but we need all bits to be valid, i.e. only
(link_bitmap & active_links) == link_bitmap
is valid, no?
> + return;
> + }
> +
> + if (drv_set_eml_op_mode(sdata, &sta->sta, &eml_params))
> + return;
> +
> + ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len);
IMHO it would make more sense to pass "ptr, opt_len" rather than "mgmt,
opt_len", since that's just what gets copied and then the callee doesn't
need to dig it out.
johannes
^ permalink raw reply
* [PATCH wireless-next v3] wifi: mac80211: Add eMLSR/eMLMR action frame parsing support
From: Lorenzo Bianconi @ 2026-01-28 11:17 UTC (permalink / raw)
To: Johannes Berg, Ryder Lee, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: linux-wireless, Felix Fietkau, Shayne Chen, Lorenzo Bianconi,
Christian Marangi, linux-mediatek, linux-arm-kernel
Introduce support in AP mode for parsing of the Operating Mode Notification
frame sent by the client to enable/disable MLO eMLSR or eMLMR if supported
by both the AP and the client.
Add drv_set_eml_op_mode mac80211 callback in order to configure underlay
driver with eMLSR/eMLMR info.
Tested-by: Christian Marangi <ansuelsmth@gmail.com>
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
---
Introduce support in AP mode for parsing of the Operating Mode Notification
frame sent by the client to enable/disable MLO eMLSR or eMLMR if supported
by both the AP and the client.
Add drv_set_eml_op_mode mac80211 callback in order to configure underlay
driver with eMLSR info (control and bitmap).
Implement drv_set_eml_op_mode callback for MT7996 driver.
---
Changes in v3:
- Get rid of ieee80211_get_emlsr_pad_delay_update and
ieee80211_get_emlsr_trans_delay_update routines and add sanity checks
for pad_delay/trans_delay directly in ieee80211_rx_eml_op_mode_notif()
- Add mcs_map sanity checks.
- Add ieee80211_eml_params struct kernel-doc.
- Get rid of emlsr_pad_delay/emlsr_trans_delay in ieee80211_eml_params
struct and rely on ieee80211_sta fields.
- Improve sanity checks.
- Remove mt7996 patch from the series.
- Link to v2: https://lore.kernel.org/r/20260125-mac80211-emlsr-v2-0-466329d61c88@kernel.org
Changes in v2:
- Improve sanity check against device EML capabilities
- Squash patch 1/2 and 2/2
- Validate link_bitmap with vif->active_links
- Introduce ieee80211_eml_params struct as containe for EML info to pass
to the underlay driver.
- Pass padding_delay and transition_delay to the underlay driver.
- Implement drv_set_eml_op_mode callback for MT7996 driver.
- Link to v2: https://lore.kernel.org/r/20260122-mac80211-emlsr-v1-0-f0e43bb6d95a@kernel.org
---
include/linux/ieee80211-eht.h | 11 +++
include/linux/ieee80211.h | 6 ++
include/net/mac80211.h | 32 ++++++++
net/mac80211/driver-ops.h | 21 +++++
net/mac80211/eht.c | 175 ++++++++++++++++++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 2 +
net/mac80211/iface.c | 10 ++-
net/mac80211/rx.c | 8 ++
net/mac80211/trace.h | 32 ++++++++
9 files changed, 296 insertions(+), 1 deletion(-)
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index f9782e46c5e5241bccc84c3bbc18b1cc9ec1879c..11105528a2c60d8351a5c9be04fcb714878601fe 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -558,6 +558,17 @@ struct ieee80211_mle_tdls_common_info {
#define IEEE80211_MLC_PRIO_ACCESS_PRES_AP_MLD_MAC_ADDR 0x0010
+#define IEEE80211_EML_CTRL_EMLSR_MODE BIT(0)
+#define IEEE80211_EML_CTRL_EMLMR_MODE BIT(1)
+#define IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE BIT(2)
+#define IEEE80211_EML_CTRL_INDEV_COEX_ACT BIT(3)
+
+#define IEEE80211_EML_EMLSR_PAD_DELAY 0x07
+#define IEEE80211_EML_EMLSR_TRANS_DELAY 0x38
+
+#define IEEE80211_EML_EMLMR_RX_MCS_MAP 0xf0
+#define IEEE80211_EML_EMLMR_TX_MCS_MAP 0x0f
+
/* no fixed fields in PRIO_ACCESS */
/**
diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h
index fbde215c25aa79efd339aa530896a29dbb1a8ff8..f2c6f34f39f24ce59cbb2650f70e898d24d12901 100644
--- a/include/linux/ieee80211.h
+++ b/include/linux/ieee80211.h
@@ -1186,6 +1186,12 @@ struct ieee80211_mgmt {
u8 action_code;
u8 variable[];
} __packed epcs;
+ struct {
+ u8 action_code;
+ u8 dialog_token;
+ u8 control;
+ u8 variable[];
+ } __packed eml_omn;
} u;
} __packed action;
DECLARE_FLEX_ARRAY(u8, body); /* Generic frame body */
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 36ae7fe9ddf35190921f4fee0fe3294418007a56..388076b1b17c2560756deb2966903e21049205ac 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1902,6 +1902,31 @@ enum ieee80211_offload_flags {
IEEE80211_OFFLOAD_DECAP_ENABLED = BIT(2),
};
+/**
+ * struct ieee80211_eml_params - EHT Operating mode notification parameters
+ *
+ * EML Operating mode notification parameters received in the Operating mode
+ * notification frame. This struct is used as a container to pass the info to
+ * the underlay driver.
+ *
+ * @link_id: the link ID where the Operating mode notification frame has been
+ * received.
+ * @control: EML control field defined in P802.11be section 9.4.1.76.
+ * @link_bitmap: eMLSR/eMLMR enabled links defined in P802.11be
+ * section 9.4.1.76.
+ * @emlmr_mcs_map_count: eMLMR number of valid mcs_map_bw fields according to
+ * P802.11be section 9.4.1.76 (valid if eMLMR mode control bit is set).
+ * @emlmr_mcs_map_bw: eMLMR supported MCS and NSS set subfileds defined in
+ * P802.11be section 9.4.1.76 (valid if eMLMR mode control bit is set).
+ */
+struct ieee80211_eml_params {
+ u8 link_id;
+ u8 control;
+ u16 link_bitmap;
+ u8 emlmr_mcs_map_count;
+ u8 emlmr_mcs_map_bw[9];
+};
+
/**
* struct ieee80211_vif_cfg - interface configuration
* @assoc: association status
@@ -4513,6 +4538,9 @@ struct ieee80211_prep_tx_info {
* interface with the specified type would be added, and thus drivers that
* implement this callback need to handle such cases. The type is the full
* &enum nl80211_iftype.
+ * @set_eml_op_mode: Configure eMLSR/eMLMR operation mode in the underlay
+ * driver according to the parameter received in the EML Operating mode
+ * notification frame.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@@ -4908,6 +4936,10 @@ struct ieee80211_ops {
struct ieee80211_neg_ttlm *ttlm);
void (*prep_add_interface)(struct ieee80211_hw *hw,
enum nl80211_iftype type);
+ int (*set_eml_op_mode)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_eml_params *eml_params);
};
/**
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 55105d238d6bc5963eb2863575805bee72c42399..51bf3c7822a76007e6ec59fb70e97fa50d43632b 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1772,4 +1772,25 @@ drv_prep_add_interface(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline int drv_set_eml_op_mode(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct ieee80211_eml_params *eml_params)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = -EOPNOTSUPP;
+
+ might_sleep();
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ trace_drv_set_eml_op_mode(local, sdata, sta, eml_params->link_id,
+ eml_params->control,
+ eml_params->link_bitmap);
+ if (local->ops->set_eml_op_mode)
+ ret = local->ops->set_eml_op_mode(&local->hw, &sdata->vif,
+ sta, eml_params);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c
index fd41046e3b681b753e6cc7ddf82046e4bc5df9b3..68c6908f0ff8b5a3ab7d8d08cb0cb05f9769274c 100644
--- a/net/mac80211/eht.c
+++ b/net/mac80211/eht.c
@@ -5,6 +5,7 @@
* Copyright(c) 2021-2025 Intel Corporation
*/
+#include "driver-ops.h"
#include "ieee80211_i.h"
void
@@ -102,3 +103,177 @@ ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
}
+
+static void
+ieee80211_send_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *req, int opt_len)
+{
+ int len = offsetofend(struct ieee80211_mgmt, u.action.u.eml_omn);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_mgmt *mgmt;
+ struct sk_buff *skb;
+
+ len += opt_len; /* optional len */
+ skb = dev_alloc_skb(local->tx_headroom + len);
+ if (!skb)
+ return;
+
+ skb_reserve(skb, local->tx_headroom);
+ mgmt = skb_put_zero(skb, len);
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_ACTION);
+ memcpy(mgmt->da, req->sa, ETH_ALEN);
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
+ memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN);
+
+ mgmt->u.action.category = WLAN_CATEGORY_PROTECTED_EHT;
+ mgmt->u.action.u.eml_omn.action_code =
+ WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF;
+ mgmt->u.action.u.eml_omn.dialog_token =
+ req->u.action.u.eml_omn.dialog_token;
+ mgmt->u.action.u.eml_omn.control = req->u.action.u.eml_omn.control &
+ ~(IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE |
+ IEEE80211_EML_CTRL_INDEV_COEX_ACT);
+ /* Copy optional fields from the received notification frame */
+ memcpy(mgmt->u.action.u.eml_omn.variable,
+ req->u.action.u.eml_omn.variable, opt_len);
+
+ ieee80211_tx_skb(sdata, skb);
+}
+
+void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ int len = offsetofend(struct ieee80211_mgmt, u.action.u.eml_omn);
+ enum nl80211_iftype type = ieee80211_vif_type_p2p(&sdata->vif);
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ const struct wiphy_iftype_ext_capab *ift_ext_capa;
+ struct ieee80211_mgmt *mgmt = (void *)skb->data;
+ struct ieee80211_local *local = sdata->local;
+ u8 control = mgmt->u.action.u.eml_omn.control;
+ u8 *ptr = mgmt->u.action.u.eml_omn.variable;
+ struct ieee80211_eml_params eml_params = {
+ .link_id = status->link_id,
+ };
+ struct sta_info *sta;
+ int opt_len = 0;
+
+ if (!ieee80211_vif_is_mld(&sdata->vif))
+ return;
+
+ /* eMLSR and eMLMR can't be enabled at the same time */
+ if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) &&
+ (control & IEEE80211_EML_CTRL_EMLMR_MODE))
+ return;
+
+ if ((control & IEEE80211_EML_CTRL_EMLMR_MODE) &&
+ (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE))
+ return;
+
+ ift_ext_capa = cfg80211_get_iftype_ext_capa(local->hw.wiphy, type);
+ if (!ift_ext_capa)
+ return;
+
+ if (!status->link_valid)
+ return;
+
+ sta = sta_info_get_bss(sdata, mgmt->sa);
+ if (!sta)
+ return;
+
+ if (control & IEEE80211_EML_CTRL_EMLSR_MODE) {
+ u8 emlsr_param_update_len;
+
+ if (!(ift_ext_capa->eml_capabilities &
+ IEEE80211_EML_CAP_EMLSR_SUPP))
+ return;
+
+ opt_len += sizeof(__le16); /* eMLSR link_bitmap */
+ /* eMLSR param update field is not part of Notfication frame
+ * sent by the AP to client so account it separately.
+ */
+ emlsr_param_update_len =
+ !!(control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE);
+
+ if (skb->len < len + opt_len + emlsr_param_update_len)
+ return;
+
+ if (control & IEEE80211_EML_CTRL_EMLSR_PARAM_UPDATE) {
+ u8 pad_delay, trans_delay;
+
+ pad_delay = u8_get_bits(ptr[2],
+ IEEE80211_EML_EMLSR_PAD_DELAY);
+ if (pad_delay >
+ IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US)
+ return;
+
+ trans_delay = u8_get_bits(ptr[2],
+ IEEE80211_EML_EMLSR_TRANS_DELAY);
+ if (trans_delay >
+ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US)
+ return;
+
+ /* Update sta padding and transition delay */
+ sta->sta.eml_cap =
+ u8_replace_bits(sta->sta.eml_cap,
+ pad_delay,
+ IEEE80211_EML_CAP_EMLSR_PADDING_DELAY);
+ sta->sta.eml_cap =
+ u8_replace_bits(sta->sta.eml_cap,
+ trans_delay,
+ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY);
+ }
+ }
+
+ if (control & IEEE80211_EML_CTRL_EMLMR_MODE) {
+ u8 mcs_map_size;
+ int i;
+
+ if (!(ift_ext_capa->eml_capabilities &
+ IEEE80211_EML_CAP_EMLMR_SUPPORT))
+ return;
+
+ opt_len += sizeof(__le16); /* eMLMR link_bitmap */
+ opt_len++; /* eMLMR mcs_map_count */
+ if (skb->len < len + opt_len)
+ return;
+
+ eml_params.emlmr_mcs_map_count = ptr[2];
+ if (eml_params.emlmr_mcs_map_count > 2)
+ return;
+
+ mcs_map_size = 3 * (1 + eml_params.emlmr_mcs_map_count);
+ opt_len += mcs_map_size;
+ if (skb->len < len + opt_len)
+ return;
+
+ for (i = 0; i < mcs_map_size; i++) {
+ u8 rx_mcs, tx_mcs;
+
+ rx_mcs = u8_get_bits(ptr[3 + i],
+ IEEE80211_EML_EMLMR_RX_MCS_MAP);
+ if (rx_mcs > 8)
+ return;
+
+ tx_mcs = u8_get_bits(ptr[3 + i],
+ IEEE80211_EML_EMLMR_TX_MCS_MAP);
+ if (tx_mcs > 8)
+ return;
+ }
+
+ memcpy(eml_params.emlmr_mcs_map_bw, &ptr[3], mcs_map_size);
+ }
+
+ if ((control & IEEE80211_EML_CTRL_EMLSR_MODE) ||
+ (control & IEEE80211_EML_CTRL_EMLMR_MODE)) {
+ eml_params.link_bitmap = get_unaligned_le16(ptr);
+ if (eml_params.link_bitmap &&
+ !(eml_params.link_bitmap & sdata->vif.active_links))
+ return;
+ }
+
+ if (drv_set_eml_op_mode(sdata, &sta->sta, &eml_params))
+ return;
+
+ ieee80211_send_eml_op_mode_notif(sdata, mgmt, opt_len);
+}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index dc757cb329740d621f6a9deb4e9ffe258e1b7d67..3ef93cd1fb33e2614c3e06c51d6b1ebcafa4824c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2837,6 +2837,8 @@ void ieee80211_destroy_frag_cache(struct ieee80211_fragment_cache *cache);
u8 ieee80211_ie_len_eht_cap(struct ieee80211_sub_if_data *sdata);
+void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
void
ieee80211_eht_cap_ie_to_sta_eht_cap(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 3ce94b95decd64eea4ea063ae98c111bdfd57e9c..17476fd9259b68b3d9c649eabad10efa42d56cd7 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1664,7 +1664,15 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
}
} else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_PROTECTED_EHT) {
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ switch (mgmt->u.action.u.eml_omn.action_code) {
+ case WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF:
+ ieee80211_rx_eml_op_mode_notif(sdata, skb);
+ break;
+ default:
+ break;
+ }
+ } else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
switch (mgmt->u.action.u.ttlm_req.action_code) {
case WLAN_PROTECTED_EHT_ACTION_TTLM_REQ:
ieee80211_process_neg_ttlm_req(sdata, mgmt,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 30b9b4d763578feea179ad33be5174fc3fc19209..6a13460b8b6a9176f50ca9ec7fb365befc9b317c 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3928,6 +3928,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
u.action.u.epcs))
goto invalid;
goto queue;
+ case WLAN_PROTECTED_EHT_ACTION_EML_OP_MODE_NOTIF:
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ break;
+
+ if (len < offsetofend(typeof(*mgmt),
+ u.action.u.eml_omn))
+ goto invalid;
+ goto queue;
default:
break;
}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 0bfbce1574862b5a6a2ca39794abea7fe9a3f34a..c04d4547e8f4655ad77b83bed6e5a832d959e61b 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -3353,6 +3353,38 @@ TRACE_EVENT(drv_prep_add_interface,
)
);
+TRACE_EVENT(drv_set_eml_op_mode,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ unsigned int link_id,
+ u8 control, u16 link_bitmap),
+
+ TP_ARGS(local, sdata, sta, link_id, control, link_bitmap),
+
+ TP_STRUCT__entry(LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u32, link_id)
+ __field(u8, control)
+ __field(u16, link_bitmap)),
+
+ TP_fast_assign(LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_NAMED_ASSIGN(sta);
+ __entry->link_id = link_id;
+ __entry->control = control;
+ __entry->link_bitmap = link_bitmap;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT
+ " (link:%d control:%02x link_bitmap:%04x)",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->link_id,
+ __entry->control, __entry->link_bitmap
+ )
+);
+
#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH
---
base-commit: 541745f3178da6a6997ffcba31692fe9a6b1ecbb
change-id: 20260121-mac80211-emlsr-5774082ff8cc
Best regards,
--
Lorenzo Bianconi <lorenzo@kernel.org>
^ permalink raw reply related
* Re: [PATCH wireless-next v3 3/3] wifi: mac80211: add initial UHR support
From: Johannes Berg @ 2026-01-28 10:54 UTC (permalink / raw)
To: Pablo MARTIN-GOMEZ, linux-wireless
In-Reply-To: <b9b2cf39-8989-40c9-8ffa-b1626217b773@freebox.fr>
On Tue, 2026-01-27 at 17:12 +0100, Pablo MARTIN-GOMEZ wrote:
>
> > @@ -1040,8 +1064,15 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
> >
> > r = STA_STATS_FIELD(BW, s->bw);
> >
> > - if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
> > - r |= STA_STATS_FIELD(SGI, 1);
> > + switch (s->encoding) {
> > + case RX_ENC_HT:
> > + case RX_ENC_VHT:
> > + if (s->enc_flags & RX_ENC_FLAG_SHORT_GI)
> > + r |= STA_STATS_FIELD(SGI, 1);
> > + break;
> > + default:
> > + break;
> > + }
> Not sure this change should be in this commit
Well, it is needed here, and I didn't think it was worth its own commit.
It didn't previously matter because the SGI bit wasn't overloaded, but I
also repacked all the bits and overloaded the SGI bit (HT/VHT only) with
other bits for HE/EHT/UHR, since now I couldn't keep it all separate any
more.
I guess I could do it separately _before_ this commit, but that seemed
fairly pointless.
johannes
^ permalink raw reply
* [PATCH] wireless-regdb: add IN rules for lower 6 GHz (5925-6425 MHz) LPI and VLP
From: gaurav.kansal @ 2026-01-28 10:25 UTC (permalink / raw)
To: wens; +Cc: linux-wireless, wireless-regdb, Gaurav Kansal
From: Gaurav Kansal <gaurav.kansal@nic.in>
DoT / Gazette notification (20 Jan 2026) delicensed lower 6 GHz (5925-6425 MHz).
Add rules for:
- Low Power Indoor (LPI) 5925-6425 MHz, up to 1000 mW (30 dBm) EIRP, NO-OUTDOOR
- Very Low Power (VLP) 5925-6425 MHz, up to 25 mW (14 dBm) EIRP
References:
- DoT / Gazette notification (Jan 20, 2026)
https://blog.gauravkansal.in/2026/01/Gazette%20on%20the%20Use%20of%20Low%20Power%20and%20Very%20Low%20Power%20Wireless%20Access%20System%20in%20Lower%206%20GHz%20band%20-%202026.pdf
https://blog.gauravkansal.in/2026/01/Opening-India-Lower-6-GHz-Band-for-Wireless-Use:-A-Strategic-Regulatory-Landmark.html
https://en.wikipedia.org/w/index.php?title=List_of_WLAN_channels#India_2
- Linux wireless-regdb db.txt format docs
Signed-off-by: Gaurav Kansal gaurav.kansal@nic.in
---
db.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/db.txt b/db.txt
index dc72989..0f90f6e 100644
--- a/db.txt
+++ b/db.txt
@@ -974,12 +974,20 @@ country IL: DFS-ETSI
# Source:
# https://dot.gov.in/spectrummanagement/delicensing-24-24835-ghz-band-gsr-45-e-5150-5350-ghz-gsr-46-e-and-5725-5875-ghz
# https://dot.gov.in/spectrummanagement/license-exemption-5-ghz-gsr-1048e-dated-22102018
+# Info about 6 GHz added by Gaurav Kansal (gaurav.kansal@nic.in)
+# Lower 6 GHz delicensed for India: 5925-6425 MHz (on 21.Jan.2026)
+# LPI: Low Power Indoor (indoor only) — 1000 mW = 30 dBm EIRP
+# VLP: Very Low Power (outdoor allowed at very low EIRP) — 25 mW = 14 dBm EIRP
+# https://blog.gauravkansal.in/2026/01/Opening-India-Lower-6-GHz-Band-for-Wireless-Use:-A-Strategic-Regulatory-Landmark.html
+# https://blog.gauravkansal.in/2026/01/Gazette%20on%20the%20Use%20of%20Low%20Power%20and%20Very%20Low%20Power%20Wireless%20Access%20System%20in%20Lower%206%20GHz%20band%20-%202026.pdf
country IN:
(2402 - 2482 @ 40), (30)
(5150 - 5250 @ 80), (30)
(5250 - 5350 @ 80), (24), DFS
(5470 - 5725 @ 160), (24), DFS
(5725 - 5875 @ 80), (30)
+ (5925 - 6425 @ 320), (1000 mW), NO-OUTDOOR, AUTO-BW
+ (5925 - 6425 @ 320), (25 mW), AUTO-BW
# Source:
# https://asnad.cra.ir/fa/Public/Documents/Details/73af8590-f065-eb11-968f-0050569b0899
--
2.47.3
^ permalink raw reply related
* [PATCH] wireless-regdb: Update regulatory info for Tunisia (TN) on 6GHz for 2025
From: Ping-Ke Shih @ 2026-01-28 9:16 UTC (permalink / raw)
To: wens; +Cc: linux-wireless, wireless-regdb
From: Ping-Ke Shih <pkshih@realtek.com>
By decree no. 2024-641 of December 11, 2024 [1], which applies to
frequency band 5925-6425 MHz, with power limits 200 mW, for use inside
buildings, in compliance with EN 303 687.
[1] https://www.anf.tn/sites/default/files/2025-01/Arr%C3%AAt%C3%A92024_4611.pdf
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
db.txt | 3 +++
1 file changed, 3 insertions(+)
diff --git a/db.txt b/db.txt
index dc72989b5b40..7abbe3f1341a 100644
--- a/db.txt
+++ b/db.txt
@@ -1929,10 +1929,13 @@ country TH: DFS-FCC
(5735 - 5835 @ 80), (30)
(5925 - 6425 @ 320), (250 mW), NO-OUTDOOR
+# Source:
+# https://www.anf.tn/sites/default/files/2025-01/Arr%C3%AAt%C3%A92024_4611.pdf
country TN: DFS-ETSI
(2402 - 2482 @ 40), (20)
(5170 - 5250 @ 80), (20), AUTO-BW
(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+ (5925 - 6425 @ 320), (200 mW), NO-OUTDOOR
# Source:
# Technical Criteria for Radio Devices and Systems Excluded from Frequency Allocation, Sept 9, 2022
--
2.25.1
^ permalink raw reply related
* Re: Clarification on the usage of NL80211_SCAN_FLAG_FLUSH
From: Matthew Schwartz @ 2026-01-28 8:53 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
In-Reply-To: <a4afd8690916a518e3919c52770fb423339b321d.camel@sipsolutions.net>
> On Jan 28, 2026, at 12:39 AM, Johannes Berg <johannes@sipsolutions.net> wrote:
>
> Hi,
>
>> Is this intended behavior of NL80211_SCAN_FLAG_FLUSH where it clears all BSS entries regardless of which channels were scanned, or should NL80211_SCAN_FLAG_FLUSH be limited to channels that were scanned in the request which triggered them? The uAPI definition for it is simply "flush cache before scanning", but this seems open to interpretation as to whether it means the entire cache or just the cache on scanned channels.
>
> Well, AFAICT it has always done that, and so I think regardless of what
> might have been the original intent, it simply is that behaviour now.
> Changing it in the kernel would just end up having to fix two places,
> unless you somehow don't care about running on older kernels without
> fixes.
>
> I also tend to think it makes sense since the scan could, at least in
> theory, do e.g. colocated scanning, so networks on channels other than
> the ones explicitly listed could be discovered. Trying to do anything
> other than "flush all" would be far more complex.
Okay, thanks for the clarification. I’ll try and take a look at how iwd is using the flush then because it seems like it’s overusing it, or maybe needs some other logic to prevent the connection issues.
Thanks,
Matt
>
> johannes
^ permalink raw reply
* Re: Clarification on the usage of NL80211_SCAN_FLAG_FLUSH
From: Johannes Berg @ 2026-01-28 8:38 UTC (permalink / raw)
To: Matthew Schwartz, linux-wireless
In-Reply-To: <3b3bdd35-b831-4274-8b91-7ab90f6d4eec@linux.dev>
Hi,
> Is this intended behavior of NL80211_SCAN_FLAG_FLUSH where it clears all BSS entries regardless of which channels were scanned, or should NL80211_SCAN_FLAG_FLUSH be limited to channels that were scanned in the request which triggered them? The uAPI definition for it is simply "flush cache before scanning", but this seems open to interpretation as to whether it means the entire cache or just the cache on scanned channels.
Well, AFAICT it has always done that, and so I think regardless of what
might have been the original intent, it simply is that behaviour now.
Changing it in the kernel would just end up having to fix two places,
unless you somehow don't care about running on older kernels without
fixes.
I also tend to think it makes sense since the scan could, at least in
theory, do e.g. colocated scanning, so networks on channels other than
the ones explicitly listed could be discovered. Trying to do anything
other than "flush all" would be far more complex.
johannes
^ permalink raw reply
* Re: [PATCH v4 ath-current 0/2] wifi: ath11k: add usecase firmware handling based on device compatible
From: Miaoqing Pan @ 2026-01-28 8:06 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jeff Johnson, jjohnson, johannes, robh,
krzk+dt, conor+dt
Cc: ath11k, linux-wireless, linux-kernel, devicetree, jonas.gorski
In-Reply-To: <32845805-5758-4c35-8818-ebccbfd9546a@kernel.org>
On 1/28/2026 4:01 PM, Krzysztof Kozlowski wrote:
> On 27/01/2026 18:31, Jeff Johnson wrote:
>> On 1/21/2026 1:50 AM, Miaoqing Pan wrote:
>>> The 'firmware-name' property was introduced to allow end-users and
>>> integrators to select use-case-specific firmware for the WCN6855.
>>> But for M.2 WLAN chips, there is no suitable DTS node to specify
>>> the 'firmware-name' property. In addition, assigning firmware for
>>> the M.2 PCIe interface causes chips that do not use use-case-specific
>>> firmware to fail. Therefore, abandoning the approach of specifying
>>> firmware in DTS. As an alternative, propose a static lookup table
>>> mapping device compatible to firmware names.
>>>
>>> ---
>>> v2:
>>> - Drops `firmware-name` from completely.
>>> - Updates the commit message to clearly state that the property is
>>> obsolete and the change is ABI-breaking but safe for upstream.
>>> v3:
>>> - Deprecate 'firmware-name' property instead of obsolete.
>>> - Keep the ABI backwards compatible.
>>> v4:
>>> - Use of_machine_is_compatible() to simplify the code.
>>> - Add back Acked-by tag.
>>> ---
>>>
>>> Miaoqing Pan (2):
>>> wifi: ath11k: add usecase firmware handling based on device compatible
>>> dt-bindings: net: wireless: ath11k-pci: deprecate 'firmware-name'
>>> property
>>>
>>> .../net/wireless/qcom,ath11k-pci.yaml | 1 +
>>> drivers/net/wireless/ath/ath11k/core.c | 27 +++++++++++++++++++
>>> drivers/net/wireless/ath/ath11k/core.h | 4 +++
>>> 3 files changed, 32 insertions(+)
>>>
>>>
>>> base-commit: d8e1f4a193101a72235416f189b01131a57e26e9
>>
>> Krzysztof,
>> Since you previously NAKed this series, can you confirm that your review
>> comments have been addressed?
>
>
> Binding looks fine, but I did not check the driver.
>
> Best regards,
> Krzysztof
The driver has retained compatibility with the firmware-name.
static inline const char *ath11k_scan_state_str(enum ath11k_scan_state
state)
{
@@ -1346,6 +1347,9 @@ static inline void
ath11k_core_create_firmware_path(struct ath11k_base *ab,
of_property_read_string(ab->dev->of_node, "firmware-name", &fw_name);
//here
+ if (!fw_name)
+ fw_name = ath11k_core_get_usecase_firmware(ab);
+
^ permalink raw reply
* Re: [PATCH v4 ath-current 0/2] wifi: ath11k: add usecase firmware handling based on device compatible
From: Krzysztof Kozlowski @ 2026-01-28 8:01 UTC (permalink / raw)
To: Jeff Johnson, Miaoqing Pan, jjohnson, johannes, robh, krzk+dt,
conor+dt
Cc: ath11k, linux-wireless, linux-kernel, devicetree, jonas.gorski
In-Reply-To: <74176aab-03d4-4095-890d-7ef1739b914b@oss.qualcomm.com>
On 27/01/2026 18:31, Jeff Johnson wrote:
> On 1/21/2026 1:50 AM, Miaoqing Pan wrote:
>> The 'firmware-name' property was introduced to allow end-users and
>> integrators to select use-case-specific firmware for the WCN6855.
>> But for M.2 WLAN chips, there is no suitable DTS node to specify
>> the 'firmware-name' property. In addition, assigning firmware for
>> the M.2 PCIe interface causes chips that do not use use-case-specific
>> firmware to fail. Therefore, abandoning the approach of specifying
>> firmware in DTS. As an alternative, propose a static lookup table
>> mapping device compatible to firmware names.
>>
>> ---
>> v2:
>> - Drops `firmware-name` from completely.
>> - Updates the commit message to clearly state that the property is
>> obsolete and the change is ABI-breaking but safe for upstream.
>> v3:
>> - Deprecate 'firmware-name' property instead of obsolete.
>> - Keep the ABI backwards compatible.
>> v4:
>> - Use of_machine_is_compatible() to simplify the code.
>> - Add back Acked-by tag.
>> ---
>>
>> Miaoqing Pan (2):
>> wifi: ath11k: add usecase firmware handling based on device compatible
>> dt-bindings: net: wireless: ath11k-pci: deprecate 'firmware-name'
>> property
>>
>> .../net/wireless/qcom,ath11k-pci.yaml | 1 +
>> drivers/net/wireless/ath/ath11k/core.c | 27 +++++++++++++++++++
>> drivers/net/wireless/ath/ath11k/core.h | 4 +++
>> 3 files changed, 32 insertions(+)
>>
>>
>> base-commit: d8e1f4a193101a72235416f189b01131a57e26e9
>
> Krzysztof,
> Since you previously NAKed this series, can you confirm that your review
> comments have been addressed?
Binding looks fine, but I did not check the driver.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 ath-current 2/2] dt-bindings: net: wireless: ath11k-pci: deprecate 'firmware-name' property
From: Krzysztof Kozlowski @ 2026-01-28 8:00 UTC (permalink / raw)
To: Miaoqing Pan, jjohnson, johannes, robh, krzk+dt, conor+dt
Cc: ath11k, linux-wireless, linux-kernel, devicetree, jonas.gorski
In-Reply-To: <20260121095055.3683957-3-miaoqing.pan@oss.qualcomm.com>
On 21/01/2026 10:50, Miaoqing Pan wrote:
> The firmware-name property was originally introduced to allow end-users
> and integrators to select use-case-specific firmware for the WCN6855.
> However, specifying firmware for an M.2 WLAN module in the Device Tree
> is not appropriate. Instead, this functionality will be handled within
> the ath11k driver. Therefore, the firmware-name property is now
> deprecated.
>
> Acked-by: Rob Herring (Arm) <robh@kernel.org>
> Signed-off-by: Miaoqing Pan <miaoqing.pan@oss.qualcomm.com>
> ---
> .../devicetree/bindings/net/wireless/qcom,ath11k-pci.yaml | 1 +
> 1 file changed, 1 insertion(+)
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH] wireless-regdb: Update regulatory info for Canada (CA) for 2025
From: Chen-Yu Tsai @ 2026-01-28 7:42 UTC (permalink / raw)
To: Chen-Yu Tsai, druth, Ping-Ke Shih
Cc: wireless-regdb, linux-wireless, Johannes Berg
Canada issued a new revision of its regulatory standards for 2.4 GHz
and 5 GHz license-exempt local area network devices - RSS-247.
Update the rules to match the new version. Fix up band boundaries and
power limit values and units to match the text.
Note: the database is supposed to have values for e.i.r.p.. Most
regulatory documents specify e.i.r.p. as well, except for FCC ones and
any regions that have rules based on FCC ones.
Details about the changes and justification for them are as follows.
- 2.4 GHz band boundary aligned to actual text instead of WiFi channels
- 2.4 GHz band changed from 30 dBm (1W) to 4 W
section 6.3.2 explicitly states e.i.r.p. limit at 4 W.
- 5.15 ~ 5.25 GHz band power limit unit changed to mW
section 7.3.1.2 explicitly states e.i.r.p. limit at 200 mW.
- 5.25 ~ 5.35 GHz band power limit changed to 500 mW
section 7.3.2.2 states e.i.r.p. limit at 1 W, but requires TPC for
e.i.r.p. above 500 mW. As the database nor Linux implements TPC,
the 500 mW limit is chosen.
- 5.4 ~ 5.725 GHz band combined and power limit unit changed to mW
the new revision removed the restriction 5.6 ~ 5.65 GHz usage.
the two existing rules are combined to cover the whole band,
and the upper boundary has been increased to 5.73 GHz to cover
the whole WiFi channel and make it easier to write the rule,
like how the US rules are written.
section 7.3.3.2 states e.i.r.p. limit at 1 W, but requires TPC for
e.i.r.p. above 500 mW. As the database nor Linux implements TPC,
the 500 mW limit is chosen.
- 5.725 ~ 5.85 GHz boundaries aligned and limit changed from 30 dBm (1W)
to 4 W; AUTO-BW added for combined usage with next band.
section 7.3.4.3 states maximum conducted power limit at 1 W, but no
explicit e.i.r.p. limit is given.
apply "e.i.r.p. = max conducted power + 6 dBi" conversion, arriving
at roughly 4 W. This aligns with the ratio for the 2.4 GHz band.
- 5.85 ~ 5.895 GHz band newly opened in this revision
section 7.3.5.3 states maximum e.i.r.p. for indoor clients at 1 W or
30 dBm, with maximum e.i.r.p. spectral density at 14 dBm / MHz.
Apply spectral density limit at 20 MHz channel width, arriving at
roughly 27 dBm e.i.r.p. limit.
While the limit comes from the "indoor clients" rule, it is the lowest
limit of all the types, hence it should be applicable without any
restrictions.
Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
---
db.txt | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/db.txt b/db.txt
index dc72989..64bc853 100644
--- a/db.txt
+++ b/db.txt
@@ -388,15 +388,20 @@ country BZ: DFS-JP
(5735 - 5835 @ 80), (30)
# Sources:
-# https://www.ic.gc.ca/eic/site/smt-gst.nsf/vwapj/rss-247-i2-e.pdf/$file/rss-247-i2-e.pdf
+# RSS-247 Issue 4
+# https://ised-isde.canada.ca/site/spectrum-management-telecommunications/en/devices-and-equipment/radio-equipment-standards/radio-standards-specifications-rss/rss-247-digital-transmission-systems-dtss-frequency-hopping-systems-fhss-and-licence-exempt-local
# https://www.ic.gc.ca/eic/site/smt-gst.nsf/eng/sf11750.html (6 GHz operation)
country CA: DFS-FCC
- (2402 - 2472 @ 40), (30)
- (5150 - 5250 @ 80), (23), NO-OUTDOOR, AUTO-BW
- (5250 - 5350 @ 80), (24), DFS, AUTO-BW
- (5470 - 5600 @ 80), (24), DFS
- (5650 - 5730 @ 80), (24), DFS
- (5735 - 5835 @ 80), (30)
+ (2400 - 2483.5 @ 40), (4000 mW)
+ (5150 - 5250 @ 80), (200 mW), NO-OUTDOOR, AUTO-BW
+ (5250 - 5350 @ 80), (500 mW), DFS, AUTO-BW
+ # This range ends at 5725 MHz, but channel 144 extends to 5730 MHz.
+ # Since 5725 ~ 5730 MHz belongs to the next range which has looser
+ # requirements, we can extend the range by 5 MHz to make the kernel
+ # happy and be able to use channel 144.
+ (5470 - 5730 @ 160), (500 mW), DFS
+ (5730 - 5850 @ 80), (4000 mW), AUTO-BW
+ (5850 - 5895 @ 40), (27), AUTO-BW
(5925 - 7125 @ 320), (12), NO-OUTDOOR
# Source:
--
2.47.3
^ permalink raw reply related
* [PATCH wireless-next] wifi: mac80211: use wiphy_hrtimer_work for CAC timeout
From: Amith A @ 2026-01-28 6:48 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless, amith.a
Prior initiating communication in a DFS channel, there should be a
monitoring of RADAR in that channel for a minimum of 600 seconds if it
is a Weather RADAR channel and 60 seconds for other DFS channels. This
Channel Availability Check(CAC) is currently implemented by scheduling
a work item for execution with a delay equal to an appropriate timeout.
But this work item is observed to take more delay than specified
(4-5 seconds in regular DFS channels and 25-30 seconds in Weather RADAR
channels). Even though this delay is expected in case of delayed work
queue as there is no guarantee that the work will be scheduled exactly
after the specified delay, a delay of more than 20 seconds is too much
for the AP to be in non-operational state.
Recently commit 7ceba45a6658 ("wifi: cfg80211: add an hrtimer based
delayed work item") added an infrastructure to overcome this issue by
supporting high resolution timers for mac80211 delayed work, which do not
have this timeout latency. The other patches in that series converted some
mac80211 work items to use the new infrastructure.
Unfortunately the CAC timeout work was not addressed as part of that series,
so address it now.
Signed-off-by: Amith A <amith.a@oss.qualcomm.com>
---
net/mac80211/cfg.c | 8 ++++----
net/mac80211/ieee80211_i.h | 2 +-
net/mac80211/iface.c | 2 +-
net/mac80211/link.c | 4 ++--
net/mac80211/util.c | 2 +-
5 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 964f440e31cd..acec44957040 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1916,7 +1916,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
if (sdata->wdev.links[link_id].cac_started) {
chandef = link_conf->chanreq.oper;
- wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
+ wiphy_hrtimer_work_cancel(wiphy, &link->dfs_cac_timer_work);
cfg80211_cac_event(sdata->dev, &chandef,
NL80211_RADAR_CAC_ABORTED,
GFP_KERNEL, link_id);
@@ -3851,6 +3851,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
u32 cac_time_ms, int link_id)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ktime_t ktime = ktime_set(0, cac_time_ms * NSEC_PER_MSEC);
struct ieee80211_chan_req chanreq = { .oper = *chandef };
struct ieee80211_local *local = sdata->local;
struct ieee80211_link_data *link_data;
@@ -3874,8 +3875,7 @@ static int ieee80211_start_radar_detection(struct wiphy *wiphy,
if (err)
return err;
- wiphy_delayed_work_queue(wiphy, &link_data->dfs_cac_timer_work,
- msecs_to_jiffies(cac_time_ms));
+ wiphy_hrtimer_work_queue(wiphy, &link_data->dfs_cac_timer_work, ktime);
return 0;
}
@@ -3894,7 +3894,7 @@ static void ieee80211_end_cac(struct wiphy *wiphy,
if (!link_data)
continue;
- wiphy_delayed_work_cancel(wiphy,
+ wiphy_hrtimer_work_cancel(wiphy,
&link_data->dfs_cac_timer_work);
if (sdata->wdev.links[link_id].cac_started) {
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index dc757cb32974..df5056463388 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1101,7 +1101,7 @@ struct ieee80211_link_data {
int ap_power_level; /* in dBm */
bool radar_required;
- struct wiphy_delayed_work dfs_cac_timer_work;
+ struct wiphy_hrtimer_work dfs_cac_timer_work;
union {
struct ieee80211_link_data_managed mgd;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 3ce94b95decd..15583fc7003a 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -561,7 +561,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
wiphy_work_cancel(local->hw.wiphy, &sdata->deflink.csa.finalize_work);
wiphy_work_cancel(local->hw.wiphy,
&sdata->deflink.color_change_finalize_work);
- wiphy_delayed_work_cancel(local->hw.wiphy,
+ wiphy_hrtimer_work_cancel(local->hw.wiphy,
&sdata->deflink.dfs_cac_timer_work);
if (sdata->wdev.links[0].cac_started) {
diff --git a/net/mac80211/link.c b/net/mac80211/link.c
index 1e05845872af..17bf55dabd31 100644
--- a/net/mac80211/link.c
+++ b/net/mac80211/link.c
@@ -116,7 +116,7 @@ void ieee80211_link_init(struct ieee80211_sub_if_data *sdata,
ieee80211_color_change_finalize_work);
wiphy_delayed_work_init(&link->color_collision_detect_work,
ieee80211_color_collision_detection_work);
- wiphy_delayed_work_init(&link->dfs_cac_timer_work,
+ wiphy_hrtimer_work_init(&link->dfs_cac_timer_work,
ieee80211_dfs_cac_timer_work);
if (!deflink) {
@@ -155,7 +155,7 @@ void ieee80211_link_stop(struct ieee80211_link_data *link)
&link->csa.finalize_work);
if (link->sdata->wdev.links[link->link_id].cac_started) {
- wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
+ wiphy_hrtimer_work_cancel(link->sdata->local->hw.wiphy,
&link->dfs_cac_timer_work);
cfg80211_cac_event(link->sdata->dev,
&link->conf->chanreq.oper,
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 4d5680da7aa0..260893b83df1 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -3597,7 +3597,7 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
if (ctx && &ctx->conf != chanctx_conf)
continue;
- wiphy_delayed_work_cancel(local->hw.wiphy,
+ wiphy_hrtimer_work_cancel(local->hw.wiphy,
&link->dfs_cac_timer_work);
if (!sdata->wdev.links[link_id].cac_started)
base-commit: c30e188bd2a886258be5facb970a804d8ef549b5
--
2.34.1
^ permalink raw reply related
* Re: [PATCH ath-current v2] wifi: ath11k: fix wrong TID passed when stopping AMPDU session
From: Vasanthakumar Thiagarajan @ 2026-01-28 4:42 UTC (permalink / raw)
To: Pablo Martin-Gomez, ath11k, jeff.johnson; +Cc: linux-wireless
In-Reply-To: <20260126174049.1370659-1-pmartin-gomez@freebox.fr>
On 1/26/2026 11:10 PM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath11k_dp_rx_ampdu_stop() calls
> ath11k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
>
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
>
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
>
> Fix this by passing the correct TID entry:
> &peer->rx_tid[params->tid]
>
> Fixes: fe201947f8bd ("ath11k: update bawindow size in delba process")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath12k: clear stale link mapping of ahvif->links_map
From: Vasanthakumar Thiagarajan @ 2026-01-28 4:40 UTC (permalink / raw)
To: Aaradhana Sahu, ath12k; +Cc: linux-wireless
In-Reply-To: <20260127033400.1721220-1-aaradhana.sahu@oss.qualcomm.com>
On 1/27/2026 9:04 AM, Aaradhana Sahu wrote:
> When an arvif is initialized in non-AP STA mode but MLO connection
> preparation fails before the arvif is created
> (arvif->is_created remains false), the error path attempts to delete all
> links. However, link deletion only executes when arvif->is_created is true.
> As a result, ahvif retains a stale entry of arvif that is initialized but
> not created.
>
> When a new arvif is initialized with the same link id, this stale mapping
> triggers the following WARN_ON.
>
> WARNING: drivers/net/wireless/ath/ath12k/mac.c:4271 at ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k], CPU#3: wpa_supplicant/275
>
> Call trace:
> ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k] (P)
> drv_change_vif_links+0xbc/0x1a4 [mac80211]
> ieee80211_vif_update_links+0x54c/0x6a0 [mac80211]
> ieee80211_vif_set_links+0x40/0x70 [mac80211]
> ieee80211_prep_connection+0x84/0x450 [mac80211]
> ieee80211_mgd_auth+0x200/0x480 [mac80211]
> ieee80211_auth+0x14/0x20 [mac80211]
> cfg80211_mlme_auth+0x90/0xf0 [cfg80211]
> nl80211_authenticate+0x32c/0x380 [cfg80211]
> genl_family_rcv_msg_doit+0xc8/0x134
>
> Fix this issue by unassigning the link vif and clearing ahvif->links_map
> if arvif is only initialized but not created.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
>
> Fixes: 81e4be30544e ("wifi: ath12k: handle link removal in change_vif_links()")
> Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-current v2] wifi: ath12k: fix wrong TID passed when stopping AMPDU session
From: Vasanthakumar Thiagarajan @ 2026-01-28 4:40 UTC (permalink / raw)
To: Pablo Martin-Gomez, ath12k, jeff.johnson; +Cc: linux-wireless
In-Reply-To: <20260126173655.1369715-1-pmartin-gomez@freebox.fr>
On 1/26/2026 11:06 PM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath12k_dp_rx_ampdu_stop() calls
> ath12k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
>
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
>
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
>
> Fix this by passing the correct TID entry:
> &peer->rx_tid[params->tid]
>
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH rtw-next 01/11] wifi: rtw89: rfk: add firmware command to do TX IQK
From: Ping-Ke Shih @ 2026-01-28 3:45 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless; +Cc: kevin_yang
In-Reply-To: <20260123013957.16418-2-pkshih@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> TX IQK is a RF calibration, which driver call this H2C command to trigger
> the calibration.
>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
11 patch(es) applied to rtw-next branch of rtw.git, thanks.
ebd8a1d736ee wifi: rtw89: rfk: add firmware command to do TX IQK
9df3575ed2bb wifi: rtw89: rfk: add to print debug log of TX IQK
17b75fbf9c71 wifi: rtw89: rfk: add firmware command to do CIM3K
571f945fb91b wifi: rtw89: rfk: add to print debug log of CIM3K
21344e741377 wifi: rtw89: rfk: update RFK report format of IQK, DACK and TXGAPK
c938cb486254 wifi: rtw89: fw: correct content of DACK H2C command
69ed25f25faa wifi: rtw89: phy: add PHY C2H event dummy handler for func 1-7 and 2-10
3b85a8948f52 wifi: rtw89: 8922a: configure FW version for SIM_SER_L0L1_BY_HALT_H2C
986aa89b7613 wifi: rtw89: get designated link to replace link instance 0
5f93d611b33a wifi: rtw89: pci: validate release report content before using for RTL8922DE
599b1b9fb967 wifi: rtw89: regd: update regulatory map to R73-R54
---
https://github.com/pkshih/rtw.git
^ permalink raw reply
* [PATCH 6.6.y] wifi: ath11k: fix RCU stall while reaping monitor destination ring
From: Li hongliang @ 2026-01-28 3:27 UTC (permalink / raw)
To: gregkh, stable, quic_ppranees
Cc: patches, linux-kernel, quic_kangyang, kvalo, quic_jjohnson,
jeff.johnson, jjohnson, quic_msinada, rmanohar, julia.lawall,
quic_pradeepc, linux-wireless, ath11k
From: P Praneesh <quic_ppranees@quicinc.com>
[ Upstream commit 16c6c35c03ea73054a1f6d3302a4ce4a331b427d ]
While processing the monitor destination ring, MSDUs are reaped from the
link descriptor based on the corresponding buf_id.
However, sometimes the driver cannot obtain a valid buffer corresponding
to the buf_id received from the hardware. This causes an infinite loop
in the destination processing, resulting in a kernel crash.
kernel log:
ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309
ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed
ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309
ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed
Fix this by skipping the problematic buf_id and reaping the next entry,
replacing the break with the next MSDU processing.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
Acked-by: Kalle Valo <kvalo@kernel.org>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Link: https://patch.msgid.link/20241219110531.2096-2-quic_kangyang@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 8cc51ab699de..dc54ea16c12a 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -4728,7 +4728,7 @@ ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id,
if (!msdu) {
ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
"msdu_pop: invalid buf_id %d\n", buf_id);
- break;
+ goto next_msdu;
}
rxcb = ATH11K_SKB_RXCB(msdu);
if (!rxcb->unmapped) {
@@ -5362,7 +5362,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
"full mon msdu_pop: invalid buf_id %d\n",
buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
- break;
+ goto next_msdu;
}
idr_remove(&rx_ring->bufs_idr, buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
--
2.34.1
^ permalink raw reply related
* [PATCH 6.12.y] wifi: ath11k: fix RCU stall while reaping monitor destination ring
From: Li hongliang @ 2026-01-28 3:26 UTC (permalink / raw)
To: gregkh, stable, quic_ppranees
Cc: patches, linux-kernel, quic_kangyang, kvalo, quic_jjohnson,
jeff.johnson, jjohnson, quic_msinada, rmanohar, julia.lawall,
quic_pradeepc, linux-wireless, ath11k
From: P Praneesh <quic_ppranees@quicinc.com>
[ Upstream commit 16c6c35c03ea73054a1f6d3302a4ce4a331b427d ]
While processing the monitor destination ring, MSDUs are reaped from the
link descriptor based on the corresponding buf_id.
However, sometimes the driver cannot obtain a valid buffer corresponding
to the buf_id received from the hardware. This causes an infinite loop
in the destination processing, resulting in a kernel crash.
kernel log:
ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309
ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed
ath11k_pci 0000:58:00.0: data msdu_pop: invalid buf_id 309
ath11k_pci 0000:58:00.0: data dp_rx_monitor_link_desc_return failed
Fix this by skipping the problematic buf_id and reaping the next entry,
replacing the break with the next MSDU processing.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.30
Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: P Praneesh <quic_ppranees@quicinc.com>
Signed-off-by: Kang Yang <quic_kangyang@quicinc.com>
Acked-by: Kalle Valo <kvalo@kernel.org>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Link: https://patch.msgid.link/20241219110531.2096-2-quic_kangyang@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index 66a00f330734..9373bfe50526 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -4777,7 +4777,7 @@ ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id,
if (!msdu) {
ath11k_dbg(ar->ab, ATH11K_DBG_DATA,
"msdu_pop: invalid buf_id %d\n", buf_id);
- break;
+ goto next_msdu;
}
rxcb = ATH11K_SKB_RXCB(msdu);
if (!rxcb->unmapped) {
@@ -5404,7 +5404,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar,
"full mon msdu_pop: invalid buf_id %d\n",
buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
- break;
+ goto next_msdu;
}
idr_remove(&rx_ring->bufs_idr, buf_id);
spin_unlock_bh(&rx_ring->idr_lock);
--
2.34.1
^ permalink raw reply related
* Re: [PATCH ath-current v2] wifi: ath12k: fix wrong TID passed when stopping AMPDU session
From: Baochen Qiang @ 2026-01-28 2:06 UTC (permalink / raw)
To: Jeff Johnson, Pablo Martin-Gomez, ath12k; +Cc: linux-wireless
In-Reply-To: <9c418260-b67a-48aa-80c6-6a03c91e4ba3@oss.qualcomm.com>
On 1/28/2026 7:39 AM, Jeff Johnson wrote:
> On 1/26/2026 9:36 AM, Pablo Martin-Gomez wrote:
>> When handling a DELBA request, ath12k_dp_rx_ampdu_stop() calls
>> ath12k_peer_rx_tid_reo_update() to tear down the BA session for the
>> specified TID. However, it currently passes peer->rx_tid instead of the
>> entry corresponding to params->tid.
>>
>> Since peer->rx_tid is an array, this decays to a pointer to the first
>> element, effectively operating on TID 0 regardless of the TID in the
>> DELBA request. As a result, the BA session for TID 0 is stopped while
>> the intended TID remains active.
>>
>> This leads to incorrect BA session state and may significantly reduce
>> RX throughput, as traffic that should use aggregation falls back to a
>> BA window size of 1 on TID 0.
>>
>> Fix this by passing the correct TID entry:
>> &peer->rx_tid[params->tid]
>>
>> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
>> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
>> ---
>> drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
>> index d28d8ffec0f8..f2327c82953b 100644
>> --- a/drivers/net/wireless/ath/ath12k/dp_rx.c
>> +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
>> @@ -1289,7 +1289,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
>> return 0;
>> }
>>
>> - ret = ath12k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
>> + ret = ath12k_peer_rx_tid_reo_update(ar, peer, &peer->rx_tid[params->tid], 1, 0, false);
>
> like the ath11k change, this also triggers ath12k-check:
> drivers/net/wireless/ath/ath12k/dp_rx.c:1292: line length of 95 exceeds 90 columns
>
> I'll break this line when I process the patch
>
>> spin_unlock_bh(&ab->base_lock);
>> if (ret) {
>> ath12k_warn(ab, "failed to update reo for rx tid %d: %d\n",
>
>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-current v2] wifi: ath11k: fix wrong TID passed when stopping AMPDU session
From: Baochen Qiang @ 2026-01-28 2:04 UTC (permalink / raw)
To: Pablo Martin-Gomez, ath11k, jeff.johnson; +Cc: linux-wireless
In-Reply-To: <20260126174049.1370659-1-pmartin-gomez@freebox.fr>
On 1/27/2026 1:40 AM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath11k_dp_rx_ampdu_stop() calls
> ath11k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
>
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
>
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
>
> Fix this by passing the correct TID entry:
> &peer->rx_tid[params->tid]
>
> Fixes: fe201947f8bd ("ath11k: update bawindow size in delba process")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
> ---
> drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
> index b9e976ddcbbf..8db04c38dfba 100644
> --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
> +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
> @@ -1132,7 +1132,7 @@ int ath11k_dp_rx_ampdu_stop(struct ath11k *ar,
> return 0;
> }
>
> - ret = ath11k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
> + ret = ath11k_peer_rx_tid_reo_update(ar, peer, &peer->rx_tid[params->tid], 1, 0, false);
> spin_unlock_bh(&ab->base_lock);
> if (ret) {
> ath11k_warn(ab, "failed to update reo for rx tid %d: %d\n",
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath12k: clear stale link mapping of ahvif->links_map
From: Baochen Qiang @ 2026-01-28 2:02 UTC (permalink / raw)
To: Aaradhana Sahu, ath12k; +Cc: linux-wireless
In-Reply-To: <20260127033400.1721220-1-aaradhana.sahu@oss.qualcomm.com>
On 1/27/2026 11:34 AM, Aaradhana Sahu wrote:
> When an arvif is initialized in non-AP STA mode but MLO connection
> preparation fails before the arvif is created
> (arvif->is_created remains false), the error path attempts to delete all
> links. However, link deletion only executes when arvif->is_created is true.
> As a result, ahvif retains a stale entry of arvif that is initialized but
> not created.
>
> When a new arvif is initialized with the same link id, this stale mapping
> triggers the following WARN_ON.
>
> WARNING: drivers/net/wireless/ath/ath12k/mac.c:4271 at ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k], CPU#3: wpa_supplicant/275
>
> Call trace:
> ath12k_mac_op_change_vif_links+0x140/0x180 [ath12k] (P)
> drv_change_vif_links+0xbc/0x1a4 [mac80211]
> ieee80211_vif_update_links+0x54c/0x6a0 [mac80211]
> ieee80211_vif_set_links+0x40/0x70 [mac80211]
> ieee80211_prep_connection+0x84/0x450 [mac80211]
> ieee80211_mgd_auth+0x200/0x480 [mac80211]
> ieee80211_auth+0x14/0x20 [mac80211]
> cfg80211_mlme_auth+0x90/0xf0 [cfg80211]
> nl80211_authenticate+0x32c/0x380 [cfg80211]
> genl_family_rcv_msg_doit+0xc8/0x134
>
> Fix this issue by unassigning the link vif and clearing ahvif->links_map
> if arvif is only initialized but not created.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
>
> Fixes: 81e4be30544e ("wifi: ath12k: handle link removal in change_vif_links()")
> Signed-off-by: Aaradhana Sahu <aaradhana.sahu@oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath12k/mac.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index cdb72439dcf4..9a926f5b7e8a 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -4281,8 +4281,10 @@ ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw,
> if (WARN_ON(!arvif))
> return -EINVAL;
>
> - if (!arvif->is_created)
> + if (!arvif->is_created) {
> + ath12k_mac_unassign_link_vif(arvif);
> continue;
> + }
>
> if (WARN_ON(!arvif->ar))
> return -EINVAL;
>
> base-commit: 13f9e5344a517048f3d8c61e49a9a72f97410de3
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* [wireless-next:main] BUILD SUCCESS c30e188bd2a886258be5facb970a804d8ef549b5
From: kernel test robot @ 2026-01-28 1:43 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git main
branch HEAD: c30e188bd2a886258be5facb970a804d8ef549b5 Merge tag 'iwlwifi-next-2026-01-21' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next
elapsed time: 722m
configs tested: 217
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-15.2.0
alpha allyesconfig gcc-15.2.0
alpha defconfig gcc-15.2.0
arc allmodconfig clang-16
arc allmodconfig gcc-15.2.0
arc allnoconfig gcc-15.2.0
arc allyesconfig clang-22
arc allyesconfig gcc-15.2.0
arc axs103_smp_defconfig gcc-15.2.0
arc defconfig gcc-15.2.0
arc nsimosci_hs_defconfig gcc-15.2.0
arc nsimosci_hs_smp_defconfig gcc-15.2.0
arc randconfig-001-20260128 gcc-8.5.0
arc randconfig-002-20260128 gcc-8.5.0
arm allnoconfig clang-22
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm allyesconfig gcc-15.2.0
arm defconfig gcc-15.2.0
arm pxa3xx_defconfig clang-22
arm randconfig-001-20260128 gcc-8.5.0
arm randconfig-002-20260128 gcc-8.5.0
arm randconfig-003-20260128 gcc-8.5.0
arm randconfig-004-20260128 gcc-8.5.0
arm64 allmodconfig clang-19
arm64 allmodconfig clang-22
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260128 gcc-14.3.0
arm64 randconfig-002-20260128 gcc-14.3.0
arm64 randconfig-003-20260128 gcc-14.3.0
arm64 randconfig-004-20260128 gcc-14.3.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260128 gcc-14.3.0
csky randconfig-002-20260128 gcc-14.3.0
hexagon allmodconfig clang-17
hexagon allmodconfig gcc-15.2.0
hexagon allnoconfig clang-22
hexagon allnoconfig gcc-15.2.0
hexagon defconfig gcc-15.2.0
hexagon randconfig-001-20260128 clang-22
hexagon randconfig-002-20260128 clang-22
i386 allmodconfig clang-20
i386 allmodconfig gcc-14
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 allyesconfig gcc-14
i386 buildonly-randconfig-001-20260128 clang-20
i386 buildonly-randconfig-002-20260128 clang-20
i386 buildonly-randconfig-003-20260128 clang-20
i386 buildonly-randconfig-004-20260128 clang-20
i386 buildonly-randconfig-005-20260128 clang-20
i386 buildonly-randconfig-006-20260128 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260128 gcc-14
i386 randconfig-002-20260128 gcc-14
i386 randconfig-003-20260128 gcc-14
i386 randconfig-004-20260128 gcc-14
i386 randconfig-005-20260128 gcc-14
i386 randconfig-006-20260128 gcc-14
i386 randconfig-007-20260128 gcc-14
i386 randconfig-011-20260128 clang-20
i386 randconfig-012-20260128 gcc-14
i386 randconfig-013-20260128 gcc-14
i386 randconfig-014-20260128 clang-20
i386 randconfig-015-20260128 clang-20
i386 randconfig-016-20260128 gcc-14
i386 randconfig-017-20260128 clang-20
loongarch allmodconfig clang-19
loongarch allmodconfig clang-22
loongarch allnoconfig clang-22
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260128 clang-22
loongarch randconfig-002-20260128 clang-22
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k allyesconfig gcc-15.2.0
m68k defconfig clang-19
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips cu1830-neo_defconfig gcc-15.2.0
mips ip27_defconfig gcc-15.2.0
mips ip30_defconfig gcc-15.2.0
nios2 allmodconfig clang-22
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-22
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-19
nios2 randconfig-001-20260128 clang-22
nios2 randconfig-002-20260128 clang-22
openrisc allmodconfig clang-22
openrisc allmodconfig gcc-15.2.0
openrisc allnoconfig clang-22
openrisc allnoconfig gcc-15.2.0
openrisc defconfig gcc-15.2.0
parisc allmodconfig gcc-15.2.0
parisc allnoconfig clang-22
parisc allnoconfig gcc-15.2.0
parisc allyesconfig gcc-15.2.0
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260128 gcc-11.5.0
parisc randconfig-002-20260128 gcc-11.5.0
parisc randconfig-002-20260128 gcc-12.5.0
parisc64 alldefconfig gcc-15.2.0
parisc64 defconfig clang-19
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-22
powerpc allnoconfig gcc-15.2.0
powerpc fsp2_defconfig gcc-15.2.0
powerpc mgcoge_defconfig clang-22
powerpc mpc83xx_defconfig clang-22
powerpc mvme5100_defconfig gcc-15.2.0
powerpc ppc6xx_defconfig gcc-15.2.0
powerpc randconfig-001-20260128 clang-22
powerpc randconfig-001-20260128 gcc-11.5.0
powerpc randconfig-002-20260128 gcc-11.5.0
powerpc randconfig-002-20260128 gcc-8.5.0
powerpc redwood_defconfig clang-22
powerpc64 randconfig-001-20260128 clang-22
powerpc64 randconfig-001-20260128 gcc-11.5.0
powerpc64 randconfig-002-20260128 clang-22
powerpc64 randconfig-002-20260128 gcc-11.5.0
riscv allmodconfig clang-22
riscv allnoconfig clang-22
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260128 gcc-13.4.0
riscv randconfig-002-20260128 gcc-13.4.0
s390 allmodconfig clang-18
s390 allnoconfig clang-22
s390 allyesconfig gcc-15.2.0
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260128 gcc-13.4.0
s390 randconfig-002-20260128 gcc-13.4.0
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-22
sh allnoconfig gcc-15.2.0
sh allyesconfig gcc-15.2.0
sh defconfig gcc-14
sh randconfig-001-20260128 gcc-13.4.0
sh randconfig-002-20260128 gcc-13.4.0
sparc allnoconfig clang-22
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260128 gcc-11.5.0
sparc randconfig-001-20260128 gcc-8.5.0
sparc randconfig-002-20260128 gcc-11.5.0
sparc randconfig-002-20260128 gcc-8.5.0
sparc sparc64_defconfig gcc-15.2.0
sparc64 allmodconfig clang-22
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260128 gcc-11.5.0
sparc64 randconfig-001-20260128 gcc-12.5.0
sparc64 randconfig-002-20260128 gcc-10.5.0
sparc64 randconfig-002-20260128 gcc-11.5.0
um allmodconfig clang-19
um allnoconfig clang-22
um allyesconfig gcc-14
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260128 gcc-11.5.0
um randconfig-001-20260128 gcc-12
um randconfig-002-20260128 clang-22
um randconfig-002-20260128 gcc-11.5.0
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-20
x86_64 allnoconfig clang-20
x86_64 allnoconfig clang-22
x86_64 allyesconfig clang-20
x86_64 buildonly-randconfig-001-20260128 gcc-14
x86_64 buildonly-randconfig-002-20260128 gcc-14
x86_64 buildonly-randconfig-003-20260128 gcc-14
x86_64 buildonly-randconfig-004-20260128 gcc-14
x86_64 buildonly-randconfig-005-20260128 gcc-14
x86_64 buildonly-randconfig-006-20260128 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-011-20260128 clang-20
x86_64 randconfig-012-20260128 clang-20
x86_64 randconfig-012-20260128 gcc-14
x86_64 randconfig-013-20260128 clang-20
x86_64 randconfig-014-20260128 clang-20
x86_64 randconfig-015-20260128 clang-20
x86_64 randconfig-015-20260128 gcc-14
x86_64 randconfig-016-20260128 clang-20
x86_64 randconfig-071-20260128 clang-20
x86_64 randconfig-072-20260128 clang-20
x86_64 randconfig-073-20260128 clang-20
x86_64 randconfig-074-20260128 clang-20
x86_64 randconfig-075-20260128 clang-20
x86_64 randconfig-076-20260128 clang-20
x86_64 rhel-9.4 clang-20
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-20
x86_64 rhel-9.4-kselftests clang-20
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-20
xtensa allnoconfig clang-22
xtensa allnoconfig gcc-15.2.0
xtensa allyesconfig clang-22
xtensa allyesconfig gcc-15.2.0
xtensa nommu_kc705_defconfig gcc-15.2.0
xtensa randconfig-001-20260128 gcc-11.5.0
xtensa randconfig-001-20260128 gcc-8.5.0
xtensa randconfig-002-20260128 gcc-11.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH v2] wifi: mt76: fix backoff fields and max_power calculation
From: Ryder Lee @ 2026-01-27 23:55 UTC (permalink / raw)
To: Felix Fietkau; +Cc: linux-wireless, linux-mediatek, Allen Ye, Ryder Lee
From: Allen Ye <allen.ye@mediatek.com>
The maximum power value may exist in either the data or backoff field.
Previously, backoff power limits were not considered in txpower reporting.
This patch ensures mt76 also considers backoff values in the SKU table.
For connac2 devices,
- paths-ru = RU26, RU52, RU106, BW20, BW40, BW80, BW160
- paths-ru-bf = RU26, RU52, RU106, BW20, BW40, BW80, BW160
Only the first three entries use 1T1S and do not need index adjustment;
the remaining four require index offset.
Fixes: b05ab4be9fd7 ("wifi: mt76: mt7915: add bf backoff limit table support")
Signed-off-by: Allen Ye <allen.ye@mediatek.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
v1:
- Add "wifi:" prefix to the subject.
v2:
- Fix checkpatch errors.
- Remove unnecessary style changes.
- Add explanation for connac2 index adjustment.
---
drivers/net/wireless/mediatek/mt76/eeprom.c | 180 +++++++++++++-------
drivers/net/wireless/mediatek/mt76/mt76.h | 1 -
2 files changed, 122 insertions(+), 59 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c
index 573400d57..6ca0274b4 100644
--- a/drivers/net/wireless/mediatek/mt76/eeprom.c
+++ b/drivers/net/wireless/mediatek/mt76/eeprom.c
@@ -9,6 +9,13 @@
#include <linux/nvmem-consumer.h>
#include <linux/etherdevice.h>
#include "mt76.h"
+#include "mt76_connac.h"
+
+enum mt76_sku_type {
+ MT76_SKU_RATE,
+ MT76_SKU_BACKOFF,
+ MT76_SKU_BACKOFF_BF_OFFSET,
+};
static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len)
{
@@ -292,7 +299,6 @@ mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
}
EXPORT_SYMBOL_GPL(mt76_find_channel_node);
-
static s8
mt76_get_txs_delta(struct device_node *np, u8 nss)
{
@@ -306,9 +312,24 @@ mt76_get_txs_delta(struct device_node *np, u8 nss)
return be32_to_cpu(val[nss - 1]);
}
+static inline u8 mt76_backoff_n_chains(struct mt76_dev *dev, u8 idx)
+{
+ /* 0:1T1ss, 1:2T1ss, ..., 14:5T5ss */
+ static const u8 connac3_table[] = {
+ 1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5};
+ static const u8 connac2_table[] = {
+ 1, 2, 3, 4, 2, 3, 4, 3, 4, 4, 0, 0, 0, 0, 0};
+
+ if (idx < 0 || idx >= ARRAY_SIZE(connac3_table))
+ return 0;
+
+ return is_mt799x(dev) ? connac3_table[idx] : connac2_table[idx];
+}
+
static void
-mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
- s8 target_power, s8 nss_delta, s8 *max_power)
+mt76_apply_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len,
+ const s8 *data, s8 target_power, s8 nss_delta,
+ s8 *max_power, int n_chains, enum mt76_sku_type type)
{
int i;
@@ -316,18 +337,51 @@ mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const s8 *data,
return;
for (i = 0; i < pwr_len; i++) {
- pwr[i] = min_t(s8, target_power, data[i] + nss_delta);
+ u8 backoff_chain_idx = i;
+ int backoff_n_chains;
+ s8 backoff_delta;
+ s8 delta;
+
+ switch (type) {
+ case MT76_SKU_RATE:
+ delta = 0;
+ backoff_delta = 0;
+ backoff_n_chains = 0;
+ break;
+ case MT76_SKU_BACKOFF:
+ backoff_chain_idx += 1;
+ fallthrough;
+ case MT76_SKU_BACKOFF_BF_OFFSET:
+ delta = mt76_tx_power_path_delta(n_chains);
+ backoff_n_chains = mt76_backoff_n_chains(dev, backoff_chain_idx);
+ backoff_delta = mt76_tx_power_path_delta(backoff_n_chains);
+ break;
+ default:
+ return;
+ }
+
+ pwr[i] = min_t(s8, target_power + delta - backoff_delta, data[i] + nss_delta);
+
+ /* used for padding, doesn't need to be considered */
+ if (data[i] >= S8_MAX - 1)
+ continue;
+
+ /* only consider backoff value for the configured chain number */
+ if (type != MT76_SKU_RATE && n_chains != backoff_n_chains)
+ continue;
+
*max_power = max(*max_power, pwr[i]);
}
}
static void
-mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
- const s8 *data, size_t len, s8 target_power,
- s8 nss_delta)
+mt76_apply_multi_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len,
+ s8 pwr_num, const s8 *data, size_t len,
+ s8 target_power, s8 nss_delta, s8 *max_power,
+ int n_chains, enum mt76_sku_type type)
{
+ static const int connac2_backoff_ru_idx = 2;
int i, cur;
- s8 max_power = -128;
if (!data)
return;
@@ -337,8 +391,19 @@ mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
if (len < pwr_len + 1)
break;
- mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
- target_power, nss_delta, &max_power);
+ /* For connac2 devices,
+ * - paths-ru = RU26, RU52, RU106, BW20, BW40, BW80, BW160
+ * - paths-ru-bf = RU26, RU52, RU106, BW20, BW40, BW80, BW160
+ * Only the first three entries use 1T1ss and do not need index
+ * adjustment; the remaining four require index offset.
+ */
+ if (!is_mt799x(dev) && type == MT76_SKU_BACKOFF &&
+ i > connac2_backoff_ru_idx)
+ type = MT76_SKU_BACKOFF_BF_OFFSET;
+
+ mt76_apply_array_limit(dev, pwr + pwr_len * i, pwr_len, data + 1,
+ target_power, nss_delta, max_power,
+ n_chains, type);
if (--cur > 0)
continue;
@@ -360,18 +425,11 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
struct device_node *np;
const s8 *val;
char name[16];
- u32 mcs_rates = dev->drv->mcs_rates;
- u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
char band;
size_t len;
- s8 max_power = 0;
- s8 max_power_backoff = -127;
+ s8 max_power = -127;
s8 txs_delta;
int n_chains = hweight16(phy->chainmask);
- s8 target_power_combine = target_power + mt76_tx_power_path_delta(n_chains);
-
- if (!mcs_rates)
- mcs_rates = 10;
memset(dest, target_power, sizeof(*dest) - sizeof(dest->path));
memset(&dest->path, 0, sizeof(dest->path));
@@ -409,46 +467,52 @@ s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask));
val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
- mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
- target_power, txs_delta, &max_power);
-
- val = mt76_get_of_array_s8(np, "rates-ofdm",
- &len, ARRAY_SIZE(dest->ofdm));
- mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
- target_power, txs_delta, &max_power);
-
- val = mt76_get_of_array_s8(np, "rates-mcs", &len, mcs_rates + 1);
- mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
- ARRAY_SIZE(dest->mcs), val, len,
- target_power, txs_delta);
-
- val = mt76_get_of_array_s8(np, "rates-ru", &len, ru_rates + 1);
- mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
- ARRAY_SIZE(dest->ru), val, len,
- target_power, txs_delta);
-
- max_power_backoff = max_power;
- val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck));
- mt76_apply_array_limit(dest->path.cck, ARRAY_SIZE(dest->path.cck), val,
- target_power_combine, txs_delta, &max_power_backoff);
-
- val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm));
- mt76_apply_array_limit(dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val,
- target_power_combine, txs_delta, &max_power_backoff);
-
- val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf));
- mt76_apply_array_limit(dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val,
- target_power_combine, txs_delta, &max_power_backoff);
-
- val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1);
- mt76_apply_multi_array_limit(dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]),
- ARRAY_SIZE(dest->path.ru), val, len,
- target_power_combine, txs_delta);
-
- val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1);
- mt76_apply_multi_array_limit(dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]),
- ARRAY_SIZE(dest->path.ru_bf), val, len,
- target_power_combine, txs_delta);
+ mt76_apply_array_limit(dev, dest->cck, ARRAY_SIZE(dest->cck), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE);
+
+ val = mt76_get_of_array_s8(np, "rates-ofdm", &len, ARRAY_SIZE(dest->ofdm));
+ mt76_apply_array_limit(dev, dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE);
+
+ val = mt76_get_of_array_s8(np, "rates-mcs", &len,
+ ARRAY_SIZE(dest->mcs[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
+ ARRAY_SIZE(dest->mcs), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_RATE);
+
+ val = mt76_get_of_array_s8(np, "rates-ru", &len,
+ ARRAY_SIZE(dest->ru[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->ru[0], ARRAY_SIZE(dest->ru[0]),
+ ARRAY_SIZE(dest->ru), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_RATE);
+
+ val = mt76_get_of_array_s8(np, "paths-cck", &len,
+ ARRAY_SIZE(dest->path.cck));
+ mt76_apply_array_limit(dev, dest->path.cck, ARRAY_SIZE(dest->path.cck), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
+
+ val = mt76_get_of_array_s8(np, "paths-ofdm", &len,
+ ARRAY_SIZE(dest->path.ofdm));
+ mt76_apply_array_limit(dev, dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val,
+ target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
+
+ val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len,
+ ARRAY_SIZE(dest->path.ofdm_bf));
+ mt76_apply_array_limit(dev, dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val,
+ target_power, txs_delta, &max_power, n_chains,
+ MT76_SKU_BACKOFF_BF_OFFSET);
+
+ val = mt76_get_of_array_s8(np, "paths-ru", &len,
+ ARRAY_SIZE(dest->path.ru[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]),
+ ARRAY_SIZE(dest->path.ru), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
+
+ val = mt76_get_of_array_s8(np, "paths-ru-bf", &len,
+ ARRAY_SIZE(dest->path.ru_bf[0]) + 1);
+ mt76_apply_multi_array_limit(dev, dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]),
+ ARRAY_SIZE(dest->path.ru_bf), val, len, target_power,
+ txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF);
return max_power;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index d05e83ea1..32876eab2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -540,7 +540,6 @@ struct mt76_driver_ops {
u32 survey_flags;
u16 txwi_size;
u16 token_size;
- u8 mcs_rates;
unsigned int link_data_size;
--
2.45.2
^ permalink raw reply related
* Re: [PATCH ath-current v2] wifi: ath12k: fix wrong TID passed when stopping AMPDU session
From: Jeff Johnson @ 2026-01-27 23:39 UTC (permalink / raw)
To: Pablo Martin-Gomez, ath12k; +Cc: linux-wireless
In-Reply-To: <20260126173655.1369715-1-pmartin-gomez@freebox.fr>
On 1/26/2026 9:36 AM, Pablo Martin-Gomez wrote:
> When handling a DELBA request, ath12k_dp_rx_ampdu_stop() calls
> ath12k_peer_rx_tid_reo_update() to tear down the BA session for the
> specified TID. However, it currently passes peer->rx_tid instead of the
> entry corresponding to params->tid.
>
> Since peer->rx_tid is an array, this decays to a pointer to the first
> element, effectively operating on TID 0 regardless of the TID in the
> DELBA request. As a result, the BA session for TID 0 is stopped while
> the intended TID remains active.
>
> This leads to incorrect BA session state and may significantly reduce
> RX throughput, as traffic that should use aggregation falls back to a
> BA window size of 1 on TID 0.
>
> Fix this by passing the correct TID entry:
> &peer->rx_tid[params->tid]
>
> Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices")
> Signed-off-by: Pablo Martin-Gomez <pmartin-gomez@freebox.fr>
> ---
> drivers/net/wireless/ath/ath12k/dp_rx.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
> index d28d8ffec0f8..f2327c82953b 100644
> --- a/drivers/net/wireless/ath/ath12k/dp_rx.c
> +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
> @@ -1289,7 +1289,7 @@ int ath12k_dp_rx_ampdu_stop(struct ath12k *ar,
> return 0;
> }
>
> - ret = ath12k_peer_rx_tid_reo_update(ar, peer, peer->rx_tid, 1, 0, false);
> + ret = ath12k_peer_rx_tid_reo_update(ar, peer, &peer->rx_tid[params->tid], 1, 0, false);
like the ath11k change, this also triggers ath12k-check:
drivers/net/wireless/ath/ath12k/dp_rx.c:1292: line length of 95 exceeds 90 columns
I'll break this line when I process the patch
> spin_unlock_bh(&ab->base_lock);
> if (ret) {
> ath12k_warn(ab, "failed to update reo for rx tid %d: %d\n",
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox