* [PATCH wireless-next v7 3/3] wifi: mac80211: add initial UHR support
From: Johannes Berg @ 2026-01-30 13:20 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260130142019.bd561c0eae91.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>
From: Johannes Berg <johannes.berg@intel.com>
Add support for making UHR connections and accepting AP
stations with UHR support.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v7:
- adjust and normalize naming
v6:
- fix ieee80211_uhr_phy_cap() usage
v5:
- parse UHR capa as from AP
v4:
- fix NPCA validation
v3:
- use uhr_oper instead of removed uhr_capa
- fix indentation (Jeff Johnson)
---
include/net/mac80211.h | 35 ++++++++++-
net/mac80211/Makefile | 2 +-
net/mac80211/cfg.c | 16 +++++-
net/mac80211/ieee80211_i.h | 19 +++++-
net/mac80211/main.c | 15 ++++-
net/mac80211/mlme.c | 115 ++++++++++++++++++++++++++++++++++---
net/mac80211/parse.c | 22 ++++++-
net/mac80211/rx.c | 26 +++++++++
net/mac80211/sta_info.c | 13 ++++-
net/mac80211/sta_info.h | 80 +++++++++++++++++++-------
net/mac80211/uhr.c | 30 ++++++++++
net/mac80211/util.c | 36 +++++++++++-
12 files changed, 370 insertions(+), 39 deletions(-)
create mode 100644 net/mac80211/uhr.c
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 36ae7fe9ddf3..7a55762f9af8 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7,7 +7,7 @@
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*/
#ifndef MAC80211_H
@@ -706,6 +706,7 @@ struct ieee80211_parsed_tpe {
* @pwr_reduction: power constraint of BSS.
* @eht_support: does this BSS support EHT
* @epcs_support: does this BSS support EPCS
+ * @uhr_support: does this BSS support UHR
* @csa_active: marks whether a channel switch is going on.
* @mu_mimo_owner: indicates interface owns MU-MIMO capability
* @chanctx_conf: The channel context this interface is assigned to, or %NULL
@@ -832,6 +833,8 @@ struct ieee80211_bss_conf {
u8 pwr_reduction;
bool eht_support;
bool epcs_support;
+ bool uhr_support;
+
bool csa_active;
bool mu_mimo_owner;
@@ -1598,6 +1601,7 @@ enum mac80211_rx_encoding {
RX_ENC_VHT,
RX_ENC_HE,
RX_ENC_EHT,
+ RX_ENC_UHR,
};
/**
@@ -1631,7 +1635,7 @@ enum mac80211_rx_encoding {
* @antenna: antenna used
* @rate_idx: index of data rate into band's supported rates or MCS index if
* HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT)
- * @nss: number of streams (VHT, HE and EHT only)
+ * @nss: number of streams (VHT, HE, EHT and UHR only)
* @flag: %RX_FLAG_\*
* @encoding: &enum mac80211_rx_encoding
* @bw: &enum rate_info_bw
@@ -1642,6 +1646,11 @@ enum mac80211_rx_encoding {
* @eht: EHT specific rate information
* @eht.ru: EHT RU, from &enum nl80211_eht_ru_alloc
* @eht.gi: EHT GI, from &enum nl80211_eht_gi
+ * @uhr: UHR specific rate information
+ * @uhr.ru: UHR RU, from &enum nl80211_eht_ru_alloc
+ * @uhr.gi: UHR GI, from &enum nl80211_eht_gi
+ * @uhr.elr: UHR ELR MCS was used
+ * @uhr.im: UHR interference mitigation was used
* @rx_flags: internal RX flags for mac80211
* @ampdu_reference: A-MPDU reference number, must be a different value for
* each A-MPDU but the same for each subframe within one A-MPDU
@@ -1673,6 +1682,12 @@ struct ieee80211_rx_status {
u8 ru:4;
u8 gi:2;
} eht;
+ struct {
+ u8 ru:4;
+ u8 gi:2;
+ u8 elr:1;
+ u8 im:1;
+ } uhr;
};
u8 rate_idx;
u8 nss;
@@ -2434,6 +2449,7 @@ struct ieee80211_sta_aggregates {
* @he_cap: HE capabilities of this STA
* @he_6ghz_capa: on 6 GHz, holds the HE 6 GHz band capabilities
* @eht_cap: EHT capabilities of this STA
+ * @uhr_cap: UHR capabilities of this STA
* @s1g_cap: S1G capabilities of this STA
* @agg: per-link data for multi-link aggregation
* @bandwidth: current bandwidth the station can receive with
@@ -2457,6 +2473,7 @@ struct ieee80211_link_sta {
struct ieee80211_sta_he_cap he_cap;
struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
+ struct ieee80211_sta_uhr_cap uhr_cap;
struct ieee80211_sta_s1g_cap s1g_cap;
struct ieee80211_sta_aggregates agg;
@@ -7289,6 +7306,20 @@ ieee80211_get_eht_iftype_cap_vif(const struct ieee80211_supported_band *sband,
return ieee80211_get_eht_iftype_cap(sband, ieee80211_vif_type_p2p(vif));
}
+/**
+ * ieee80211_get_uhr_iftype_cap_vif - return UHR capabilities for sband/vif
+ * @sband: the sband to search for the iftype on
+ * @vif: the vif to get the iftype from
+ *
+ * Return: pointer to the struct ieee80211_sta_uhr_cap, or %NULL is none found
+ */
+static inline const struct ieee80211_sta_uhr_cap *
+ieee80211_get_uhr_iftype_cap_vif(const struct ieee80211_supported_band *sband,
+ struct ieee80211_vif *vif)
+{
+ return ieee80211_get_uhr_iftype_cap(sband, ieee80211_vif_type_p2p(vif));
+}
+
/**
* ieee80211_update_mu_groups - set the VHT MU-MIMO groud data
*
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a33884967f21..b0e392eb7753 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -36,7 +36,7 @@ mac80211-y := \
tdls.o \
ocb.o \
airtime.o \
- eht.o
+ eht.o uhr.o
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 964f440e31cd..f83dda0755a7 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5,7 +5,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -1608,6 +1608,13 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
link_conf->eht_mu_beamformer = false;
}
+ if (params->uhr_oper) {
+ if (!link_conf->eht_support)
+ return -EOPNOTSUPP;
+
+ link_conf->uhr_support = true;
+ }
+
if (sdata->vif.type == NL80211_IFTYPE_AP &&
params->mbssid_config.tx_wdev) {
err = ieee80211_set_ap_mbssid_options(sdata,
@@ -2085,6 +2092,7 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
params->vht_capa ||
params->he_capa ||
params->eht_capa ||
+ params->uhr_capa ||
params->s1g_capa ||
params->opmode_notif_used;
@@ -2163,6 +2171,12 @@ static int sta_link_apply_parameters(struct ieee80211_local *local,
params->eht_capa_len,
link_sta);
+ if (params->uhr_capa)
+ ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband,
+ params->uhr_capa,
+ params->uhr_capa_len,
+ link_sta);
+
if (params->s1g_capa)
ieee80211_s1g_cap_to_sta_s1g_cap(sdata, params->s1g_capa,
link_sta);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index dc757cb32974..56fd8fb305c7 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#ifndef IEEE80211_I_H
@@ -394,9 +394,10 @@ enum ieee80211_conn_mode {
IEEE80211_CONN_MODE_VHT,
IEEE80211_CONN_MODE_HE,
IEEE80211_CONN_MODE_EHT,
+ IEEE80211_CONN_MODE_UHR,
};
-#define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_EHT
+#define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_UHR
enum ieee80211_conn_bw_limit {
IEEE80211_CONN_BW_LIMIT_20,
@@ -1826,6 +1827,8 @@ struct ieee802_11_elems {
const struct ieee80211_multi_link_elem *ml_epcs;
const struct ieee80211_bandwidth_indication *bandwidth_indication;
const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT];
+ const struct ieee80211_uhr_cap *uhr_cap;
+ const struct ieee80211_uhr_operation *uhr_operation;
/* not the order in the psd values is per element, not per chandef */
struct ieee80211_parsed_tpe tpe;
@@ -1850,6 +1853,8 @@ struct ieee802_11_elems {
u8 country_elem_len;
u8 bssid_index_len;
u8 eht_cap_len;
+ u8 uhr_cap_len;
+ u8 uhr_operation_len;
/* mult-link element can be de-fragmented and thus u8 is not sufficient */
size_t ml_basic_len;
@@ -2693,6 +2698,9 @@ int ieee80211_put_eht_cap(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata,
const struct ieee80211_supported_band *sband,
const struct ieee80211_conn_settings *conn);
+int ieee80211_put_uhr_cap(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_supported_band *sband);
int ieee80211_put_reg_conn(struct sk_buff *skb,
enum ieee80211_channel_flags flags);
@@ -2868,6 +2876,13 @@ void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len);
void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata);
+void
+ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_uhr_cap *uhr_cap,
+ u8 uhr_cap_len,
+ struct link_sta_info *link_sta);
+
#if IS_ENABLED(CONFIG_MAC80211_KUNIT_TEST)
#define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym)
#define VISIBLE_IF_MAC80211_KUNIT
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index b05e313c7f17..bedc81956fbc 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -5,7 +5,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <net/mac80211.h>
@@ -1123,7 +1123,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
int result, i;
enum nl80211_band band;
int channels, max_bitrates;
- bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g;
+ bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g, supp_uhr;
struct cfg80211_chan_def dflt_chandef = {};
if (ieee80211_hw_check(hw, QUEUE_CONTROL) &&
@@ -1237,6 +1237,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_he = false;
supp_eht = false;
supp_s1g = false;
+ supp_uhr = false;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
const struct ieee80211_sband_iftype_data *iftd;
struct ieee80211_supported_band *sband;
@@ -1293,6 +1294,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_he = supp_he || iftd->he_cap.has_he;
supp_eht = supp_eht || iftd->eht_cap.has_eht;
+ supp_uhr = supp_uhr || iftd->uhr_cap.has_uhr;
if (band == NL80211_BAND_2GHZ)
he_40_mhz_cap =
@@ -1325,6 +1327,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (WARN_ON(supp_eht && !supp_he))
return -EINVAL;
+ /* UHR requires EHT support */
+ if (WARN_ON(supp_uhr && !supp_eht))
+ return -EINVAL;
+
if (!sband->ht_cap.ht_supported)
continue;
@@ -1437,6 +1443,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
IEEE80211_EHT_PPE_THRES_MAX_LEN;
}
+ if (supp_uhr)
+ local->scan_ies_len +=
+ 3 + sizeof(struct ieee80211_uhr_cap) +
+ sizeof(struct ieee80211_uhr_cap_phy);
+
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
local->hw.wiphy->max_scan_ssids = 4;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 6e468c4fcda2..804e822b1287 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -162,6 +162,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_vht_operation *vht_oper = elems->vht_operation;
const struct ieee80211_he_operation *he_oper = elems->he_operation;
const struct ieee80211_eht_operation *eht_oper = elems->eht_operation;
+ const struct ieee80211_uhr_operation *uhr_oper = elems->uhr_operation;
struct ieee80211_supported_band *sband =
sdata->local->hw.wiphy->bands[channel->band];
struct cfg80211_chan_def vht_chandef;
@@ -192,7 +193,7 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
/* get special 6 GHz case out of the way */
if (sband->band == NL80211_BAND_6GHZ) {
- enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_EHT;
+ enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_HIGHEST;
/* this is an error */
if (conn->mode < IEEE80211_CONN_MODE_HE)
@@ -215,7 +216,9 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
return IEEE80211_CONN_MODE_LEGACY;
}
- return mode;
+ if (mode <= IEEE80211_CONN_MODE_EHT)
+ return mode;
+ goto check_uhr;
}
/* now we have the progression HT, VHT, ... */
@@ -340,7 +343,63 @@ ieee80211_determine_ap_chan(struct ieee80211_sub_if_data *sdata,
*chandef = eht_chandef;
}
- return IEEE80211_CONN_MODE_EHT;
+check_uhr:
+ if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper)
+ return IEEE80211_CONN_MODE_EHT;
+
+ /*
+ * In beacons we don't have all the data - but we know the size was OK,
+ * so if the size is valid as a non-beacon case, we have more data and
+ * can validate the NPCA parameters.
+ */
+ if (ieee80211_uhr_oper_size_ok((const void *)uhr_oper,
+ elems->uhr_operation_len,
+ false)) {
+ struct cfg80211_chan_def npca_chandef = *chandef;
+ const struct ieee80211_uhr_npca_info *npca;
+ const __le16 *dis_subch_bmap;
+ u16 punct = chandef->punctured, npca_punct;
+
+ npca = ieee80211_uhr_npca_info(uhr_oper);
+ if (npca) {
+ int width = cfg80211_chandef_get_width(chandef);
+ u8 offs = le32_get_bits(npca->params,
+ IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS);
+ u32 cf1 = chandef->center_freq1;
+ bool pri_upper, npca_upper;
+
+ pri_upper = chandef->chan->center_freq > cf1;
+ npca_upper = 20 * offs >= width / 2;
+
+ if (20 * offs >= cfg80211_chandef_get_width(chandef) ||
+ pri_upper == npca_upper) {
+ sdata_info(sdata,
+ "AP UHR NPCA primary channel invalid, disabling UHR\n");
+ return IEEE80211_CONN_MODE_EHT;
+ }
+ }
+
+ dis_subch_bmap = ieee80211_uhr_npca_dis_subch_bitmap(uhr_oper);
+
+ if (dis_subch_bmap) {
+ npca_punct = get_unaligned_le16(dis_subch_bmap);
+ npca_chandef.punctured = npca_punct;
+ }
+
+ /*
+ * must be a valid puncturing pattern for this channel as
+ * well as puncturing all subchannels that are already in
+ * the disabled subchannel bitmap on the primary channel
+ */
+ if (!cfg80211_chandef_valid(&npca_chandef) ||
+ ((punct & npca_punct) != punct)) {
+ sdata_info(sdata,
+ "AP UHR NPCA disabled subchannel bitmap invalid, disabling UHR\n");
+ return IEEE80211_CONN_MODE_EHT;
+ }
+ }
+
+ return IEEE80211_CONN_MODE_UHR;
}
static bool
@@ -1091,6 +1150,7 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
IEEE80211_CONN_BW_LIMIT_160);
break;
case IEEE80211_CONN_MODE_EHT:
+ case IEEE80211_CONN_MODE_UHR:
conn->bw_limit = min_t(enum ieee80211_conn_bw_limit,
conn->bw_limit,
IEEE80211_CONN_BW_LIMIT_320);
@@ -1108,6 +1168,8 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
set_bit(BSS_MEMBERSHIP_SELECTOR_HE_PHY, sta_selectors);
if (conn->mode >= IEEE80211_CONN_MODE_EHT)
set_bit(BSS_MEMBERSHIP_SELECTOR_EHT_PHY, sta_selectors);
+ if (conn->mode >= IEEE80211_CONN_MODE_UHR)
+ set_bit(BSS_MEMBERSHIP_SELECTOR_UHR_PHY, sta_selectors);
/*
* We do not support EPD or GLK so never add them.
@@ -1155,6 +1217,11 @@ ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
IEEE80211_CONN_BW_LIMIT_160);
}
+ if (conn->mode >= IEEE80211_CONN_MODE_UHR &&
+ !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper,
+ IEEE80211_CHAN_NO_UHR))
+ conn->mode = IEEE80211_CONN_MODE_EHT;
+
if (chanreq->oper.width != ap_chandef->width || ap_mode != conn->mode)
link_id_info(sdata, link_id,
"regulatory prevented using AP config, downgraded\n");
@@ -1884,11 +1951,13 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata,
/*
* careful - need to know about all the present elems before
- * calling ieee80211_assoc_add_ml_elem(), so add this one if
- * we're going to put it after the ML element
+ * calling ieee80211_assoc_add_ml_elem(), so add these if
+ * we're going to put them after the ML element
*/
if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT)
ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY);
+ if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR)
+ ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_UHR_CAPA);
if (link_id == assoc_data->assoc_link_id)
ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa,
@@ -1901,6 +1970,9 @@ ieee80211_add_link_elems(struct ieee80211_sub_if_data *sdata,
ieee80211_put_eht_cap(skb, sdata, sband,
&assoc_data->link[link_id].conn);
+ if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR)
+ ieee80211_put_uhr_cap(skb, sdata, sband);
+
if (sband->band == NL80211_BAND_S1GHZ) {
ieee80211_add_aid_request_ie(sdata, skb);
ieee80211_add_s1g_capab_ie(sdata, &sband->s1g_cap, skb);
@@ -2135,6 +2207,9 @@ ieee80211_link_common_elems_size(struct ieee80211_sub_if_data *sdata,
sizeof(struct ieee80211_eht_mcs_nss_supp) +
IEEE80211_EHT_PPE_THRES_MAX_LEN;
+ size += 2 + 1 + sizeof(struct ieee80211_uhr_cap) +
+ sizeof(struct ieee80211_uhr_cap_phy);
+
return size;
}
@@ -5531,6 +5606,18 @@ static bool ieee80211_assoc_config_link(struct ieee80211_link_data *link,
bss_conf->epcs_support = false;
}
+ if (elems->uhr_operation && elems->uhr_cap &&
+ link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) {
+ ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband,
+ elems->uhr_cap,
+ elems->uhr_cap_len,
+ link_sta);
+
+ bss_conf->uhr_support = link_sta->pub->uhr_cap.has_uhr;
+ } else {
+ bss_conf->uhr_support = false;
+ }
+
if (elems->s1g_oper &&
link->u.mgd.conn.mode == IEEE80211_CONN_MODE_S1G &&
elems->s1g_capab)
@@ -5821,6 +5908,7 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
bool is_6ghz = sband->band == NL80211_BAND_6GHZ;
const struct ieee80211_sta_he_cap *he_cap;
const struct ieee80211_sta_eht_cap *eht_cap;
+ const struct ieee80211_sta_uhr_cap *uhr_cap;
struct ieee80211_sta_vht_cap vht_cap;
if (sband->band == NL80211_BAND_S1GHZ) {
@@ -5996,9 +6084,6 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
"no EHT support, limiting to HE\n");
goto out;
}
-
- /* we have EHT */
-
conn->mode = IEEE80211_CONN_MODE_EHT;
/* check bandwidth */
@@ -6009,6 +6094,20 @@ ieee80211_determine_our_sta_mode(struct ieee80211_sub_if_data *sdata,
mlme_link_id_dbg(sdata, link_id,
"no EHT 320 MHz cap in 6 GHz, limiting to 160 MHz\n");
+ if (req && req->flags & ASSOC_REQ_DISABLE_UHR) {
+ mlme_link_id_dbg(sdata, link_id,
+ "UHR disabled by flag, limiting to EHT\n");
+ goto out;
+ }
+
+ uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
+ if (!uhr_cap) {
+ mlme_link_id_dbg(sdata, link_id,
+ "no UHR support, limiting to EHT\n");
+ goto out;
+ }
+ conn->mode = IEEE80211_CONN_MODE_UHR;
+
out:
mlme_link_id_dbg(sdata, link_id,
"determined local STA to be %s, BW limited to %d MHz\n",
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 667021bc60c6..8260f6bdd5b2 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -6,7 +6,7 @@
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*
* element parsing for mac80211
*/
@@ -189,6 +189,26 @@ ieee80211_parse_extension_element(u32 *crc,
elems->ttlm_num++;
}
break;
+ case WLAN_EID_EXT_UHR_OPER:
+ if (params->mode < IEEE80211_CONN_MODE_UHR)
+ break;
+ calc_crc = true;
+ if (ieee80211_uhr_oper_size_ok(data, len,
+ params->type == (IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_BEACON))) {
+ elems->uhr_operation = data;
+ elems->uhr_operation_len = len;
+ }
+ break;
+ case WLAN_EID_EXT_UHR_CAPA:
+ if (params->mode < IEEE80211_CONN_MODE_UHR)
+ break;
+ calc_crc = true;
+ if (ieee80211_uhr_capa_size_ok(data, len, true)) {
+ elems->uhr_cap = data;
+ elems->uhr_cap_len = len;
+ }
+ break;
}
if (crc && calc_crc)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 30b9b4d76357..69034d83a7b6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -5518,6 +5518,32 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
status->rate_idx, status->nss, status->eht.gi))
goto drop;
break;
+ case RX_ENC_UHR:
+ if (WARN_ONCE(!(status->rate_idx <= 15 ||
+ status->rate_idx == 17 ||
+ status->rate_idx == 19 ||
+ status->rate_idx == 20 ||
+ status->rate_idx == 23) ||
+ !status->nss ||
+ status->nss > 8 ||
+ status->uhr.gi > NL80211_RATE_INFO_EHT_GI_3_2,
+ "Rate marked as a UHR rate but data is invalid: MCS:%d, NSS:%d, GI:%d\n",
+ status->rate_idx, status->nss, status->uhr.gi))
+ goto drop;
+ if (WARN_ONCE(status->uhr.elr &&
+ (status->nss != 1 || status->rate_idx > 1 ||
+ status->uhr.gi != NL80211_RATE_INFO_EHT_GI_1_6 ||
+ status->bw != RATE_INFO_BW_20 || status->uhr.im),
+ "bad UHR ELR MCS MCS:%d, NSS:%d, GI:%d, BW:%d, IM:%d\n",
+ status->rate_idx, status->nss, status->uhr.gi,
+ status->bw, status->uhr.im))
+ goto drop;
+ if (WARN_ONCE(status->uhr.im &&
+ (status->nss != 1 || status->rate_idx == 15),
+ "bad UHR IM MCS MCS:%d, NSS:%d\n",
+ status->rate_idx, status->nss))
+ goto drop;
+ break;
default:
WARN_ON_ONCE(1);
fallthrough;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 22e8561ad6fc..a79ebeb43585 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -4,7 +4,7 @@
* Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015 - 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/module.h>
@@ -2567,6 +2567,17 @@ static void sta_stats_decode_rate(struct ieee80211_local *local, u32 rate,
rinfo->eht_gi = STA_STATS_GET(EHT_GI, rate);
rinfo->eht_ru_alloc = STA_STATS_GET(EHT_RU, rate);
break;
+ case STA_STATS_RATE_TYPE_UHR:
+ rinfo->flags = RATE_INFO_FLAGS_UHR_MCS;
+ rinfo->mcs = STA_STATS_GET(UHR_MCS, rate);
+ rinfo->nss = STA_STATS_GET(UHR_NSS, rate);
+ rinfo->eht_gi = STA_STATS_GET(UHR_GI, rate);
+ rinfo->eht_ru_alloc = STA_STATS_GET(UHR_RU, rate);
+ if (STA_STATS_GET(UHR_ELR, rate))
+ rinfo->flags |= RATE_INFO_FLAGS_UHR_ELR_MCS;
+ if (STA_STATS_GET(UHR_IM, rate))
+ rinfo->flags |= RATE_INFO_FLAGS_UHR_IM;
+ break;
}
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index b1edf8ed102f..2875ef7d7946 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -3,7 +3,7 @@
* Copyright 2002-2005, Devicescape Software, Inc.
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright(c) 2020-2024 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
*/
#ifndef STA_INFO_H
@@ -1009,25 +1009,49 @@ enum sta_stats_type {
STA_STATS_RATE_TYPE_HE,
STA_STATS_RATE_TYPE_S1G,
STA_STATS_RATE_TYPE_EHT,
+ STA_STATS_RATE_TYPE_UHR,
};
-#define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0)
-#define STA_STATS_FIELD_LEGACY_IDX GENMASK( 3, 0)
-#define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4)
-#define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0)
-#define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4)
-#define STA_STATS_FIELD_HE_MCS GENMASK( 3, 0)
-#define STA_STATS_FIELD_HE_NSS GENMASK( 7, 4)
-#define STA_STATS_FIELD_EHT_MCS GENMASK( 3, 0)
-#define STA_STATS_FIELD_EHT_NSS GENMASK( 7, 4)
-#define STA_STATS_FIELD_BW GENMASK(12, 8)
-#define STA_STATS_FIELD_SGI GENMASK(13, 13)
-#define STA_STATS_FIELD_TYPE GENMASK(16, 14)
-#define STA_STATS_FIELD_HE_RU GENMASK(19, 17)
-#define STA_STATS_FIELD_HE_GI GENMASK(21, 20)
-#define STA_STATS_FIELD_HE_DCM GENMASK(22, 22)
-#define STA_STATS_FIELD_EHT_RU GENMASK(20, 17)
-#define STA_STATS_FIELD_EHT_GI GENMASK(22, 21)
+/* common */
+#define STA_STATS_FIELD_TYPE 0x0000000F
+#define STA_STATS_FIELD_BW 0x000001F0
+#define STA_STATS_FIELD_RESERVED 0x00000E00
+
+/* STA_STATS_RATE_TYPE_LEGACY */
+#define STA_STATS_FIELD_LEGACY_IDX 0x0000F000
+#define STA_STATS_FIELD_LEGACY_BAND 0x000F0000
+
+/* STA_STATS_RATE_TYPE_HT */
+#define STA_STATS_FIELD_HT_MCS 0x000FF000
+
+/* STA_STATS_RATE_TYPE_VHT */
+#define STA_STATS_FIELD_VHT_MCS 0x0000F000
+#define STA_STATS_FIELD_VHT_NSS 0x000F0000
+
+/* HT & VHT */
+#define STA_STATS_FIELD_SGI 0x00100000
+
+/* STA_STATS_RATE_TYPE_HE */
+#define STA_STATS_FIELD_HE_MCS 0x0000F000
+#define STA_STATS_FIELD_HE_NSS 0x000F0000
+#define STA_STATS_FIELD_HE_RU 0x00700000
+#define STA_STATS_FIELD_HE_GI 0x01800000
+#define STA_STATS_FIELD_HE_DCM 0x02000000
+
+/* STA_STATS_RATE_TYPE_EHT */
+#define STA_STATS_FIELD_EHT_MCS 0x0000F000
+#define STA_STATS_FIELD_EHT_NSS 0x000F0000
+#define STA_STATS_FIELD_EHT_RU 0x00F00000
+#define STA_STATS_FIELD_EHT_GI 0x03000000
+
+/* STA_STATS_RATE_TYPE_UHR */
+#define STA_STATS_FIELD_UHR_MCS 0x0001F000
+#define STA_STATS_FIELD_UHR_NSS 0x001E0000
+#define STA_STATS_FIELD_UHR_RU 0x01E00000
+#define STA_STATS_FIELD_UHR_GI 0x06000000
+#define STA_STATS_FIELD_UHR_ELR 0x08000000
+#define STA_STATS_FIELD_UHR_IM 0x10000000
+
#define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v)
#define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v)
@@ -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;
+ }
switch (s->encoding) {
case RX_ENC_VHT:
@@ -1073,6 +1104,15 @@ static inline u32 sta_stats_encode_rate(struct ieee80211_rx_status *s)
r |= STA_STATS_FIELD(EHT_GI, s->eht.gi);
r |= STA_STATS_FIELD(EHT_RU, s->eht.ru);
break;
+ case RX_ENC_UHR:
+ r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_UHR);
+ r |= STA_STATS_FIELD(UHR_NSS, s->nss);
+ r |= STA_STATS_FIELD(UHR_MCS, s->rate_idx);
+ r |= STA_STATS_FIELD(UHR_GI, s->uhr.gi);
+ r |= STA_STATS_FIELD(UHR_RU, s->uhr.ru);
+ r |= STA_STATS_FIELD(UHR_ELR, s->uhr.elr);
+ r |= STA_STATS_FIELD(UHR_IM, s->uhr.im);
+ break;
default:
WARN_ON(1);
return STA_STATS_RATE_INVALID;
diff --git a/net/mac80211/uhr.c b/net/mac80211/uhr.c
new file mode 100644
index 000000000000..2d8f5e5480ef
--- /dev/null
+++ b/net/mac80211/uhr.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * UHR handling
+ *
+ * Copyright(c) 2025-2026 Intel Corporation
+ */
+
+#include "ieee80211_i.h"
+
+void
+ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ const struct ieee80211_uhr_cap *uhr_cap,
+ u8 uhr_cap_len,
+ struct link_sta_info *link_sta)
+{
+ struct ieee80211_sta_uhr_cap *sta_uhr_cap = &link_sta->pub->uhr_cap;
+ bool from_ap;
+
+ memset(sta_uhr_cap, 0, sizeof(*sta_uhr_cap));
+
+ if (!ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif))
+ return;
+
+ sta_uhr_cap->has_uhr = true;
+
+ sta_uhr_cap->mac = uhr_cap->mac;
+ from_ap = sdata->vif.type == NL80211_IFTYPE_STATION;
+ sta_uhr_cap->phy = *ieee80211_uhr_phy_cap(uhr_cap, from_ap);
+}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 4d5680da7aa0..868346f9e5c9 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -6,7 +6,7 @@
* Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*
* utilities for mac80211
*/
@@ -1421,6 +1421,13 @@ static int ieee80211_put_preq_ies_band(struct sk_buff *skb,
if (err)
return err;
+ if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band),
+ IEEE80211_CHAN_NO_UHR)) {
+ err = ieee80211_put_uhr_cap(skb, sdata, sband);
+ if (err)
+ return err;
+ }
+
/*
* If adding more here, adjust code in main.c
* that calculates local->scan_ies_len.
@@ -4527,6 +4534,32 @@ int ieee80211_put_eht_cap(struct sk_buff *skb,
return 0;
}
+int ieee80211_put_uhr_cap(struct sk_buff *skb,
+ struct ieee80211_sub_if_data *sdata,
+ const struct ieee80211_supported_band *sband)
+{
+ const struct ieee80211_sta_uhr_cap *uhr_cap =
+ ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif);
+ int len;
+
+ if (!uhr_cap)
+ return 0;
+
+ len = 2 + 1 + sizeof(struct ieee80211_uhr_cap) +
+ sizeof(struct ieee80211_uhr_cap_phy);
+
+ if (skb_tailroom(skb) < len)
+ return -ENOBUFS;
+
+ skb_put_u8(skb, WLAN_EID_EXTENSION);
+ skb_put_u8(skb, len - 2);
+ skb_put_u8(skb, WLAN_EID_EXT_UHR_CAPA);
+ skb_put_data(skb, &uhr_cap->mac, sizeof(uhr_cap->mac));
+ skb_put_data(skb, &uhr_cap->phy, sizeof(uhr_cap->phy));
+
+ return 0;
+}
+
const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
{
static const char * const modes[] = {
@@ -4536,6 +4569,7 @@ const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode)
[IEEE80211_CONN_MODE_VHT] = "VHT",
[IEEE80211_CONN_MODE_HE] = "HE",
[IEEE80211_CONN_MODE_EHT] = "EHT",
+ [IEEE80211_CONN_MODE_UHR] = "UHR",
};
if (WARN_ON(mode >= ARRAY_SIZE(modes)))
--
2.52.0
^ permalink raw reply related
* [PATCH wireless-next v7 2/3] wifi: cfg80211: add initial UHR support
From: Johannes Berg @ 2026-01-30 13:20 UTC (permalink / raw)
To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260130142019.bd561c0eae91.I5b11fb0345a933bf497fd802aecc72932d58dd68@changeid>
From: Johannes Berg <johannes.berg@intel.com>
Add initial support for making UHR connections (or suppressing
that), adding UHR capable stations on the AP side, encoding
and decoding UHR MCSes (except rate calculation for the new
MCSes 17, 19, 20 and 23) as well as regulatory support.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
v7:
- adjust for changed naming
v5:
- validate NL80211_ATTR_UHR_CAPABILITY for non-AP (only)
v4:
- check for correct NSS/MCS for interference mitigation
v3:
- remove UHR capa pointer from AP settings, it's not in
the beacon anyway
- fix kernel-doc (Jeff Johnson)
---
include/net/cfg80211.h | 58 ++++++++++++++++++--
include/uapi/linux/nl80211.h | 30 +++++++++++
net/wireless/nl80211.c | 102 +++++++++++++++++++++++++++++++++--
net/wireless/reg.c | 4 +-
net/wireless/util.c | 101 ++++++++++++++++++++++++++--------
5 files changed, 265 insertions(+), 30 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 0ae0aa7594a3..aca349d4cc1a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -7,7 +7,7 @@
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/ethtool.h>
@@ -126,6 +126,7 @@ struct wiphy;
* @IEEE80211_CHAN_NO_4MHZ: 4 MHz bandwidth is not permitted on this channel.
* @IEEE80211_CHAN_NO_8MHZ: 8 MHz bandwidth is not permitted on this channel.
* @IEEE80211_CHAN_NO_16MHZ: 16 MHz bandwidth is not permitted on this channel.
+ * @IEEE80211_CHAN_NO_UHR: UHR operation is not permitted on this channel.
*/
enum ieee80211_channel_flags {
IEEE80211_CHAN_DISABLED = BIT(0),
@@ -143,6 +144,7 @@ enum ieee80211_channel_flags {
IEEE80211_CHAN_NO_10MHZ = BIT(12),
IEEE80211_CHAN_NO_HE = BIT(13),
/* can use free bits here */
+ IEEE80211_CHAN_NO_UHR = BIT(18),
IEEE80211_CHAN_NO_320MHZ = BIT(19),
IEEE80211_CHAN_NO_EHT = BIT(20),
IEEE80211_CHAN_DFS_CONCURRENT = BIT(21),
@@ -429,6 +431,18 @@ struct ieee80211_sta_eht_cap {
u8 eht_ppe_thres[IEEE80211_EHT_PPE_THRES_MAX_LEN];
};
+/**
+ * struct ieee80211_sta_uhr_cap - STA's UHR capabilities
+ * @has_uhr: true iff UHR is supported and data is valid
+ * @mac: fixed MAC capabilities
+ * @phy: fixed PHY capabilities
+ */
+struct ieee80211_sta_uhr_cap {
+ bool has_uhr;
+ struct ieee80211_uhr_cap_mac mac;
+ struct ieee80211_uhr_cap_phy phy;
+};
+
/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
#ifdef __CHECKER__
/*
@@ -454,6 +468,7 @@ struct ieee80211_sta_eht_cap {
* @he_6ghz_capa: HE 6 GHz capabilities, must be filled in for a
* 6 GHz band channel (and 0 may be valid value).
* @eht_cap: STA's EHT capabilities
+ * @uhr_cap: STA's UHR capabilities
* @vendor_elems: vendor element(s) to advertise
* @vendor_elems.data: vendor element(s) data
* @vendor_elems.len: vendor element(s) length
@@ -463,6 +478,7 @@ struct ieee80211_sband_iftype_data {
struct ieee80211_sta_he_cap he_cap;
struct ieee80211_he_6ghz_capa he_6ghz_capa;
struct ieee80211_sta_eht_cap eht_cap;
+ struct ieee80211_sta_uhr_cap uhr_cap;
struct {
const u8 *data;
unsigned int len;
@@ -704,6 +720,26 @@ ieee80211_get_eht_iftype_cap(const struct ieee80211_supported_band *sband,
return NULL;
}
+/**
+ * ieee80211_get_uhr_iftype_cap - return UHR capabilities for an sband's iftype
+ * @sband: the sband to search for the iftype on
+ * @iftype: enum nl80211_iftype
+ *
+ * Return: pointer to the struct ieee80211_sta_uhr_cap, or NULL is none found
+ */
+static inline const struct ieee80211_sta_uhr_cap *
+ieee80211_get_uhr_iftype_cap(const struct ieee80211_supported_band *sband,
+ enum nl80211_iftype iftype)
+{
+ const struct ieee80211_sband_iftype_data *data =
+ ieee80211_get_sband_iftype_data(sband, iftype);
+
+ if (data && data->uhr_cap.has_uhr)
+ return &data->uhr_cap;
+
+ return NULL;
+}
+
/**
* wiphy_read_of_freq_limits - read frequency limits from device tree
*
@@ -1486,6 +1522,7 @@ struct cfg80211_s1g_short_beacon {
* @he_cap: HE capabilities (or %NULL if HE isn't enabled)
* @eht_cap: EHT capabilities (or %NULL if EHT isn't enabled)
* @eht_oper: EHT operation IE (or %NULL if EHT isn't enabled)
+ * @uhr_oper: UHR operation (or %NULL if UHR isn't enabled)
* @ht_required: stations must support HT
* @vht_required: stations must support VHT
* @twt_responder: Enable Target Wait Time
@@ -1525,6 +1562,7 @@ struct cfg80211_ap_settings {
const struct ieee80211_he_operation *he_oper;
const struct ieee80211_eht_cap_elem *eht_cap;
const struct ieee80211_eht_operation *eht_oper;
+ const struct ieee80211_uhr_oper *uhr_oper;
bool ht_required, vht_required, he_required, sae_h2e_required;
bool twt_responder;
u32 flags;
@@ -1698,6 +1736,8 @@ struct sta_txpwr {
* @eht_capa: EHT capabilities of station
* @eht_capa_len: the length of the EHT capabilities
* @s1g_capa: S1G capabilities of station
+ * @uhr_capa: UHR capabilities of the station
+ * @uhr_capa_len: the length of the UHR capabilities
*/
struct link_station_parameters {
const u8 *mld_mac;
@@ -1717,6 +1757,8 @@ struct link_station_parameters {
const struct ieee80211_eht_cap_elem *eht_capa;
u8 eht_capa_len;
const struct ieee80211_s1g_cap *s1g_capa;
+ const struct ieee80211_uhr_cap *uhr_capa;
+ u8 uhr_capa_len;
};
/**
@@ -1898,6 +1940,11 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
* @RATE_INFO_FLAGS_EXTENDED_SC_DMG: 60GHz extended SC MCS
* @RATE_INFO_FLAGS_EHT_MCS: EHT MCS information
* @RATE_INFO_FLAGS_S1G_MCS: MCS field filled with S1G MCS
+ * @RATE_INFO_FLAGS_UHR_MCS: UHR MCS information
+ * @RATE_INFO_FLAGS_UHR_ELR_MCS: UHR ELR MCS was used
+ * (set together with @RATE_INFO_FLAGS_UHR_MCS)
+ * @RATE_INFO_FLAGS_UHR_IM: UHR Interference Mitigation
+ * was used
*/
enum rate_info_flags {
RATE_INFO_FLAGS_MCS = BIT(0),
@@ -1909,6 +1956,9 @@ enum rate_info_flags {
RATE_INFO_FLAGS_EXTENDED_SC_DMG = BIT(6),
RATE_INFO_FLAGS_EHT_MCS = BIT(7),
RATE_INFO_FLAGS_S1G_MCS = BIT(8),
+ RATE_INFO_FLAGS_UHR_MCS = BIT(9),
+ RATE_INFO_FLAGS_UHR_ELR_MCS = BIT(10),
+ RATE_INFO_FLAGS_UHR_IM = BIT(11),
};
/**
@@ -1924,7 +1974,7 @@ enum rate_info_flags {
* @RATE_INFO_BW_160: 160 MHz bandwidth
* @RATE_INFO_BW_HE_RU: bandwidth determined by HE RU allocation
* @RATE_INFO_BW_320: 320 MHz bandwidth
- * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT RU allocation
+ * @RATE_INFO_BW_EHT_RU: bandwidth determined by EHT/UHR RU allocation
* @RATE_INFO_BW_1: 1 MHz bandwidth
* @RATE_INFO_BW_2: 2 MHz bandwidth
* @RATE_INFO_BW_4: 4 MHz bandwidth
@@ -1955,7 +2005,7 @@ enum rate_info_bw {
*
* @flags: bitflag of flags from &enum rate_info_flags
* @legacy: bitrate in 100kbit/s for 802.11abg
- * @mcs: mcs index if struct describes an HT/VHT/HE/EHT/S1G rate
+ * @mcs: mcs index if struct describes an HT/VHT/HE/EHT/S1G/UHR rate
* @nss: number of streams (VHT & HE only)
* @bw: bandwidth (from &enum rate_info_bw)
* @he_gi: HE guard interval (from &enum nl80211_he_gi)
@@ -3265,6 +3315,7 @@ struct cfg80211_ml_reconf_req {
* Drivers shall disable MLO features for the current association if this
* flag is not set.
* @ASSOC_REQ_SPP_AMSDU: SPP A-MSDUs will be used on this connection (if any)
+ * @ASSOC_REQ_DISABLE_UHR: Disable UHR
*/
enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_HT = BIT(0),
@@ -3275,6 +3326,7 @@ enum cfg80211_assoc_req_flags {
ASSOC_REQ_DISABLE_EHT = BIT(5),
CONNECT_REQ_MLO_SUPPORT = BIT(6),
ASSOC_REQ_SPP_AMSDU = BIT(7),
+ ASSOC_REQ_DISABLE_UHR = BIT(8),
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 54ddbd9a5459..2254a6dead39 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2976,6 +2976,13 @@ enum nl80211_commands {
* @NL80211_ATTR_EPP_PEER: A flag attribute to indicate if the peer is an EPP
* STA. Used with %NL80211_CMD_NEW_STA and %NL80211_CMD_ADD_LINK_STA
*
+ * @NL80211_ATTR_UHR_CAPABILITY: UHR Capability information element (from
+ * association request when used with NL80211_CMD_NEW_STATION). Can be set
+ * only if HE/EHT are also available.
+ * @NL80211_ATTR_DISABLE_UHR: Force UHR capable interfaces to disable
+ * this feature during association. This is a flag attribute.
+ * Currently only supported in mac80211 drivers.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3546,6 +3553,9 @@ enum nl80211_attrs {
NL80211_ATTR_EPP_PEER,
+ NL80211_ATTR_UHR_CAPABILITY,
+ NL80211_ATTR_DISABLE_UHR,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3898,6 +3908,12 @@ enum nl80211_eht_ru_alloc {
* @NL80211_RATE_INFO_4_MHZ_WIDTH: 4 MHz S1G rate
* @NL80211_RATE_INFO_8_MHZ_WIDTH: 8 MHz S1G rate
* @NL80211_RATE_INFO_16_MHZ_WIDTH: 16 MHz S1G rate
+ * @NL80211_RATE_INFO_UHR_MCS: UHR MCS index (u8, 0-15, 17, 19, 20, 23)
+ * Note that the other EHT attributes (such as @NL80211_RATE_INFO_EHT_NSS)
+ * are used in conjunction with this where applicable
+ * @NL80211_RATE_INFO_UHR_ELR: UHR ELR flag, which restricts NSS to 1,
+ * MCS to 0 or 1, and GI to %NL80211_RATE_INFO_EHT_GI_1_6.
+ * @NL80211_RATE_INFO_UHR_IM: UHR Interference Mitigation flag
* @__NL80211_RATE_INFO_AFTER_LAST: internal use
*/
enum nl80211_rate_info {
@@ -3931,6 +3947,9 @@ enum nl80211_rate_info {
NL80211_RATE_INFO_4_MHZ_WIDTH,
NL80211_RATE_INFO_8_MHZ_WIDTH,
NL80211_RATE_INFO_16_MHZ_WIDTH,
+ NL80211_RATE_INFO_UHR_MCS,
+ NL80211_RATE_INFO_UHR_ELR,
+ NL80211_RATE_INFO_UHR_IM,
/* keep last */
__NL80211_RATE_INFO_AFTER_LAST,
@@ -4253,6 +4272,10 @@ enum nl80211_mpath_info {
* capabilities element
* @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE: EHT PPE thresholds information as
* defined in EHT capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC: UHR MAC capabilities as in UHR
+ * capabilities element
+ * @NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY: UHR PHY capabilities as in UHR
+ * capabilities element
* @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use
* @NL80211_BAND_IFTYPE_ATTR_MAX: highest band attribute currently defined
*/
@@ -4270,6 +4293,8 @@ enum nl80211_band_iftype_attr {
NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY,
NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET,
NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE,
+ NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC,
+ NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY,
/* keep last */
__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST,
@@ -4452,6 +4477,8 @@ enum nl80211_wmm_rule {
* @NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY: Channel is not permitted for use
* as a primary channel. Does not prevent the channel from existing
* as a non-primary subchannel. Only applicable to S1G channels.
+ * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel
+ * in current regulatory domain.
* @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
* currently defined
* @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4501,6 +4528,7 @@ enum nl80211_frequency_attr {
NL80211_FREQUENCY_ATTR_NO_8MHZ,
NL80211_FREQUENCY_ATTR_NO_16MHZ,
NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY,
+ NL80211_FREQUENCY_ATTR_NO_UHR,
/* keep last */
__NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -4714,6 +4742,7 @@ enum nl80211_sched_scan_match_attr {
* despite NO_IR configuration.
* @NL80211_RRF_ALLOW_20MHZ_ACTIVITY: Allow activity in 20 MHz bandwidth,
* despite NO_IR configuration.
+ * @NL80211_RRF_NO_UHR: UHR operation not allowed
*/
enum nl80211_reg_rule_flags {
NL80211_RRF_NO_OFDM = 1 << 0,
@@ -4740,6 +4769,7 @@ enum nl80211_reg_rule_flags {
NL80211_RRF_NO_6GHZ_AFC_CLIENT = 1 << 23,
NL80211_RRF_ALLOW_6GHZ_VLP_AP = 1 << 24,
NL80211_RRF_ALLOW_20MHZ_ACTIVITY = 1 << 25,
+ NL80211_RRF_NO_UHR = 1 << 26,
};
#define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 85e30fda4c46..181205eae755 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -332,6 +332,15 @@ static int validate_nan_cluster_id(const struct nlattr *attr,
return 0;
}
+static int validate_uhr_capa(const struct nlattr *attr,
+ struct netlink_ext_ack *extack)
+{
+ const u8 *data = nla_data(attr);
+ unsigned int len = nla_len(attr);
+
+ return ieee80211_uhr_capa_size_ok(data, len, false);
+}
+
/* policy for the attributes */
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR];
@@ -934,6 +943,9 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG },
[NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG },
[NL80211_ATTR_EPP_PEER] = { .type = NLA_FLAG },
+ [NL80211_ATTR_UHR_CAPABILITY] =
+ NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255),
+ [NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -1319,6 +1331,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
if ((chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY))
goto nla_put_failure;
+ if ((chan->flags & IEEE80211_CHAN_NO_UHR) &&
+ nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHR))
+ goto nla_put_failure;
}
if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -1954,6 +1969,7 @@ nl80211_send_iftype_data(struct sk_buff *msg,
{
const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap;
const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap;
+ const struct ieee80211_sta_uhr_cap *uhr_cap = &iftdata->uhr_cap;
if (nl80211_put_iftypes(msg, NL80211_BAND_IFTYPE_ATTR_IFTYPES,
iftdata->types_mask))
@@ -2005,6 +2021,14 @@ nl80211_send_iftype_data(struct sk_buff *msg,
return -ENOBUFS;
}
+ if (uhr_cap->has_uhr) {
+ if (nla_put(msg, NL80211_BAND_IFTYPE_ATTR_UHR_CAP_MAC,
+ sizeof(uhr_cap->mac), &uhr_cap->mac) ||
+ nla_put(msg, NL80211_BAND_IFTYPE_ATTR_UHR_CAP_PHY,
+ sizeof(uhr_cap->phy), &uhr_cap->phy))
+ return -ENOBUFS;
+ }
+
if (sband->band == NL80211_BAND_6GHZ &&
nla_put(msg, NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA,
sizeof(iftdata->he_6ghz_capa),
@@ -6462,6 +6486,17 @@ static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params)
cap->datalen - 1))
return -EINVAL;
}
+
+ cap = cfg80211_find_ext_elem(WLAN_EID_EXT_UHR_OPER, ies, ies_len);
+ if (cap) {
+ if (!cap->datalen)
+ return -EINVAL;
+ params->uhr_oper = (void *)(cap->data + 1);
+ if (!ieee80211_uhr_oper_size_ok((const u8 *)params->uhr_oper,
+ cap->datalen - 1, true))
+ return -EINVAL;
+ }
+
return 0;
}
@@ -6593,6 +6628,9 @@ static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params
(channel->flags & IEEE80211_CHAN_NO_EHT))
return -EOPNOTSUPP;
+ if (params->uhr_oper && (channel->flags & IEEE80211_CHAN_NO_UHR))
+ return -EOPNOTSUPP;
+
return 0;
}
@@ -7175,7 +7213,8 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
break;
case RATE_INFO_BW_EHT_RU:
rate_flg = 0;
- WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS));
+ WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS) &&
+ !(info->flags & RATE_INFO_FLAGS_UHR_MCS));
break;
}
@@ -7228,6 +7267,23 @@ bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr)
nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC,
info->eht_ru_alloc))
return false;
+ } else if (info->flags & RATE_INFO_FLAGS_UHR_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_UHR_MCS, info->mcs))
+ return false;
+ if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_NSS, info->nss))
+ return false;
+ if (nla_put_u8(msg, NL80211_RATE_INFO_EHT_GI, info->eht_gi))
+ return false;
+ if (info->bw == RATE_INFO_BW_EHT_RU &&
+ nla_put_u8(msg, NL80211_RATE_INFO_EHT_RU_ALLOC,
+ info->eht_ru_alloc))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_UHR_ELR_MCS &&
+ nla_put_flag(msg, NL80211_RATE_INFO_UHR_ELR))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_UHR_IM &&
+ nla_put_flag(msg, NL80211_RATE_INFO_UHR_IM))
+ return false;
}
nla_nest_end(msg, rate);
@@ -8101,7 +8157,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy,
if (params->ext_capab || params->link_sta_params.ht_capa ||
params->link_sta_params.vht_capa ||
params->link_sta_params.he_capa ||
- params->link_sta_params.eht_capa)
+ params->link_sta_params.eht_capa ||
+ params->link_sta_params.uhr_capa)
return -EINVAL;
if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU))
return -EINVAL;
@@ -8321,6 +8378,16 @@ static int nl80211_set_station_tdls(struct genl_info *info,
}
}
+ if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+ if (!params->link_sta_params.eht_capa)
+ return -EINVAL;
+
+ params->link_sta_params.uhr_capa =
+ nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ params->link_sta_params.uhr_capa_len =
+ nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ }
+
if (info->attrs[NL80211_ATTR_S1G_CAPABILITY])
params->link_sta_params.s1g_capa =
nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]);
@@ -8641,6 +8708,16 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
}
}
+ if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+ if (!params.link_sta_params.eht_capa)
+ return -EINVAL;
+
+ params.link_sta_params.uhr_capa =
+ nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ params.link_sta_params.uhr_capa_len =
+ nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ }
+
if (info->attrs[NL80211_ATTR_EML_CAPABILITY]) {
params.eml_cap_present = true;
params.eml_cap =
@@ -8700,10 +8777,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.link_sta_params.ht_capa = NULL;
params.link_sta_params.vht_capa = NULL;
- /* HE and EHT require WME */
+ /* HE, EHT and UHR require WME */
if (params.link_sta_params.he_capa_len ||
params.link_sta_params.he_6ghz_capa ||
- params.link_sta_params.eht_capa_len)
+ params.link_sta_params.eht_capa_len ||
+ params.link_sta_params.uhr_capa_len)
return -EINVAL;
}
@@ -12379,6 +12457,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_EHT]))
req.flags |= ASSOC_REQ_DISABLE_EHT;
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_UHR]))
+ req.flags |= ASSOC_REQ_DISABLE_UHR;
+
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
memcpy(&req.vht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
@@ -13258,6 +13339,9 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_EHT]))
connect.flags |= ASSOC_REQ_DISABLE_EHT;
+ if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_UHR]))
+ connect.flags |= ASSOC_REQ_DISABLE_UHR;
+
if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK])
memcpy(&connect.vht_capa_mask,
nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]),
@@ -17690,6 +17774,16 @@ nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info,
}
}
+ if (info->attrs[NL80211_ATTR_UHR_CAPABILITY]) {
+ if (!params.eht_capa)
+ return -EINVAL;
+
+ params.uhr_capa =
+ nla_data(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ params.uhr_capa_len =
+ nla_len(info->attrs[NL80211_ATTR_UHR_CAPABILITY]);
+ }
+
if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY])
params.he_6ghz_capa =
nla_data(info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]);
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 6cbfa3b78311..139cb27e5a81 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -5,7 +5,7 @@
* Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2025 Intel Corporation
+ * Copyright (C) 2018 - 2026 Intel Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -1605,6 +1605,8 @@ static u32 map_regdom_flags(u32 rd_flags)
channel_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
if (rd_flags & NL80211_RRF_ALLOW_20MHZ_ACTIVITY)
channel_flags |= IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY;
+ if (rd_flags & NL80211_RRF_NO_UHR)
+ channel_flags |= IEEE80211_CHAN_NO_UHR;
return channel_flags;
}
diff --git a/net/wireless/util.c b/net/wireless/util.c
index cc55b759694e..371149220031 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -5,7 +5,7 @@
* Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2013-2014 Intel Mobile Communications GmbH
* Copyright 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2023, 2025 Intel Corporation
+ * Copyright (C) 2018-2023, 2025-2026 Intel Corporation
*/
#include <linux/export.h>
#include <linux/bitops.h>
@@ -1572,26 +1572,30 @@ static u32 cfg80211_calculate_bitrate_he(struct rate_info *rate)
return result / 10000;
}
-static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
+static u32 _cfg80211_calculate_bitrate_eht_uhr(struct rate_info *rate)
{
#define SCALE 6144
- static const u32 mcs_divisors[16] = {
- 102399, /* 16.666666... */
- 51201, /* 8.333333... */
- 34134, /* 5.555555... */
- 25599, /* 4.166666... */
- 17067, /* 2.777777... */
- 12801, /* 2.083333... */
- 11377, /* 1.851725... */
- 10239, /* 1.666666... */
- 8532, /* 1.388888... */
- 7680, /* 1.250000... */
- 6828, /* 1.111111... */
- 6144, /* 1.000000... */
- 5690, /* 0.926106... */
- 5120, /* 0.833333... */
- 409600, /* 66.666666... */
- 204800, /* 33.333333... */
+ static const u32 mcs_divisors[] = {
+ [ 0] = 102399, /* 16.666666... */
+ [ 1] = 51201, /* 8.333333... */
+ [ 2] = 34134, /* 5.555555... */
+ [ 3] = 25599, /* 4.166666... */
+ [ 4] = 17067, /* 2.777777... */
+ [ 5] = 12801, /* 2.083333... */
+ [ 6] = 11377, /* 1.851725... */
+ [ 7] = 10239, /* 1.666666... */
+ [ 8] = 8532, /* 1.388888... */
+ [ 9] = 7680, /* 1.250000... */
+ [10] = 6828, /* 1.111111... */
+ [11] = 6144, /* 1.000000... */
+ [12] = 5690, /* 0.926106... */
+ [13] = 5120, /* 0.833333... */
+ [14] = 409600, /* 66.666666... */
+ [15] = 204800, /* 33.333333... */
+ [17] = 38400, /* 6.250180... */
+ [19] = 19200, /* 3.125090... */
+ [20] = 15360, /* 2.500000... */
+ [23] = 9600, /* 1.562545... */
};
static const u32 rates_996[3] = { 480388888, 453700000, 408333333 };
static const u32 rates_484[3] = { 229411111, 216666666, 195000000 };
@@ -1602,8 +1606,6 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
u64 tmp;
u32 result;
- if (WARN_ON_ONCE(rate->mcs > 15))
- return 0;
if (WARN_ON_ONCE(rate->eht_gi > NL80211_RATE_INFO_EHT_GI_3_2))
return 0;
if (WARN_ON_ONCE(rate->eht_ru_alloc >
@@ -1684,7 +1686,7 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
rate->eht_ru_alloc == NL80211_RATE_INFO_EHT_RU_ALLOC_26)
result = rates_26[rate->eht_gi];
else {
- WARN(1, "invalid EHT MCS: bw:%d, ru:%d\n",
+ WARN(1, "invalid EHT or UHR MCS: bw:%d, ru:%d\n",
rate->bw, rate->eht_ru_alloc);
return 0;
}
@@ -1698,11 +1700,64 @@ static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
tmp *= rate->nss;
do_div(tmp, 8);
+ /* and handle interference mitigation - 0.9x */
+ if (rate->flags & RATE_INFO_FLAGS_UHR_IM) {
+ if (WARN(rate->nss != 1 || rate->mcs != 15,
+ "invalid NSS or MCS for UHR IM\n"))
+ return 0;
+ tmp *= 9000;
+ do_div(tmp, 10000);
+ }
+
result = tmp;
return result / 10000;
}
+static u32 cfg80211_calculate_bitrate_eht(struct rate_info *rate)
+{
+ if (WARN_ONCE(rate->mcs > 15, "bad EHT MCS %d\n", rate->mcs))
+ return 0;
+
+ if (WARN_ONCE(rate->flags & (RATE_INFO_FLAGS_UHR_ELR_MCS |
+ RATE_INFO_FLAGS_UHR_IM),
+ "bad EHT MCS flags 0x%x\n", rate->flags))
+ return 0;
+
+ return _cfg80211_calculate_bitrate_eht_uhr(rate);
+}
+
+static u32 cfg80211_calculate_bitrate_uhr(struct rate_info *rate)
+{
+ if (rate->flags & RATE_INFO_FLAGS_UHR_ELR_MCS) {
+ WARN_ONCE(rate->eht_gi != NL80211_RATE_INFO_EHT_GI_1_6,
+ "bad UHR ELR guard interval %d\n",
+ rate->eht_gi);
+ WARN_ONCE(rate->mcs > 1, "bad UHR ELR MCS %d\n", rate->mcs);
+ WARN_ONCE(rate->nss != 1, "bad UHR ELR NSS %d\n", rate->nss);
+ WARN_ONCE(rate->bw != RATE_INFO_BW_20,
+ "bad UHR ELR bandwidth %d\n",
+ rate->bw);
+ WARN_ONCE(rate->flags & RATE_INFO_FLAGS_UHR_IM,
+ "bad UHR MCS flags 0x%x\n", rate->flags);
+ if (rate->mcs == 0)
+ return 17;
+ return 33;
+ }
+
+ switch (rate->mcs) {
+ case 0 ... 15:
+ case 17:
+ case 19:
+ case 20:
+ case 23:
+ return _cfg80211_calculate_bitrate_eht_uhr(rate);
+ }
+
+ WARN_ONCE(1, "bad UHR MCS %d\n", rate->mcs);
+ return 0;
+}
+
static u32 cfg80211_calculate_bitrate_s1g(struct rate_info *rate)
{
/* For 1, 2, 4, 8 and 16 MHz channels */
@@ -1827,6 +1882,8 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
return cfg80211_calculate_bitrate_he(rate);
if (rate->flags & RATE_INFO_FLAGS_EHT_MCS)
return cfg80211_calculate_bitrate_eht(rate);
+ if (rate->flags & RATE_INFO_FLAGS_UHR_MCS)
+ return cfg80211_calculate_bitrate_uhr(rate);
if (rate->flags & RATE_INFO_FLAGS_S1G_MCS)
return cfg80211_calculate_bitrate_s1g(rate);
--
2.52.0
^ permalink raw reply related
* [PATCH wireless-next v7 1/3] wifi: ieee80211: add some initial UHR definitions
From: Johannes Berg @ 2026-01-30 13:20 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>
---
v6:
- add 'ap' argument to ieee80211_uhr_phy_cap()
v5:
- use correct EHT MCS len (24 bits, not 24 bytes)
- handle DBE AP/non-AP in ieee80211_uhr_capa_size_ok()
v4:
- update to D1.2, including DBE in UHR capabilities
- fold in suggestions from Pablo
---
include/linux/ieee80211-uhr.h | 220 ++++++++++++++++++++++++++++++++++
include/linux/ieee80211.h | 33 ++++-
2 files changed, 251 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..132acced7d79
--- /dev/null
+++ b/include/linux/ieee80211-uhr.h
@@ -0,0 +1,220 @@
+/* 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_operation {
+ __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_operation *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_operation *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_operation *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_cap_mac {
+ u8 mac_cap[5];
+} __packed;
+
+struct ieee80211_uhr_cap {
+ struct ieee80211_uhr_cap_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_cap_phy {
+ u8 cap;
+} __packed;
+
+static inline bool ieee80211_uhr_capa_size_ok(const u8 *data, u8 len,
+ bool from_ap)
+{
+ const struct ieee80211_uhr_cap *cap = (const void *)data;
+ size_t needed = sizeof(*cap) + sizeof(struct ieee80211_uhr_cap_phy);
+
+ if (len < needed)
+ return false;
+
+ /*
+ * A non-AP STA does not include the DBE Capability Parameters field
+ * in the UHR MAC Capabilities Information field.
+ */
+ if (from_ap && 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 += 3;
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+ needed += 3;
+ }
+
+ return len >= needed;
+}
+
+static inline const struct ieee80211_uhr_cap_phy *
+ieee80211_uhr_phy_cap(const struct ieee80211_uhr_cap *cap, bool from_ap)
+{
+ u8 offs = 0;
+
+ if (from_ap && 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 += 3;
+
+ if (dbe & IEEE80211_UHR_MAC_CAP_DBE_EHT_MCS_MAP_320_PRES)
+ offs += 3;
+ }
+
+ 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
* [PATCH] wifi: ath9k: add range check for epid in htc_issue_send()
From: Aleksandr Nesterenko @ 2026-01-30 12:59 UTC (permalink / raw)
To: jeff.johnson, davem, kuba
Cc: linux-wireless, netdev, linux-kernel, Aleksandr Nesterenko
The fix for CVE-2024-53156 (commit 8619593634cb ("wifi: ath9k: add
range check for conn_rsp_epid in htc_connect_service()")) added a
bounds check for conn_rsp_epid in htc_connect_service() to prevent
out-of-bounds array access. However, htc_issue_send() accesses
target->endpoint[epid] directly without validating the epid parameter.
While htc_connect_service() now validates the endpoint ID before storing
it, htc_issue_send() can still receive invalid epid values from callers
such as htc_send() and htc_send_epid(). This provides defense-in-depth
against out-of-bounds access.
Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.")
Signed-off-by: Aleksandr Nesterenko <alexandernesterenko837@gmail.com>
---
drivers/net/wireless/ath/ath9k/htc_hst.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index 00dc97ac53b9..7821a31c0abb 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -23,9 +23,16 @@ static int htc_issue_send(struct htc_target *target, struct sk_buff* skb,
{
struct htc_frame_hdr *hdr;
- struct htc_endpoint *endpoint = &target->endpoint[epid];
+ struct htc_endpoint *endpoint;
int status;
+ if (epid >= ENDPOINT_MAX) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ endpoint = &target->endpoint[epid];
+
hdr = skb_push(skb, sizeof(struct htc_frame_hdr));
hdr->endpoint_id = epid;
hdr->flags = flags;
--
2.34.1
^ permalink raw reply related
* [PATCH] wifi: ath9k: add range check for epid in htc_issue_send()
From: Aleksandr Nesterenko @ 2026-01-30 12:56 UTC (permalink / raw)
To: quic_kvalo, davem, kuba
Cc: linux-wireless, netdev, linux-kernel, Aleksandr Nesterenko
The fix for CVE-2024-53156 (commit 8619593634cb ("wifi: ath9k: add
range check for conn_rsp_epid in htc_connect_service()")) added a
bounds check for conn_rsp_epid in htc_connect_service() to prevent
out-of-bounds array access. However, htc_issue_send() accesses
target->endpoint[epid] directly without validating the epid parameter.
While htc_connect_service() now validates the endpoint ID before storing
it, htc_issue_send() can still receive invalid epid values from callers
such as htc_send() and htc_send_epid(). This provides defense-in-depth
against out-of-bounds access.
Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.")
Signed-off-by: Aleksandr Nesterenko <alexandernesterenko837@gmail.com>
---
drivers/net/wireless/ath/ath9k/htc_hst.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index 00dc97ac53b9..7821a31c0abb 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -23,9 +23,16 @@ static int htc_issue_send(struct htc_target *target, struct sk_buff* skb,
{
struct htc_frame_hdr *hdr;
- struct htc_endpoint *endpoint = &target->endpoint[epid];
+ struct htc_endpoint *endpoint;
int status;
+ if (epid >= ENDPOINT_MAX) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ endpoint = &target->endpoint[epid];
+
hdr = skb_push(skb, sizeof(struct htc_frame_hdr));
hdr->endpoint_id = epid;
hdr->flags = flags;
--
2.34.1
^ permalink raw reply related
* [PATCH] wifi: ath9k: add range check for epid in htc_issue_send()
From: Aleksandr Nesterenko @ 2026-01-30 12:53 UTC (permalink / raw)
To: kvalo, davem, kuba
Cc: linux-wireless, netdev, linux-kernel, Aleksandr Nesterenko
The fix for CVE-2024-53156 (commit 8619593634cb ("wifi: ath9k: add
range check for conn_rsp_epid in htc_connect_service()")) added a
bounds check for conn_rsp_epid in htc_connect_service() to prevent
out-of-bounds array access. However, htc_issue_send() accesses
target->endpoint[epid] directly without validating the epid parameter.
While htc_connect_service() now validates the endpoint ID before storing
it, htc_issue_send() can still receive invalid epid values from callers
such as htc_send() and htc_send_epid(). This provides defense-in-depth
against out-of-bounds access.
Fixes: fb9987d0f748 ("ath9k_htc: Support for AR9271 chipset.")
Signed-off-by: Aleksandr Nesterenko <alexandernesterenko837@gmail.com>
---
drivers/net/wireless/ath/ath9k/htc_hst.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
index 00dc97ac53b9..7821a31c0abb 100644
--- a/drivers/net/wireless/ath/ath9k/htc_hst.c
+++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
@@ -23,9 +23,16 @@ static int htc_issue_send(struct htc_target *target, struct sk_buff* skb,
{
struct htc_frame_hdr *hdr;
- struct htc_endpoint *endpoint = &target->endpoint[epid];
+ struct htc_endpoint *endpoint;
int status;
+ if (epid >= ENDPOINT_MAX) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ endpoint = &target->endpoint[epid];
+
hdr = skb_push(skb, sizeof(struct htc_frame_hdr));
hdr->endpoint_id = epid;
hdr->flags = flags;
--
2.34.1
^ permalink raw reply related
* brcmfmac: iwd fails to connect on multi-AP networks (MacBook Pro 12,1, BCM43602)
From: Utu Hopiavuori @ 2026-01-30 12:18 UTC (permalink / raw)
To: linux-wireless; +Cc: brcm80211-dev-list
On MacBook Pro 12,1 with BCM43602, iwd 3.8 fails to connect to a network
with multiple APs under the same SSID (connect-failed, status: 16).
Non-free firmware loads successfully; no NetworkManager installed.
Workaround using wpa_supplicant works. Applying options brcmfmac
feature_disable=0x82000 allows reliable connection.
Debug/logs:
*
https://p.kapsi.fi/?955065d6c0d0caf1#D8NZwkNPxayrNRWHxR8UofT3htVDRw64nu5SG41v3BvS
*
https://p.kapsi.fi/?58aefcff5920f733#F4MdmgRbmTUtgsbVm76ixMRwFktkZMJEuwCW9ZLQMNJo
- Boot 7db8fc4051a14fba8c19086100bf2f0d: before fix
- Boot 2bf8b79cf80c4abb95b2ad8c074a4249: after fix
- Boot 402b96dbfcc44b5c9d31bf5f15af8bf5: always known-good-network
--
Utu Hopiavuori
^ permalink raw reply
* Re: [PATCH v2 2/3] AP: Always re-add stations that use MLO
From: Benjamin Berg @ 2026-01-30 12:02 UTC (permalink / raw)
To: Rameshkumar Sundaram, hostap; +Cc: linux-wireless, Andrei Otcheretianski
In-Reply-To: <fcef3c86-5384-4211-9f6b-be6ef562cef5@oss.qualcomm.com>
Hi,
On Thu, 2026-01-29 at 22:56 +0530, Rameshkumar Sundaram wrote:
> On 1/29/2026 3:38 PM, Benjamin Berg wrote:
> > [SNIP]
> > That said, to properly fix this we need an nl80211/mac80211 API that
> > permits us to disable address translation for the frame. Otherwise we
> > would still get the address translated to the old link address should
> > the new link address match the MLD address.
> >
>
> That's true—even probe responses get translated when the old link
> address matches the new link MLD address.
>
> Few other cases I encountered:
> Consider an ML STA with ML address M associated with link A and link B,
> using link addresses X and Y respectively. If the STA sends an
> authentication frame with address Y on link A, it gets translated to M
> (even though link A has no STA with address Y) and forwarded to link B.
> As a result, hostapd is unaware of the actual TA and queues the
> authentication reply to the MLD address on link B. This frame will
> eventually be transmitted over the air on link B with address Y.
Honestly, that type of link confusion seems like a bug. We should be
able to avoid that as we hopefully know on which link the frame was
received.
> This will be true even if STA associated in one link and tries to roam
> to other link of MLD using same link and ML addresses.
>
> There also cases where an ML STA roams/re-associates as legacy (non-ML)
> STA with ML address as link address. The reply would go out with old
> link address.
Yes, I think that case is similar a station using its MLD Address on
the association link and returning on another link.
> It seems that address translation at the driver/mac80211 level for
> management frames could be avoided for both TX and RX, allowing hostapd
> to handle these frames and their replies more efficiently in cases of
> roaming and address reuse.
Doing address translation in the TX path is required if the
hardware/driver should decide on which link to TX the frame. For RX, it
seems sensible to me to do the translation when it is possible.
I talked a bit to Johannes about this today, and my current proposal
would be add a new flag that is set when mac80211 did not find (RX) or
should not use (TX) a STA for the frame.
More specifically I think that we could:
* Fix the link address based STA lookup to only work when the frame
was received on the correct link (the bug from above).
* Make sure we drop robust management frames without a STA as we do
not want to get into trouble with the next change.
* Change ieee80211_rx_for_interface so that it uses only
link_sta_info_get_bss if we are an MLD and sta_info_get_bss
otherwise.
Right now, we will find the station if we see the MLD Address in the
frame even when it is not a valid link address.
* Add a new nl80211 attribute NL80211_ATTR_FRAME_NO_STA to be used
together with NL80211_CMD_FRAME for both TX and RX.
- In the RX case, add the attribute if we have no station. If the
attribute does not exist, then hostapd should assume the address was
translated.
- In the TX case, plumb the information through to mac80211 and avoid
doing a station lookup based on the address.
I think this would be enough to then solve the problem in hostapd.
Benjamin
^ permalink raw reply
* Re: [PATCH v3] wifi: ath11k: fix memory leaks in beacon template setup
From: Baochen Qiang @ 2026-01-30 10:32 UTC (permalink / raw)
To: Zilin Guan, jeff.johnson
Cc: ath11k, jianhao.xu, jjohnson, linux-kernel, linux-wireless
In-Reply-To: <20260130084451.110768-1-zilin@seu.edu.cn>
On 1/30/2026 4:44 PM, Zilin Guan wrote:
> The functions ath11k_mac_setup_bcn_tmpl_ema() and
> ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
> but fail to free it when parameter setup returns an error.
>
> Since beacon templates must be released during normal execution, they
> must also be released in the error handling paths to prevent memory
> leaks.
>
> Fix this by using unified exit paths with proper cleanup in the respective
> error paths.
>
> Compile tested only. Issue found using a prototype static analysis tool
> and code review.
>
> Fixes: 3a415daa3e8b ("wifi: ath11k: add P2P IE in beacon template")
> Fixes: 335a92765d30 ("wifi: ath11k: MBSSID beacon support")
> Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> ---
> Changes in v3:
> - Add goto path for the beacons->cnt check for strict logical consistency.
>
> Changes in v2:
> - Use unified exit paths for cleanup.
>
> drivers/net/wireless/ath/ath11k/mac.c | 28 ++++++++++++++++-----------
> 1 file changed, 17 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index 4dfd08b58416..e872f416ea97 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -1557,12 +1557,15 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
> if (!beacons || !beacons->cnt) {
> ath11k_warn(arvif->ar->ab,
> "failed to get ema beacon templates from mac80211\n");
> - return -EPERM;
> + ret = -EPERM;
> + goto free;
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
> - return -EINVAL;
> + if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
> + ret = -EINVAL;
> + goto free;
> + }
> } else {
> arvif->wpaie_present = tx_arvif->wpaie_present;
> }
> @@ -1589,11 +1592,11 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
> }
> }
>
> - ieee80211_beacon_free_ema_list(beacons);
> -
> if (tx_arvif != arvif && !nontx_vif_params_set)
> - return -EINVAL; /* Profile not found in the beacons */
> + ret = -EINVAL; /* Profile not found in the beacons */
>
> +free:
> + ieee80211_beacon_free_ema_list(beacons);
> return ret;
> }
>
> @@ -1622,19 +1625,22 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, bcn))
> - return -EINVAL;
> + if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
> + ret = -EINVAL;
> + goto free;
> + }
> } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
> - return -EINVAL;
> + ret = -EINVAL;
> + goto free;
> }
>
> ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
> - kfree_skb(bcn);
> -
> if (ret)
> ath11k_warn(ab, "failed to submit beacon template command: %d\n",
> ret);
>
> +free:
> + kfree_skb(bcn);
> return ret;
> }
>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* [PATCH v3] wifi: ath11k: fix memory leaks in beacon template setup
From: Zilin Guan @ 2026-01-30 8:44 UTC (permalink / raw)
To: jeff.johnson
Cc: ath11k, baochen.qiang, jianhao.xu, jjohnson, linux-kernel,
linux-wireless, Zilin Guan
The functions ath11k_mac_setup_bcn_tmpl_ema() and
ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
but fail to free it when parameter setup returns an error.
Since beacon templates must be released during normal execution, they
must also be released in the error handling paths to prevent memory
leaks.
Fix this by using unified exit paths with proper cleanup in the respective
error paths.
Compile tested only. Issue found using a prototype static analysis tool
and code review.
Fixes: 3a415daa3e8b ("wifi: ath11k: add P2P IE in beacon template")
Fixes: 335a92765d30 ("wifi: ath11k: MBSSID beacon support")
Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
---
Changes in v3:
- Add goto path for the beacons->cnt check for strict logical consistency.
Changes in v2:
- Use unified exit paths for cleanup.
drivers/net/wireless/ath/ath11k/mac.c | 28 ++++++++++++++++-----------
1 file changed, 17 insertions(+), 11 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 4dfd08b58416..e872f416ea97 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1557,12 +1557,15 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
if (!beacons || !beacons->cnt) {
ath11k_warn(arvif->ar->ab,
"failed to get ema beacon templates from mac80211\n");
- return -EPERM;
+ ret = -EPERM;
+ goto free;
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else {
arvif->wpaie_present = tx_arvif->wpaie_present;
}
@@ -1589,11 +1592,11 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
}
}
- ieee80211_beacon_free_ema_list(beacons);
-
if (tx_arvif != arvif && !nontx_vif_params_set)
- return -EINVAL; /* Profile not found in the beacons */
+ ret = -EINVAL; /* Profile not found in the beacons */
+free:
+ ieee80211_beacon_free_ema_list(beacons);
return ret;
}
@@ -1622,19 +1625,22 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, bcn))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto free;
}
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
- kfree_skb(bcn);
-
if (ret)
ath11k_warn(ab, "failed to submit beacon template command: %d\n",
ret);
+free:
+ kfree_skb(bcn);
return ret;
}
--
2.34.1
^ permalink raw reply related
* Re: [GIT PULL] wireless-next-2026-01-29
From: Johannes Berg @ 2026-01-30 8:18 UTC (permalink / raw)
To: Ping-Ke Shih, Paolo Abeni, netdev@vger.kernel.org
Cc: linux-wireless@vger.kernel.org
In-Reply-To: <a3298543662a4ba68395a61d687b5e0f@realtek.com>
On Fri, 2026-01-30 at 07:22 +0000, Ping-Ke Shih wrote:
> Hi Johannes,
> >
> > I just did a 32-bit build manually, and only see some warnings like
> > this:
> > drivers/net/wireless/realtek/rtw89/util.c:43:45: warning: decimal
> > constant 2238721139 is between LONG_MAX and ULONG_MAX. For C99 that
> > means long long, C90 compilers are very likely to produce unsigned long
> > (and a warning) here
>
> Our local 32-bit build can't find this warning. Could you share .config
> you are using?
My bad. It was an old version of _sparse_ giving me this, not anything
about the .config etc.
johannes
^ permalink raw reply
* Re: [PATCH v2 2/2] wifi: ath12k: Fix firmware stats leak when pdev list is empty
From: Baochen Qiang @ 2026-01-30 7:50 UTC (permalink / raw)
To: Saikiran B; +Cc: ath12k, linux-wireless, kvalo
In-Reply-To: <CAAFDt1uSQce_9AG2488yaeeEjCwQwQubW51K51VHiJF8Rp6xRQ@mail.gmail.com>
On 1/30/2026 3:32 PM, Saikiran B wrote:
> On Fri, Jan 30, 2026 at 7:39 AM Baochen Qiang
> <baochen.qiang@oss.qualcomm.com> wrote:
>>
>>
>>
>> On 1/29/2026 10:06 PM, Saikiran B wrote:
>>> On Thu, Jan 29, 2026 at 7:57 AM Baochen Qiang
>>> <baochen.qiang@oss.qualcomm.com> wrote:
>>>>
>>>>
>>>>
>>>> On 1/27/2026 12:40 PM, Saikiran B wrote:
>>>>> I have analyzed the logs and code flow in depth to provide more
>>>>> definitive answers for your questions.
>>>>>
>>>>> The log entries showing the failure are:
>>>>> [ 563.574076] ath12k_pci 0004:01:00.0: failed to pull fw stats: -71
>>>>> [ 564.575896] ath12k_pci 0004:01:00.0: time out while waiting for get fw stats
>>>>>
>>>>> 1. Why are other stats populated?
>>>>> The "failed to pull fw stats: -71" error is not the initial failure
>>>>> but a symptom that appears after repeated operations. The leak happens
>>>>> during *successful* calls prior to this error.
>>>>>
>>>>> Code flow proving the leak:
>>>>> - ath12k_mac_get_fw_stats() sends WMI_REQUEST_PDEV_STAT.
>>>>> - Firmware responds. ath12k_update_stats_event() parses the response.
>>>>> - ath12k_wmi_fw_stats_process() is called, which splices 'vdevs' and
>>>>> 'beacon' stats into ar->fw_stats.vdevs/bcn.
>>>>> - ath12k_mac_get_fw_stats() returns 0 (Success).
>>>>> - In ath12k_mac_op_get_txpower(), the check `if (!pdev)` fails if the
>>>>> pdev-specific list is empty (but vdev list is NOT empty).
>>>>> - The function exits via `err_fallback` WITHOUT calling ath12k_fw_stats_reset().
>>>>> - Result: The 'vdev' and 'beacon' stats that were spliced into
>>>>> ar->fw_stats remain there, leaking memory and accumulating with every
>>>>> call.
>>>>>
>>>>> 2. Exact place where -71 is printed:
>>>>> The error "failed to pull fw stats: -71" is printed in
>>>>> [ath12k_update_stats_event()](drivers/net/wireless/ath/ath12k/wmi.c).
>>>>> It corresponds to "ret = ath12k_wmi_pull_fw_stats()" returning -EPROTO.
>>>>> This propagates from
>>>>> [ath12k_wmi_tlv_fw_stats_data_parse()](drivers/net/wireless/ath/ath12k/wmi.c),
>>>>> when buffer validation checks (like `len < sizeof(*src)`) fail.
>>>>>
>>>>> Conclusion:
>>>>> The fix in my patch (resetting stats when `!pdev`) is critical because
>>>>> it ensures that the accumulated 'vdev' and 'beacon' stats are freed
>>>>> even when the 'pdev' list ends up empty.
>>>>>
>>>>> Let me know if you need anything else.
>>>>
>>>> can you please try below to see if it can fix your issue?
>>>>
>>>> https://lore.kernel.org/r/20260129-ath12k-fw-stats-fixes-v1-0-55d66064f4d5@oss.qualcomm.com
>>>>
>>>>>
>>>>> Thanks & Regards,
>>>>> Saikiran
>>>>>
>>>>> On Tue, Jan 27, 2026 at 9:47 AM Saikiran B <bjsaikiran@gmail.com> wrote:
>>>>>>
>>>>>> Hi Baochen,
>>>>>>
>>>>>> Regarding your questions:
>>>>>>
>>>>>> "Are other stats populated?"
>>>>>>
>>>>>> Yes. When ath12k_mac_get_fw_stats() returns success (0), it means the
>>>>>> firmware response was received and valid WMI events were processed.
>>>>>> The firmware response to WMI_REQUEST_PDEV_STAT typically includes
>>>>>> multiple stats TLVs (vdev stats, beacon stats, etc.). Even if the
>>>>>> "pdev stats" list ends up empty (e.g., due to specific filtering or
>>>>>> availability), the firmware should have populated other lists (like
>>>>>> vdevs or beacons) in the ar->fw_stats structure. If we don't reset,
>>>>>> these valid entries leak and accumulate.
>>>>>>
>>>>>> "Where exactly is -71 (EPROTO) printed?"
>>>>>>
>>>>>> The log "failed to pull fw stats: -71" is printed in
>>>>>> ath12k_update_stats_event() (wmi.c line 8500 in my tree). This error
>>>>>> code (-EPROTO) propagates from ath12k_wmi_tlv_fw_stats_data_parse(),
>>>>>> where it is returned when buffer validation checks fail (e.g., if (len
>>>>>> < sizeof(*src))). This failure suggests that the accumulated state or
>>>>>> memory corruption from the leak eventually causes the parser to fail
>>>>>> on subsequent events.
>>>>>>
>>>>>> So, fixing the leak is necessary for correctness regardless of the
>>>>>> specific side-effect error code.
>>>>>>
>>>>>> Thanks & Regards,
>>>>>> Saikiran
>>>>>>
>>>>>> On Tue, Jan 27, 2026 at 8:57 AM Baochen Qiang
>>>>>> <baochen.qiang@oss.qualcomm.com> wrote:
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On 1/26/2026 5:52 PM, Saikiran wrote:
>>>>>>>> The commits bd6ec8111e65 and 2977567b244f changed firmware stats handling
>>>>>>>> to be caller-driven, requiring explicit ath12k_fw_stats_reset() calls
>>>>>>>> after using ath12k_mac_get_fw_stats().
>>>>>>>>
>>>>>>>> In ath12k_mac_op_get_txpower(), when ath12k_mac_get_fw_stats() succeeds
>>>>>>>> but the pdev stats list is empty, the function exits without calling
>>>>>>>> ath12k_fw_stats_reset(). Even though the pdev list is empty, the firmware
>>>>>>>> may have populated other stats lists (vdevs, beacons, etc.) in the
>>>>>>>
>>>>>>> 'may' is not enough, we need to be 100% sure whether other stats are populated. This is
>>>>>>> critical for us to find the root cause.
>>>>>>>
>>>>>>>> ar->fw_stats structure.
>>>>>>>>
>>>>>>>> Without resetting the stats buffer, this data accumulates across multiple
>>>>>>>> calls, eventually causing the stats buffer to overflow and leading to
>>>>>>>> firmware communication failures (error -71/EPROTO) during subsequent
>>>>>>>> operations.
>>>>>>>>
>>>>>>>> The issue manifests during 5GHz scanning which triggers multiple TX power
>>>>>>>> queries. Symptoms include:
>>>>>>>> - "failed to pull fw stats: -71" errors in dmesg
>>>>>>>
>>>>>>> still, can you please check the logs to see at which exact place is this printed?
>>>>>>>
>>>>>>>> - 5GHz networks not detected despite hardware support
>>>>>>>> - 2.4GHz networks work normally
>>>>>>>>
>>>>>>>> Fix by calling ath12k_fw_stats_reset() when the pdev list is empty,
>>>>>>>> ensuring the stats buffer is properly cleaned up even when only partial
>>>>>>>> stats data is received from firmware.
>>>>>>>>
>>>>>>>> Fixes: bd6ec8111e65 ("wifi: ath12k: Make firmware stats reset caller-driven")
>>>>>>>> Link: https://bugs.launchpad.net/ubuntu-concept/+bug/2138308
>>>>>>>> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302 (Lenovo Yoga Slim 7x)
>>>>>>>> Signed-off-by: Saikiran <bjsaikiran@gmail.com>
>>>>>>>> ---
>>>>>>>> drivers/net/wireless/ath/ath12k/mac.c | 1 +
>>>>>>>> 1 file changed, 1 insertion(+)
>>>>>>>>
>>>>>>>> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
>>>>>>>> index e0e49f782bf8..6e35c3ee9864 100644
>>>>>>>> --- a/drivers/net/wireless/ath/ath12k/mac.c
>>>>>>>> +++ b/drivers/net/wireless/ath/ath12k/mac.c
>>>>>>>> @@ -5169,6 +5169,7 @@ static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
>>>>>>>> struct ath12k_fw_stats_pdev, list);
>>>>>>>> if (!pdev) {
>>>>>>>> spin_unlock_bh(&ar->data_lock);
>>>>>>>> + ath12k_fw_stats_reset(ar);
>>>>>>>> goto err_fallback;
>>>>>>>> }
>>>>>>>>
>>>>>>>
>>>>
>>>
>>> Hi Baochen,
>>>
>>> I tried applying your patches on top of v6.19-rc7 (which is the latest
>>> mainline release candidate I'm testing on), but I ran into build
>>> issues because some of the dependencies seem missing.
>>>
>>> Specifically:
>>> Patch 2 ("wifi: ath12k: fix station lookup failure when disconnecting
>>> from AP") uses `ath12k_link_sta_find_by_addr()`, which does not exist
>>> in my tree. It seems your patches are based on a different tree
>>> (ath-next?) that has newer changes not yet in the mainline.
>>>
>>> Could you please point me to the specific git repo/branch you are
>>> using? I can try to build and test on that baseline to be sure.
>>
>> My bad. Please try the latest ath tree:
>>
>> https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git/
>>
>> the base commit is ath-202601271544 tag.
>>
>>>
>>> Regarding the firmware stats issue:
>>> I verified the firmware files match the latest available (MD5 sums
>>> matched), yet the "-71" errors and memory leak persist on my device
>>> without fixes.
>>>
>>> I successfully applied the logic from your Patch 1 manually (since
>>> [ath12k_mac_get_target_pdev_id](cci:1://file:///home/saikiran/linux/kernel/x1e/x1e/drivers/net/wireless/ath/ath12k/mac.c:989:0-1008:1)
>>> exists), but I haven't fully validated if it alone resolves the leak
>>> in all scenarios.
>>>
>>> However, the fix I proposed in my v2 patch (resetting stats when pdev
>>> list is empty) definitely stops the leak mechanism by ensuring cleanup
>>> happens even when the firmware returns partial stats (which seems to
>>> be the trigger condition).
>>>
>>> I'll wait for your pointer to the base tree to do a proper test of your series.
>>>
>>> Thanks & Regards,
>>> Saikiran
>>
>
> Hi,
>
> I have merged the tag, applied your patches and I can confirm that the
> leak has been fixed.
Thank you Saikiran for the test!
>
> Thanks & Regards,
> Saikiran
^ permalink raw reply
* Re: [PATCH v2] wifi: ath11k: fix memory leaks in beacon template setup
From: Zilin Guan @ 2026-01-30 7:44 UTC (permalink / raw)
To: jeff.johnson
Cc: ath11k, baochen.qiang, jianhao.xu, jjohnson, linux-kernel,
linux-wireless, zilin
In-Reply-To: <97b132e8-d3b2-4e52-aa74-c8aeb0f5d516@oss.qualcomm.com>
On Thu, Jan 29, 2026 at 07:36:43AM -0800, Jeff Johnson wrote:
> I prefer strict logical consistency so I prefer either adding the goto or
> removing the beacons->cnt check.
>
> Or a completely different approach would be to use cleanup.h functionality and
> annotate beacons with __free(ieee80211_beacon_free_ema_list) so that no
> explicit calls to that function are required. If you try this approach then
> beacons must be defined at the point of allocation:
>
> struct ieee80211_ema_beacons *beacons __free(ieee80211_beacon_free_ema_list) =
> ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw,
> tx_arvif->vif, 0);
>
> Note that I have not tried this approach with allocations other than from the
> kmalloc() family with __free(kfree), but in theory this should work.
>
> /jeff
Thanks! I'll add the goto path for the beacons->cnt check in v3.
Best regards,
Zilin
^ permalink raw reply
* Re: [PATCH v2 2/2] wifi: ath12k: Fix firmware stats leak when pdev list is empty
From: Saikiran B @ 2026-01-30 7:32 UTC (permalink / raw)
To: Baochen Qiang; +Cc: ath12k, linux-wireless, kvalo
In-Reply-To: <fbcbeb0f-c073-4da5-9dbe-2518a1d31f20@oss.qualcomm.com>
On Fri, Jan 30, 2026 at 7:39 AM Baochen Qiang
<baochen.qiang@oss.qualcomm.com> wrote:
>
>
>
> On 1/29/2026 10:06 PM, Saikiran B wrote:
> > On Thu, Jan 29, 2026 at 7:57 AM Baochen Qiang
> > <baochen.qiang@oss.qualcomm.com> wrote:
> >>
> >>
> >>
> >> On 1/27/2026 12:40 PM, Saikiran B wrote:
> >>> I have analyzed the logs and code flow in depth to provide more
> >>> definitive answers for your questions.
> >>>
> >>> The log entries showing the failure are:
> >>> [ 563.574076] ath12k_pci 0004:01:00.0: failed to pull fw stats: -71
> >>> [ 564.575896] ath12k_pci 0004:01:00.0: time out while waiting for get fw stats
> >>>
> >>> 1. Why are other stats populated?
> >>> The "failed to pull fw stats: -71" error is not the initial failure
> >>> but a symptom that appears after repeated operations. The leak happens
> >>> during *successful* calls prior to this error.
> >>>
> >>> Code flow proving the leak:
> >>> - ath12k_mac_get_fw_stats() sends WMI_REQUEST_PDEV_STAT.
> >>> - Firmware responds. ath12k_update_stats_event() parses the response.
> >>> - ath12k_wmi_fw_stats_process() is called, which splices 'vdevs' and
> >>> 'beacon' stats into ar->fw_stats.vdevs/bcn.
> >>> - ath12k_mac_get_fw_stats() returns 0 (Success).
> >>> - In ath12k_mac_op_get_txpower(), the check `if (!pdev)` fails if the
> >>> pdev-specific list is empty (but vdev list is NOT empty).
> >>> - The function exits via `err_fallback` WITHOUT calling ath12k_fw_stats_reset().
> >>> - Result: The 'vdev' and 'beacon' stats that were spliced into
> >>> ar->fw_stats remain there, leaking memory and accumulating with every
> >>> call.
> >>>
> >>> 2. Exact place where -71 is printed:
> >>> The error "failed to pull fw stats: -71" is printed in
> >>> [ath12k_update_stats_event()](drivers/net/wireless/ath/ath12k/wmi.c).
> >>> It corresponds to "ret = ath12k_wmi_pull_fw_stats()" returning -EPROTO.
> >>> This propagates from
> >>> [ath12k_wmi_tlv_fw_stats_data_parse()](drivers/net/wireless/ath/ath12k/wmi.c),
> >>> when buffer validation checks (like `len < sizeof(*src)`) fail.
> >>>
> >>> Conclusion:
> >>> The fix in my patch (resetting stats when `!pdev`) is critical because
> >>> it ensures that the accumulated 'vdev' and 'beacon' stats are freed
> >>> even when the 'pdev' list ends up empty.
> >>>
> >>> Let me know if you need anything else.
> >>
> >> can you please try below to see if it can fix your issue?
> >>
> >> https://lore.kernel.org/r/20260129-ath12k-fw-stats-fixes-v1-0-55d66064f4d5@oss.qualcomm.com
> >>
> >>>
> >>> Thanks & Regards,
> >>> Saikiran
> >>>
> >>> On Tue, Jan 27, 2026 at 9:47 AM Saikiran B <bjsaikiran@gmail.com> wrote:
> >>>>
> >>>> Hi Baochen,
> >>>>
> >>>> Regarding your questions:
> >>>>
> >>>> "Are other stats populated?"
> >>>>
> >>>> Yes. When ath12k_mac_get_fw_stats() returns success (0), it means the
> >>>> firmware response was received and valid WMI events were processed.
> >>>> The firmware response to WMI_REQUEST_PDEV_STAT typically includes
> >>>> multiple stats TLVs (vdev stats, beacon stats, etc.). Even if the
> >>>> "pdev stats" list ends up empty (e.g., due to specific filtering or
> >>>> availability), the firmware should have populated other lists (like
> >>>> vdevs or beacons) in the ar->fw_stats structure. If we don't reset,
> >>>> these valid entries leak and accumulate.
> >>>>
> >>>> "Where exactly is -71 (EPROTO) printed?"
> >>>>
> >>>> The log "failed to pull fw stats: -71" is printed in
> >>>> ath12k_update_stats_event() (wmi.c line 8500 in my tree). This error
> >>>> code (-EPROTO) propagates from ath12k_wmi_tlv_fw_stats_data_parse(),
> >>>> where it is returned when buffer validation checks fail (e.g., if (len
> >>>> < sizeof(*src))). This failure suggests that the accumulated state or
> >>>> memory corruption from the leak eventually causes the parser to fail
> >>>> on subsequent events.
> >>>>
> >>>> So, fixing the leak is necessary for correctness regardless of the
> >>>> specific side-effect error code.
> >>>>
> >>>> Thanks & Regards,
> >>>> Saikiran
> >>>>
> >>>> On Tue, Jan 27, 2026 at 8:57 AM Baochen Qiang
> >>>> <baochen.qiang@oss.qualcomm.com> wrote:
> >>>>>
> >>>>>
> >>>>>
> >>>>> On 1/26/2026 5:52 PM, Saikiran wrote:
> >>>>>> The commits bd6ec8111e65 and 2977567b244f changed firmware stats handling
> >>>>>> to be caller-driven, requiring explicit ath12k_fw_stats_reset() calls
> >>>>>> after using ath12k_mac_get_fw_stats().
> >>>>>>
> >>>>>> In ath12k_mac_op_get_txpower(), when ath12k_mac_get_fw_stats() succeeds
> >>>>>> but the pdev stats list is empty, the function exits without calling
> >>>>>> ath12k_fw_stats_reset(). Even though the pdev list is empty, the firmware
> >>>>>> may have populated other stats lists (vdevs, beacons, etc.) in the
> >>>>>
> >>>>> 'may' is not enough, we need to be 100% sure whether other stats are populated. This is
> >>>>> critical for us to find the root cause.
> >>>>>
> >>>>>> ar->fw_stats structure.
> >>>>>>
> >>>>>> Without resetting the stats buffer, this data accumulates across multiple
> >>>>>> calls, eventually causing the stats buffer to overflow and leading to
> >>>>>> firmware communication failures (error -71/EPROTO) during subsequent
> >>>>>> operations.
> >>>>>>
> >>>>>> The issue manifests during 5GHz scanning which triggers multiple TX power
> >>>>>> queries. Symptoms include:
> >>>>>> - "failed to pull fw stats: -71" errors in dmesg
> >>>>>
> >>>>> still, can you please check the logs to see at which exact place is this printed?
> >>>>>
> >>>>>> - 5GHz networks not detected despite hardware support
> >>>>>> - 2.4GHz networks work normally
> >>>>>>
> >>>>>> Fix by calling ath12k_fw_stats_reset() when the pdev list is empty,
> >>>>>> ensuring the stats buffer is properly cleaned up even when only partial
> >>>>>> stats data is received from firmware.
> >>>>>>
> >>>>>> Fixes: bd6ec8111e65 ("wifi: ath12k: Make firmware stats reset caller-driven")
> >>>>>> Link: https://bugs.launchpad.net/ubuntu-concept/+bug/2138308
> >>>>>> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302 (Lenovo Yoga Slim 7x)
> >>>>>> Signed-off-by: Saikiran <bjsaikiran@gmail.com>
> >>>>>> ---
> >>>>>> drivers/net/wireless/ath/ath12k/mac.c | 1 +
> >>>>>> 1 file changed, 1 insertion(+)
> >>>>>>
> >>>>>> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> >>>>>> index e0e49f782bf8..6e35c3ee9864 100644
> >>>>>> --- a/drivers/net/wireless/ath/ath12k/mac.c
> >>>>>> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> >>>>>> @@ -5169,6 +5169,7 @@ static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
> >>>>>> struct ath12k_fw_stats_pdev, list);
> >>>>>> if (!pdev) {
> >>>>>> spin_unlock_bh(&ar->data_lock);
> >>>>>> + ath12k_fw_stats_reset(ar);
> >>>>>> goto err_fallback;
> >>>>>> }
> >>>>>>
> >>>>>
> >>
> >
> > Hi Baochen,
> >
> > I tried applying your patches on top of v6.19-rc7 (which is the latest
> > mainline release candidate I'm testing on), but I ran into build
> > issues because some of the dependencies seem missing.
> >
> > Specifically:
> > Patch 2 ("wifi: ath12k: fix station lookup failure when disconnecting
> > from AP") uses `ath12k_link_sta_find_by_addr()`, which does not exist
> > in my tree. It seems your patches are based on a different tree
> > (ath-next?) that has newer changes not yet in the mainline.
> >
> > Could you please point me to the specific git repo/branch you are
> > using? I can try to build and test on that baseline to be sure.
>
> My bad. Please try the latest ath tree:
>
> https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git/
>
> the base commit is ath-202601271544 tag.
>
> >
> > Regarding the firmware stats issue:
> > I verified the firmware files match the latest available (MD5 sums
> > matched), yet the "-71" errors and memory leak persist on my device
> > without fixes.
> >
> > I successfully applied the logic from your Patch 1 manually (since
> > [ath12k_mac_get_target_pdev_id](cci:1://file:///home/saikiran/linux/kernel/x1e/x1e/drivers/net/wireless/ath/ath12k/mac.c:989:0-1008:1)
> > exists), but I haven't fully validated if it alone resolves the leak
> > in all scenarios.
> >
> > However, the fix I proposed in my v2 patch (resetting stats when pdev
> > list is empty) definitely stops the leak mechanism by ensuring cleanup
> > happens even when the firmware returns partial stats (which seems to
> > be the trigger condition).
> >
> > I'll wait for your pointer to the base tree to do a proper test of your series.
> >
> > Thanks & Regards,
> > Saikiran
>
Hi,
I have merged the tag, applied your patches and I can confirm that the
leak has been fixed.
Thanks & Regards,
Saikiran
^ permalink raw reply
* pull-request: rtw-next-2026-01-30
From: Ping-Ke Shih @ 2026-01-30 7:20 UTC (permalink / raw)
To: linux-wireless
Hi,
This is another rtw-next pull-request to wireless-next tree in this -next
cycle. Please let me know if any problems.
Thanks
Ping-Ke
---
The following changes since commit 1e1dd9eeaab3908746d1dce5db6b0c29e0d28d6d:
wifi: mac80211: mark iface work SKBs as consumed (2026-01-19 10:22:35 +0100)
are available in the Git repository at:
https://github.com/pkshih/rtw.git tags/rtw-next-2026-01-30
for you to fetch changes up to 4c1552473acf03cad828884b4e1c90b97a89b265:
wifi: rtw89: pci: warn if SPS OCP happens for RTL8922DE (2026-01-30 13:56:19 +0800)
----------------------------------------------------------------
rtw-next patches for -next
Mainly refactor flow for preparation of rtw89 RTL8922DE. Others are random
fixes and refinements.
----------------------------------------------------------------
Chih-Kang Chang (1):
wifi: rtw89: wow: disable interrupt before swapping FW for 8922D
Dian-Syuan Yang (1):
wifi: rtw89: pci: restore LDO setting after device resume
Eric Huang (5):
wifi: rtw89: phy: extend register to read history 2 of PHY env_monitor
wifi: rtw89: phy: update bb wrapper TPU init
wifi: rtw89: phy: handle C2H event for PS mode report
wifi: rtw89: phy: add H2C command to send detail RX gain and link parameters for PS mode
wifi: rtw89: phy: update edcca log parsing for RTL8922D
Marco Crivellari (1):
wifi: rtw88: add WQ_PERCPU to alloc_workqueue users
Ping-Ke Shih (22):
wifi: rtw89: mac: clear global interrupt right after power-on
wifi: rtw89: phy: add {read,write}_rf_v3 for RTL8922D
wifi: rtw89: phy: add ops rtw89_phy_gen_be_v1 for RTL8922D
wifi: rtw89: phy: abstract start address and EHT of PHY status bitmap
wifi: rtw89: phy: abstract BB wrap registers to share initial flow
wifi: rtw89: phy: update BB wrapper RFSI
wifi: rtw89: phy: write BB wrapper registers with flush
wifi: rtw89: phy: refine initial flow of BB wrapper
wifi: rtw89: phy: add chip_ops to calculate RX gain from efuse to support PS mode
wifi: rtw89: phy: add firmware element of digital TX power compensation
wifi: rtw89: mac: clear DTOP disable excluding CID7090 variant for RTL8922D
wifi: rtw89: mac: set force MBA duration to 0
wifi: rtw89: rfk: add firmware command to do TX IQK
wifi: rtw89: rfk: add to print debug log of TX IQK
wifi: rtw89: rfk: add firmware command to do CIM3K
wifi: rtw89: rfk: add to print debug log of CIM3K
wifi: rtw89: rfk: update RFK report format of IQK, DACK and TXGAPK
wifi: rtw89: fw: correct content of DACK H2C command
wifi: rtw89: phy: add PHY C2H event dummy handler for func 1-7 and 2-10
wifi: rtw89: pci: validate release report content before using for RTL8922DE
wifi: rtw89: mac: set MU group membership and position to registers
wifi: rtw89: pci: warn if SPS OCP happens for RTL8922DE
Po-Hao Huang (3):
wifi: rtw89: phy: fix incorrect power limit by mac_id
wifi: rtw89: fix unable to receive probe responses under MLO connection
wifi: rtw89: 8922a: add digital compensation for 2GHz
Shin-Yi Lin (1):
wifi: rtw89: Add default ID 28de:2432 for RTL8832CU
Zilin Guan (1):
wifi: rtw89: debug: Fix memory leak in __print_txpwr_map()
Zong-Zhe Yang (6):
wifi: rtw89: phy: update TSSI flow for RTL8922D
wifi: rtw89: 8922a: configure FW version for SIM_SER_L0L1_BY_HALT_H2C
wifi: rtw89: get designated link to replace link instance 0
wifi: rtw89: regd: update regulatory map to R73-R54
wifi: rtw89: debug: rename mac/ctrl error to L0/L1 error
wifi: rtw89: debug: tweak Wi-Fi 7 SER L0/L1 simulation methods
drivers/net/wireless/realtek/rtw88/usb.c | 3 +-
drivers/net/wireless/realtek/rtw89/chan.c | 31 +-
drivers/net/wireless/realtek/rtw89/core.h | 40 +-
drivers/net/wireless/realtek/rtw89/debug.c | 97 +++--
drivers/net/wireless/realtek/rtw89/debug.h | 1 +
drivers/net/wireless/realtek/rtw89/fw.c | 297 ++++++++++++-
drivers/net/wireless/realtek/rtw89/fw.h | 188 ++++++++-
drivers/net/wireless/realtek/rtw89/mac.c | 23 +-
drivers/net/wireless/realtek/rtw89/mac.h | 23 +
drivers/net/wireless/realtek/rtw89/mac80211.c | 4 +-
drivers/net/wireless/realtek/rtw89/mac_be.c | 23 +-
drivers/net/wireless/realtek/rtw89/pci.c | 17 +-
drivers/net/wireless/realtek/rtw89/pci.h | 1 +
drivers/net/wireless/realtek/rtw89/pci_be.c | 2 +
drivers/net/wireless/realtek/rtw89/phy.c | 381 +++++++++++++++--
drivers/net/wireless/realtek/rtw89/phy.h | 36 +-
drivers/net/wireless/realtek/rtw89/phy_be.c | 563 ++++++++++++++++++++++++-
drivers/net/wireless/realtek/rtw89/ps.c | 2 +
drivers/net/wireless/realtek/rtw89/reg.h | 368 +++++++++++++++-
drivers/net/wireless/realtek/rtw89/regd.c | 20 +-
drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852bt.c | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 +
drivers/net/wireless/realtek/rtw89/rtw8852cu.c | 2 +
drivers/net/wireless/realtek/rtw89/rtw8922a.c | 58 ++-
drivers/net/wireless/realtek/rtw89/wow.c | 6 +-
28 files changed, 2047 insertions(+), 144 deletions(-)
^ permalink raw reply
* RE: [GIT PULL] wireless-next-2026-01-29
From: Ping-Ke Shih @ 2026-01-30 7:22 UTC (permalink / raw)
To: Johannes Berg, Paolo Abeni, netdev@vger.kernel.org
Cc: linux-wireless@vger.kernel.org
In-Reply-To: <f20c64ea4f4a2d3567ab707d98a5164457b02735.camel@sipsolutions.net>
Hi Johannes,
>
> I just did a 32-bit build manually, and only see some warnings like
> this:
> drivers/net/wireless/realtek/rtw89/util.c:43:45: warning: decimal
> constant 2238721139 is between LONG_MAX and ULONG_MAX. For C99 that
> means long long, C90 compilers are very likely to produce unsigned long
> (and a warning) here
Our local 32-bit build can't find this warning. Could you share .config
you are using?
Ping-Ke
^ permalink raw reply
* Re: [PATCH rtw-next 1/6] wifi: rtw89: debug: rename mac/ctrl error to L0/L1 error
From: Ping-Ke Shih @ 2026-01-30 6:47 UTC (permalink / raw)
To: Ping-Ke Shih, linux-wireless; +Cc: gary.chang, dian_syuan0116, kevin_yang
In-Reply-To: <20260127085036.44060-2-pkshih@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> From: Zong-Zhe Yang <kevin_yang@realtek.com>
>
> Sync Realtek terms on SER (system error recovery) simulation.
>
> No logic is changed.
>
> Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
> Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
6 patch(es) applied to rtw-next branch of rtw.git, thanks.
9e1e967b4dbe wifi: rtw89: debug: rename mac/ctrl error to L0/L1 error
6792fcf6a691 wifi: rtw89: debug: tweak Wi-Fi 7 SER L0/L1 simulation methods
734bb61782d4 wifi: rtw89: wow: disable interrupt before swapping FW for 8922D
2258f2770e19 wifi: rtw89: mac: set MU group membership and position to registers
af1e82232b98 wifi: rtw89: pci: restore LDO setting after device resume
4c1552473acf wifi: rtw89: pci: warn if SPS OCP happens for RTL8922DE
---
https://github.com/pkshih/rtw.git
^ permalink raw reply
* [INQUIRY] wireless driver kernel-doc format
From: Lachlan Hodges @ 2026-01-30 5:11 UTC (permalink / raw)
To: linux-wireless; +Cc: arien.judge
Hi,
I am wondering what the modern consensus is on kernel doc formatting
when it comes to multi line definitions. Take the following example from
the kernel doc documentation [1]:
/**
* struct struct_name - Brief description.
* @member1: Description of member1.
* @member2: Description of member2.
* One can provide multiple line descriptions
* for members.
*
* Description of the structure.
*/
where the each subsequent line for @member2 is indented until the start of
the first line. However I know mac80211, cfg80211 and some drivers use
the following:
/**
* struct struct_name - Brief description.
* @member1: Description of member1.
* @member2: Description of member2.
* One can provide multiple line descriptions
* for members.
*
* Description of the structure.
*/
where each subsequent line is indented by a single tab. This is my preferred
method but the docs say otherwise and there appears to be a general mix in
drivers so I am wondering is there any expectation on style for new drivers?
Or are either OK? Thanks in advance.
lachlan
[1] https://docs.kernel.org/doc-guide/kernel-doc.html#structure-union-and-enumeration-documentation
^ permalink raw reply
* [wireless:for-next] BUILD SUCCESS 0858206732250d24bb43b0e95beb50dffcbae665
From: kernel test robot @ 2026-01-30 4:26 UTC (permalink / raw)
To: Paolo Abeni; +Cc: Johannes Berg, linux-wireless
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless.git for-next
branch HEAD: 0858206732250d24bb43b0e95beb50dffcbae665 Merge tag 'wireless-2026-01-29' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless
elapsed time: 935m
configs tested: 212
configs skipped: 3
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 allnoconfig gcc-15.2.0
arc allyesconfig clang-22
arc defconfig gcc-15.2.0
arc randconfig-001-20260130 clang-17
arc randconfig-002-20260130 clang-17
arm allnoconfig clang-22
arm allnoconfig gcc-15.2.0
arm allyesconfig clang-16
arm defconfig gcc-15.2.0
arm jornada720_defconfig clang-22
arm moxart_defconfig clang-22
arm randconfig-001-20260130 clang-17
arm randconfig-002-20260130 clang-17
arm randconfig-003-20260130 clang-17
arm randconfig-004-20260130 clang-17
arm stm32_defconfig clang-22
arm64 allmodconfig clang-22
arm64 allnoconfig gcc-15.2.0
arm64 defconfig gcc-15.2.0
arm64 randconfig-001-20260130 gcc-8.5.0
arm64 randconfig-002-20260130 gcc-8.5.0
arm64 randconfig-003-20260130 gcc-8.5.0
arm64 randconfig-004-20260130 clang-22
arm64 randconfig-004-20260130 gcc-8.5.0
csky allmodconfig gcc-15.2.0
csky allnoconfig gcc-15.2.0
csky defconfig gcc-15.2.0
csky randconfig-001-20260130 gcc-15.2.0
csky randconfig-001-20260130 gcc-8.5.0
csky randconfig-002-20260130 gcc-15.2.0
csky randconfig-002-20260130 gcc-8.5.0
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-20260129 clang-22
hexagon randconfig-001-20260130 gcc-11.5.0
hexagon randconfig-002-20260129 clang-22
hexagon randconfig-002-20260130 gcc-11.5.0
i386 allmodconfig clang-20
i386 allnoconfig gcc-14
i386 allnoconfig gcc-15.2.0
i386 allyesconfig clang-20
i386 buildonly-randconfig-001-20260129 clang-20
i386 buildonly-randconfig-001-20260130 clang-20
i386 buildonly-randconfig-002-20260129 clang-20
i386 buildonly-randconfig-002-20260130 clang-20
i386 buildonly-randconfig-003-20260129 clang-20
i386 buildonly-randconfig-003-20260130 clang-20
i386 buildonly-randconfig-004-20260129 clang-20
i386 buildonly-randconfig-004-20260130 clang-20
i386 buildonly-randconfig-005-20260129 clang-20
i386 buildonly-randconfig-005-20260130 clang-20
i386 buildonly-randconfig-006-20260129 clang-20
i386 buildonly-randconfig-006-20260130 clang-20
i386 defconfig gcc-15.2.0
i386 randconfig-001-20260130 clang-20
i386 randconfig-001-20260130 gcc-14
i386 randconfig-002-20260130 clang-20
i386 randconfig-002-20260130 gcc-14
i386 randconfig-003-20260130 clang-20
i386 randconfig-003-20260130 gcc-14
i386 randconfig-004-20260130 clang-20
i386 randconfig-004-20260130 gcc-14
i386 randconfig-005-20260130 gcc-14
i386 randconfig-006-20260130 clang-20
i386 randconfig-006-20260130 gcc-14
i386 randconfig-007-20260130 gcc-14
i386 randconfig-011-20260130 clang-20
i386 randconfig-012-20260130 clang-20
i386 randconfig-013-20260130 clang-20
i386 randconfig-014-20260130 clang-20
i386 randconfig-015-20260130 clang-20
i386 randconfig-016-20260130 clang-20
i386 randconfig-017-20260130 clang-20
loongarch allmodconfig clang-22
loongarch allnoconfig clang-22
loongarch allnoconfig gcc-15.2.0
loongarch defconfig clang-19
loongarch randconfig-001-20260129 clang-22
loongarch randconfig-001-20260130 gcc-11.5.0
loongarch randconfig-002-20260129 clang-22
loongarch randconfig-002-20260130 gcc-11.5.0
m68k allmodconfig gcc-15.2.0
m68k allnoconfig gcc-15.2.0
m68k allyesconfig clang-16
m68k defconfig clang-19
m68k defconfig gcc-15.2.0
microblaze allnoconfig gcc-15.2.0
microblaze allyesconfig gcc-15.2.0
microblaze defconfig clang-19
microblaze defconfig gcc-15.2.0
mips allmodconfig gcc-15.2.0
mips allnoconfig gcc-15.2.0
mips allyesconfig gcc-15.2.0
mips jazz_defconfig clang-22
nios2 allmodconfig clang-22
nios2 allnoconfig clang-22
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-19
nios2 defconfig gcc-11.5.0
nios2 randconfig-001-20260129 clang-22
nios2 randconfig-001-20260130 gcc-11.5.0
nios2 randconfig-002-20260129 clang-22
nios2 randconfig-002-20260130 gcc-11.5.0
openrisc allmodconfig clang-22
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 clang-19
parisc defconfig gcc-15.2.0
parisc randconfig-001-20260130 gcc-8.5.0
parisc randconfig-002-20260130 gcc-8.5.0
parisc64 defconfig clang-19
parisc64 defconfig gcc-15.2.0
powerpc allmodconfig gcc-15.2.0
powerpc allnoconfig clang-22
powerpc allnoconfig gcc-15.2.0
powerpc ksi8560_defconfig clang-22
powerpc randconfig-001-20260130 gcc-8.5.0
powerpc randconfig-002-20260130 gcc-8.5.0
powerpc xes_mpc85xx_defconfig clang-22
powerpc64 randconfig-001-20260130 gcc-8.5.0
powerpc64 randconfig-002-20260130 gcc-8.5.0
riscv allmodconfig clang-22
riscv allnoconfig clang-22
riscv allnoconfig gcc-15.2.0
riscv allyesconfig clang-16
riscv defconfig clang-22
riscv defconfig gcc-15.2.0
riscv randconfig-001-20260130 clang-22
riscv randconfig-002-20260130 clang-22
s390 allmodconfig clang-19
s390 allnoconfig clang-22
s390 allyesconfig gcc-15.2.0
s390 defconfig clang-22
s390 defconfig gcc-15.2.0
s390 randconfig-001-20260130 clang-22
s390 randconfig-002-20260130 clang-22
sh allmodconfig gcc-15.2.0
sh allnoconfig clang-22
sh allnoconfig gcc-15.2.0
sh allyesconfig clang-19
sh defconfig gcc-14
sh randconfig-001-20260130 clang-22
sh randconfig-002-20260130 clang-22
sh rsk7269_defconfig clang-22
sparc allnoconfig clang-22
sparc allnoconfig gcc-15.2.0
sparc defconfig gcc-15.2.0
sparc randconfig-001-20260130 clang-22
sparc randconfig-002-20260130 clang-22
sparc sparc32_defconfig clang-22
sparc64 allmodconfig clang-22
sparc64 defconfig gcc-14
sparc64 randconfig-001-20260130 clang-22
sparc64 randconfig-002-20260130 clang-22
um allmodconfig clang-19
um allnoconfig clang-22
um allyesconfig gcc-15.2.0
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001-20260130 clang-22
um randconfig-002-20260130 clang-22
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-20260130 clang-20
x86_64 buildonly-randconfig-002-20260130 clang-20
x86_64 buildonly-randconfig-002-20260130 gcc-14
x86_64 buildonly-randconfig-003-20260130 clang-20
x86_64 buildonly-randconfig-004-20260130 clang-20
x86_64 buildonly-randconfig-005-20260130 clang-20
x86_64 buildonly-randconfig-006-20260130 clang-20
x86_64 buildonly-randconfig-006-20260130 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-20
x86_64 randconfig-001-20260130 clang-20
x86_64 randconfig-002-20260130 clang-20
x86_64 randconfig-003-20260130 clang-20
x86_64 randconfig-004-20260130 clang-20
x86_64 randconfig-005-20260130 clang-20
x86_64 randconfig-006-20260130 clang-20
x86_64 randconfig-011-20260130 clang-20
x86_64 randconfig-012-20260130 clang-20
x86_64 randconfig-013-20260130 clang-20
x86_64 randconfig-014-20260130 clang-20
x86_64 randconfig-014-20260130 gcc-14
x86_64 randconfig-015-20260130 clang-20
x86_64 randconfig-016-20260130 clang-20
x86_64 randconfig-016-20260130 gcc-14
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 randconfig-001-20260130 clang-22
xtensa randconfig-002-20260130 clang-22
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [GIT PULL] wireless-next-2026-01-29
From: Jakub Kicinski @ 2026-01-30 4:25 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <20260129110136.176980-39-johannes@sipsolutions.net>
On Thu, 29 Jan 2026 11:58:31 +0100 Johannes Berg wrote:
> Another fairly large set of changes, notably:
> - cfg80211/mac80211
> - most of EPPKE/802.1X over auth frames support
> - additional FTM capabilities
> - split up drop reasons better, removing generic RX_DROP
> - NAN cleanups/fixes
> - ath11k:
> - support for Channel Frequency Response measurement
> - ath12k:
> - support for the QCC2072 chipset
> - iwlwifi:
> - partial NAN support
> - UNII-9 support
> - some UHR/802.11bn FW APIs
> - remove most of MLO/EHT from iwlmvm
> (such devices use iwlmld)
> - rtw89:
> - preparations for RTL8922DE support
Pulled, thanks!
^ permalink raw reply
* [PATCH wireless-next v2] wifi: mac80211: use wiphy_hrtimer_work for CAC timeout
From: Amith A @ 2026-01-30 3:55 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. Switch the CAC timeout to use this
infrastructure, so the CAC completion handling is triggered with tighter
timing and reduced latency.
Signed-off-by: Amith A <amith.a@oss.qualcomm.com>
---
Changes in v2:
- Updated the commit message to imply that the change adds a performance
improvement and not a correctness fix.
---
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..f1d19accae27 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, ms_to_ktime(cac_time_ms));
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
* [PATCH] wifi: mt76: add missing lock protection in mt76_sta_state for sta_event callback
From: Ziyi Guo @ 2026-01-30 3:25 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee
Cc: Shayne Chen, Sean Wang, Matthias Brugger,
AngeloGioacchino Del Regno, linux-wireless, linux-kernel,
linux-mediatek, linux-arm-kernel, Ziyi Guo
mt76_sta_state() calls the sta_event callback without holding dev->mutex.
However, mt7915_mac_sta_event() (MT7915 implementation of this callback)
calls mt7915_mac_twt_teardown_flow() which has
lockdep_assert_held(&dev->mt76.mutex) indicating that callers must
hold this lock.
The locking pattern in mt76_sta_state() is inconsistent:
- mt76_sta_add() acquires dev->mutex before calling dev->drv->sta_add
- mt76_sta_remove() acquires dev->mutex before calling __mt76_sta_remove
- But sta_event callback is called without acquiring the lock
Add mutex_lock()/mutex_unlock() around the sta_event callback invocation
to fix the missing lock protection and maintain consistency with the
existing locking pattern.
Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 75772979f438..468bc176bacb 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1640,6 +1640,7 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct mt76_phy *phy = hw->priv;
struct mt76_dev *dev = phy->dev;
enum mt76_sta_event ev;
+ int ret;
phy = mt76_vif_phy(hw, vif);
if (!phy)
@@ -1668,7 +1669,11 @@ int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
else
return 0;
- return dev->drv->sta_event(dev, vif, sta, ev);
+ mutex_lock(&dev->mutex);
+ ret = dev->drv->sta_event(dev, vif, sta, ev);
+ mutex_unlock(&dev->mutex);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(mt76_sta_state);
--
2.34.1
^ permalink raw reply related
* Re: [GIT PULL] wireless-2026-01-29
From: Jakub Kicinski @ 2026-01-30 2:19 UTC (permalink / raw)
To: Johannes Berg; +Cc: netdev, linux-wireless
In-Reply-To: <20260129110403.178036-3-johannes@sipsolutions.net>
On Thu, 29 Jan 2026 12:03:45 +0100 Johannes Berg wrote:
> Here's just a single potential out-of-bounds read fix,
> and I really hope that's the last for this cycle.
>
> Please pull and let us know if there's any problem.
This was merged and part of today's PR to Linus, thanks!
(Looks like the bot didn't fire.)
^ permalink raw reply
* Re: [PATCH v2 2/2] wifi: ath12k: Fix firmware stats leak when pdev list is empty
From: Baochen Qiang @ 2026-01-30 2:09 UTC (permalink / raw)
To: Saikiran B; +Cc: ath12k, linux-wireless, kvalo
In-Reply-To: <CAAFDt1s_NtY1vXa5STRW7oQn9yDJBC0g7CPSZXn0tFhd+CSHrQ@mail.gmail.com>
On 1/29/2026 10:06 PM, Saikiran B wrote:
> On Thu, Jan 29, 2026 at 7:57 AM Baochen Qiang
> <baochen.qiang@oss.qualcomm.com> wrote:
>>
>>
>>
>> On 1/27/2026 12:40 PM, Saikiran B wrote:
>>> I have analyzed the logs and code flow in depth to provide more
>>> definitive answers for your questions.
>>>
>>> The log entries showing the failure are:
>>> [ 563.574076] ath12k_pci 0004:01:00.0: failed to pull fw stats: -71
>>> [ 564.575896] ath12k_pci 0004:01:00.0: time out while waiting for get fw stats
>>>
>>> 1. Why are other stats populated?
>>> The "failed to pull fw stats: -71" error is not the initial failure
>>> but a symptom that appears after repeated operations. The leak happens
>>> during *successful* calls prior to this error.
>>>
>>> Code flow proving the leak:
>>> - ath12k_mac_get_fw_stats() sends WMI_REQUEST_PDEV_STAT.
>>> - Firmware responds. ath12k_update_stats_event() parses the response.
>>> - ath12k_wmi_fw_stats_process() is called, which splices 'vdevs' and
>>> 'beacon' stats into ar->fw_stats.vdevs/bcn.
>>> - ath12k_mac_get_fw_stats() returns 0 (Success).
>>> - In ath12k_mac_op_get_txpower(), the check `if (!pdev)` fails if the
>>> pdev-specific list is empty (but vdev list is NOT empty).
>>> - The function exits via `err_fallback` WITHOUT calling ath12k_fw_stats_reset().
>>> - Result: The 'vdev' and 'beacon' stats that were spliced into
>>> ar->fw_stats remain there, leaking memory and accumulating with every
>>> call.
>>>
>>> 2. Exact place where -71 is printed:
>>> The error "failed to pull fw stats: -71" is printed in
>>> [ath12k_update_stats_event()](drivers/net/wireless/ath/ath12k/wmi.c).
>>> It corresponds to "ret = ath12k_wmi_pull_fw_stats()" returning -EPROTO.
>>> This propagates from
>>> [ath12k_wmi_tlv_fw_stats_data_parse()](drivers/net/wireless/ath/ath12k/wmi.c),
>>> when buffer validation checks (like `len < sizeof(*src)`) fail.
>>>
>>> Conclusion:
>>> The fix in my patch (resetting stats when `!pdev`) is critical because
>>> it ensures that the accumulated 'vdev' and 'beacon' stats are freed
>>> even when the 'pdev' list ends up empty.
>>>
>>> Let me know if you need anything else.
>>
>> can you please try below to see if it can fix your issue?
>>
>> https://lore.kernel.org/r/20260129-ath12k-fw-stats-fixes-v1-0-55d66064f4d5@oss.qualcomm.com
>>
>>>
>>> Thanks & Regards,
>>> Saikiran
>>>
>>> On Tue, Jan 27, 2026 at 9:47 AM Saikiran B <bjsaikiran@gmail.com> wrote:
>>>>
>>>> Hi Baochen,
>>>>
>>>> Regarding your questions:
>>>>
>>>> "Are other stats populated?"
>>>>
>>>> Yes. When ath12k_mac_get_fw_stats() returns success (0), it means the
>>>> firmware response was received and valid WMI events were processed.
>>>> The firmware response to WMI_REQUEST_PDEV_STAT typically includes
>>>> multiple stats TLVs (vdev stats, beacon stats, etc.). Even if the
>>>> "pdev stats" list ends up empty (e.g., due to specific filtering or
>>>> availability), the firmware should have populated other lists (like
>>>> vdevs or beacons) in the ar->fw_stats structure. If we don't reset,
>>>> these valid entries leak and accumulate.
>>>>
>>>> "Where exactly is -71 (EPROTO) printed?"
>>>>
>>>> The log "failed to pull fw stats: -71" is printed in
>>>> ath12k_update_stats_event() (wmi.c line 8500 in my tree). This error
>>>> code (-EPROTO) propagates from ath12k_wmi_tlv_fw_stats_data_parse(),
>>>> where it is returned when buffer validation checks fail (e.g., if (len
>>>> < sizeof(*src))). This failure suggests that the accumulated state or
>>>> memory corruption from the leak eventually causes the parser to fail
>>>> on subsequent events.
>>>>
>>>> So, fixing the leak is necessary for correctness regardless of the
>>>> specific side-effect error code.
>>>>
>>>> Thanks & Regards,
>>>> Saikiran
>>>>
>>>> On Tue, Jan 27, 2026 at 8:57 AM Baochen Qiang
>>>> <baochen.qiang@oss.qualcomm.com> wrote:
>>>>>
>>>>>
>>>>>
>>>>> On 1/26/2026 5:52 PM, Saikiran wrote:
>>>>>> The commits bd6ec8111e65 and 2977567b244f changed firmware stats handling
>>>>>> to be caller-driven, requiring explicit ath12k_fw_stats_reset() calls
>>>>>> after using ath12k_mac_get_fw_stats().
>>>>>>
>>>>>> In ath12k_mac_op_get_txpower(), when ath12k_mac_get_fw_stats() succeeds
>>>>>> but the pdev stats list is empty, the function exits without calling
>>>>>> ath12k_fw_stats_reset(). Even though the pdev list is empty, the firmware
>>>>>> may have populated other stats lists (vdevs, beacons, etc.) in the
>>>>>
>>>>> 'may' is not enough, we need to be 100% sure whether other stats are populated. This is
>>>>> critical for us to find the root cause.
>>>>>
>>>>>> ar->fw_stats structure.
>>>>>>
>>>>>> Without resetting the stats buffer, this data accumulates across multiple
>>>>>> calls, eventually causing the stats buffer to overflow and leading to
>>>>>> firmware communication failures (error -71/EPROTO) during subsequent
>>>>>> operations.
>>>>>>
>>>>>> The issue manifests during 5GHz scanning which triggers multiple TX power
>>>>>> queries. Symptoms include:
>>>>>> - "failed to pull fw stats: -71" errors in dmesg
>>>>>
>>>>> still, can you please check the logs to see at which exact place is this printed?
>>>>>
>>>>>> - 5GHz networks not detected despite hardware support
>>>>>> - 2.4GHz networks work normally
>>>>>>
>>>>>> Fix by calling ath12k_fw_stats_reset() when the pdev list is empty,
>>>>>> ensuring the stats buffer is properly cleaned up even when only partial
>>>>>> stats data is received from firmware.
>>>>>>
>>>>>> Fixes: bd6ec8111e65 ("wifi: ath12k: Make firmware stats reset caller-driven")
>>>>>> Link: https://bugs.launchpad.net/ubuntu-concept/+bug/2138308
>>>>>> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302 (Lenovo Yoga Slim 7x)
>>>>>> Signed-off-by: Saikiran <bjsaikiran@gmail.com>
>>>>>> ---
>>>>>> drivers/net/wireless/ath/ath12k/mac.c | 1 +
>>>>>> 1 file changed, 1 insertion(+)
>>>>>>
>>>>>> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
>>>>>> index e0e49f782bf8..6e35c3ee9864 100644
>>>>>> --- a/drivers/net/wireless/ath/ath12k/mac.c
>>>>>> +++ b/drivers/net/wireless/ath/ath12k/mac.c
>>>>>> @@ -5169,6 +5169,7 @@ static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw,
>>>>>> struct ath12k_fw_stats_pdev, list);
>>>>>> if (!pdev) {
>>>>>> spin_unlock_bh(&ar->data_lock);
>>>>>> + ath12k_fw_stats_reset(ar);
>>>>>> goto err_fallback;
>>>>>> }
>>>>>>
>>>>>
>>
>
> Hi Baochen,
>
> I tried applying your patches on top of v6.19-rc7 (which is the latest
> mainline release candidate I'm testing on), but I ran into build
> issues because some of the dependencies seem missing.
>
> Specifically:
> Patch 2 ("wifi: ath12k: fix station lookup failure when disconnecting
> from AP") uses `ath12k_link_sta_find_by_addr()`, which does not exist
> in my tree. It seems your patches are based on a different tree
> (ath-next?) that has newer changes not yet in the mainline.
>
> Could you please point me to the specific git repo/branch you are
> using? I can try to build and test on that baseline to be sure.
My bad. Please try the latest ath tree:
https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git/
the base commit is ath-202601271544 tag.
>
> Regarding the firmware stats issue:
> I verified the firmware files match the latest available (MD5 sums
> matched), yet the "-71" errors and memory leak persist on my device
> without fixes.
>
> I successfully applied the logic from your Patch 1 manually (since
> [ath12k_mac_get_target_pdev_id](cci:1://file:///home/saikiran/linux/kernel/x1e/x1e/drivers/net/wireless/ath/ath12k/mac.c:989:0-1008:1)
> exists), but I haven't fully validated if it alone resolves the leak
> in all scenarios.
>
> However, the fix I proposed in my v2 patch (resetting stats when pdev
> list is empty) definitely stops the leak mechanism by ensuring cleanup
> happens even when the firmware returns partial stats (which seems to
> be the trigger condition).
>
> I'll wait for your pointer to the base tree to do a proper test of your series.
>
> Thanks & Regards,
> Saikiran
^ permalink raw reply
* [PATCH wireless-next] wifi: mac80211: correct ieee80211-{s1g/eht}.h include guard comments
From: Lachlan Hodges @ 2026-01-30 0:53 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless, arien.judge, Lachlan Hodges
After the split of ieee80211.h some include guard comments weren't
updated, update them to their new file names.
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
include/linux/ieee80211-eht.h | 2 +-
include/linux/ieee80211-s1g.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/ieee80211-eht.h b/include/linux/ieee80211-eht.h
index f9782e46c5e5..620dad3995cc 100644
--- a/include/linux/ieee80211-eht.h
+++ b/include/linux/ieee80211-eht.h
@@ -1179,4 +1179,4 @@ static inline u32 ieee80211_eml_trans_timeout_in_us(u16 eml_cap)
_data + ieee80211_mle_common_size(_data),\
_len - ieee80211_mle_common_size(_data))
-#endif /* LINUX_IEEE80211_H */
+#endif /* LINUX_IEEE80211_EHT_H */
diff --git a/include/linux/ieee80211-s1g.h b/include/linux/ieee80211-s1g.h
index 5b9ed2dcc00e..22dde4cbc1b0 100644
--- a/include/linux/ieee80211-s1g.h
+++ b/include/linux/ieee80211-s1g.h
@@ -572,4 +572,4 @@ static inline bool ieee80211_s1g_check_tim(const struct ieee80211_tim_ie *tim,
}
}
-#endif /* LINUX_IEEE80211_H */
+#endif /* LINUX_IEEE80211_S1G_H */
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox