Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
The 5th batch of qwz commits
To:
Stefan Sperling <stsp@stsp.name>
Cc:
OpenBSD tech <tech@openbsd.org>
Date:
Wed, 30 Sep 2026 20:59:37 +0200

Download raw body.

Thread
  • Kirill A. Korinsky:

    The 5th batch of qwz commits

Stefan,

here the 5th batch of qwz commits, some backports were quite large and I
follow qwx code move.

Ok?

From 3a380b473e95f34156bef4f952b1d22042ce4812 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:55:22 +0200
Subject: [PATCH 1/8] sys/qwz: report hardware RX aggregation

Based on sys/dev/ic/qwx.c,v 1.85 and sys/dev/ic/qwx.c,v 1.90

Report hardware deaggregation and reordering after successful RX
reconstruction; allow repaeted sequence numbers for later A-MSDU
subframes and clear the AMSDU QoS bit.
---
 sys/dev/ic/qwz.c | 34 +++++++++++++++++++++++++++++-----
 1 file changed, 29 insertions(+), 5 deletions(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index 94d3c5d8e9f..2148259098e 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -15433,19 +15433,43 @@ qwz_dp_rx_h_undecap(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
     struct hal_rx_desc *rx_desc, enum hal_encrypt_type enctype,
     int decrypted)
 {
-	uint8_t decap;
+	struct ieee80211_frame *wh;
+	uint8_t decap, *qos;
+	int ret;
 
 	decap = qwz_dp_rx_h_msdu_start_decap_type(sc, rx_desc);
 
 	switch (decap) {
 	case DP_RX_DECAP_TYPE_NATIVE_WIFI:
-		return qwz_dp_rx_h_undecap_nwifi(sc, msdu, NULL, enctype);
-	case DP_RX_DECAP_TYPE_RAW:
-		qwz_dp_rx_h_undecap_raw(sc, msdu, enctype, decrypted);
+		ret = qwz_dp_rx_h_undecap_nwifi(sc, msdu, NULL, enctype);
 		break;
 	case DP_RX_DECAP_TYPE_ETHERNET2_DIX:
-		return qwz_dp_rx_h_undecap_eth(sc, msdu, rx_desc);
+		ret = qwz_dp_rx_h_undecap_eth(sc, msdu, rx_desc);
+		break;
+	case DP_RX_DECAP_TYPE_RAW:
+		qwz_dp_rx_h_undecap_raw(sc, msdu, enctype, decrypted);
+		return 0;
+	default:
+		return 0;
 	}
+	if (ret)
+		return ret;
+
+	wh = mtod(msdu->m, struct ieee80211_frame *);
+	if (msdu->m->m_len < ieee80211_get_hdrlen(wh))
+		return EINVAL;
+	if (ieee80211_has_qos(wh)) {
+		if (ieee80211_has_addr4(wh))
+			qos = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos;
+		else
+			qos = ((struct ieee80211_qosframe *)wh)->i_qos;
+		qos[0] &= ~IEEE80211_QOS_AMSDU;
+	}
+
+	/* Hardware has deaggregated and reordered these frames. */
+	if (!msdu->is_first_msdu)
+		msdu->rxi.rxi_flags |= IEEE80211_RXI_SAME_SEQ;
+	msdu->rxi.rxi_flags |= IEEE80211_RXI_AMPDU_DONE;
 	return 0;
 }
 
-- 
2.55.0


From 38680aa80d9c50ad66fe6962d641c3991e7ed45d Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:56:36 +0200
Subject: [PATCH 2/8] sys/qwz: handle WBM RX errors

Based on sys/dev/ic/qwx.c,v 1.35 and sys/dev/ic/qwxvar.h,v 1.18 ,
sys/dev/ic/qwx.c,v 1.89 , sys/dev/ic/qwx.c,v 1.121 and sys/dev/ic/qwxvar.h,v 1.36

Process WBM RX releases using WCN7850 descriptor and cookie formats.

Deliver valid null queue frames through existing RX processing, clear
mbuf pointers after delivery, and then replenish descriptors
---
 sys/dev/ic/qwz.c    | 252 ++++++++++++++++++++++++--------------------
 sys/dev/ic/qwzreg.h |  23 ++++
 sys/dev/ic/qwzvar.h |   3 +
 3 files changed, 166 insertions(+), 112 deletions(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index 2148259098e..2b916eab0a8 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -154,6 +154,8 @@ int qwz_dp_tx_send_reo_cmd(struct qwz_softc *, struct dp_rx_tid *,
     enum hal_reo_cmd_type , struct ath12k_hal_reo_cmd *,
     void (*func)(struct qwz_dp *, void *, enum hal_reo_cmd_status));
 void qwz_dp_rx_deliver_msdu(struct qwz_softc *, struct qwz_rx_msdu *);
+int qwz_dp_rx_h_null_q_desc(struct qwz_softc *, struct qwz_rx_msdu *,
+    struct qwz_rx_msdu_list *);
 void qwz_dp_service_mon_ring(void *);
 void qwz_peer_frags_flush(struct qwz_softc *, struct ath12k_peer *);
 int qwz_wmi_vdev_install_key(struct qwz_softc *,
@@ -8503,6 +8505,32 @@ qwz_dp_get_rx_desc(struct qwz_softc *sc, uint32_t cookie)
 	return *desc_addr_ptr;
 }
 
+struct qwz_rx_msdu *
+qwz_dp_rx_reap_desc(struct qwz_softc *sc, uint32_t cookie, void *list)
+{
+	TAILQ_HEAD(, ath12k_rx_desc_info) *used_list = list;
+	struct ath12k_rx_desc_info *desc_info;
+	struct qwz_rx_msdu *msdu;
+
+	desc_info = qwz_dp_get_rx_desc(sc, cookie);
+	if (desc_info == NULL || desc_info->magic != ATH12K_DP_RX_DESC_MAGIC ||
+	    !desc_info->in_use || desc_info->m == NULL)
+		return NULL;
+
+	bus_dmamap_sync(sc->sc_dmat, desc_info->map, 0,
+	    desc_info->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+	bus_dmamap_unload(sc->sc_dmat, desc_info->map);
+
+	msdu = &desc_info->rx_msdu;
+	memset(msdu, 0, sizeof(*msdu));
+	msdu->m = desc_info->m;
+	msdu->rx_desc = mtod(msdu->m, struct hal_rx_desc *);
+	desc_info->m = NULL;
+	desc_info->in_use = 0;
+	TAILQ_INSERT_TAIL(used_list, desc_info, entry);
+	return msdu;
+}
+
 int
 qwz_dp_cc_desc_init(struct qwz_softc *sc)
 {
@@ -14875,65 +14903,67 @@ qwz_dp_process_rx_err(struct qwz_softc *sc)
 }
 
 int
-qwz_hal_wbm_desc_parse_err(void *desc, struct hal_rx_wbm_rel_info *rel_info)
+qwz_hal_wbm_desc_parse_err(struct qwz_softc *sc, void *desc,
+    struct hal_rx_wbm_rel_info *rel_info)
 {
-	struct hal_wbm_release_ring *wbm_desc = desc;
+	struct hal_wbm_release_ring_rx *wbm_desc = desc;
+	struct hal_wbm_release_ring_cc_rx *cc_desc = desc;
 	enum hal_wbm_rel_desc_type type;
 	enum hal_wbm_rel_src_module rel_src;
-	enum hal_rx_buf_return_buf_manager ret_buf_mgr;
-
-	type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, wbm_desc->info0);
+	enum hal_rx_buf_return_buf_manager rbm;
+	uint32_t info0 = le32toh(wbm_desc->info0);
+	uint32_t msdu_info = le32toh(wbm_desc->rx_msdu_info.info0);
 
-	/* We expect only WBM_REL buffer type */
+	type = FIELD_GET(HAL_WBM_RELEASE_INFO0_DESC_TYPE, info0);
 	if (type != HAL_WBM_REL_DESC_TYPE_REL_MSDU)
-		return -EINVAL;
+		return EINVAL;
 
-	rel_src = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE,
-	    wbm_desc->info0);
+	rel_src = FIELD_GET(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE, info0);
 	if (rel_src != HAL_WBM_REL_SRC_MODULE_RXDMA &&
 	    rel_src != HAL_WBM_REL_SRC_MODULE_REO)
 		return EINVAL;
 
-	ret_buf_mgr = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR,
-	    wbm_desc->buf_addr_info.info1);
-	if (ret_buf_mgr != HAL_RX_BUF_RBM_SW3_BM) {
-#if 0
-		ab->soc_stats.invalid_rbm++;
-#endif
-		return EINVAL;
+	/* Cookie conversion changes both the cookie and RBM locations. */
+	if (info0 & HAL_WBM_RELEASE_RX_INFO0_CC_STATUS) {
+		rbm = FIELD_GET(HAL_WBM_RELEASE_RX_CC_INFO0_RBM, info0);
+		rel_info->cookie = FIELD_GET(HAL_WBM_RELEASE_RX_CC_INFO1_COOKIE,
+		    le32toh(cc_desc->info1));
+	} else {
+		rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR,
+		    le32toh(wbm_desc->buf_addr_info.info1));
+		rel_info->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
+		    le32toh(wbm_desc->buf_addr_info.info1));
 	}
+	if (rbm != HAL_RX_BUF_RBM_SW3_BM &&
+	    rbm != sc->hw_params.hal_params->rx_buf_rbm)
+		return EINVAL;
 
-	rel_info->cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
-	    wbm_desc->buf_addr_info.info1);
 	rel_info->err_rel_src = rel_src;
 	if (rel_src == HAL_WBM_REL_SRC_MODULE_REO) {
 		rel_info->push_reason = FIELD_GET(
-		    HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON, wbm_desc->info0);
+		    HAL_WBM_RELEASE_INFO0_REO_PUSH_REASON, info0);
 		rel_info->err_code = FIELD_GET(
-		    HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE, wbm_desc->info0);
+		    HAL_WBM_RELEASE_INFO0_REO_ERROR_CODE, info0);
 	} else {
 		rel_info->push_reason = FIELD_GET(
-		    HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON, wbm_desc->info0);
+		    HAL_WBM_RELEASE_INFO0_RXDMA_PUSH_REASON, info0);
 		rel_info->err_code = FIELD_GET(
-		    HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE, wbm_desc->info0);
-	}
-
-	rel_info->first_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_FIRST_MSDU,
-	    wbm_desc->info2);
-	rel_info->last_msdu = FIELD_GET(HAL_WBM_RELEASE_INFO2_LAST_MSDU,
-	    wbm_desc->info2);
-
+		    HAL_WBM_RELEASE_INFO0_RXDMA_ERROR_CODE, info0);
+	}
+
+	rel_info->first_msdu = !!(msdu_info &
+	    RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU);
+	rel_info->last_msdu = !!(msdu_info &
+	    RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU);
+	rel_info->continuation = !!(msdu_info &
+	    RX_MSDU_DESC_INFO0_MSDU_CONTINUATION);
+	rel_info->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID,
+	    le32toh(wbm_desc->rx_mpdu_info.meta_data));
+	rel_info->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM,
+	    le32toh(wbm_desc->rx_mpdu_info.info0));
 	return 0;
 }
 
-int
-qwz_dp_rx_h_null_q_desc(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
-    struct qwz_rx_msdu_list *msdu_list)
-{
-	printf("%s: not implemented\n", __func__);
-	return ENOTSUP;
-}
-
 int
 qwz_dp_rx_h_reo_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
     struct qwz_rx_msdu_list *msdu_list)
@@ -15013,113 +15043,72 @@ qwz_dp_rx_wbm_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
 	}
 
 	qwz_dp_rx_deliver_msdu(sc, msdu);
+	msdu->m = NULL;
 }
 
 int
 qwz_dp_rx_process_wbm_err(struct qwz_softc *sc)
 {
-#if 0
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_ic.ic_if;
 	struct qwz_dp *dp = &sc->dp;
-	struct dp_rxdma_ring *rx_ring;
 	struct hal_rx_wbm_rel_info err_info;
 	struct hal_srng *srng;
-	struct qwz_rx_msdu_list msdu_list[MAX_RADIOS];
+	struct qwz_rx_msdu_list msdu_list;
+	TAILQ_HEAD(, ath12k_rx_desc_info) used_list;
 	struct qwz_rx_msdu *msdu;
-	struct mbuf *m;
-	struct qwz_rx_data *rx_data;
-	uint32_t *rx_desc;
-	int idx, mac_id;
-	int num_buffs_reaped[MAX_RADIOS] = {0};
-	int total_num_buffs_reaped = 0;
-	int ret, i;
-
-	for (i = 0; i < sc->num_radios; i++)
-		TAILQ_INIT(&msdu_list[i]);
+	void *rx_desc;
+	int num_buffs_reaped = 0;
 
+	TAILQ_INIT(&msdu_list);
+	TAILQ_INIT(&used_list);
 	srng = &sc->hal.srng_list[dp->rx_rel_ring.ring_id];
-#ifdef notyet
-	spin_lock_bh(&srng->lock);
-#endif
 	qwz_hal_srng_access_begin(sc, srng);
 
 	while ((rx_desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) {
-		ret = qwz_hal_wbm_desc_parse_err(rx_desc, &err_info);
-		if (ret) {
-			printf("%s: failed to parse rx error in wbm_rel "
-			    "ring desc %d\n", sc->sc_dev.dv_xname, ret);
+		if (qwz_hal_wbm_desc_parse_err(sc, rx_desc, &err_info)) {
+			ifp->if_ierrors++;
 			continue;
 		}
 
-		idx = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, err_info.cookie);
-		mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, err_info.cookie);
-
-		if (mac_id >= MAX_RADIOS)
-			continue;
-
-		rx_ring = &sc->pdev_dp.rx_refill_buf_ring;
-		if (idx >= rx_ring->bufs_max || isset(rx_ring->freemap, idx))
+		msdu = qwz_dp_rx_reap_desc(sc, err_info.cookie, &used_list);
+		if (msdu == NULL) {
+			ifp->if_ierrors++;
 			continue;
-
-		rx_data = &rx_ring->rx_data[idx];
-		bus_dmamap_unload(sc->sc_dmat, rx_data->map);
-		m = rx_data->m;
-		rx_data->m = NULL;
-		setbit(rx_ring->freemap, idx);
-
-		num_buffs_reaped[mac_id]++;
-		total_num_buffs_reaped++;
+		}
+		num_buffs_reaped++;
 
 		if (err_info.push_reason !=
-		    HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) {
-			m_freem(m);
+		    HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED ||
+		    test_bit(ATH12K_CAC_RUNNING, sc->sc_flags)) {
+			m_freem(msdu->m);
+			msdu->m = NULL;
+			ifp->if_ierrors++;
 			continue;
 		}
 
-		msdu = &rx_data->rx_msdu;
-		memset(&msdu->rxi, 0, sizeof(msdu->rxi));
-		msdu->m = m;
 		msdu->err_rel_src = err_info.err_rel_src;
 		msdu->err_code = err_info.err_code;
-		msdu->rx_desc = mtod(m, struct hal_rx_desc *);
-		TAILQ_INSERT_TAIL(&msdu_list[mac_id], msdu, entry);
+		msdu->is_first_msdu = err_info.first_msdu;
+		msdu->is_last_msdu = err_info.last_msdu;
+		msdu->is_continuation = err_info.continuation;
+		msdu->peer_id = err_info.peer_id;
+		msdu->seq_no = err_info.seq_no;
+		msdu->tid = sc->hal_rx_ops->rx_desc_get_mpdu_tid(msdu->rx_desc);
+		TAILQ_INSERT_TAIL(&msdu_list, msdu, entry);
 	}
 
 	qwz_hal_srng_access_end(sc, srng);
-#ifdef notyet
-	spin_unlock_bh(&srng->lock);
-#endif
-	if (!total_num_buffs_reaped)
-		goto done;
 
-	for (i = 0; i < sc->num_radios; i++) {
-		if (!num_buffs_reaped[i])
-			continue;
-
-		rx_ring = &sc->pdev_dp.rx_refill_buf_ring;
-		qwz_dp_rxbufs_replenish(sc, i, rx_ring, num_buffs_reaped[i],
-		    sc->hw_params.hal_params->rx_buf_rbm);
+	while ((msdu = TAILQ_FIRST(&msdu_list))) {
+		TAILQ_REMOVE(&msdu_list, msdu, entry);
+		qwz_dp_rx_wbm_err(sc, msdu, &msdu_list);
 	}
 
-	for (i = 0; i < sc->num_radios; i++) {
-		while ((msdu = TAILQ_FIRST(msdu_list))) {
-			TAILQ_REMOVE(msdu_list, msdu, entry);
-			if (test_bit(ATH12K_CAC_RUNNING, sc->sc_flags)) {
-				m_freem(msdu->m);
-				msdu->m = NULL;
-				continue;
-			}
-			qwz_dp_rx_wbm_err(sc, msdu, &msdu_list[i]);
-			msdu->m = NULL;
-		}
-	}
-done:
-	ifp->if_ierrors += total_num_buffs_reaped;
+	if (num_buffs_reaped)
+		qwz_dp_rxbufs_replenish(sc, &dp->rx_refill_buf_ring,
+		    &used_list, num_buffs_reaped);
 
-	return total_num_buffs_reaped;
-#endif
-	return 0;
+	return num_buffs_reaped;
 }
 
 struct qwz_rx_msdu *
@@ -15647,6 +15636,45 @@ qwz_dp_rx_process_msdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
 	return qwz_dp_rx_h_mpdu(sc, msdu, rx_desc);
 }
 
+int
+qwz_dp_rx_h_null_q_desc(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
+    struct qwz_rx_msdu_list *msdu_list)
+{
+	struct qwz_rx_msdu *next, *tmp;
+	uint32_t desc_sz = sc->hal.hal_desc_sz;
+	uint16_t msdu_len;
+	uint8_t l3pad;
+	int nbufs;
+
+	msdu_len = qwz_dp_rx_h_msdu_start_msdu_len(sc, msdu->rx_desc);
+	if (msdu_len + desc_sz > DP_RX_BUFFER_SIZE) {
+		nbufs = howmany(msdu_len - (DP_RX_BUFFER_SIZE - desc_sz),
+		    DP_RX_BUFFER_SIZE - desc_sz);
+		TAILQ_FOREACH_SAFE(next, msdu_list, entry, tmp) {
+			if (!nbufs)
+				break;
+			if (next->err_rel_src != HAL_WBM_REL_SRC_MODULE_REO ||
+			    next->err_code !=
+			    HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO)
+				continue;
+			TAILQ_REMOVE(msdu_list, next, entry);
+			m_freem(next->m);
+			next->m = NULL;
+			sc->sc_ic.ic_if.if_ierrors++;
+			nbufs--;
+		}
+		return EINVAL;
+	}
+	if (msdu->is_continuation)
+		return EINVAL;
+
+	l3pad = qwz_dp_rx_h_msdu_end_l3pad(sc, msdu->rx_desc);
+	if (desc_sz + l3pad + msdu_len > DP_RX_BUFFER_SIZE)
+		return EINVAL;
+
+	return qwz_dp_rx_process_msdu(sc, msdu, msdu_list);
+}
+
 void
 qwz_dp_rx_deliver_msdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu)
 {
diff --git a/sys/dev/ic/qwzreg.h b/sys/dev/ic/qwzreg.h
index 35f1426525c..214db309aa4 100644
--- a/sys/dev/ic/qwzreg.h
+++ b/sys/dev/ic/qwzreg.h
@@ -9632,6 +9632,29 @@ struct hal_wbm_release_ring {
 	uint32_t info3;
 } __packed;
 
+#define HAL_WBM_RELEASE_RX_INFO0_CC_STATUS		BIT(16)
+#define HAL_WBM_RELEASE_RX_CC_INFO0_RBM		GENMASK(12, 9)
+#define HAL_WBM_RELEASE_RX_CC_INFO1_COOKIE		GENMASK(27, 8)
+
+struct hal_wbm_release_ring_rx {
+	struct ath12k_buffer_addr buf_addr_info;
+	uint32_t info0;
+	struct rx_mpdu_desc rx_mpdu_info;
+	struct rx_msdu_desc rx_msdu_info;
+	uint32_t info1;
+	uint32_t info2;
+} __packed;
+
+struct hal_wbm_release_ring_cc_rx {
+	uint32_t buf_va_lo;
+	uint32_t buf_va_hi;
+	uint32_t info0;
+	struct rx_mpdu_desc rx_mpdu_info;
+	struct rx_msdu_desc rx_msdu_info;
+	uint32_t buf_pa_lo;
+	uint32_t info1;
+} __packed;
+
 /* hal_wbm_release_ring
  *
  *	Producer: SW/TQM/RXDMA/REO/SWITCH
diff --git a/sys/dev/ic/qwzvar.h b/sys/dev/ic/qwzvar.h
index c21ad3ee860..f92bbeb2765 100644
--- a/sys/dev/ic/qwzvar.h
+++ b/sys/dev/ic/qwzvar.h
@@ -1644,6 +1644,9 @@ struct hal_rx_wbm_rel_info {
 	uint32_t err_code;
 	int first_msdu;
 	int last_msdu;
+	int continuation;
+	uint16_t peer_id;
+	uint16_t seq_no;
 };
 
 #define HAL_INVALID_PEERID 0xffff
-- 
2.55.0


From 86550cd54f0a1f8f9e135bf85ab9f7f9de021b61 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:56:45 +0200
Subject: [PATCH 3/8] sys/qwz: read RX metadata from MPDU TLVs

Read sequence numbers and TIDs from WCN7850 MPDU descriptors.
---
 sys/dev/ic/qwz.c    | 12 ++++++------
 sys/dev/ic/qwzvar.h |  1 -
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index 2b916eab0a8..a693d138e49 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -14959,8 +14959,6 @@ qwz_hal_wbm_desc_parse_err(struct qwz_softc *sc, void *desc,
 	    RX_MSDU_DESC_INFO0_MSDU_CONTINUATION);
 	rel_info->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID,
 	    le32toh(wbm_desc->rx_mpdu_info.meta_data));
-	rel_info->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM,
-	    le32toh(wbm_desc->rx_mpdu_info.info0));
 	return 0;
 }
 
@@ -15092,7 +15090,8 @@ qwz_dp_rx_process_wbm_err(struct qwz_softc *sc)
 		msdu->is_last_msdu = err_info.last_msdu;
 		msdu->is_continuation = err_info.continuation;
 		msdu->peer_id = err_info.peer_id;
-		msdu->seq_no = err_info.seq_no;
+		msdu->seq_no = sc->hal_rx_ops->rx_desc_get_mpdu_start_seq_no(
+		    msdu->rx_desc);
 		msdu->tid = sc->hal_rx_ops->rx_desc_get_mpdu_tid(msdu->rx_desc);
 		TAILQ_INSERT_TAIL(&msdu_list, msdu, entry);
 	}
@@ -15813,9 +15812,10 @@ try_again:
 		    RX_MSDU_DESC_INFO0_MSDU_CONTINUATION);
 		msdu->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID,
 		    desc->rx_mpdu_info.meta_data);
-		msdu->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM,
-		    desc->rx_mpdu_info.info0);
-		msdu->tid = 0; /* no RX_QUEUE_NUM in wifi7 */
+		msdu->rx_desc = mtod(m, struct hal_rx_desc *);
+		msdu->seq_no = sc->hal_rx_ops->rx_desc_get_mpdu_start_seq_no(
+		    msdu->rx_desc);
+		msdu->tid = sc->hal_rx_ops->rx_desc_get_mpdu_tid(msdu->rx_desc);
 
 		msdu->mac_id = mac_id;
 		TAILQ_INSERT_TAIL(&msdu_list[mac_id], msdu, entry);
diff --git a/sys/dev/ic/qwzvar.h b/sys/dev/ic/qwzvar.h
index f92bbeb2765..b182f7001db 100644
--- a/sys/dev/ic/qwzvar.h
+++ b/sys/dev/ic/qwzvar.h
@@ -1646,7 +1646,6 @@ struct hal_rx_wbm_rel_info {
 	int last_msdu;
 	int continuation;
 	uint16_t peer_id;
-	uint16_t seq_no;
 };
 
 #define HAL_INVALID_PEERID 0xffff
-- 
2.55.0


From 988802b1c4ce22c4f6581799954b7918f33b9b78 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:56:42 +0200
Subject: [PATCH 4/8] sys/qwz: drain REO RX exceptions

Based on sys/dev/ic/qwx.c,v 1.33

Drain REO RX exceptions using descriptor layouts and qwz cookie.
Reclaim packet buffers, return link descriptors and replenish RX,
checking bank bounds and release ring space.

---
 sys/dev/ic/qwz.c    | 253 +++++++++++++++++---------------------------
 sys/dev/ic/qwzreg.h |   5 +
 2 files changed, 104 insertions(+), 154 deletions(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index a693d138e49..d5d6e230efd 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -14611,10 +14611,11 @@ qwz_hal_rx_reo_ent_paddr_get(struct qwz_softc *sc, void *desc, uint64_t *paddr,
 	struct ath12k_buffer_addr *buff_addr = desc;
 
 	*paddr = ((uint64_t)(FIELD_GET(BUFFER_ADDR_INFO1_ADDR,
-	    buff_addr->info1)) << 32) |
-	    FIELD_GET(BUFFER_ADDR_INFO0_ADDR, buff_addr->info0);
+	    le32toh(buff_addr->info1))) << 32) |
+	    FIELD_GET(BUFFER_ADDR_INFO0_ADDR, le32toh(buff_addr->info0));
 
-	*desc_bank = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, buff_addr->info1);
+	*desc_bank = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
+	    le32toh(buff_addr->info1));
 }
 
 int
@@ -14623,15 +14624,9 @@ qwz_hal_desc_reo_parse_err(struct qwz_softc *sc, uint32_t *rx_desc,
 {
 	struct hal_reo_dest_ring *desc = (struct hal_reo_dest_ring *)rx_desc;
 	enum hal_reo_dest_ring_push_reason push_reason;
-	enum hal_reo_dest_ring_error_code err_code;
 
 	push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON,
-	    desc->info0);
-	err_code = FIELD_GET(HAL_REO_DEST_RING_INFO0_ERROR_CODE,
-	    desc->info0);
-#if 0
-	ab->soc_stats.reo_error[err_code]++;
-#endif
+	    le32toh(desc->info0));
 	if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED &&
 	    push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) {
 		printf("%s: expected error push reason code, received %d\n",
@@ -14639,7 +14634,8 @@ qwz_hal_desc_reo_parse_err(struct qwz_softc *sc, uint32_t *rx_desc,
 		return EINVAL;
 	}
 
-	if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE, desc->info0) !=
+	if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE,
+	    le32toh(desc->info0)) !=
 	    HAL_REO_DEST_RING_BUFFER_TYPE_LINK_DESC) {
 		printf("%s: expected buffer type link_desc",
 		    sc->sc_dev.dv_xname);
@@ -14647,6 +14643,7 @@ qwz_hal_desc_reo_parse_err(struct qwz_softc *sc, uint32_t *rx_desc,
 	}
 
 	qwz_hal_rx_reo_ent_paddr_get(sc, rx_desc, paddr, desc_bank);
+	*desc_bank = FIELD_GET(DP_LINK_DESC_BANK_MASK, *desc_bank);
 
 	return 0;
 }
@@ -14663,18 +14660,18 @@ qwz_hal_rx_msdu_link_info_get(void *link_desc, uint32_t *num_msdus,
 
 	msdu = &link->msdu_link[0];
 	*rbm = FIELD_GET(BUFFER_ADDR_INFO1_RET_BUF_MGR,
-	    msdu->buf_addr_info.info1);
+	    le32toh(msdu->buf_addr_info.info1));
 
 	for (i = 0; i < *num_msdus; i++) {
 		msdu = &link->msdu_link[i];
 
 		if (!FIELD_GET(BUFFER_ADDR_INFO0_ADDR,
-		    msdu->buf_addr_info.info0)) {
+		    le32toh(msdu->buf_addr_info.info0))) {
 			*num_msdus = i;
 			break;
 		}
 		*msdu_cookies = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
-		    msdu->buf_addr_info.info1);
+		    le32toh(msdu->buf_addr_info.info1));
 		msdu_cookies++;
 	}
 }
@@ -14686,12 +14683,13 @@ qwz_hal_rx_msdu_link_desc_set(struct qwz_softc *sc, void *desc,
 	struct hal_wbm_release_ring *dst_desc = desc;
 	struct hal_wbm_release_ring *src_desc = link_desc;
 
+	memset(dst_desc, 0, sizeof(*dst_desc));
 	dst_desc->buf_addr_info = src_desc->buf_addr_info;
-	dst_desc->info0 |= FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE,
+	dst_desc->info0 = htole32(FIELD_PREP(HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE,
 	    HAL_WBM_REL_SRC_MODULE_SW) |
 	    FIELD_PREP(HAL_WBM_RELEASE_INFO0_BM_ACTION, action) |
 	    FIELD_PREP(HAL_WBM_RELEASE_INFO0_DESC_TYPE,
-	    HAL_WBM_REL_DESC_TYPE_MSDU_LINK);
+	    HAL_WBM_REL_DESC_TYPE_MSDU_LINK));
 }
 
 int
@@ -14740,166 +14738,113 @@ qwz_dp_rx_h_msdu_start_msdu_len(struct qwz_softc *sc, struct hal_rx_desc *desc)
 	return sc->hal_rx_ops->rx_desc_get_msdu_len(desc);
 }
 
-void
-qwz_dp_process_rx_err_buf(struct qwz_softc *sc, uint32_t *ring_desc,
-    int buf_id, int drop)
+int
+qwz_dp_process_rx_err_buf(struct qwz_softc *sc, uint32_t cookie, void *list)
 {
-#if 0
-	struct qwz_pdev_dp *dp = &sc->pdev_dp;
-	struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring;
-	struct mbuf *m;
-	struct qwz_rx_data *rx_data;
-	struct hal_rx_desc *rx_desc;
-	uint16_t msdu_len;
-	uint32_t hal_rx_desc_sz = sc->hal.hal_desc_sz;
-
-	if (buf_id >= rx_ring->bufs_max || isset(rx_ring->freemap, buf_id))
-		return;
-
-	rx_data = &rx_ring->rx_data[buf_id];
-	bus_dmamap_unload(sc->sc_dmat, rx_data->map);
-	m = rx_data->m;
-	rx_data->m = NULL;
-	setbit(rx_ring->freemap, buf_id);
-
-	if (drop) {
-		m_freem(m);
-		return;
-	}
-
-	rx_desc = mtod(m, struct hal_rx_desc *);
-	msdu_len = qwz_dp_rx_h_msdu_start_msdu_len(sc, rx_desc);
-	if ((msdu_len + hal_rx_desc_sz) > DP_RX_BUFFER_SIZE) {
-#if 0
-		uint8_t *hdr_status = ath12k_dp_rx_h_80211_hdr(ar->ab, rx_desc);
-		ath12k_warn(ar->ab, "invalid msdu leng %u", msdu_len);
-		ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "", hdr_status,
-				sizeof(struct ieee80211_hdr));
-		ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "", rx_desc,
-				sizeof(struct hal_rx_desc));
-#endif
-		m_freem(m);
-		return;
-	}
+	struct qwz_rx_msdu *msdu;
 
-	if (qwz_dp_rx_frag_h_mpdu(sc, m, ring_desc)) {
-		qwz_dp_rx_link_desc_return(sc, ring_desc,
-		    HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
-	}
+	msdu = qwz_dp_rx_reap_desc(sc, cookie, list);
+	if (msdu == NULL)
+		return 0;
 
-	m_freem(m);
-#endif
-	printf("%s:%d\n", __func__, __LINE__);
+	m_freem(msdu->m);
+	msdu->m = NULL;
+	return 1;
 }
 
 int
 qwz_dp_process_rx_err(struct qwz_softc *sc)
 {
-#if 0
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
-	uint32_t msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC];
-	struct dp_link_desc_bank *link_desc_banks;
+	struct ifnet *ifp = &sc->sc_ic.ic_if;
+	struct qwz_dp *dp = &sc->dp;
+	TAILQ_HEAD(, ath12k_rx_desc_info) used_list;
+	struct hal_srng *srng, *release_srng;
+	struct hal_reo_dest_ring *reo_desc;
+	struct dp_link_desc_bank *bank;
+	struct hal_rx_msdu_link *link;
 	enum hal_rx_buf_return_buf_manager rbm;
-	int tot_n_bufs_reaped, ret, i;
-	int n_bufs_reaped[MAX_RADIOS] = {0};
-	struct dp_rxdma_ring *rx_ring;
-	struct dp_srng *reo_except;
-	uint32_t desc_bank, num_msdus;
-	struct hal_srng *srng;
-	struct qwz_dp *dp;
-	void *link_desc_va;
-	int buf_id, mac_id;
+	enum hal_wbm_rel_bm_act action;
+	uint32_t cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC];
+	uint32_t desc_bank, num_msdus, cookie;
 	uint64_t paddr;
-	uint32_t *desc;
-	int is_frag;
-	uint8_t drop = 0;
-
-	tot_n_bufs_reaped = 0;
-
-	dp = &sc->dp;
-	reo_except = &dp->reo_except_ring;
-	link_desc_banks = dp->link_desc_banks;
+	bus_size_t offset;
+	void *release_desc;
+	int num_buffs_reaped = 0;
+	int n, i;
 
-	srng = &sc->hal.srng_list[reo_except->ring_id];
-#ifdef notyet
-	spin_lock_bh(&srng->lock);
-#endif
+	TAILQ_INIT(&used_list);
+	srng = &sc->hal.srng_list[dp->reo_except_ring.ring_id];
+	release_srng = &sc->hal.srng_list[dp->wbm_desc_rel_ring.ring_id];
 	qwz_hal_srng_access_begin(sc, srng);
+	qwz_hal_srng_access_begin(sc, release_srng);
 
-	while ((desc = qwz_hal_srng_dst_get_next_entry(sc, srng))) {
-		struct hal_reo_dest_ring *reo_desc =
-		    (struct hal_reo_dest_ring *)desc;
-#if 0
-		ab->soc_stats.err_ring_pkts++;
-#endif
-		ret = qwz_hal_desc_reo_parse_err(sc, desc, &paddr, &desc_bank);
-		if (ret) {
-			printf("%s: failed to parse error reo desc %d\n",
-			    sc->sc_dev.dv_xname, ret);
-			continue;
-		}
-		link_desc_va = link_desc_banks[desc_bank].vaddr +
-		    (paddr - link_desc_banks[desc_bank].paddr);
-		qwz_hal_rx_msdu_link_info_get(link_desc_va, &num_msdus,
-		    msdu_cookies, &rbm);
-		if (rbm != HAL_RX_BUF_RBM_WBM_DEV0_IDLE_DESC_LIST &&
-		    rbm != HAL_RX_BUF_RBM_SW3_BM) {
-#if 0
-			ab->soc_stats.invalid_rbm++;
-#endif
-			printf("%s: invalid return buffer manager %d\n",
-			    sc->sc_dev.dv_xname, rbm);
-			qwz_dp_rx_link_desc_return(sc, desc,
-			    HAL_WBM_REL_BM_ACT_REL_MSDU);
+	while (qwz_hal_srng_src_num_free(sc, release_srng, 1) > 0 &&
+	    (reo_desc = (struct hal_reo_dest_ring *)
+	    qwz_hal_srng_dst_get_next_entry(sc, srng))) {
+		if (FIELD_GET(HAL_REO_DEST_RING_INFO0_BUFFER_TYPE,
+		    le32toh(reo_desc->info0)) ==
+		    HAL_REO_DEST_RING_BUFFER_TYPE_MSDU) {
+			cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE,
+			    le32toh(reo_desc->buf_addr_info.info1));
+			n = qwz_dp_process_rx_err_buf(sc, cookie, &used_list);
+			num_buffs_reaped += n;
+			if (!n)
+				ifp->if_ierrors++;
 			continue;
 		}
 
-		is_frag = !!(reo_desc->rx_mpdu_info.info0 &
-		    RX_MPDU_DESC_INFO0_FRAG_FLAG);
-
-		/* Process only rx fragments with one msdu per link desc below,
-		 * and drop msdu's indicated due to error reasons.
-		 */
-		if (!is_frag || num_msdus > 1) {
-			drop = 1;
-			/* Return the link desc back to wbm idle list */
-			qwz_dp_rx_link_desc_return(sc, desc,
-			   HAL_WBM_REL_BM_ACT_PUT_IN_IDLE);
+		if (qwz_hal_desc_reo_parse_err(sc, (uint32_t *)reo_desc,
+		    &paddr, &desc_bank)) {
+			ifp->if_ierrors++;
+			continue;
 		}
-
-		for (i = 0; i < num_msdus; i++) {
-			buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID,
-			    msdu_cookies[i]);
-
-			mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID,
-			    msdu_cookies[i]);
-
-			qwz_dp_process_rx_err_buf(sc, desc, buf_id, drop);
-			n_bufs_reaped[mac_id]++;
-			tot_n_bufs_reaped++;
+		if (desc_bank >= nitems(dp->link_desc_banks)) {
+			ifp->if_ierrors++;
+			continue;
 		}
-	}
-
-	qwz_hal_srng_access_end(sc, srng);
-#ifdef notyet
-	spin_unlock_bh(&srng->lock);
-#endif
-	for (i = 0; i < sc->num_radios; i++) {
-		if (!n_bufs_reaped[i])
+		bank = &dp->link_desc_banks[desc_bank];
+		if (bank->mem == NULL || bank->size < sizeof(*link) ||
+		    paddr < bank->paddr ||
+		    paddr - bank->paddr > bank->size - sizeof(*link)) {
+			ifp->if_ierrors++;
 			continue;
+		}
+		offset = paddr - bank->paddr;
+		link = (struct hal_rx_msdu_link *)
+		    ((uint8_t *)bank->vaddr + offset);
+		bus_dmamap_sync(sc->sc_dmat, bank->mem->map, offset,
+		    sizeof(*link), BUS_DMASYNC_POSTREAD);
+		qwz_hal_rx_msdu_link_info_get(link, &num_msdus, cookies, &rbm);
+		action = HAL_WBM_REL_BM_ACT_PUT_IN_IDLE;
+		if (rbm != dp->idle_link_rbm && rbm != HAL_RX_BUF_RBM_SW3_BM &&
+		    rbm != sc->hw_params.hal_params->rx_buf_rbm) {
+			action = HAL_WBM_REL_BM_ACT_REL_MSDU;
+			ifp->if_ierrors++;
+		} else {
+			/* Fragment reassembly is not implemented. */
+			for (i = 0; i < num_msdus; i++) {
+				n = qwz_dp_process_rx_err_buf(sc, cookies[i],
+				    &used_list);
+				num_buffs_reaped += n;
+				if (!n)
+					ifp->if_ierrors++;
+			}
+		}
+		bus_dmamap_sync(sc->sc_dmat, bank->mem->map, offset,
+		    sizeof(*link), BUS_DMASYNC_PREREAD);
 
-		rx_ring = &sc->pdev_dp.rx_refill_buf_ring;
-
-		qwz_dp_rxbufs_replenish(sc, i, rx_ring, n_bufs_reaped[i],
-		    sc->hw_params.hal_params->rx_buf_rbm);
+		release_desc = qwz_hal_srng_src_get_next_entry(sc, release_srng);
+		KASSERT(release_desc != NULL);
+		qwz_hal_rx_msdu_link_desc_set(sc, release_desc, reo_desc, action);
 	}
 
-	ifp->if_ierrors += tot_n_bufs_reaped;
-
-	return tot_n_bufs_reaped;
-#endif
-	return 0;
+	qwz_hal_srng_access_end(sc, release_srng);
+	qwz_hal_srng_access_end(sc, srng);
+	if (num_buffs_reaped)
+		qwz_dp_rxbufs_replenish(sc, &dp->rx_refill_buf_ring,
+		    &used_list, num_buffs_reaped);
+	ifp->if_ierrors += num_buffs_reaped;
+	return num_buffs_reaped;
 }
 
 int
diff --git a/sys/dev/ic/qwzreg.h b/sys/dev/ic/qwzreg.h
index 214db309aa4..602fc4de356 100644
--- a/sys/dev/ic/qwzreg.h
+++ b/sys/dev/ic/qwzreg.h
@@ -9881,9 +9881,14 @@ struct hal_rx_mpdu_link_ptr {
 	struct ath12k_buffer_addr addr_info;
 } __packed;
 
+struct rx_msdu_ext_desc {
+	uint32_t info0;
+} __packed;
+
 struct hal_rx_msdu_details {
 	struct ath12k_buffer_addr buf_addr_info;
 	struct rx_msdu_desc rx_msdu_info;
+	struct rx_msdu_ext_desc rx_msdu_ext_info;
 } __packed;
 
 #define HAL_RX_MSDU_LNK_INFO0_RX_QUEUE_NUMBER		GENMASK(15, 0)
-- 
2.55.0


From efb0bc2bc6857d147e4241dc6dd9b6d7bf9aebc8 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:56:39 +0200
Subject: [PATCH 5/8] sys/qwz: count discarded RX packets

Backport of sys/dev/ic/qwx.c,v 1.91 and sys/dev/ic/qwx.c,v 1.95
---
 sys/dev/ic/qwz.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index d5d6e230efd..f963445d8d9 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -14982,6 +14982,7 @@ qwz_dp_rx_wbm_err(struct qwz_softc *sc, struct qwz_rx_msdu *msdu,
 	if (drop) {
 		m_freem(msdu->m);
 		msdu->m = NULL;
+		sc->sc_ic.ic_if.if_ierrors++;
 		return;
 	}
 
@@ -15656,6 +15657,7 @@ qwz_dp_rx_process_received_packets(struct qwz_softc *sc,
 			DNPRINTF(QWZ_D_MAC, "Unable to process msdu: %d", ret);
 			m_freem(msdu->m);
 			msdu->m = NULL;
+			sc->sc_ic.ic_if.if_ierrors++;
 			continue;
 		}
 
-- 
2.55.0


From 9f3f11dc694dab0f0da30f94bd1796e97841a26b Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:54:46 +0200
Subject: [PATCH 6/8] sys/qwz: report radiotap channels and rates

Based on sys/dev/ic/qwx.c,v 1.93 and sys/dev/ic/qwxvar.h,v 1.31

Populate radiotap channel and rate fields with WCN7850 RX rate decoding.
Use QWZ presence masks and omit unavailable timestamps, noise and
signal strength for data frames.

Correct 54 Mb/s encoding from 104 to 108 in 500 kb/s.
---
 sys/dev/ic/qwz.c    | 99 +++++++++++++++++++++++++++++++++++++++++++--
 sys/dev/ic/qwzvar.h | 22 ++++++++--
 2 files changed, 114 insertions(+), 7 deletions(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index f963445d8d9..e6a08bdc2e7 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -105,6 +105,17 @@
 	const __typeof( ((type *)0)->member ) *__mptr = (ptr);	\
 	(type *)( (char *)__mptr - offsetof(type,member) );})
 
+#if NBPFILTER > 0
+static inline uint8_t
+hweight8(uint8_t x)
+{
+	x = (x & 0x55) + ((x & 0xaa) >> 1);
+	x = (x & 0x33) + ((x & 0xcc) >> 2);
+	x = (x + (x >> 4)) & 0x0f;
+	return (x);
+}
+#endif
+
 /* #define QWZ_DEBUG */
 
 #include <dev/ic/qwzreg.h>
@@ -515,6 +526,33 @@ qwz_tx(struct qwz_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
 #if NBPFILTER > 0
 	if (sc->sc_drvbpf != NULL) {
 		struct qwz_tx_radiotap_header *tap = &sc->sc_txtap;
+		struct ieee80211com *ic = &sc->sc_ic;
+		uint16_t chan_flags;
+
+		tap->wt_flags = 0;
+		tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
+		chan_flags = ni->ni_chan->ic_flags;
+		if (ic->ic_curmode != IEEE80211_MODE_11N &&
+		    ic->ic_curmode != IEEE80211_MODE_11AC) {
+			chan_flags &= ~IEEE80211_CHAN_HT;
+			chan_flags &= ~IEEE80211_CHAN_40MHZ;
+		}
+		if (ic->ic_curmode != IEEE80211_MODE_11AC)
+			chan_flags &= ~IEEE80211_CHAN_VHT;
+		tap->wt_chan_flags = htole16(chan_flags);
+		if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+		    !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+		    frame_type == IEEE80211_FC0_TYPE_DATA) {
+			tap->wt_rate = (0x80 | ni->ni_txmcs);
+		} else {
+			struct ieee80211_rateset *rs = &ni->ni_rates;
+			uint8_t rate = rs->rs_rates[ni->ni_txrate];
+
+			tap->wt_rate = rate & IEEE80211_RATE_VAL;
+		}
+		if ((ic->ic_flags & IEEE80211_F_WEPON) &&
+		    (wh->i_fc[1] & IEEE80211_FC1_PROTECTED))
+			tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
 
 		bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_txtap_len,
 		    m, BPF_DIRECTION_OUT);
@@ -1806,9 +1844,9 @@ const struct hal_rx_ops hal_rx_wcn7850_ops = {
 	.rx_desc_get_msdu_len = qwz_hw_wcn7850_rx_desc_get_msdu_len,
 #ifdef notyet
 	.rx_desc_get_msdu_sgi = qwz_hw_wcn7850_rx_desc_get_msdu_sgi,
-	.rx_desc_get_msdu_rate_mcs = qwz_hw_wcn7850_rx_desc_get_msdu_rate_mcs,
 	.rx_desc_get_msdu_rx_bw = qwz_hw_wcn7850_rx_desc_get_msdu_rx_bw,
 #endif
+	.rx_desc_get_msdu_rate_mcs = qwz_hw_wcn7850_rx_desc_get_msdu_rate_mcs,
 	.rx_desc_get_msdu_freq = qwz_hw_wcn7850_rx_desc_get_msdu_freq,
 	.rx_desc_get_msdu_pkt_type = qwz_hw_wcn7850_rx_desc_get_msdu_pkt_type,
 	.rx_desc_get_msdu_nss = qwz_hw_wcn7850_rx_desc_get_msdu_nss,
@@ -11858,6 +11896,23 @@ qwz_mgmt_rx_event(struct qwz_softc *sc, struct mbuf *m)
 #if NBPFILTER > 0
 	if (sc->sc_drvbpf != NULL) {
 		struct qwz_rx_radiotap_header *tap = &sc->sc_rxtap;
+		uint16_t chan_flags;
+		uint32_t freq;
+
+		tap->wr_ihdr.it_present = htole32(QWZ_RX_RADIOTAP_PRESENT);
+		tap->wr_flags = 0;
+		freq = le32toh(rx_ev.chan_freq);
+		tap->wr_chan_freq = htole16(freq);
+		chan_flags = ic->ic_channels[rx_ev.channel & 0xff].ic_flags;
+		if (ic->ic_curmode != IEEE80211_MODE_11N &&
+		    ic->ic_curmode != IEEE80211_MODE_11AC) {
+			chan_flags &= ~IEEE80211_CHAN_HT;
+			chan_flags &= ~IEEE80211_CHAN_VHT;
+			chan_flags &= ~IEEE80211_CHAN_40MHZ;
+		}
+		tap->wr_rate = rx_ev.rate / 500;
+		tap->wr_chan_flags = htole16(chan_flags);
+		tap->wr_dbm_antsignal = rxi.rxi_rssi;
 
 		bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len,
 		    m, BPF_DIRECTION_IN);
@@ -14450,7 +14505,7 @@ qwz_mac_hw_ratecode_to_legacy_rate(struct ieee80211_node *ni, uint8_t hw_rc,
 				*rate = 96;
 				break;
 			case ATH12K_HW_RATE_OFDM_54M:
-				*rate = 104;
+				*rate = 108;
 				break;
 			default:
 				return EINVAL;
@@ -15634,6 +15689,42 @@ qwz_dp_rx_deliver_msdu(struct qwz_softc *sc, struct qwz_rx_msdu *msdu)
 #if NBPFILTER > 0
 	if (sc->sc_drvbpf != NULL) {
 		struct qwz_rx_radiotap_header *tap = &sc->sc_rxtap;
+		uint8_t mcs, pkt_type, nss;
+		uint16_t chan_flags;
+		uint32_t freq;
+
+		mcs = sc->hal_rx_ops->rx_desc_get_msdu_rate_mcs(msdu->rx_desc);
+		pkt_type = sc->hal_rx_ops->rx_desc_get_msdu_pkt_type(
+		    msdu->rx_desc);
+		nss = hweight8(sc->hal_rx_ops->rx_desc_get_msdu_nss(
+		    msdu->rx_desc));
+		freq = qwz_dp_rx_h_msdu_start_freq(sc, msdu->rx_desc);
+
+		tap->wr_ihdr.it_present = htole32(QWZ_RX_RADIOTAP_PRESENT &
+		    ~(1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL));
+		tap->wr_flags = 0;
+		tap->wr_chan_freq = htole16(ic->ic_channels[freq & 0xff].ic_freq);
+		chan_flags = ic->ic_channels[freq & 0xff].ic_flags;
+		if (pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11N) {
+			if (nss > 1)
+				mcs += 8 * (nss - 1);
+			tap->wr_rate = (0x80 | mcs);
+		} else {
+			uint8_t rateidx;
+			uint16_t rate;
+
+			if (qwz_mac_hw_ratecode_to_legacy_rate(ni, mcs,
+			    pkt_type, &rateidx, &rate) == 0)
+				tap->wr_rate = rate;
+			else
+				tap->wr_rate = 0;
+
+			chan_flags &= ~IEEE80211_CHAN_HT;
+			chan_flags &= ~IEEE80211_CHAN_VHT;
+			chan_flags &= ~IEEE80211_CHAN_40MHZ;
+		}
+		tap->wr_chan_flags = htole16(chan_flags);
+		tap->wr_dbm_antsignal = 0;
 
 		bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len,
 		    msdu->m, BPF_DIRECTION_IN);
@@ -24849,11 +24940,11 @@ qwz_radiotap_attach(struct qwz_softc *sc)
 
 	sc->sc_rxtap_len = sizeof(sc->sc_rxtapu);
 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
-	sc->sc_rxtap.wr_ihdr.it_present = htole32(IWX_RX_RADIOTAP_PRESENT);
+	sc->sc_rxtap.wr_ihdr.it_present = htole32(QWZ_RX_RADIOTAP_PRESENT);
 
 	sc->sc_txtap_len = sizeof(sc->sc_txtapu);
 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
-	sc->sc_txtap.wt_ihdr.it_present = htole32(IWX_TX_RADIOTAP_PRESENT);
+	sc->sc_txtap.wt_ihdr.it_present = htole32(QWZ_TX_RADIOTAP_PRESENT);
 }
 #endif
 
diff --git a/sys/dev/ic/qwzvar.h b/sys/dev/ic/qwzvar.h
index b182f7001db..924adab0ce2 100644
--- a/sys/dev/ic/qwzvar.h
+++ b/sys/dev/ic/qwzvar.h
@@ -289,9 +289,9 @@ struct hal_rx_ops {
 	uint16_t (*rx_desc_get_msdu_len)(struct hal_rx_desc *desc);
 #ifdef notyet
 	uint8_t (*rx_desc_get_msdu_sgi)(struct hal_rx_desc *desc);
-	uint8_t (*rx_desc_get_msdu_rate_mcs)(struct hal_rx_desc *desc);
 	uint8_t (*rx_desc_get_msdu_rx_bw)(struct hal_rx_desc *desc);
 #endif
+	uint8_t (*rx_desc_get_msdu_rate_mcs)(struct hal_rx_desc *desc);
 	uint32_t (*rx_desc_get_msdu_freq)(struct hal_rx_desc *desc);
 	uint8_t (*rx_desc_get_msdu_pkt_type)(struct hal_rx_desc *desc);
 	uint8_t (*rx_desc_get_msdu_nss)(struct hal_rx_desc *desc);
@@ -1889,15 +1889,31 @@ struct qwz_ext_irq_grp {
 
 struct qwz_rx_radiotap_header {
 	struct ieee80211_radiotap_header wr_ihdr;
+	uint8_t		wr_flags;
+	uint8_t		wr_rate;
+	uint16_t	wr_chan_freq;
+	uint16_t	wr_chan_flags;
+	int8_t		wr_dbm_antsignal;
 } __packed;
 
-#define IWX_RX_RADIOTAP_PRESENT	0 /* TODO add more information */
+#define QWZ_RX_RADIOTAP_PRESENT						\
+	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
+	 (1 << IEEE80211_RADIOTAP_RATE) |				\
+	 (1 << IEEE80211_RADIOTAP_CHANNEL) |				\
+	 (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
 
 struct qwz_tx_radiotap_header {
 	struct ieee80211_radiotap_header wt_ihdr;
+	uint8_t		wt_flags;
+	uint8_t		wt_rate;
+	uint16_t	wt_chan_freq;
+	uint16_t	wt_chan_flags;
 } __packed;
 
-#define IWX_TX_RADIOTAP_PRESENT	0 /* TODO add more information */
+#define QWZ_TX_RADIOTAP_PRESENT						\
+	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
+	 (1 << IEEE80211_RADIOTAP_RATE) |				\
+	 (1 << IEEE80211_RADIOTAP_CHANNEL))
 
 struct qwz_setkey_task_arg {
 	struct ieee80211_node *ni;
-- 
2.55.0


From 8db03414d84b4bc2ab6cee6304232abce91a9e16 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:56:51 +0200
Subject: [PATCH 7/8] sys/qwz: preserve decoded radiotap frequency

Management RX parameters already contain a hostorder chanel
frequency. Avoid decoding it again before writing the little endian
radiotap field.
---
 sys/dev/ic/qwz.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/sys/dev/ic/qwz.c b/sys/dev/ic/qwz.c
index e6a08bdc2e7..54f58f1f9ce 100644
--- a/sys/dev/ic/qwz.c
+++ b/sys/dev/ic/qwz.c
@@ -11901,7 +11901,7 @@ qwz_mgmt_rx_event(struct qwz_softc *sc, struct mbuf *m)
 
 		tap->wr_ihdr.it_present = htole32(QWZ_RX_RADIOTAP_PRESENT);
 		tap->wr_flags = 0;
-		freq = le32toh(rx_ev.chan_freq);
+		freq = rx_ev.chan_freq;
 		tap->wr_chan_freq = htole16(freq);
 		chan_flags = ic->ic_channels[rx_ev.channel & 0xff].ic_flags;
 		if (ic->ic_curmode != IEEE80211_MODE_11N &&
-- 
2.55.0


From 5bf6ecff371848346ab02d43a599f5e9d4394a08 Mon Sep 17 00:00:00 2001
From: "Kirill A. Korinsky" <kirill@korins.ky>
Date: Sun, 27 Sep 2026 12:57:06 +0200
Subject: [PATCH 8/8] sys/qwz: retain cached firmware filenames

Backport of sys/dev/pci/if_qwx_pci.c,v 1.29
---
 sys/dev/pci/if_qwz_pci.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/sys/dev/pci/if_qwz_pci.c b/sys/dev/pci/if_qwz_pci.c
index 1c7c3895c72..e1997e2671c 100644
--- a/sys/dev/pci/if_qwz_pci.c
+++ b/sys/dev/pci/if_qwz_pci.c
@@ -3033,16 +3033,15 @@ qwz_mhi_fw_load_handler(struct qwz_pci_softc *psc)
 	u_char *data;
 	size_t len;
 
-	amss_path[0] = '\0';
+	ret = snprintf(amss_path, sizeof(amss_path), "%s-%s-%s",
+	    ATH12K_FW_DIR, sc->hw_params.fw.dir, ATH12K_AMSS_FILE);
+	if (ret < 0 || ret >= sizeof(amss_path))
+		return ENOSPC;
+
 	if (sc->fw_img[QWZ_FW_AMSS].data) {
 		data = sc->fw_img[QWZ_FW_AMSS].data;
 		len = sc->fw_img[QWZ_FW_AMSS].size;
 	} else {
-		ret = snprintf(amss_path, sizeof(amss_path), "%s-%s-%s",
-		    ATH12K_FW_DIR, sc->hw_params.fw.dir, ATH12K_AMSS_FILE);
-		if (ret < 0 || ret >= sizeof(amss_path))
-			return ENOSPC;
-
 		ret = loadfirmware(amss_path, &data, &len);
 		if (ret) {
 			printf("%s: could not read %s (error %d)\n",
-- 
2.55.0


-- 
wbr, Kirill