Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v6 1/6] wifi: mac80211: Use struct instead of macro for PREQ frame
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 43 +++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 68 +++++++++++++++++-----------------
 2 files changed, 76 insertions(+), 35 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 4b829bcb38b6..623894140300 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -28,12 +28,40 @@ struct ieee80211s_hdr {
 	u8 eaddr2[ETH_ALEN];
 } __packed __aligned(2);
 
+struct ieee80211_mesh_hwmp_preq_target {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	__le32 preq_id;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+
+	/* optional AE, lifetime, metric, target */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_preq_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 target_count;
+	struct ieee80211_mesh_hwmp_preq_target targets[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
 #define MESH_FLAGS_AE		0x3
 #define MESH_FLAGS_PS_DEEP	0x4
 
+/* HWMP IE processing macros */
+#define AE_F			(1<<6)
+
 /**
  * enum ieee80211_preq_flags - mesh PREQ element flags
  *
@@ -227,4 +255,19 @@ enum ieee80211_root_mode_identifier {
 	IEEE80211_PROACTIVE_RANN = 4,
 };
 
+static inline bool ieee80211_mesh_preq_prep_ae_enabled(const u8 *ie)
+{
+	return ie[0] & AE_F;
+}
+
+static inline struct ieee80211_mesh_hwmp_preq_bottom *
+	ieee80211_mesh_hwmp_preq_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_preq_top *top =
+		(struct ieee80211_mesh_hwmp_preq_top *)ie;
+	return (struct ieee80211_mesh_hwmp_preq_bottom *)
+		&top->variable[ieee80211_mesh_preq_prep_ae_enabled(ie) ?
+			ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 9d89ebcce1c1..e0c8ba8cb1e0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -35,24 +35,11 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 }
 
 /* HWMP IE processing macros */
-#define AE_F			(1<<6)
 #define AE_F_SET(x)		(*x & AE_F)
-#define PREQ_IE_FLAGS(x)	(*(x))
-#define PREQ_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREQ_IE_TTL(x)		(*(x + 2))
-#define PREQ_IE_PREQ_ID(x)	u32_field_get(x, 3, 0)
-#define PREQ_IE_ORIG_ADDR(x)	(x + 7)
-#define PREQ_IE_ORIG_SN(x)	u32_field_get(x, 13, 0)
-#define PREQ_IE_LIFETIME(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREQ_IE_METRIC(x) 	u32_field_get(x, 21, AE_F_SET(x))
-#define PREQ_IE_TARGET_F(x)	(*(AE_F_SET(x) ? x + 32 : x + 26))
-#define PREQ_IE_TARGET_ADDR(x) 	(AE_F_SET(x) ? x + 33 : x + 27)
-#define PREQ_IE_TARGET_SN(x) 	u32_field_get(x, 33, AE_F_SET(x))
-
-
-#define PREP_IE_FLAGS(x)	PREQ_IE_FLAGS(x)
-#define PREP_IE_HOPCOUNT(x)	PREQ_IE_HOPCOUNT(x)
-#define PREP_IE_TTL(x)		PREQ_IE_TTL(x)
+
+#define PREP_IE_FLAGS(x)	(*(x))
+#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
+#define PREP_IE_TTL(x)		(*(x + 2))
 #define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
 #define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
 #define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
@@ -415,11 +402,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 
 	switch (action) {
 	case MPATH_PREQ:
-		orig_addr = PREQ_IE_ORIG_ADDR(hwmp_ie);
-		orig_sn = PREQ_IE_ORIG_SN(hwmp_ie);
-		orig_lifetime = PREQ_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREQ_IE_METRIC(hwmp_ie);
-		hopcount = PREQ_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
+			(struct ieee80211_mesh_hwmp_preq_top *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(hwmp_ie);
+
+		orig_addr = preq_elem_top->orig_addr;
+		orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
+		orig_lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
+		orig_metric = get_unaligned_le32(&preq_elem_bottom->metric);
+		hopcount = preq_elem_top->hopcount + 1;
 		break;
 	case MPATH_PREP:
 		/* Originator here refers to the MP that was the target in the
@@ -579,6 +571,12 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *preq_elem, u32 orig_metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_preq_top *preq_elem_top =
+		(struct ieee80211_mesh_hwmp_preq_top *)preq_elem;
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(preq_elem);
+	struct ieee80211_mesh_hwmp_preq_target *target =
+		preq_elem_bottom->targets;
 	struct mesh_path *mpath = NULL;
 	const u8 *target_addr, *orig_addr;
 	const u8 *da;
@@ -589,13 +587,13 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	bool root_is_gate;
 
 	/* Update target SN, if present */
-	target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-	orig_addr = PREQ_IE_ORIG_ADDR(preq_elem);
-	target_sn = PREQ_IE_TARGET_SN(preq_elem);
-	orig_sn = PREQ_IE_ORIG_SN(preq_elem);
-	target_flags = PREQ_IE_TARGET_F(preq_elem);
+	target_addr = target[0].addr;
+	orig_addr = preq_elem_top->orig_addr;
+	target_sn = get_unaligned_le32(&target[0].sn);
+	orig_sn = get_unaligned_le32(&preq_elem_top->orig_sn);
+	target_flags = target[0].flags;
 	/* Proactive PREQ gate announcements */
-	flags = PREQ_IE_FLAGS(preq_elem);
+	flags = preq_elem_top->flags;
 	root_is_gate = !!(flags & RANN_FLAG_IS_GATE);
 
 	mhwmp_dbg(sdata, "received PREQ from %pM\n", orig_addr);
@@ -655,7 +653,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 	}
 
 	if (reply) {
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
 		ttl = ifmsh->mshcfg.element_ttl;
 		if (ttl != 0) {
 			mhwmp_dbg(sdata, "replying to the PREQ\n");
@@ -673,22 +671,22 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
 		u32 preq_id;
 		u8 hopcount;
 
-		ttl = PREQ_IE_TTL(preq_elem);
-		lifetime = PREQ_IE_LIFETIME(preq_elem);
+		ttl = preq_elem_top->ttl;
+		lifetime = get_unaligned_le32(&preq_elem_bottom->lifetime);
 		if (ttl <= 1) {
 			ifmsh->mshstats.dropped_frames_ttl++;
 			return;
 		}
 		mhwmp_dbg(sdata, "forwarding the PREQ from %pM\n", orig_addr);
 		--ttl;
-		preq_id = PREQ_IE_PREQ_ID(preq_elem);
-		hopcount = PREQ_IE_HOPCOUNT(preq_elem) + 1;
+		preq_id = get_unaligned_le32(&preq_elem_top->preq_id);
+		hopcount = preq_elem_top->hopcount + 1;
 		da = (mpath && mpath->is_root) ?
 			mpath->rann_snd_addr : broadcast_addr;
 
 		if (flags & IEEE80211_PREQ_PROACTIVE_PREP_FLAG) {
-			target_addr = PREQ_IE_TARGET_ADDR(preq_elem);
-			target_sn = PREQ_IE_TARGET_SN(preq_elem);
+			target_addr = target[0].addr;
+			target_sn = get_unaligned_le32(&target[0].sn);
 		}
 
 		mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 2/6] wifi: mac80211: Use struct instead of macro for PREP frame
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260515233839.86829-1-masashi.honma@gmail.com>

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 28 ++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 47 +++++++++++++++++-----------------
 2 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 623894140300..093c6b80a983 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -53,6 +53,24 @@ struct ieee80211_mesh_hwmp_preq_bottom {
 	struct ieee80211_mesh_hwmp_preq_target targets[];
 } __packed;
 
+struct ieee80211_mesh_hwmp_prep_top {
+	u8 flags;
+	u8 hopcount;
+	u8 ttl;
+	u8 target_addr[ETH_ALEN];
+	__le32 target_sn;
+
+	/* optional Target External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_prep_bottom {
+	__le32 lifetime;
+	__le32 metric;
+	u8 orig_addr[ETH_ALEN];
+	__le32 orig_sn;
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -270,4 +288,14 @@ static inline struct ieee80211_mesh_hwmp_preq_bottom *
 			ETH_ALEN : 0];
 }
 
+static inline struct ieee80211_mesh_hwmp_prep_bottom *
+	ieee80211_mesh_hwmp_prep_get_bottom(const u8 *ie)
+{
+	struct ieee80211_mesh_hwmp_prep_top *top =
+		(struct ieee80211_mesh_hwmp_prep_top *)ie;
+	return (struct ieee80211_mesh_hwmp_prep_bottom *)
+		&top->variable[ieee80211_mesh_preq_prep_ae_enabled(ie) ?
+			ETH_ALEN : 0];
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index e0c8ba8cb1e0..d9394091cea9 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -37,16 +37,6 @@ static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
 /* HWMP IE processing macros */
 #define AE_F_SET(x)		(*x & AE_F)
 
-#define PREP_IE_FLAGS(x)	(*(x))
-#define PREP_IE_HOPCOUNT(x)	(*(x + 1))
-#define PREP_IE_TTL(x)		(*(x + 2))
-#define PREP_IE_ORIG_ADDR(x)	(AE_F_SET(x) ? x + 27 : x + 21)
-#define PREP_IE_ORIG_SN(x)	u32_field_get(x, 27, AE_F_SET(x))
-#define PREP_IE_LIFETIME(x)	u32_field_get(x, 13, AE_F_SET(x))
-#define PREP_IE_METRIC(x)	u32_field_get(x, 17, AE_F_SET(x))
-#define PREP_IE_TARGET_ADDR(x)	(x + 3)
-#define PREP_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-
 #define PERR_IE_TTL(x)		(*(x))
 #define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
 #define PERR_IE_TARGET_ADDR(x)	(x + 3)
@@ -419,11 +409,16 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
 		 * so that we can easily use a single function to gather path
 		 * information from both PREQ and PREP frames.
 		 */
-		orig_addr = PREP_IE_TARGET_ADDR(hwmp_ie);
-		orig_sn = PREP_IE_TARGET_SN(hwmp_ie);
-		orig_lifetime = PREP_IE_LIFETIME(hwmp_ie);
-		orig_metric = PREP_IE_METRIC(hwmp_ie);
-		hopcount = PREP_IE_HOPCOUNT(hwmp_ie) + 1;
+		struct ieee80211_mesh_hwmp_prep_top *prep_elem_top =
+			(struct ieee80211_mesh_hwmp_prep_top *)hwmp_ie;
+		struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+			ieee80211_mesh_hwmp_prep_get_bottom(hwmp_ie);
+
+		orig_addr = prep_elem_top->target_addr;
+		orig_sn = get_unaligned_le32(&prep_elem_top->target_sn);
+		orig_lifetime = get_unaligned_le32(&prep_elem_bottom->lifetime);
+		orig_metric = get_unaligned_le32(&prep_elem_bottom->metric);
+		hopcount = prep_elem_top->hopcount + 1;
 		break;
 	default:
 		rcu_read_unlock();
@@ -715,6 +710,10 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *prep_elem, u32 metric)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_prep_top *prep_elem_top =
+		(struct ieee80211_mesh_hwmp_prep_top *)prep_elem;
+	struct ieee80211_mesh_hwmp_prep_bottom *prep_elem_bottom =
+		ieee80211_mesh_hwmp_prep_get_bottom(prep_elem);
 	struct mesh_path *mpath;
 	const u8 *target_addr, *orig_addr;
 	u8 ttl, hopcount, flags;
@@ -722,9 +721,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	u32 target_sn, orig_sn, lifetime;
 
 	mhwmp_dbg(sdata, "received PREP from %pM\n",
-		  PREP_IE_TARGET_ADDR(prep_elem));
+		  prep_elem_top->target_addr);
 
-	orig_addr = PREP_IE_ORIG_ADDR(prep_elem);
+	orig_addr = prep_elem_bottom->orig_addr;
 	if (ether_addr_equal(orig_addr, sdata->vif.addr))
 		/* destination, no forwarding required */
 		return;
@@ -732,7 +731,7 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	if (!ifmsh->mshcfg.dot11MeshForwarding)
 		return;
 
-	ttl = PREP_IE_TTL(prep_elem);
+	ttl = prep_elem_top->ttl;
 	if (ttl <= 1) {
 		sdata->u.mesh.mshstats.dropped_frames_ttl++;
 		return;
@@ -751,12 +750,12 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
 	memcpy(next_hop, next_hop_deref_protected(mpath)->sta.addr, ETH_ALEN);
 	spin_unlock_bh(&mpath->state_lock);
 	--ttl;
-	flags = PREP_IE_FLAGS(prep_elem);
-	lifetime = PREP_IE_LIFETIME(prep_elem);
-	hopcount = PREP_IE_HOPCOUNT(prep_elem) + 1;
-	target_addr = PREP_IE_TARGET_ADDR(prep_elem);
-	target_sn = PREP_IE_TARGET_SN(prep_elem);
-	orig_sn = PREP_IE_ORIG_SN(prep_elem);
+	flags = prep_elem_top->flags;
+	lifetime = get_unaligned_le32(&prep_elem_bottom->lifetime);
+	hopcount = prep_elem_top->hopcount + 1;
+	target_addr = prep_elem_top->target_addr;
+	target_sn = get_unaligned_le32(&prep_elem_top->target_sn);
+	orig_sn = get_unaligned_le32(&prep_elem_bottom->orig_sn);
 
 	mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0,
 			       target_addr, target_sn, next_hop, hopcount,
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 3/6] wifi: mac80211: Use struct instead of macro for PERR frame
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260515233839.86829-1-masashi.honma@gmail.com>

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 26 ++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 32 ++++++--------------------------
 2 files changed, 32 insertions(+), 26 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 093c6b80a983..6b56f462fc21 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -71,6 +71,20 @@ struct ieee80211_mesh_hwmp_prep_bottom {
 	__le32 orig_sn;
 } __packed;
 
+struct ieee80211_mesh_hwmp_perr_dst {
+	u8 flags;
+	u8 addr[ETH_ALEN];
+	__le32 sn;
+	/* optional Destination External Address */
+	u8 variable[];
+} __packed;
+
+struct ieee80211_mesh_hwmp_perr {
+	u8 ttl;
+	u8 number_of_dst;
+	struct ieee80211_mesh_hwmp_perr_dst dsts[];
+} __packed;
+
 /* Mesh flags */
 #define MESH_FLAGS_AE_A4 	0x1
 #define MESH_FLAGS_AE_A5_A6	0x2
@@ -298,4 +312,16 @@ static inline struct ieee80211_mesh_hwmp_prep_bottom *
 			ETH_ALEN : 0];
 }
 
+static inline __le16 ieee80211_mesh_hwmp_perr_get_rcode(
+	const u8 *ie, u8 dst_idx)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_ie =
+		(struct ieee80211_mesh_hwmp_perr *)ie;
+	struct ieee80211_mesh_hwmp_perr_dst *dst =
+		&perr_ie->dsts[dst_idx];
+
+	return get_unaligned_le16(&dst->variable[
+		(dst->flags & AE_F) ? ETH_ALEN : 0]);
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index d9394091cea9..044555c42a86 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -20,29 +20,7 @@
 
 static void mesh_queue_preq(struct mesh_path *, u8);
 
-static inline u32 u32_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le32(preq_elem + offset);
-}
-
-static inline u16 u16_field_get(const u8 *preq_elem, int offset, bool ae)
-{
-	if (ae)
-		offset += 6;
-	return get_unaligned_le16(preq_elem + offset);
-}
-
 /* HWMP IE processing macros */
-#define AE_F_SET(x)		(*x & AE_F)
-
-#define PERR_IE_TTL(x)		(*(x))
-#define PERR_IE_TARGET_FLAGS(x)	(*(x + 2))
-#define PERR_IE_TARGET_ADDR(x)	(x + 3)
-#define PERR_IE_TARGET_SN(x)	u32_field_get(x, 9, 0)
-#define PERR_IE_TARGET_RCODE(x)	u16_field_get(x, 13, 0)
-
 #define MSEC_TO_TU(x) (x*1000/1024)
 #define SN_GT(x, y) ((s32)(y - x) < 0)
 #define SN_LT(x, y) ((s32)(x - y) < 0)
@@ -776,6 +754,8 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 				    const u8 *perr_elem)
 {
 	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+	struct ieee80211_mesh_hwmp_perr *perr_elem_s =
+		(struct ieee80211_mesh_hwmp_perr *)perr_elem;
 	struct mesh_path *mpath;
 	u8 ttl;
 	const u8 *ta, *target_addr;
@@ -783,15 +763,15 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
 	u16 target_rcode;
 
 	ta = mgmt->sa;
-	ttl = PERR_IE_TTL(perr_elem);
+	ttl = perr_elem_s->ttl;
 	if (ttl <= 1) {
 		ifmsh->mshstats.dropped_frames_ttl++;
 		return;
 	}
 	ttl--;
-	target_addr = PERR_IE_TARGET_ADDR(perr_elem);
-	target_sn = PERR_IE_TARGET_SN(perr_elem);
-	target_rcode = PERR_IE_TARGET_RCODE(perr_elem);
+	target_addr = perr_elem_s->dsts[0].addr;
+	target_sn = get_unaligned_le32(&perr_elem_s->dsts[0].sn);
+	target_rcode = ieee80211_mesh_hwmp_perr_get_rcode(perr_elem, 0);
 
 	rcu_read_lock();
 	mpath = mesh_path_lookup(sdata, target_addr);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 4/6] wifi: mac80211: Fix overread in PREQ frame processing
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260515233839.86829-1-masashi.honma@gmail.com>

When the AF flag is enabled, hwmp_preq_frame_process() overreads target_addr
by 2 bytes. Since this occurs within the socket buffer, it does not read across
memory boundaries and therefore poses no security risk; however, we will fix it
as a precaution.

In this fix, a new function mesh_path_parse_request_frame() is established to
separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 31 +++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 12 ++++++++++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 48 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 6b56f462fc21..2ff94da770b1 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -324,4 +324,35 @@ static inline __le16 ieee80211_mesh_hwmp_perr_get_rcode(
 		(dst->flags & AE_F) ? ETH_ALEN : 0]);
 }
 
+/* IEEE Std 802.11-2016 9.4.2.113 PREQ element */
+static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+		ieee80211_mesh_hwmp_preq_get_bottom(pos);
+	u8 target_count;
+	u8 needed;
+
+	/* Check if the element contains flags */
+	if (elen < 1)
+		return false;
+
+	/* Check if the element contains target_count */
+	needed = sizeof(struct ieee80211_mesh_hwmp_preq_top) +
+		 (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Originator External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_preq_bottom);
+	if (elen < needed)
+		return false;
+
+	target_count = preq_elem_bottom->target_count;
+	if (target_count < 1 || target_count > 20)
+		return false;
+
+	needed += target_count * sizeof(struct ieee80211_mesh_hwmp_preq_target);
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 044555c42a86..70973d53605d 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -932,9 +932,17 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 		return;
 
 	if (elems->preq) {
-		if (elems->preq_len != 37)
-			/* Right now we support just 1 destination and no AE */
+		struct ieee80211_mesh_hwmp_preq_bottom *preq_elem_bottom =
+			ieee80211_mesh_hwmp_preq_get_bottom(elems->preq);
+
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->preq))
 			goto free;
+
+		/* Right now we only supports 1 target */
+		if (preq_elem_bottom->target_count != 1)
+			goto free;
+
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->preq,
 						  MPATH_PREQ);
 		if (path_metric)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 5e61457be0f3..9e52cc48fc18 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -547,8 +547,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 				elems->awake_window = (void *)pos;
 			break;
 		case WLAN_EID_PREQ:
-			elems->preq = pos;
-			elems->preq_len = elen;
+			if (ieee80211_mesh_preq_size_ok(pos, elen)) {
+				elems->preq = pos;
+				elems->preq_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PREP:
 			elems->prep = pos;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 5/6] wifi: mac80211: Fix overread in PREP frame processing
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260515233839.86829-1-masashi.honma@gmail.com>

When the AF flag is enabled, hwmp_prep_frame_process() overreads orig_addr
by 2 bytes. Since this occurs within the socket buffer, it does not read across
memory boundaries and therefore poses no security risk; however, we will fix it
as a precaution.

In this fix, a new function mesh_path_parse_reply_frame() is established to
separate the implementation of frame format validation and the check for
unsupported features. This is intended to facilitate future work when
implementing the currently unsupported parts.

Assisted-by: Claude:Sonnet 4.6
Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 19 +++++++++++++++++++
 net/mac80211/mesh_hwmp.c       |  4 ++--
 net/mac80211/parse.c           |  9 +++++++--
 3 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 2ff94da770b1..8c32a52dbd6a 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -355,4 +355,23 @@ static inline bool ieee80211_mesh_preq_size_ok(const u8 *pos, u8 elen)
 	return true;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.114 PREP element */
+static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
+{
+	u8 needed;
+
+	/* Check if the element contains flags */
+	if (elen < 1)
+		return false;
+
+	needed = sizeof(struct ieee80211_mesh_hwmp_prep_top) +
+		 (ieee80211_mesh_preq_prep_ae_enabled(pos) ? ETH_ALEN : 0)
+		 /* Target External Address */ +
+		 sizeof(struct ieee80211_mesh_hwmp_prep_bottom);
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 70973d53605d..fdde827bc1b0 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -950,8 +950,8 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->prep) {
-		if (elems->prep_len != 31)
-			/* Right now we support no AE */
+		/* Right now we do not support AE (Address Extension) */
+		if (ieee80211_mesh_preq_prep_ae_enabled(elems->prep))
 			goto free;
 		path_metric = hwmp_route_info_get(sdata, mgmt, elems->prep,
 						  MPATH_PREP);
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index 9e52cc48fc18..bbd1e1bc77b4 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -556,8 +556,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PREP:
-			elems->prep = pos;
-			elems->prep_len = elen;
+			if (ieee80211_mesh_prep_size_ok(pos, elen)) {
+				elems->prep = pos;
+				elems->prep_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_PERR:
 			elems->perr = pos;
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 6/6] wifi: mac80211: Fix PERR frame processing
From: Masashi Honma @ 2026-05-15 23:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: johannes, Masashi Honma
In-Reply-To: <20260515233839.86829-1-masashi.honma@gmail.com>

There are no issues with the PERR processing itself; however, to maintain
consistency with the previous PREQ/PREP code modifications, I will create a new
mesh_path_parse_error_frame() function to separately implement the frame format
validation and the "not supported" check.

Signed-off-by: Masashi Honma <masashi.honma@gmail.com>
---
 include/linux/ieee80211-mesh.h | 45 ++++++++++++++++++++++++++++++++++
 net/mac80211/mesh_hwmp.c       | 14 +++++++++--
 net/mac80211/parse.c           |  9 +++++--
 3 files changed, 64 insertions(+), 4 deletions(-)

diff --git a/include/linux/ieee80211-mesh.h b/include/linux/ieee80211-mesh.h
index 8c32a52dbd6a..2bcd3704ad3b 100644
--- a/include/linux/ieee80211-mesh.h
+++ b/include/linux/ieee80211-mesh.h
@@ -374,4 +374,49 @@ static inline bool ieee80211_mesh_prep_size_ok(const u8 *pos, u8 elen)
 	return true;
 }
 
+/* IEEE Std 802.11-2016 9.4.2.115 PERR element */
+static inline bool ieee80211_mesh_perr_size_ok(const u8 *pos, u8 elen)
+{
+	struct ieee80211_mesh_hwmp_perr *perr_elem =
+		(struct ieee80211_mesh_hwmp_perr *)pos;
+	u8 number_of_dst;
+	u8 needed;
+	const u8 *start;
+	int i;
+
+	start = pos;
+	needed = sizeof(struct ieee80211_mesh_hwmp_perr);
+	pos += sizeof(struct ieee80211_mesh_hwmp_perr);
+
+	/* Check if the element contains number of dst */
+	if (elen < needed)
+		return false;
+
+	number_of_dst = perr_elem->number_of_dst;
+	if (number_of_dst < 1 || number_of_dst > 19)
+		return false;
+
+	for (i = 0; i < number_of_dst; i++) {
+		struct ieee80211_mesh_hwmp_perr_dst *perr_dst =
+			&perr_elem->dsts[i];
+		u8 dst_len;
+
+		/* Check if the element contains flags */
+		if (elen < pos - start + 1)
+			return false;
+
+		dst_len = sizeof(struct ieee80211_mesh_hwmp_perr_dst) +
+			  ((perr_dst->flags & AE_F) ? ETH_ALEN : 0)
+			  /* Destination External Address */ +
+			  2 /* Reason Code */;
+		needed += dst_len;
+		pos += dst_len;
+	}
+
+	if (elen != needed)
+		return false;
+
+	return true;
+}
+
 #endif /* LINUX_IEEE80211_MESH_H */
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index fdde827bc1b0..45ee9ea1ff56 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -960,9 +960,19 @@ void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
 						path_metric);
 	}
 	if (elems->perr) {
-		if (elems->perr_len != 15)
-			/* Right now we support only one destination per PERR */
+		struct ieee80211_mesh_hwmp_perr *perr_elem =
+			(struct ieee80211_mesh_hwmp_perr *)elems->perr;
+		int i;
+
+		/* Right now we support only one destination per PERR */
+		if (perr_elem->number_of_dst != 1)
 			goto free;
+
+		/* Right now we do not support AE (Address Extension) */
+		for (i = 0; i < perr_elem->number_of_dst; i++)
+			if (perr_elem->dsts[i].flags & AE_F)
+				goto free;
+
 		hwmp_perr_frame_process(sdata, mgmt, elems->perr);
 	}
 	if (elems->rann)
diff --git a/net/mac80211/parse.c b/net/mac80211/parse.c
index bbd1e1bc77b4..d84e5e12ad24 100644
--- a/net/mac80211/parse.c
+++ b/net/mac80211/parse.c
@@ -565,8 +565,13 @@ _ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params,
 			}
 			break;
 		case WLAN_EID_PERR:
-			elems->perr = pos;
-			elems->perr_len = elen;
+			if (ieee80211_mesh_perr_size_ok(pos, elen)) {
+				elems->perr = pos;
+				elems->perr_len = elen;
+			} else {
+				elem_parse_failed =
+					IEEE80211_PARSE_ERR_BAD_ELEM_SIZE;
+			}
 			break;
 		case WLAN_EID_RANN:
 			if (elen >= sizeof(struct ieee80211_rann_ie))
-- 
2.43.0


^ permalink raw reply related

* RE: [PATCH] wireless-regdb: add regulatory rules for Iraq (IQ)
From: Mohammed.Al-Obaidi @ 2026-05-16  7:42 UTC (permalink / raw)
  To: wireless-regdb, linux-wireless; +Cc: mnewiraq2000, mnew_iraq
In-Reply-To: <1886262646.6291779.1777997531793@mail.yahoo.com>

Kind reminder, any update on the subject?

Mohammed

From: Mohammed Abdalla [mailto:mnew_iraq@yahoo.com]
Sent: Tuesday, May 5, 2026 7:12 PM
To: wireless-regdb@lists.infradead.org; linux-wireless@vger.kernel.org
Cc: Mohammed Abdullah Ali Al-Obaidi <Mohammed.Al-Obaidi@badraproject.com>
Subject: [PATCH] wireless-regdb: add regulatory rules for Iraq (IQ)

## 1. Why this patch exists

Iraq is currently absent from `wireless-regdb/db.txt`. As a
consequence, every OpenWrt and Linux device set to `country=IQ`
falls back to the world domain (`00`), which marks most of the
5 GHz spectrum as `no IR` and limits 2.4 GHz EIRP to 20 dBm. A
public OpenWrt forum thread on the Archer AX23 in Iraq concluded
with the maintainers' standard answer:

> *"IQ is the correct code for the place; once an engineer shares
> the local radio laws with regdb maintainers it will be added."*

This patch is that contribution.

## 2. The primary source

The Iraqi Communications and Media Commission (CMC), the national
regulator, has issued a numerical regulation specifically governing
unlicensed Wi-Fi, SRD, and UWB devices:

- **Title:** Regulation on short-range radio communication devices
  (SRD) and devices using ultra-broadband (UWB) technology
- **Issuer:** Republic of Iraq, CMC, Telecommunications Regulatory
  Department, International Relations Section
- **Decree:** Council of Commissioners decision No. 122/q-2025
- **In force from:** 2025-09-22
- **Edition:** First edition, 2025; 26 pages
- **Direct PDF:**
  <https://cmc.iq/wp-content/uploads/2025/09/Regulation-on-short-range-radio-communication-devices-SRD-and-devices-using-ultra-broadband-UWB-technology.pdf>

Article 4-1-13 of that regulation, titled "Wireless Access Systems
(WAS)", contains a full numerical table for every Wi-Fi band. This
patch reproduces that table directly. Nothing in the proposed
`country IQ:` block is inferred or extrapolated.

## 3. The Article 4-1-13 table, verbatim

| Band | Use | Max EIRP | Required mitigations | Cited standard |
|---|---|---|---|---|
| 2400 – 2483.5 MHz | Indoor and outdoor | 100 mW | LBT and DAA | EN 300 328, ERC/REC 70-03 |
| 5150 – 5250 MHz | Indoor | 200 mW | — | EN 301 893, ITU Res. 229 (Rev. WRC-19) |
| 5250 – 5350 MHz | Indoor | 200 mW | — (DFS implied via EN 301 893) | EN 301 893 |
| 5470 – 5725 MHz | Indoor | 1000 mW | DFS and TPC (stated explicitly) | EN 301 893 |
| 5725 – 5875 MHz | Indoor and outdoor | 2000 mW (10 MHz ch) / 4000 mW (20 MHz ch) | — | EN 302 502 |
| 5945 – 6425 MHz | Indoor | 200 mW | — | EN 303 687, ECC Report 75 |
| 57000 – 66000 MHz | Indoor | 10000 mW | LBT and DAA | EN 302 567 |

The proposed `country IQ:` block encodes this table line for line.

## 4. The Iraqi regulation also defines its own glossary terms

For the avoidance of doubt, the regulation's Annex A explicitly
defines `Wi-Fi` as *"802.11 Local Area Networking in 2.4 and 5 GHz
ISM bands"*. So when the maintainers ask whether this regulation
in fact covers Wi-Fi, the answer from the regulator is yes,
in writing, in the regulation itself.

The same annex defines DFS, TPC, LBT, DAA, EIRP and AFA in the
exact wireless-regdb sense.

## 5. Encoding choices and where they came from

A few wireless-regdb encoding details require explanation, because
they are interpretations of the regulation's wording rather than
direct copies of numerical limits:

1. **NO-OUTDOOR on 5150–5725 MHz.** The regulation labels these
   rows simply as "Indoor". The wireless-regdb idiom for that is
   the `NO-OUTDOOR` flag.

2. **No NO-OUTDOOR on 5725–5875 MHz.** The regulation explicitly
   labels this row "Indoor and outdoor".

3. **DFS on 5250–5350 MHz.** The regulation's own column for this
   row is empty for mitigations, but the cited standard
   (EN 301 893) requires DFS in this sub-band, and the corresponding
   row for 5470–5725 in the same table does state DFS+TPC. Reading
   the regulation as a whole, DFS for 5250–5350 is required by the
   incorporated standard.

4. **Single EIRP figure for 5725–5875 MHz.** The regulation gives
   two figures (2000 mW for 10 MHz channels, 4000 mW for 20 MHz
   channels). The wireless-regdb format expresses one ceiling per
   band; the 4000 mW figure is used because it is the higher value
   that the regulation explicitly permits.

5. **6 GHz channel width set to 80 MHz.** The regulation does not
   explicitly distinguish standard-power from low-power indoor
   (LPI) operation, nor does it mention AFC. The conservative
   choice is to encode the 6 GHz block at 80 MHz (the widest
   non-AFC option in current practice) and leave a follow-up patch
   for a wider channelisation once CMC clarifies AFC requirements.

6. **AUTO-BW on the 5 GHz RLAN rows.** Standard practice for
   EN 301 893–compliant entries; no AUTO-BW is set on the 6 GHz
   row pending the AFC question above.

If the maintainers prefer a different encoding for any of these
six points, please push back; the underlying regulatory text is
clear and any of these can be re-encoded without changing what is
actually permitted under Iraqi law.

## 6. The 5.8 GHz figure looks unusually high — it is intentional

`(5725 - 5875 @ 80), (4000 mW)` with no NO-OUTDOOR is not a typo.
This is what Iraq's own regulation states for this sub-band, citing
EN 302 502. It is the BFWA value, not the Non-Specific SRD value.
This choice puts Iraq at the high end of the regional spectrum
policy for the 5.8 GHz band. It is included verbatim because the
purpose of wireless-regdb is to reflect what each country's
regulator actually permits.

## 7. What is not in the patch

- **5850–5925 MHz ITS / V2X bands.** Article 4-1-8 of the same
  regulation covers ITS at 5855–5925 MHz with 2 W EIRP, but this
  is a non-Wi-Fi RLAN application and is outside the scope of
  what wireless-regdb usually encodes for `country` blocks.

- **All non-Wi-Fi SRD bands.** The regulation also covers RFID,
  inductive applications, alarms, model control, automotive radar,
  level probing radar, hearing aids, active medical implants, and
  the full UWB regime (Articles 4-2-1 through 4-2-6). None of these
  is a wireless-regdb concern.

- **6 GHz beyond 6425 MHz.** The Iraqi regulation only addresses
  5945–6425 MHz at 6 GHz; the 6425–7125 MHz upper portion is not
  covered, and the patch therefore does not include it.

## 8. Submission checklist

- [ ] Verify the patch applies cleanly against the current
      `wireless-regdb` master; the IQ block must be inserted in
      alphabetical order, between `IN` and `IR`.
- [ ] Build `regulatory.db` locally and confirm with
      `regdbdump regulatory.db | grep -A8 'country IQ'` that the
      output matches the proposed table exactly.
- [ ] Post the cover letter and patch on the OpenWrt forum thread
      (231380) for community review by Iraqi engineers before
      sending upstream.
- [ ] Send to `linux-wireless@vger.kernel.org` with cc to
      `wireless-regdb@lists.infradead.org`.

## 9. A note on responsibility

The numerical content of this patch is taken verbatim from a public
Iraqi government regulation. The encoding choices listed in §5 are
the patch author's, and they are reversible.

Author: Mohammed Abdullah Ali Al-Obaidi (mnew_iraq@yahoo.com),
OpenWrt forum handle `mnewiraq`. Any objection to the encoding
choices should be raised to that author or in the upstream review
thread, not to the CMC.


CONFIDENTIALITY. This communication is intended exclusively for use by the addressee and may contain confidential and/or privileged information. If you receive this communication unintentionally please inform the sender by reply immediately and permanently delete it from your system; you should not copy this communication or disclose its contents to anyone.

^ permalink raw reply

* [PATCH v2] wireless-regdb: Update regulatory rules for Sri Lanka (LK)
From: Mohamed Aashif @ 2026-05-16 10:00 UTC (permalink / raw)
  To: linux-wireless; +Cc: wireless-regdb, pkshih, Mohamed Aashif
In-Reply-To: <CAHrRpu=Ku=cK2Hd8xzJr82iPkj4Jzp57ch9wEE1KsoCiExbhbA@mail.gmail.com>

Update Sri Lanka regulatory domain based on the RTTE Type Approval
Rules 2020 from the Telecommunications Regulatory Commission of
Sri Lanka (TRC).

Source: https://www.trc.gov.lk/content/files/licensing/RTTE_GAZETTE-English.pdf

Key changes:
- Change DFS region from FCC to ETSI (document references ETSI
  standards EN 300 328 and EN 301 893 as the applicable radio
  interface standards)
- 2.4 GHz: expand to 2400-2483.5 MHz at 23 dBm
- 5150-5250 MHz: 23 dBm with DFS
- 5250-5350 MHz: 20 dBm with DFS (no TPC, 3 dB reduction per EN 301 893)
- 5470-5725 MHz: 27 dBm with DFS (no TPC, per EN 301 893)
- 5725-5875 MHz: 24 dBm with DFS (per ETSI EN 302 502)

Note: The TRC gazette conflicts internally by allowing 30 dBm for
5725-5875 MHz while citing EN 301 893 (which doesn't cover this band).
Lacking TRC clarification, we conservatively default to the ETSI
EN 302 502 limits (24 dBm).

Signed-off-by: Mohamed Aashif <maashif011@gmail.com>
---
Thanks for the review and catching the TPC and ETSI standard details!

Changes in v2:
- Split 5150-5350 MHz into 5150-5250 MHz (23 dBm) and 5250-5350 MHz
  (20 dBm), adding AUTO-BW to both.
- Applied a 3 dB reduction to 5250-5350 MHz and 5470-5725 MHz (now
  27 dBm) to comply with ETSI limits since TPC is unsupported.
- Capped 5725-5875 MHz at 24 dBm per ETSI EN 302 502.

 db.txt | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/db.txt b/db.txt
index fdc2c13..12cdafd 100644
--- a/db.txt
+++ b/db.txt
@@ -1058,12 +1058,15 @@ country LI: DFS-ETSI
 	# 60 GHz band channels 1-4 (ETSI EN 302 567)
 	(57000 - 66000 @ 2160), (40)
 
-country LK: DFS-FCC
-	(2402 - 2482 @ 40), (20)
-	(5170 - 5250 @ 20), (17)
-	(5250 - 5330 @ 20), (24), DFS
-	(5490 - 5730 @ 20), (24), DFS
-	(5735 - 5835 @ 20), (30)
+# Source:
+# https://www.trc.gov.lk/content/files/licensing/RTTE_GAZETTE-English.pdf
+# Sri Lanka RTTE Type Approval Rules 2020, Table 2 - Wideband Data Transmission
+country LK: DFS-ETSI
+	(2400 - 2483.5 @ 40), (23)
+	(5150 - 5250 @ 80), (23), DFS, AUTO-BW
+	(5250 - 5350 @ 80), (20), DFS, AUTO-BW
+	(5470 - 5725 @ 160), (27), DFS
+	(5725 - 5875 @ 80), (24), DFS
 
 # Source:
 # http://lca.org.ls/images/documents/lesotho_national_frequency_allocation_plan.pdf
-- 
2.54.0


^ permalink raw reply related

* Re: ath12k WCN7850: Q6 Hexagon fault at WLAON region 0x1792000 ~2s post-AUTHORIZE on X1E80100
From: Marcus Glocker @ 2026-05-16 11:28 UTC (permalink / raw)
  To: Baochen Qiang, Jeff Johnson, Can Erkin Acar
  Cc: Maxim Storetvedt, ath12k, linux-wireless, jjohnson, kvalo,
	Mark Kettenis
In-Reply-To: <yjintdxwvtkgpehyjdtrs4wdwfli3c2vdyp2bweiauwssywjl5@srrtguf5eq4i>

On Thu, May 14, 2026 at 07:20:51PM +0200, Marcus Glocker wrote:

> Hi Baochen, all,
> 
> On Thu, May 14, 2026 at 09:55:30AM +0800, Baochen Qiang wrote:
> 
> > > With that fixed, the firmware error is gone, and we can now receive
> > > and IP from DHCP.  We're working on getting the TX path work next.
> > 
> > OK, good to see it gets fixed!
> 
> Since today we also got RX/TX fixed, so we have now an initial working
> ath12k device with our qwz driver on OpenBSD.
> 
> What is still missing from the firmware side;  The PCI-id of the
> WCN7850 chip included in the Samsung Galaxy Book4 Edge isn't included
> yet in to the Linux firmware package.  I've addressed this a few times,
> but unfortunately no feedback.  That's the additional PCI-id line
> which is required for the Samsung Galaxy Book4 Edge:
> 
> --- board-2.json.orig	Sun Apr 26 08:06:37 2026
> +++ board-2.json	Sun Apr 26 08:07:45 2026
> @@ -160,7 +160,8 @@
>              {
>                  "names": [
>                      "bus=pci,vendor=17cb,device=1107,subsystem-vendor=17aa,subsystem-device=e0e6,qmi-chip-id=2,qmi-board-id=255",
> -                    "bus=pci,vendor=17cb,device=1107,subsystem-vendor=1eac,subsystem-device=8003,qmi-chip-id=2,qmi-board-id=255"
> +                    "bus=pci,vendor=17cb,device=1107,subsystem-vendor=1eac,subsystem-device=8003,qmi-chip-id=2,qmi-board-id=255",
> +		    "bus=pci,vendor=17cb,device=1107,subsystem-vendor=17cb,subsystem-device=1107,qmi-chip-id=2,qmi-board-id=255"
>                  ],
>                  "data": "bus=pci,vendor=17cb,device=1107,subsystem-vendor=17aa,subsystem-device=e0e6,qmi-chip-id=2,qmi-board-id=255.bin"
>              },
> @@ -202,4 +203,4 @@
>              }
>          ]
>      }
> -]
> \ No newline at end of file
> +]
> 
> With the resulting 'qwz-wcn7850-hw2.0-board-2' firmware board file,
> we have now proved that the ath12k driver is working with Linux and
> OpenBSD on the Samsung Galaxy Book4 Edge.  Maxim Storetvedt did test
> with Linux.
> 
> Given that, would it be possible to include this PCI-id to the next
> linux-firmware package?
> 
> Thanks and Regards,
> Marcus

Hi Jeff, Can,

Could you please help me with my above request?

Thanks,
Marcus

^ permalink raw reply

* [PATCH rtw-next] wifi: rtw89: Add missing TX queue mappings for RTL8922AU
From: Bitterblue Smith @ 2026-05-16 12:32 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih

A user with MLO-capable router reports:

[ 753.080409] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26
[ 753.080417] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26
[ 813.649426] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26
[ 813.649445] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26
[ 813.855983] rtw89_8922au_git 2-2:1.0: Cannot map qsel to dma v2: 26

Map RTW89_TX_QSEL_B1_MGMT and RTW89_TX_QSEL_B1_HI to RTW89_TXCH_CH10.
This is probably the right channel for these queues.

This function is also used for RTL8852AU and RTL8852CU, but those
currently aren't used in DBCC mode with rtw89 so it makes no difference
for them.

Link: https://github.com/morrownr/rtw89/issues/83#issuecomment-4314735734
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 432d46dfd26a..e370339569f1 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -854,6 +854,9 @@ u8 rtw89_core_get_ch_dma_v2(struct rtw89_dev *rtwdev, u8 qsel)
 	case RTW89_TX_QSEL_B0_MGMT:
 	case RTW89_TX_QSEL_B0_HI:
 		return RTW89_TXCH_CH8;
+	case RTW89_TX_QSEL_B1_MGMT:
+	case RTW89_TX_QSEL_B1_HI:
+		return RTW89_TXCH_CH10;
 	}
 }
 EXPORT_SYMBOL(rtw89_core_get_ch_dma_v2);
-- 
2.53.0


^ permalink raw reply related

* [PATCH rtw-next v3] wifi: rtw89: usb: Support switching to USB 3 mode
From: Bitterblue Smith @ 2026-05-16 12:49 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org; +Cc: Ping-Ke Shih

The Realtek wifi 6/7 devices which support USB 3 are weird: when first
plugged in, they pretend to be USB 2. The driver needs to send some
commands to the device, which make it disappear and come back as a
USB 3 device.

Implement the required commands in rtw89.

Add a new function rtw89_usb_write32_quiet() to avoid the warnings
when writing to R_{AX,BE}_PAD_CTRL2. Even though the write succeeds,
usb_control_msg() returns -EPROTO, probably because the USB device
disappears immediately. This results in some confusing warnings in
the kernel log.

When a USB 3 device is plugged into a USB 2 port, rtw89 will try to
switch it to USB 3 mode only once. The device will disappear and come
back still in USB 2 mode, of course.

Tested with RTL8832AU, RTL8832BU, RTL8832CU, and RTL8912AU.

Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
v3:
 - Avoid crash when trying to switch.
 - Remove the unwanted module parameter.
 - Support RTL8922AU as well.
 - Use more bit names.
 - Hide the warnings when writing R_AX_PAD_CTRL2.
---
 drivers/net/wireless/realtek/rtw89/reg.h |  27 ++++++
 drivers/net/wireless/realtek/rtw89/usb.c | 112 +++++++++++++++++++++--
 2 files changed, 130 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 716747c3a10e..c9816ead834d 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -164,6 +164,19 @@
 #define R_AX_DBG_PORT_SEL 0x00C0
 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0)
 
+#define R_AX_PAD_CTRL2 0x00C4
+#define B_AX_FORCE_U3_CK BIT(23)
+#define B_AX_USB2_FORCE BIT(22)
+#define B_AX_USB3_FORCE BIT(21)
+#define B_AX_USB3_USB2_TRANSITION BIT(20)
+#define B_AX_USB23_SW_MODE_V1 GENMASK(19, 18)
+#define USB_MODE_U2 0x1
+#define USB_MODE_U3 0x2
+#define B_AX_NO_PDN_CHIPOFF_V1 BIT(17)
+#define B_AX_RSM_EN_V1 BIT(16)
+#define B_AX_MATCH_CNT GENMASK(15, 8)
+#define USB_SWITCH_DELAY 0xF
+
 #define R_AX_PMC_DBG_CTRL2 0x00CC
 #define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2)
 
@@ -4235,6 +4248,20 @@
 #define B_BE_TOGGLE BIT(31)
 #define B_BE_DATA_LINE_MASK GENMASK(30, 0)
 
+#define R_BE_PAD_CTRL2 0x00C4
+#define B_BE_USB23_SW_MODE BIT(31)
+#define B_BE_USB3_GEN_MODE BIT(30)
+#define B_BE_USB3_LANE_MODE BIT(29)
+#define B_BE_USB_AUTO_INSTALL_MASK BIT(28)
+#define B_BE_FORCE_CLK_U2 BIT(25)
+#define B_BE_FORCE_U2_CK BIT(24)
+#define B_BE_FORCE_U3_CK BIT(23)
+#define B_BE_USB2_FORCE BIT(22)
+#define B_BE_USB3_FORCE BIT(21)
+#define B_BE_NO_PDN_CHIPOFF_V1 BIT(17)
+#define B_BE_RSM_EN_V1 BIT(16)
+#define B_BE_MATCH_CNT GENMASK(15, 8)
+
 #define R_BE_PMC_DBG_CTRL2 0x00CC
 #define B_BE_EFUSE_BURN_GNT_MASK GENMASK(31, 24)
 #define B_BE_DIS_IOWRAP_TIMEOUT BIT(16)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index c6d55e669776..bfe004a49ccc 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -12,7 +12,7 @@
 static void rtw89_usb_read_port_complete(struct urb *urb);
 
 static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
-				void *data, u16 len, u8 reqtype)
+				void *data, u16 len, u8 reqtype, bool warn)
 {
 	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 	struct usb_device *udev = rtwusb->udev;
@@ -52,7 +52,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
 
 		if (ret == -ESHUTDOWN || ret == -ENODEV)
 			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
-		else if (ret < 0)
+		else if (ret < 0 && warn)
 			rtw89_warn(rtwdev,
 				   "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n",
 				   str_read_write(reqtype == RTW89_USB_VENQT_READ),
@@ -80,7 +80,7 @@ static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr)
 
 	for (count = 0; ; count++) {
 		rtw89_usb_vendorreq(rtwdev, addr32, &data, 4,
-				    RTW89_USB_VENQT_READ);
+				    RTW89_USB_VENQT_READ, true);
 
 		val32 = le32_to_cpu(data);
 		if (val32 != RTW89_R32_DEAD)
@@ -106,7 +106,8 @@ static u8 rtw89_usb_ops_read8(struct rtw89_dev *rtwdev, u32 addr)
 	if (ACCESS_CMAC(addr))
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_READ);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 1,
+			    RTW89_USB_VENQT_READ, true);
 
 	return data;
 }
@@ -118,7 +119,8 @@ static u16 rtw89_usb_ops_read16(struct rtw89_dev *rtwdev, u32 addr)
 	if (ACCESS_CMAC(addr))
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_READ);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 2,
+			    RTW89_USB_VENQT_READ, true);
 
 	return le16_to_cpu(data);
 }
@@ -131,7 +133,7 @@ static u32 rtw89_usb_ops_read32(struct rtw89_dev *rtwdev, u32 addr)
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
 	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
-			    RTW89_USB_VENQT_READ);
+			    RTW89_USB_VENQT_READ, true);
 
 	return le32_to_cpu(data);
 }
@@ -140,21 +142,32 @@ static void rtw89_usb_ops_write8(struct rtw89_dev *rtwdev, u32 addr, u8 val)
 {
 	u8 data = val;
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 1,
+			    RTW89_USB_VENQT_WRITE, true);
 }
 
 static void rtw89_usb_ops_write16(struct rtw89_dev *rtwdev, u32 addr, u16 val)
 {
 	__le16 data = cpu_to_le16(val);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 2,
+			    RTW89_USB_VENQT_WRITE, true);
 }
 
 static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val)
 {
 	__le32 data = cpu_to_le32(val);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
+			    RTW89_USB_VENQT_WRITE, true);
+}
+
+static void rtw89_usb_write32_quiet(struct rtw89_dev *rtwdev, u32 addr, u32 val)
+{
+	__le32 data = cpu_to_le32(val);
+
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
+			    RTW89_USB_VENQT_WRITE, false);
 }
 
 static u32
@@ -1059,6 +1072,80 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev,
 	usb_set_intfdata(intf, NULL);
 }
 
+static int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev)
+{
+	u32 pad_ctrl2;
+
+	/* No known USB 3 devices with this chip. */
+	if (rtwdev->chip->chip_id == RTL8851B)
+		return 0;
+
+	pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2);
+
+	rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n",
+		    __func__, pad_ctrl2);
+
+	/* Already tried to switch but it's a USB 2 port. */
+	if (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY)
+		return 0;
+
+	/* Add delay to prevent some platforms would not detect USB switch */
+	u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT);
+
+	pad_ctrl2 &= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE |
+		       B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION);
+
+	u32p_replace_bits(&pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1);
+
+	pad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1;
+
+	rtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2);
+
+	return 1;
+}
+
+static int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev)
+{
+	u32 pad_ctrl2;
+
+	pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2);
+
+	rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n",
+		    __func__, pad_ctrl2);
+
+	/* Already tried to switch but it's a USB 2 port. */
+	if (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY)
+		return 0;
+
+	/* Add delay to prevent some platforms would not detect USB switch */
+	u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT);
+
+	pad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 |
+		     B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE;
+
+	pad_ctrl2 &= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE |
+		       B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK |
+		       B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE |
+		       B_BE_USB3_LANE_MODE);
+
+	rtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2);
+
+	return 1;
+}
+
+static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+
+	if (rtwusb->udev->speed == USB_SPEED_SUPER)
+		return 0;
+
+	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
+		return rtw89_usb_switch_mode_ax(rtwdev);
+
+	return rtw89_usb_switch_mode_be(rtwdev);
+}
+
 int rtw89_usb_probe(struct usb_interface *intf,
 		    const struct usb_device_id *id)
 {
@@ -1091,6 +1178,13 @@ int rtw89_usb_probe(struct usb_interface *intf,
 		goto err_free_hw;
 	}
 
+	ret = rtw89_usb_switch_mode(rtwdev);
+	if (ret) {
+		/* Not a fail, but we do need to skip rtw89_core_register. */
+		ret = 0;
+		goto err_intf_deinit;
+	}
+
 	if (rtwusb->udev->speed == USB_SPEED_SUPER)
 		rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3;
 	else
-- 
2.53.0


^ permalink raw reply related

* [PATCH rtw-next] wifi: rtw88: Add more validation for the RX descriptor
From: Bitterblue Smith @ 2026-05-16 14:44 UTC (permalink / raw)
  To: linux-wireless@vger.kernel.org
  Cc: Ping-Ke Shih, LB F, Martin Blumenstingl, Fiona Klute,
	andrej.skvortzov, anarsoul, Zhen XIN

Some RTL8821CE cards can return frames with corrupted RX descriptor,
causing warnings and crashes if they are passed to the upper layers.

The PHY status size field is 4 bits wide, but in rtw88 its value should
only be 0 or 4. Checking this catches most of the corrupt frames.

If a PHY status is present, the PHY status size should not be 0.

The frame size should not be less than or equal to 4 and should not
exceed 11454.

Discard the frame if any of these checks fail.

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/pci.c  | 16 +++++++++------
 drivers/net/wireless/realtek/rtw88/rx.c   | 24 +++++++++++++++++++----
 drivers/net/wireless/realtek/rtw88/rx.h   |  6 +++---
 drivers/net/wireless/realtek/rtw88/sdio.c |  8 +++++++-
 drivers/net/wireless/realtek/rtw88/usb.c  |  9 ++++++---
 5 files changed, 46 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index c2bf44e880cf..a30467228912 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1042,20 +1042,21 @@ static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev,
 static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
 			   u8 hw_queue, u32 limit)
 {
+	struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU];
 	const struct rtw_chip_info *chip = rtwdev->chip;
 	struct napi_struct *napi = &rtwpci->napi;
-	struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU];
-	struct rtw_rx_pkt_stat pkt_stat;
+	u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
+	u32 buf_desc_sz = chip->rx_buf_desc_sz;
 	struct ieee80211_rx_status rx_status;
+	struct rtw_rx_pkt_stat pkt_stat;
 	struct sk_buff *skb, *new;
 	u32 cur_rp = ring->r.rp;
 	u32 count, rx_done = 0;
 	u32 pkt_offset;
-	u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
-	u32 buf_desc_sz = chip->rx_buf_desc_sz;
+	dma_addr_t dma;
 	u32 new_len;
 	u8 *rx_desc;
-	dma_addr_t dma;
+	int ret;
 
 	count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci);
 	count = min(count, limit);
@@ -1067,7 +1068,10 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
 		dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE,
 					DMA_FROM_DEVICE);
 		rx_desc = skb->data;
-		rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status);
+		ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+					   &pkt_stat, &rx_status);
+		if (ret)
+			goto next_rp;
 
 		/* offset from rx_desc to payload */
 		pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index d9e11343d498..65f6db3d7fcb 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -3,6 +3,7 @@
  */
 
 #include "main.h"
+#include "mac.h"
 #include "rx.h"
 #include "ps.h"
 #include "debug.h"
@@ -261,9 +262,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
 	}
 }
 
-void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
-			  struct rtw_rx_pkt_stat *pkt_stat,
-			  struct ieee80211_rx_status *rx_status)
+int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
+			 struct rtw_rx_pkt_stat *pkt_stat,
+			 struct ieee80211_rx_status *rx_status)
 {
 	u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
 	struct rtw_rx_desc *rx_desc = rx_desc8;
@@ -303,12 +304,25 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
 		pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
 	}
 
+	if (unlikely(pkt_stat->drv_info_sz &&
+		     pkt_stat->drv_info_sz != PHY_STATUS_SIZE))
+		return -EINVAL;
+
+	if (unlikely(pkt_stat->phy_status && !pkt_stat->drv_info_sz))
+		return -EINVAL;
+
+	if (unlikely(pkt_stat->pkt_len > IEEE80211_MAX_MPDU_LEN_VHT_11454))
+		return -EINVAL;
+
 	/* drv_info_sz is in unit of 8-bytes */
 	pkt_stat->drv_info_sz *= 8;
 
 	/* c2h cmd pkt's rx/phy status is not interested */
 	if (pkt_stat->is_c2h)
-		return;
+		return 0;
+
+	if (unlikely(pkt_stat->pkt_len <= FCS_LEN))
+		return -EINVAL;
 
 	phy_status = rx_desc8 + desc_sz + pkt_stat->shift;
 	hdr = phy_status + pkt_stat->drv_info_sz;
@@ -318,5 +332,7 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
 		rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat);
 
 	rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status);
+
+	return 0;
 }
 EXPORT_SYMBOL(rtw_rx_query_rx_desc);
diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h
index 6b7dee245c0a..74359f641c76 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.h
+++ b/drivers/net/wireless/realtek/rtw88/rx.h
@@ -45,9 +45,9 @@ struct rtw_rx_desc {
 
 void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
 		  struct sk_buff *skb);
-void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
-			  struct rtw_rx_pkt_stat *pkt_stat,
-			  struct ieee80211_rx_status *rx_status);
+int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
+			 struct rtw_rx_pkt_stat *pkt_stat,
+			 struct ieee80211_rx_status *rx_status);
 void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
 				struct ieee80211_rx_status *rx_status,
 				struct rtw_rx_pkt_stat *pkt_stat);
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 1318e94f8524..5b40d74b16ee 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -995,7 +995,13 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
 
 	while (true) {
 		rx_desc = skb->data;
-		rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status);
+		ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+					   &pkt_stat, &rx_status);
+		if (ret) {
+			dev_kfree_skb_any(skb);
+			return;
+		}
+
 		pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
 			     pkt_stat.shift;
 
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 718940ebba31..6dd8ffedab9a 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -610,8 +610,8 @@ static void rtw_usb_rx_handler(struct work_struct *work)
 	u32 max_skb_len = pkt_desc_sz + PHY_STATUS_SIZE * 8 +
 			  IEEE80211_MAX_MPDU_LEN_VHT_11454;
 	u32 pkt_offset, next_pkt, skb_len;
+	int limit, ret;
 	u8 *rx_desc;
-	int limit;
 
 	for (limit = 0; limit < 200; limit++) {
 		rx_skb = skb_dequeue(&rtwusb->rx_queue);
@@ -627,8 +627,11 @@ static void rtw_usb_rx_handler(struct work_struct *work)
 		rx_desc = rx_skb->data;
 
 		do {
-			rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat,
-					     &rx_status);
+			ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+						   &pkt_stat, &rx_status);
+			if (ret)
+				break;
+
 			pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
 				     pkt_stat.shift;
 
-- 
2.53.0


^ permalink raw reply related

* [PATCH mt76] wifi: mt76: mt7915: configure noise floor reporting on reset
From: David Bauer @ 2026-05-16 14:49 UTC (permalink / raw)
  To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
	Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno
  Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek

When performing a full system recovery of the MCU on a dual-phy
platform, band 0 (usually 2.4GHz) stops reading correct noise floor
data.

This is due to noise floor reporting only being configured correctly
for the second device PHY.

Configure the respective registers correctly after restarting the MCU
firmware to fix reported noise-floor values.

Signed-off-by: David Bauer <mail@david-bauer.net>
---
 drivers/net/wireless/mediatek/mt76/mt7915/main.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
index e1d83052aa6dd..b42c26d0d09a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c
@@ -25,11 +25,13 @@ int mt7915_run(struct ieee80211_hw *hw)
 	struct mt7915_dev *dev = mt7915_hw_dev(hw);
 	struct mt7915_phy *phy = mt7915_hw_phy(hw);
 	bool running;
+	bool reset;
 	int ret;
 
 	running = mt7915_dev_running(dev);
+	reset = test_bit(MT76_RESET, &phy->mt76->state);
 
-	if (!running) {
+	if (!running || (reset && phy == &dev->phy)) {
 		ret = mt76_connac_mcu_set_pm(&dev->mt76,
 					     dev->phy.mt76->band_idx, 0);
 		if (ret)
-- 
2.53.0


^ permalink raw reply related

* [PATCH wireless-next 0/3] wifi: mac80211: Add 802.3 multicast encapsulation offload support
From: Tamizh Chelvam Raja @ 2026-05-16 16:28 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Tamizh Chelvam Raja

This patch series add support for multicast eth offload, particularly
for drivers supporting multicast offload functionality.

Adding sanity check to handle sta pointer in ieee80211_8023_xmit().
And add multicast to unicast support in 802.3 Tx path.
Skip 802.11 conversion for the multicast packets for the driver
advertising IEEE80211_OFFLOAD_ENCAP_MCAST vif offload flag.

Tamizh Chelvam Raja (3):
  wifi: mac80211: Add sta pointer sanity check in ieee80211_8023_xmit()
  wifi: mac80211: Add multicast to unicast support for 802.3 path
  wifi: mac80211: Add 802.3 multicast encapsulation offload support

 include/net/mac80211.h |  4 ++
 net/mac80211/tx.c      | 87 ++++++++++++++++++++++++++++++++++++------
 2 files changed, 80 insertions(+), 11 deletions(-)


base-commit: 6a4c4656b0d2d4056a1f0c35442db4e8a5cf8021
-- 
2.34.1


^ permalink raw reply

* [PATCH wireless-next 1/3] wifi: mac80211: Add sta pointer sanity check in ieee80211_8023_xmit()
From: Tamizh Chelvam Raja @ 2026-05-16 16:28 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Tamizh Chelvam Raja
In-Reply-To: <20260516162900.1981500-1-tamizh.raja@oss.qualcomm.com>

Currently ieee80211_8023_xmit() accesses the sta pointer without any
sanity check, assuming that only unicast packets for an authorized
station are processed. But the sta pointer could become NULL when
a framework to support 802.3 offload for the multicast packets is
added in the follow-up patches. Add the valid sta pointer sanity
check to avoid the invalid pointer access.

This aligns with some of the subordinate functions called by
ieee80211_8023_xmit() that already NULL-check 'sta' such as
ieee80211_select_queue() and ieee80211_aggr_check().

Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
---
 net/mac80211/tx.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 933c86ca21c3..5f84a3bf5cd9 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4650,7 +4650,7 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
 {
 	struct ieee80211_tx_info *info;
 	struct ieee80211_local *local = sdata->local;
-	struct tid_ampdu_tx *tid_tx;
+	struct tid_ampdu_tx *tid_tx = NULL;
 	struct sk_buff *seg, *next;
 	unsigned int skbs = 0, len = 0;
 	u16 queue;
@@ -4670,7 +4670,9 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
 	ieee80211_aggr_check(sdata, sta, skb);
 
 	tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
-	tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
+
+	if (sta)
+		tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[tid]);
 	if (tid_tx) {
 		if (!test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
 			/* fall back to non-offload slow path */
@@ -4718,8 +4720,11 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
 	}
 
 	dev_sw_netstats_tx_add(dev, skbs, len);
-	sta->deflink.tx_stats.packets[queue] += skbs;
-	sta->deflink.tx_stats.bytes[queue] += len;
+
+	if (sta) {
+		sta->deflink.tx_stats.packets[queue] += skbs;
+		sta->deflink.tx_stats.bytes[queue] += len;
+	}
 
 	ieee80211_tpt_led_trig_tx(local, len);
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next 3/3] wifi: mac80211: Add 802.3 multicast encapsulation offload support
From: Tamizh Chelvam Raja @ 2026-05-16 16:29 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Tamizh Chelvam Raja
In-Reply-To: <20260516162900.1981500-1-tamizh.raja@oss.qualcomm.com>

mac80211 currently converts 802.3 multicast packets to 802.11 format before
invoking the driver TX path, even when the interface is configured for
Ethernet encapsulation offload. This prevents drivers that support
multicast Ethernet encapsulation offload from receiving frames in
native 802.3 format.

Introduce the IEEE80211_OFFLOAD_ENCAP_MCAST flag to bypass the
802.11 encapsulation step and pass the multicast packet to the
driver in 802.3 format. Drivers that support multicast Ethernet
encapsulation offload can advertise this flag.

Disable multicast encapsulation offload in MLO case for drivers not
advertising MLO_MCAST_MULTI_LINK_TX support for AP mode and for
3-address AP_VLAN multicast packets.

Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
---
 include/net/mac80211.h |  4 ++++
 net/mac80211/tx.c      | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 4fb579805e0f..6d41227cb831 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -2013,12 +2013,16 @@ enum ieee80211_vif_flags {
  * @IEEE80211_OFFLOAD_DECAP_ENABLED: rx encapsulation offload is enabled
  *	The driver supports passing received 802.11 frames as 802.3 frames to
  *	mac80211.
+ * @IEEE80211_OFFLOAD_ENCAP_MCAST: tx multicast encapsulation offload is enabled
+ *	The driver supports sending multicast frames passed as 802.3 frames
+ *	by mac80211.
  */
 
 enum ieee80211_offload_flags {
 	IEEE80211_OFFLOAD_ENCAP_ENABLED		= BIT(0),
 	IEEE80211_OFFLOAD_ENCAP_4ADDR		= BIT(1),
 	IEEE80211_OFFLOAD_DECAP_ENABLED		= BIT(2),
+	IEEE80211_OFFLOAD_ENCAP_MCAST		= BIT(3),
 };
 
 #define IEEE80211_NAN_AVAIL_BLOB_MAX_LEN	54
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index ee3017f86f02..b2c094b06dc8 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4736,11 +4736,32 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
 	kfree_skb(skb);
 }
 
+static bool ieee80211_check_mcast_offload(struct ieee80211_sub_if_data *sdata,
+					  struct sk_buff *skb)
+{
+	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+
+	if ((ieee80211_vif_is_mld(&sdata->vif) &&
+	     (sdata->vif.type == NL80211_IFTYPE_AP &&
+	      !ieee80211_hw_check(&sdata->local->hw, MLO_MCAST_MULTI_LINK_TX))) ||
+	    (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
+	     !sdata->wdev.use_4addr))
+		return false;
+
+	if (!is_multicast_ether_addr(skb->data) ||
+	    !(sdata->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_MCAST) ||
+	    sdata->control_port_protocol == ehdr->h_proto)
+		return false;
+
+	return true;
+}
+
 static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
 					      struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+	struct ieee80211_link_data *link;
 	struct ieee80211_key *key;
 	struct sta_info *sta;
 
@@ -4751,6 +4772,25 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
 		goto out;
 	}
 
+	/*
+	 * For station mode sta is valid even though dest address
+	 * is multicast and that will be sent in eth mode only.
+	 * So, add error check for sta pointer before choosing
+	 * multicast offload path.
+	 */
+	if (IS_ERR_OR_NULL(sta) &&
+	    unlikely(ieee80211_check_mcast_offload(sdata, skb))) {
+		sta = NULL;
+		if (ieee80211_vif_get_num_mcast_if(sdata) <= 0) {
+			/* No associated STAs - no need to send multicast frames. */
+			kfree_skb(skb);
+			goto out;
+		}
+		link = &sdata->deflink;
+		key = rcu_dereference(link->default_multicast_key);
+		goto tx_offload;
+	}
+
 	if (unlikely(IS_ERR_OR_NULL(sta) || !sta->uploaded ||
 	    !test_sta_flag(sta, WLAN_STA_AUTHORIZED) ||
 	    sdata->control_port_protocol == ehdr->h_proto))
@@ -4760,6 +4800,7 @@ static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
 	if (!key)
 		key = rcu_dereference(sdata->default_unicast_key);
 
+tx_offload:
 	if (key && (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) ||
 		    key->conf.cipher == WLAN_CIPHER_SUITE_TKIP))
 		goto skip_offload;
-- 
2.34.1


^ permalink raw reply related

* [PATCH wireless-next 2/3] wifi: mac80211: Add multicast to unicast support for 802.3 path
From: Tamizh Chelvam Raja @ 2026-05-16 16:28 UTC (permalink / raw)
  To: johannes; +Cc: linux-wireless, Tamizh Chelvam Raja
In-Reply-To: <20260516162900.1981500-1-tamizh.raja@oss.qualcomm.com>

mac80211 already supports multicast-to-unicast conversion for
native 802.11 TX paths, but this handling is missing for the
802.3 transmit path. Due to that the packet never converted to
unicast and directly pass it to 802.11 Tx path by checking the
destination address as multicast.

Extend ieee80211_subif_start_xmit_8023() to honor the
multicast_to_unicast setting by cloning and converting multicast
Ethernet frames into per-station unicast transmissions, following
the same behavior of the native 802.11 TX path and allow it
to take 802.3 path.

Signed-off-by: Tamizh Chelvam Raja <tamizh.raja@oss.qualcomm.com>
---
 net/mac80211/tx.c | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 5f84a3bf5cd9..ee3017f86f02 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -4736,19 +4736,14 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
 	kfree_skb(skb);
 }
 
-netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
-					    struct net_device *dev)
+static void __ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
+					      struct net_device *dev)
 {
 	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
 	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
 	struct ieee80211_key *key;
 	struct sta_info *sta;
 
-	if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) {
-		kfree_skb(skb);
-		return NETDEV_TX_OK;
-	}
-
 	rcu_read_lock();
 
 	if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) {
@@ -4777,6 +4772,30 @@ netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
 	ieee80211_subif_start_xmit(skb, dev);
 out:
 	rcu_read_unlock();
+}
+
+netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb,
+					    struct net_device *dev)
+{
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	struct ethhdr *ehdr = (struct ethhdr *)skb->data;
+
+	if (unlikely(!ieee80211_sdata_running(sdata) || skb->len < ETH_HLEN)) {
+		kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+
+	if (unlikely(is_multicast_ether_addr(ehdr->h_dest) &&
+		     ieee80211_multicast_to_unicast(skb, dev))) {
+		struct sk_buff_head queue;
+
+		__skb_queue_head_init(&queue);
+		ieee80211_convert_to_unicast(skb, dev, &queue);
+		while ((skb = __skb_dequeue(&queue)))
+			__ieee80211_subif_start_xmit_8023(skb, dev);
+	} else {
+		__ieee80211_subif_start_xmit_8023(skb, dev);
+	}
 
 	return NETDEV_TX_OK;
 }
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 00/15] wifi: iwlwifi: updates - 2026-05-16
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless

Hi,

Features fixes and cleanups from our internal tree.

Miri
--

Avinash Bhatt (2):
  wifi: iwlwifi: mld: skip MLO scan trigger when AP has no QBSS Load IE
  wifi: iwlwifi: mld: keep healthy link on EMLSR missed beacon exit

Emmanuel Grumbach (2):
  wifi: iwlwifi: move pcie content to pcie internal transport
  wifi: iwlwifi: move iwl_trans_activate_nic to iwl-trans.c

Johannes Berg (7):
  wifi: iwlwifi: clean up location format/BW encoding
  wifi: iwlwifi: fw: move struct iwl_fw_ini_dump_entry to dbg.c
  wifi: iwlwifi: fw: separate ini dump allocation
  wifi: iwlwifi: fw: dbg: always use non-tracing PRPH access
  wifi: iwlwifi: fw: separate out old-style dump code
  wifi: iwlwifi: dbg: remove unused 'range_len' arg from dump
  wifi: iwlwifi: transport: add memory read under NIC access

Miri Korenblit (3):
  wifi: iwlwifi: mld: don't flush async_handlers_wk when canceling
    notifications
  wifi: iwlwifi: mld: purge async notifications upon nic error
  wifi: iwlwifi: bump maximum core version for BZ/SC/DR to 105

Shahar Tzarfati (1):
  wifi: iwlwifi: Add names for Killer BE1735x and BE1730x

 drivers/net/wireless/intel/iwlwifi/Makefile   |    2 +-
 drivers/net/wireless/intel/iwlwifi/cfg/bz.c   |    2 +-
 drivers/net/wireless/intel/iwlwifi/cfg/dr.c   |    2 +-
 .../net/wireless/intel/iwlwifi/cfg/rf-fm.c    |    2 +
 .../net/wireless/intel/iwlwifi/cfg/rf-wh.c    |    2 +
 drivers/net/wireless/intel/iwlwifi/cfg/sc.c   |    2 +-
 .../wireless/intel/iwlwifi/fw/api/location.h  |  100 +-
 .../net/wireless/intel/iwlwifi/fw/dbg-old.c   | 1022 +++++++++++++
 drivers/net/wireless/intel/iwlwifi/fw/dbg.c   | 1292 ++---------------
 drivers/net/wireless/intel/iwlwifi/fw/dbg.h   |    7 +-
 .../wireless/intel/iwlwifi/fw/error-dump.h    |   14 +-
 .../net/wireless/intel/iwlwifi/iwl-config.h   |    2 +
 drivers/net/wireless/intel/iwlwifi/iwl-io.c   |    7 -
 drivers/net/wireless/intel/iwlwifi/iwl-io.h   |    2 -
 .../net/wireless/intel/iwlwifi/iwl-trans.c    |   15 +-
 .../net/wireless/intel/iwlwifi/iwl-trans.h    |  123 +-
 .../intel/iwlwifi/mld/ftm-initiator.c         |   30 +-
 drivers/net/wireless/intel/iwlwifi/mld/link.c |   22 +-
 drivers/net/wireless/intel/iwlwifi/mld/link.h |    3 +
 drivers/net/wireless/intel/iwlwifi/mld/mld.c  |    9 +
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c  |    7 +
 .../net/wireless/intel/iwlwifi/mld/notif.c    |    4 -
 .../intel/iwlwifi/mvm/ftm-initiator.c         |   30 +-
 .../intel/iwlwifi/mvm/ftm-responder.c         |   32 +-
 drivers/net/wireless/intel/iwlwifi/pcie/drv.c |    4 +
 .../intel/iwlwifi/pcie/gen1_2/internal.h      |  107 +-
 .../intel/iwlwifi/pcie/gen1_2/trans.c         |   54 +
 27 files changed, 1536 insertions(+), 1362 deletions(-)
 create mode 100644 drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c

-- 
2.34.1


^ permalink raw reply

* [PATCH iwlwifi-fixes 01/15] wifi: iwlwifi: mld: don't flush async_handlers_wk when canceling notifications
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

iwl_mld_cancel_async_notifications does 2 things: it purges the list of
notifications and flushes the async_handlers_wk.

We call iwl_mld_cancel_async_notifications after we stopped or suspended
the fw. So in that stage we don't expect any new notification coming,
and if erroneously there are new notifications coming, the work will be
queued again anyway, so the flush is pretty much pointless.

iwl_mld_cancel_async_notifications will need to be called in a context
w/o the wiphy lock held, and the only reason why this function requires
the lock being held is for flushing the work.

Remove the flush.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/notif.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
index d7383022a0ed..7574689e4088 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/notif.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c
@@ -694,10 +694,6 @@ void iwl_mld_cancel_async_notifications(struct iwl_mld *mld)
 {
 	struct iwl_async_handler_entry *entry, *tmp;
 
-	lockdep_assert_wiphy(mld->wiphy);
-
-	wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk);
-
 	spin_lock_bh(&mld->async_handlers_lock);
 	list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {
 		iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb);
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 02/15] wifi: iwlwifi: mld: purge async notifications upon nic error
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

This fixes a kernel panic in reconfig failure:

1. we have a BSS connection
2. we have a NAN connection
3. FW error occurs
4. reconfig restores the BSS connection
5. however, restoring the NAN connection fails due to a FW error.
6. erroneously, ieee80211_handle_reconfig_failure is called and marks all
   interfaces as not-in-driver (will be fixed in a different patch).
7. mac80211 frees the links of the BSS connection but doesn't tell the
   driver about that, as it thinks that this vif is not in the driver.
8. in ieee80211_stop_device, *ALL* wiphy works are getting flushed
   (erroneously?)
9. Therefore, async_handlers_wk is being executed, processing the
   statistics notification that was received after we restored the BSS
   connection.
10. the notification handler dereferences fw_id_to_bss_conf[id], which is
    now a dangling pointer, as mac80211 already freed this link in (7).
11. On the first access to one of the links fields, we panic.

While this can and should be fixed by removing the call to
ieee80211_handle_reconfig_failure in (6), it is also not a good idea to
carry and maybe handle notifications from a dead FW.

We do purge the notifications when we stop the FW, but in reconfig
failure we stop the FW too late, after the notifications are processed.
In addition, async_handlers_wk can always be scheduled before the
reconfig work.

Purge the notifications immediately when transport notifies about a nic
error.

Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/mld.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
index 0ef7c24831d8..78c78cf891cd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mld.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c
@@ -676,6 +676,15 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode,
 	if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT &&
 	    mld->fw_status.running)
 		mld->fw_status.in_hw_restart = true;
+
+	/* FW is dead. We don't want to process its notifications.
+	 * Right, we cancel them also in iwl_mld_stop_fw, but
+	 * iwl_mld_async_handlers_wk might be executed before
+	 * ieee80211_restart_work.
+	 * In addition, in case of an error during recovery,
+	 * iwl_mld_stop_fw might be too late.
+	 */
+	iwl_mld_cancel_async_notifications(mld);
 }
 
 static void iwl_mld_dump_error(struct iwl_op_mode *op_mode,
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 03/15] wifi: iwlwifi: bump maximum core version for BZ/SC/DR to 105
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

Start supporting Core 105 FW on these devices.

Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/cfg/bz.c | 2 +-
 drivers/net/wireless/intel/iwlwifi/cfg/dr.c | 2 +-
 drivers/net/wireless/intel/iwlwifi/cfg/sc.c | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
index 51d7c599e998..ecb4f81a99f5 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c
@@ -10,7 +10,7 @@
 #include "fw/api/txq.h"
 
 /* Highest firmware core release supported */
-#define IWL_BZ_UCODE_CORE_MAX	104
+#define IWL_BZ_UCODE_CORE_MAX	105
 
 /* Lowest firmware core release supported */
 #define IWL_BZ_UCODE_CORE_MIN	101
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
index 76fbc120a092..e8968b3051d3 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c
@@ -9,7 +9,7 @@
 #include "fw/api/txq.h"
 
 /* Highest firmware core release supported */
-#define IWL_DR_UCODE_CORE_MAX	104
+#define IWL_DR_UCODE_CORE_MAX	105
 
 /* Lowest firmware core release supported */
 #define IWL_DR_UCODE_CORE_MIN	101
diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
index db7858b2cb74..6aaa49aeec99 100644
--- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
+++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c
@@ -10,7 +10,7 @@
 #include "fw/api/txq.h"
 
 /* Highest firmware core release supported */
-#define IWL_SC_UCODE_CORE_MAX	104
+#define IWL_SC_UCODE_CORE_MAX	105
 
 /* Lowest firmware core release supported */
 #define IWL_SC_UCODE_CORE_MIN	101
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 04/15] wifi: iwlwifi: mld: skip MLO scan trigger when AP has no QBSS Load IE
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

From: Avinash Bhatt <avinash.bhatt@intel.com>

When FW PHY statistics report high channel load (MCLM), the driver
triggers an internal MLO scan to find a better link. However, link grade
computation uses the QBSS Load IE from AP beacons, not MCLM data. If
the AP does not broadcast a QBSS Load IE, the scan produces no benefit
as the grade falls back to static band defaults regardless, and the same
bad link will be selected anyway as the active link.

Skip the MLO scan trigger when the AP does not advertise a QBSS Load IE.

Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/link.c | 5 ++---
 drivers/net/wireless/intel/iwlwifi/mld/link.h | 3 +++
 drivers/net/wireless/intel/iwlwifi/mld/mlo.c  | 7 +++++++
 3 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index e5e9b7089c14..549f1c1ea22f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -859,9 +859,8 @@ iwl_mld_apply_puncturing_penalty(const struct ieee80211_bss_conf *link_conf,
 	*grade = *grade * (n_subchannels - n_punctured) / n_subchannels;
 }
 
-static int
-iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
-				   struct ieee80211_bss_conf *link_conf)
+int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
+				       struct ieee80211_bss_conf *link_conf)
 {
 	const struct cfg80211_bss_ies *ies;
 	const struct element *bss_load_elem = NULL;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h
index 0b3974d86531..f1997e280058 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h
@@ -145,6 +145,9 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
 unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
 				   struct ieee80211_bss_conf *link_conf);
 
+int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
+				       struct ieee80211_bss_conf *link_conf);
+
 int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
 				    struct ieee80211_bss_conf *link_conf,
 				    bool expect_active_link);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
index 2a3b2c883fc4..a2f8a6957535 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c
@@ -1100,6 +1100,13 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
 		if (rcu_access_pointer(link_conf->chanctx_conf) != chanctx)
 			continue;
 
+		/* No QBSS IE - links will be selected based on default channel
+		 * load values, so the same link will be selected again.
+		 * No point in scan.
+		 */
+		if (iwl_mld_get_chan_load_from_element(mld, link_conf) < 0)
+			continue;
+
 		if (iwl_mld_chan_load_requires_scan(mld,
 						    link_conf,
 						    new_chan_load)) {
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 05/15] wifi: iwlwifi: mld: keep healthy link on EMLSR missed beacon exit
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Avinash Bhatt
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

From: Avinash Bhatt <avinash.bhatt@intel.com>

When EMLSR exits due to beacon loss on the current link, the driver
should keep the link that is still receiving beacons. The previous
code always called get_primary_link(), keeping the primary link
regardless of which link is actually losing beacons. If the primary
link is the one losing beacons, the driver exits EMLSR onto the
degraded link and the connection is lost eventually.

When both links lose beacons, keep the primary link. When only the
current link loses beacons -- whether due to signal loss or a BSS
parameter change -- keep the other link.

Signed-off-by: Avinash Bhatt <avinash.bhatt@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/mld/link.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c
index 549f1c1ea22f..1bdfba6279e2 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/link.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c
@@ -663,15 +663,20 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
 	 * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED
 	 * on current link and the link's bss_param_ch_count has changed on
 	 * the other link's beacon.
+	 *
+	 * When both links lose beacons, keep the primary (symmetric failure).
+	 * When only the current link is sick, keep the other link.
 	 */
-	if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
-	     scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) ||
-	    missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
-	    (bss_param_ch_cnt_link_id != link_id &&
-	     missed_bcon >=
-	     IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
+	if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS &&
+	    scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) {
 		iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
 				   iwl_mld_get_primary_link(vif));
+	} else if (missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH ||
+		   (bss_param_ch_cnt_link_id != link_id &&
+		    missed_bcon >=
+		    IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) {
+		iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
+				   iwl_mld_get_other_link(vif, link_id));
 	}
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 06/15] wifi: iwlwifi: move pcie content to pcie internal transport
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

The iwl_txq, iwl_pcie_first_tb_buf and iwl_pcie_txq_entry don't need to
be exported to the op_mode in iwl-trans.h. Declare those in the
transport's internal header file to avoid pollution.
iwl_trans_pcie_send_hcmd can also be moved to the internal header file.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 .../net/wireless/intel/iwlwifi/iwl-trans.h    | 105 +-----------------
 .../intel/iwlwifi/pcie/gen1_2/internal.h      | 105 +++++++++++++++++-
 2 files changed, 105 insertions(+), 105 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index 61e4f4776dcb..c661807c6e6b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2005-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2005-2014, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
@@ -700,106 +700,6 @@ struct iwl_cmd_meta {
 	u32 tbs;
 };
 
-/*
- * The FH will write back to the first TB only, so we need to copy some data
- * into the buffer regardless of whether it should be mapped or not.
- * This indicates how big the first TB must be to include the scratch buffer
- * and the assigned PN.
- * Since PN location is 8 bytes at offset 12, it's 20 now.
- * If we make it bigger then allocations will be bigger and copy slower, so
- * that's probably not useful.
- */
-#define IWL_FIRST_TB_SIZE	20
-#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64)
-
-struct iwl_pcie_txq_entry {
-	void *cmd;
-	struct sk_buff *skb;
-	/* buffer to free after command completes */
-	const void *free_buf;
-	struct iwl_cmd_meta meta;
-};
-
-struct iwl_pcie_first_tb_buf {
-	u8 buf[IWL_FIRST_TB_SIZE_ALIGN];
-};
-
-/**
- * struct iwl_txq - Tx Queue for DMA
- * @tfds: transmit frame descriptors (DMA memory)
- * @first_tb_bufs: start of command headers, including scratch buffers, for
- *	the writeback -- this is DMA memory and an array holding one buffer
- *	for each command on the queue
- * @first_tb_dma: DMA address for the first_tb_bufs start
- * @entries: transmit entries (driver state)
- * @lock: queue lock
- * @reclaim_lock: reclaim lock
- * @stuck_timer: timer that fires if queue gets stuck
- * @trans: pointer back to transport (for timer)
- * @need_update: indicates need to update read/write index
- * @ampdu: true if this queue is an ampdu queue for an specific RA/TID
- * @wd_timeout: queue watchdog timeout (jiffies) - per queue
- * @frozen: tx stuck queue timer is frozen
- * @frozen_expiry_remainder: remember how long until the timer fires
- * @block: queue is blocked
- * @bc_tbl: byte count table of the queue (relevant only for gen2 transport)
- * @write_ptr: 1-st empty entry (index) host_w
- * @read_ptr: last used entry (index) host_r
- * @dma_addr:  physical addr for BD's
- * @n_window: safe queue window
- * @id: queue id
- * @low_mark: low watermark, resume queue if free space more than this
- * @high_mark: high watermark, stop queue if free space less than this
- * @overflow_q: overflow queue for handling frames that didn't fit on HW queue
- * @overflow_tx: need to transmit from overflow
- *
- * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
- * descriptors) and required locking structures.
- *
- * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
- * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
- * there might be HW changes in the future). For the normal TX
- * queues, n_window, which is the size of the software queue data
- * is also 256; however, for the command queue, n_window is only
- * 32 since we don't need so many commands pending. Since the HW
- * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256.
- * This means that we end up with the following:
- *  HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
- *  SW entries:           | 0      | ... | 31          |
- * where N is a number between 0 and 7. This means that the SW
- * data is a window overlayed over the HW queue.
- */
-struct iwl_txq {
-	void *tfds;
-	struct iwl_pcie_first_tb_buf *first_tb_bufs;
-	dma_addr_t first_tb_dma;
-	struct iwl_pcie_txq_entry *entries;
-	/* lock for syncing changes on the queue */
-	spinlock_t lock;
-	/* lock to prevent concurrent reclaim */
-	spinlock_t reclaim_lock;
-	unsigned long frozen_expiry_remainder;
-	struct timer_list stuck_timer;
-	struct iwl_trans *trans;
-	bool need_update;
-	bool frozen;
-	bool ampdu;
-	int block;
-	unsigned long wd_timeout;
-	struct sk_buff_head overflow_q;
-	struct iwl_dma_ptr bc_tbl;
-
-	int write_ptr;
-	int read_ptr;
-	dma_addr_t dma_addr;
-	int n_window;
-	u32 id;
-	int low_mark;
-	int high_mark;
-
-	bool overflow_tx;
-};
-
 /**
  * struct iwl_trans_info - transport info for outside use
  * @name: the device name
@@ -1236,9 +1136,6 @@ enum iwl_reset_mode {
 void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);
 void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans);
 
-int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
-			     struct iwl_host_cmd *cmd);
-
 /* Internal helper */
 static inline void iwl_trans_set_info(struct iwl_trans *trans,
 				      struct iwl_trans_info *info)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h
index 7b7b35e442f9..24f8714eae9f 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2003-2015, 2018-2025 Intel Corporation
+ * Copyright (C) 2003-2015, 2018-2026 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
@@ -287,6 +287,106 @@ enum iwl_pcie_imr_status {
 	IMR_D2S_ERROR,
 };
 
+/*
+ * The FH will write back to the first TB only, so we need to copy some data
+ * into the buffer regardless of whether it should be mapped or not.
+ * This indicates how big the first TB must be to include the scratch buffer
+ * and the assigned PN.
+ * Since PN location is 8 bytes at offset 12, it's 20 now.
+ * If we make it bigger then allocations will be bigger and copy slower, so
+ * that's probably not useful.
+ */
+#define IWL_FIRST_TB_SIZE	20
+#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64)
+
+struct iwl_pcie_txq_entry {
+	void *cmd;
+	struct sk_buff *skb;
+	/* buffer to free after command completes */
+	const void *free_buf;
+	struct iwl_cmd_meta meta;
+};
+
+struct iwl_pcie_first_tb_buf {
+	u8 buf[IWL_FIRST_TB_SIZE_ALIGN];
+};
+
+/**
+ * struct iwl_txq - Tx Queue for DMA
+ * @tfds: transmit frame descriptors (DMA memory)
+ * @first_tb_bufs: start of command headers, including scratch buffers, for
+ *	the writeback -- this is DMA memory and an array holding one buffer
+ *	for each command on the queue
+ * @first_tb_dma: DMA address for the first_tb_bufs start
+ * @entries: transmit entries (driver state)
+ * @lock: queue lock
+ * @reclaim_lock: reclaim lock
+ * @stuck_timer: timer that fires if queue gets stuck
+ * @trans: pointer back to transport (for timer)
+ * @need_update: indicates need to update read/write index
+ * @ampdu: true if this queue is an ampdu queue for a specific RA/TID
+ * @wd_timeout: queue watchdog timeout (jiffies) - per queue
+ * @frozen: tx stuck queue timer is frozen
+ * @frozen_expiry_remainder: remember how long until the timer fires
+ * @block: queue is blocked
+ * @bc_tbl: byte count table of the queue (relevant only for gen2 transport)
+ * @write_ptr: 1-st empty entry (index) host_w
+ * @read_ptr: last used entry (index) host_r
+ * @dma_addr: physical addr for BDs
+ * @n_window: safe queue window
+ * @id: queue id
+ * @low_mark: low watermark, resume queue if free space more than this
+ * @high_mark: high watermark, stop queue if free space less than this
+ * @overflow_q: overflow queue for handling frames that didn't fit on HW queue
+ * @overflow_tx: need to transmit from overflow
+ *
+ * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
+ * descriptors) and required locking structures.
+ *
+ * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
+ * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
+ * there might be HW changes in the future). For the normal TX
+ * queues, n_window, which is the size of the software queue data
+ * is also 256; however, for the command queue, n_window is only
+ * 32 since we don't need so many commands pending. Since the HW
+ * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256.
+ * This means that we end up with the following:
+ *  HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
+ *  SW entries:           | 0      | ... | 31          |
+ * where N is a number between 0 and 7. This means that the SW
+ * data is a window overlaid over the HW queue.
+ */
+struct iwl_txq {
+	void *tfds;
+	struct iwl_pcie_first_tb_buf *first_tb_bufs;
+	dma_addr_t first_tb_dma;
+	struct iwl_pcie_txq_entry *entries;
+	/* lock for syncing changes on the queue */
+	spinlock_t lock;
+	/* lock to prevent concurrent reclaim */
+	spinlock_t reclaim_lock;
+	unsigned long frozen_expiry_remainder;
+	struct timer_list stuck_timer;
+	struct iwl_trans *trans;
+	bool need_update;
+	bool frozen;
+	bool ampdu;
+	int block;
+	unsigned long wd_timeout;
+	struct sk_buff_head overflow_q;
+	struct iwl_dma_ptr bc_tbl;
+
+	int write_ptr;
+	int read_ptr;
+	dma_addr_t dma_addr;
+	int n_window;
+	u32 id;
+	int low_mark;
+	int high_mark;
+
+	bool overflow_tx;
+};
+
 /**
  * struct iwl_pcie_txqs - TX queues data
  *
@@ -1153,6 +1253,9 @@ int iwl_trans_pcie_copy_imr(struct iwl_trans *trans,
 int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue,
 				struct iwl_trans_rxq_dma_data *data);
 
+int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
+			     struct iwl_host_cmd *cmd);
+
 static inline bool iwl_pcie_gen1_is_pm_supported(struct iwl_trans *trans)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-- 
2.34.1


^ permalink raw reply related

* [PATCH iwlwifi-fixes 07/15] wifi: iwlwifi: move iwl_trans_activate_nic to iwl-trans.c
From: Miri Korenblit @ 2026-05-16 20:11 UTC (permalink / raw)
  To: linux-wireless; +Cc: Emmanuel Grumbach
In-Reply-To: <20260516201137.2395464-1-miriam.rachel.korenblit@intel.com>

From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>

This function reaches the transport eventually so move it to
iwl-trans.c. Now we can remove the include to the pcie transport's
internal header from iwl-io.c

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/iwl-io.c    | 7 -------
 drivers/net/wireless/intel/iwlwifi/iwl-io.h    | 2 --
 drivers/net/wireless/intel/iwlwifi/iwl-trans.c | 9 ++++++++-
 drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 2 ++
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
index c4ccfffdf6af..bb746112ddad 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c
@@ -12,7 +12,6 @@
 #include "iwl-debug.h"
 #include "iwl-prph.h"
 #include "iwl-fh.h"
-#include "pcie/gen1_2/internal.h"
 
 void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
 {
@@ -412,12 +411,6 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf)
 	return 0;
 }
 
-int iwl_trans_activate_nic(struct iwl_trans *trans)
-{
-	return iwl_pcie_gen1_2_activate_nic(trans);
-}
-IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
-
 void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
 				  u32 sw_err_bit)
 {
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
index d920a32fc173..6dce2e5267a6 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h
@@ -59,8 +59,6 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
 void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
 void iwl_force_nmi(struct iwl_trans *trans);
 
-int iwl_trans_activate_nic(struct iwl_trans *trans);
-
 /* Error handling */
 int iwl_dump_fh(struct iwl_trans *trans, char **buf);
 
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
index db1db0a42928..5b44e15fe64d 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c
@@ -2,7 +2,7 @@
 /*
  * Copyright (C) 2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2019-2021, 2023-2025 Intel Corporation
+ * Copyright (C) 2019-2021, 2023-2026 Intel Corporation
  */
 #include <linux/kernel.h>
 #include <linux/bsearch.h>
@@ -820,3 +820,10 @@ bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans)
 	return iwl_pcie_gen1_2_is_ltr_enabled(trans);
 }
 IWL_EXPORT_SYMBOL(iwl_trans_is_ltr_enabled);
+
+int iwl_trans_activate_nic(struct iwl_trans *trans)
+{
+	return iwl_pcie_gen1_2_activate_nic(trans);
+}
+IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
+
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index c661807c6e6b..1ed6bcb7882c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -1053,6 +1053,8 @@ static inline bool iwl_trans_dbg_ini_valid(struct iwl_trans *trans)
 
 void iwl_trans_interrupts(struct iwl_trans *trans, bool enable);
 
+int iwl_trans_activate_nic(struct iwl_trans *trans);
+
 static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans)
 {
 	clear_bit(STATUS_IN_SW_RESET, &trans->status);
-- 
2.34.1


^ permalink raw reply related


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