Index | Thread | Search

From:
Marcus Glocker <marcus@nazgul.ch>
Subject:
Re: sys/qwz: initialize on Snapdragon but fails on scan
To:
"Kirill A. Korinsky" <kirill@korins.ky>
Cc:
tech@openbsd.org
Date:
Sun, 12 Apr 2026 19:25:50 +0200

Download raw body.

Thread
On Sun, Apr 12, 2026 at 01:22:28PM +0200, Marcus Glocker wrote:

> On Sat, Apr 11, 2026 at 09:32:46PM +0200, Marcus Glocker wrote:
> 
> > On Thu, Apr 02, 2026 at 10:00:51PM +0200, Kirill A. Korinsky wrote:
> > 
> > > May I ask you to try update from this email?
> > > https://marc.info/?l=openbsd-tech&m=177481501901011&w=2
> > > 
> > > I haven't commited it yet
> > 
> > That's what I'm getting on my Samsung Galaxy Book4 Edge.
> > Unfortunately by applying your diff, it makes no difference for me.
> 
> With the linux-firmware-20260309 package and your diff I also can
> initialize the core now:
> 
> qwz_wmi_tlv_op_rx: 0x16005: update fw mem dump
> qwz_wmi_tlv_op_rx: unsupported event id 0xb00b
> qwz_pull_reg_chan_list_ext_update_ev: not implemented
> qwz0: failed to extract regulatory info from received event
> qwz_pull_reg_chan_list_ext_update_ev: not implemented
> qwz0: failed to extract regulatory info from received event
> qwz_wmi_tlv_op_rx: unsupported event id 0x1d021
> qwz_dp_htt_htc_t2h_msg_handler: dp_htt rx msg type: 0x0
> --> qwz0: wcn7850 hw2.0 fw 0x110cffff address 00:03:7f:12:5b:c0
> qwz_dp_htt_htc_t2h_msg_handler: dp_htt rx msg type: 0x30
> qwz_dp_htt_htc_t2h_msg_handler: htt event 48 not handled
> qwz0: unhandled qrtr type 6
> 
> That's some good progress!  I'll try to review your diff next.

I'm OK with your diff, and I think we should get it in to make further
progress (re-attached your diff for reference).

ok mglocker@


Index: sys/dev/ic/qwz.c
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v
diff -u -p -r1.22 qwz.c
--- sys/dev/ic/qwz.c	18 Feb 2026 03:10:57 -0000	1.22
+++ sys/dev/ic/qwz.c	29 Mar 2026 19:09:10 -0000
@@ -1006,6 +1006,11 @@ qwz_wmi_init_wcn7850(struct qwz_softc *s
 	config->num_multicast_filter_entries = 0x20;
 	config->num_wow_filters = 0x16;
 	config->num_keep_alive_pattern = 0;
+
+	if (isset(sc->wmi.svc_map, WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT))
+		config->peer_metadata_ver = ATH12K_PEER_METADATA_V1A;
+	else
+		config->peer_metadata_ver = sc->dp.peer_metadata_ver;
 }
 
 void
@@ -1735,6 +1740,7 @@ static const struct ath12k_hw_params ath
 		.qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01) |
 					   BIT(CNSS_PCIE_PERST_NO_PULL_V01),
 		.tx_ring_size = DP_TCL_DATA_RING_SIZE,
+		.rddm_size = 0x780000,
 	},
 };
 
@@ -8340,6 +8346,9 @@ qwz_dp_cc_cleanup(struct qwz_softc *sc)
 	/* First ATH12K_NUM_RX_SPT_PAGES of allocated SPT pages are used for RX */
 	for (i = 0; i < ATH12K_NUM_RX_SPT_PAGES; i++) {
 		rx_descs = dp->spt_info->rxbaddr[i];
+		if (!rx_descs)
+			continue;
+
 		for (j = 0; j < ATH12K_MAX_SPT_ENTRIES; j++) {
 			if (!rx_descs[j].m)
 				continue;
@@ -8364,6 +8373,8 @@ qwz_dp_cc_cleanup(struct qwz_softc *sc)
 		for (i = 0; i < ATH12K_TX_SPT_PAGES_PER_POOL; i++) {
 			tx_spt_page = i + pool_id * ATH12K_TX_SPT_PAGES_PER_POOL;
 			tx_descs = dp->spt_info->txbaddr[tx_spt_page];
+			if (!tx_descs)
+				continue;
 
 			for (j = 0; j < ATH12K_MAX_SPT_ENTRIES; j++) {
 				if (!tx_descs[j].m)
@@ -8381,6 +8392,17 @@ qwz_dp_cc_cleanup(struct qwz_softc *sc)
 		spin_unlock_bh(&dp->tx_desc_lock[pool_id]);
 #endif
 	}
+
+	for (i = 0; i < dp->num_spt_pages; i++) {
+		if (!dp->spt_info[i].mem)
+			continue;
+		qwz_dmamem_free(sc->sc_dmat, dp->spt_info[i].mem);
+		dp->spt_info[i].mem = NULL;
+	}
+
+	free(dp->spt_info, M_DEVBUF, dp->num_spt_pages * sizeof(*dp->spt_info));
+	dp->spt_info = NULL;
+	dp->num_spt_pages = 0;
 }
 
 int
@@ -8543,7 +8565,7 @@ qwz_dp_rx_alloc(struct qwz_softc *sc)
 
 	for (i = 0; i < sc->hw_params.num_rxdma_dst_ring; i++) {
 		ret = qwz_dp_srng_setup(sc, &dp->rxdma_err_dst_ring[i],
-		    HAL_RXDMA_BUF, 0, i, DP_RXDMA_ERR_DST_RING_SIZE);
+		    HAL_RXDMA_DST, 0, i, DP_RXDMA_ERR_DST_RING_SIZE);
 		if (ret) {
 			printf("%s: failed to setup "
 			    "rxdma_err_dst_ring %d\n",
@@ -9913,9 +9935,19 @@ qwz_wmi_tlv_svc_rdy_ext2_parse(struct qw
     uint16_t tag, uint16_t len, const void *ptr, void *data)
 {
 	struct wmi_tlv_svc_rdy_ext2_parse *parse = data;
+	const struct wmi_service_ready_ext2_event *ev;
 	int ret;
 
 	switch (tag) {
+	case WMI_TAG_SERVICE_READY_EXT2_EVENT:
+		if (len < sizeof(*ev))
+			return EPROTO;
+
+		ev = ptr;
+		sc->dp.peer_metadata_ver = FIELD_GET(
+		    WMI_TARGET_CAP_FLAGS_RX_PEER_METADATA_VERSION,
+		    ev->target_cap_flags);
+		break;
 	case WMI_TAG_ARRAY_STRUCT:
 		if (!parse->dma_ring_cap_done) {
 			ret = qwz_wmi_tlv_dma_ring_caps(sc, len, ptr,
@@ -10476,6 +10508,56 @@ qwz_pull_reg_chan_list_ext_update_ev(str
 }
 
 void
+qwz_init_channels_world(struct qwz_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_channel *chan;
+	uint32_t supported_bands = 0;
+	int i;
+
+	/* Same world fallback channels as ath12k reg.c. */
+	static const uint8_t channels_2ghz[] = {
+		1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+	};
+	static const uint8_t channels_5ghz[] = {
+		36, 40, 44, 48, 52, 56, 60, 64,
+		149, 153, 157, 161, 165
+	};
+
+	for (i = 0; i < sc->num_radios; i++)
+		supported_bands |= sc->pdevs[i].cap.supported_bands;
+
+	if (!(supported_bands & (WMI_HOST_WLAN_2G_CAP | WMI_HOST_WLAN_5G_CAP)))
+		supported_bands = WMI_HOST_WLAN_2G_CAP | WMI_HOST_WLAN_5G_CAP;
+
+	memset(ic->ic_channels, 0, sizeof(ic->ic_channels));
+
+	if (supported_bands & WMI_HOST_WLAN_2G_CAP) {
+		for (i = 0; i < nitems(channels_2ghz); i++) {
+			chan = &ic->ic_channels[channels_2ghz[i]];
+			chan->ic_freq = ieee80211_ieee2mhz(channels_2ghz[i],
+			    IEEE80211_CHAN_2GHZ);
+			chan->ic_flags = IEEE80211_CHAN_CCK |
+			    IEEE80211_CHAN_OFDM |
+			    IEEE80211_CHAN_DYN |
+			    IEEE80211_CHAN_2GHZ;
+		}
+	}
+
+	if (supported_bands & WMI_HOST_WLAN_5G_CAP) {
+		for (i = 0; i < nitems(channels_5ghz); i++) {
+			chan = &ic->ic_channels[channels_5ghz[i]];
+			chan->ic_freq = ieee80211_ieee2mhz(channels_5ghz[i],
+			    IEEE80211_CHAN_5GHZ);
+			chan->ic_flags = IEEE80211_CHAN_A |
+			    IEEE80211_CHAN_PASSIVE;
+		}
+	}
+
+	ieee80211_channel_init(&ic->ic_if);
+}
+
+void
 qwz_init_channels(struct qwz_softc *sc, struct cur_regulatory_info *reg_info)
 {
 	struct ieee80211com *ic = &sc->sc_ic;
@@ -10587,6 +10669,7 @@ qwz_reg_chan_list_event(struct qwz_softc
 	if (ret) {
 		printf("%s: failed to extract regulatory info from "
 		    "received event\n", sc->sc_dev.dv_xname);
+		qwz_init_channels_world(sc);
 		goto fallback;
 	}
 
@@ -10599,6 +10682,7 @@ qwz_reg_chan_list_event(struct qwz_softc
 		 */
 		printf("%s: Failed to set the requested Country "
 		    "regulatory setting\n", __func__);
+		qwz_init_channels_world(sc);
 		goto mem_free;
 	}
 
@@ -11706,7 +11790,8 @@ qwz_wmi_tlv_op_rx(struct qwz_softc *sc, 
 		DPRINTF("%s: 0x%x: update fw mem dump\n", __func__, id);
 		break;
 	case WMI_PDEV_SET_HW_MODE_RESP_EVENTID:
-		DPRINTF("%s: 0x%x: set HW mode response event\n", __func__, id);
+		sc->wmi.hw_mode_ready = 1;
+		wakeup(&sc->wmi.hw_mode_ready);
 		break;
 	case WMI_WLAN_FREQ_AVOID_EVENTID:
 		DPRINTF("%s: 0x%x: wlan freq avoid event\n", __func__, id);
@@ -16815,6 +16900,8 @@ void
 qwz_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg,
     struct wmi_resource_config_arg *tg_cfg)
 {
+	memset(wmi_cfg, 0, sizeof(*wmi_cfg));
+
 	wmi_cfg->num_vdevs = tg_cfg->num_vdevs;
 	wmi_cfg->num_peers = tg_cfg->num_peers;
 	wmi_cfg->num_offload_peers = tg_cfg->num_offload_peers;
@@ -16868,20 +16955,19 @@ qwz_wmi_copy_resource_config(struct wmi_
 	wmi_cfg->bpf_instruction_size = tg_cfg->bpf_instruction_size;
 	wmi_cfg->max_bssid_rx_filters = tg_cfg->max_bssid_rx_filters;
 	wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id;
-	wmi_cfg->flag1 = tg_cfg->atf_config | WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64;
+	wmi_cfg->flag1 = tg_cfg->atf_config | WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 |
+	    WMI_RSRC_CFG_FLAG1_ACK_RSSI;
 	wmi_cfg->peer_map_unmap_version = tg_cfg->peer_map_unmap_version;
 	wmi_cfg->sched_params = tg_cfg->sched_params;
 	wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count;
 	wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count;
-#ifdef notyet /* 6 GHz support */
-	wmi_cfg->host_service_flags &=
-	    ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
-	wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported <<
-	    WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT);
-	wmi_cfg->flags2 = WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET;
+	wmi_cfg->flags2 = FIELD_PREP(WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION,
+	    tg_cfg->peer_metadata_ver);
+	wmi_cfg->host_service_flags =
+	    tg_cfg->is_reg_cc_ext_event_supported << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT;
 	wmi_cfg->ema_max_vap_cnt = tg_cfg->ema_max_vap_cnt;
 	wmi_cfg->ema_max_profile_period = tg_cfg->ema_max_profile_period;
-#endif
+	wmi_cfg->flags2 |= WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET;
 }
 
 int
@@ -17046,6 +17132,21 @@ qwz_wmi_wait_for_unified_ready(struct qw
 }
 
 int
+qwz_wmi_wait_for_hw_mode_ready(struct qwz_softc *sc)
+{
+	int ret;
+
+	while (!sc->wmi.hw_mode_ready) {
+		ret = tsleep_nsec(&sc->wmi.hw_mode_ready, 0, "qwzhwmode",
+		    SEC_TO_NSEC(5));
+		if (ret)
+			return -1;
+	}
+
+	return 0;
+}
+
+int
 qwz_wmi_set_hw_mode(struct qwz_softc *sc,
     enum wmi_host_hw_mode_config_type mode)
 {
@@ -17685,12 +17786,20 @@ qwz_core_start(struct qwz_softc *sc)
 
 	/* put hardware to DBS mode */
 	if (sc->hw_params.single_pdev_only) {
+		sc->wmi.hw_mode_ready = 0;
 		ret = qwz_wmi_set_hw_mode(sc, WMI_HOST_HW_MODE_DBS);
 		if (ret) {
 			printf("%s: failed to send dbs mode: %d\n",
 			    __func__, ret);
 			goto err_hif_stop;
 		}
+
+		ret = qwz_wmi_wait_for_hw_mode_ready(sc);
+		if (ret) {
+			printf("%s: failed to receive dbs mode response: %d\n",
+			    __func__, ret);
+			goto err_reo_cleanup;
+		}
 	}
 
 	ret = qwz_dp_tx_htt_h2t_ver_req_msg(sc);
@@ -20255,12 +20364,13 @@ qwz_reg_update_chan_list(struct qwz_soft
 	for (channel = &ic->ic_channels[1]; channel <= lastc; channel++) {
 		if (channel->ic_flags == 0)
 			continue;
-#ifdef notyet
-		/* TODO: Set to true/false based on some condition? */
+		/*
+		 * XXX We do not populate 6 GHz channels here yet.
+		 * Linux sets these scan capability bits unconditionally too.
+		 */
 		ch->allow_ht = true;
 		ch->allow_vht = true;
 		ch->allow_he = true;
-#endif
 		ch->dfs_set = !!(IEEE80211_IS_CHAN_5GHZ(channel) &&
 		    (channel->ic_flags & IEEE80211_CHAN_PASSIVE));
 		ch->is_chan_passive = !!(channel->ic_flags &
@@ -21269,6 +21379,7 @@ qwz_mac_scan_finish(struct qwz_softc *sc
 		sc->scan.roc_freq = 0;
 
 		timeout_del(&sc->scan.timeout);
+		wakeup(&sc->scan.state);
 		if (!sc->scan.is_roc)
 			ieee80211_end_scan(ifp);
 #if 0
@@ -22632,7 +22743,7 @@ qwz_wmi_start_scan_init(struct qwz_softc
 	/* fill bssid_list[0] with 0xff, otherwise bssid and RA will be
 	 * ZEROs in probe request
 	 */
-	IEEE80211_ADDR_COPY(arg->bssid_list[0].addr, etheranyaddr);
+	IEEE80211_ADDR_COPY(arg->bssid_list[0].addr, etherbroadcastaddr);
 }
 
 int
@@ -22834,6 +22945,9 @@ qwz_start_scan(struct qwz_softc *sc, str
 			break;
 		}
 	}
+
+	if (ret == 0 && sc->scan.state == ATH12K_SCAN_IDLE)
+		ret = EIO;
 
 #ifdef notyet
 	spin_lock_bh(&ar->data_lock);
Index: sys/dev/ic/qwzreg.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwzreg.h,v
diff -u -p -r1.11 qwzreg.h
--- sys/dev/ic/qwzreg.h	23 Dec 2024 00:12:44 -0000	1.11
+++ sys/dev/ic/qwzreg.h	29 Mar 2026 18:33:24 -0000
@@ -1957,6 +1957,8 @@ enum wmi_tlv_tag {
 	WMI_TAG_MAX
 };
 
+#define WMI_TAG_SERVICE_READY_EXT2_EVENT	0x334
+
 enum wmi_tlv_service {
 	WMI_TLV_SERVICE_BEACON_OFFLOAD = 0,
 	WMI_TLV_SERVICE_SCAN_OFFLOAD = 1,
@@ -2190,6 +2192,7 @@ enum wmi_tlv_service {
 	WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326,
 	WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357,
 	WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361,
+	WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365,
 
 	/* The third 128 bits */
 	WMI_MAX_EXT2_SERVICE = 384
@@ -2479,6 +2482,7 @@ struct wmi_init_cmd {
 #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5)
 #define WMI_RSRC_CFG_FLAG2_CALC_NEXT_DTIM_COUNT_SET BIT(9)
 #define WMI_RSRC_CFG_FLAG1_ACK_RSSI BIT(18)
+#define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4)
 
 #define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4
 
@@ -2541,7 +2545,6 @@ struct wmi_resource_config {
 	uint32_t sched_params;
 	uint32_t twt_ap_pdev_count;
 	uint32_t twt_ap_sta_count;
-#ifdef notyet /* 6 GHz support */
 	uint32_t max_nlo_ssids;
 	uint32_t num_pkt_filters;
 	uint32_t num_max_sta_vdevs;
@@ -2554,7 +2557,6 @@ struct wmi_resource_config {
 	uint32_t max_rnr_neighbours;
 	uint32_t ema_max_vap_cnt;
 	uint32_t ema_max_profile_period;
-#endif
 } __packed;
 
 struct wmi_service_ready_event {
@@ -2607,6 +2609,19 @@ struct wmi_service_ready_ext_event {
 	uint32_t max_nlo_ssids;
 	uint32_t max_bssid_indicator;
 	uint32_t he_cap_info_ext;
+} __packed;
+
+#define WMI_TARGET_CAP_FLAGS_RX_PEER_METADATA_VERSION	GENMASK(1, 0)
+
+struct wmi_service_ready_ext2_event {
+	uint32_t reg_db_version;
+	uint32_t hw_min_max_tx_power_2ghz;
+	uint32_t hw_min_max_tx_power_5ghz;
+	uint32_t chwidth_num_peer_caps;
+	uint32_t preamble_puncture_bw;
+	uint32_t max_user_per_ppdu_ofdma;
+	uint32_t max_user_per_ppdu_mumimo;
+	uint32_t target_cap_flags;
 } __packed;
 
 struct wmi_soc_mac_phy_hw_mode_caps {
Index: sys/dev/ic/qwzvar.h
===================================================================
RCS file: /home/cvs/src/sys/dev/ic/qwzvar.h,v
diff -u -p -r1.12 qwzvar.h
--- sys/dev/ic/qwzvar.h	7 Jul 2025 00:55:15 -0000	1.12
+++ sys/dev/ic/qwzvar.h	29 Mar 2026 18:33:24 -0000
@@ -257,6 +257,7 @@ struct ath12k_hw_params {
 	bool tcl_ring_retry;
 #endif
 	uint32_t tx_ring_size;
+	uint32_t rddm_size;
 	bool smp2p_wow_exit;
 };
 
@@ -1378,6 +1379,7 @@ struct qwz_wmi_base {
 	uint32_t max_msg_len[QWZ_MAX_RADIOS];
 	int service_ready;
 	int unified_ready;
+	int hw_mode_ready;
 	uint8_t svc_map[howmany(WMI_MAX_EXT2_SERVICE, 8)];
 	int tx_credits;
 	const struct wmi_peer_flags_map *peer_flags;
Index: sys/dev/pci/if_qwz_pci.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_qwz_pci.c,v
diff -u -p -r1.6 if_qwz_pci.c
--- sys/dev/pci/if_qwz_pci.c	9 Dec 2024 09:35:33 -0000	1.6
+++ sys/dev/pci/if_qwz_pci.c	29 Mar 2026 18:33:24 -0000
@@ -370,7 +370,6 @@ struct qwz_pci_softc {
 	struct qwz_dmamem	 *rddm_data;
 	int			 rddm_triggered;
 	struct task		 rddm_task;
-#define	QWZ_RDDM_DUMP_SIZE	0x420000
 
 	struct qwz_dmamem	*chan_ctxt;
 	struct qwz_dmamem	*event_ctxt;
@@ -3287,7 +3286,7 @@ qwz_rddm_prepare(struct qwz_pci_softc *p
 	struct qwz_dmamem *data_adm, *vec_adm;
 	uint32_t seq, reg;
 	uint64_t paddr;
-	const size_t len = QWZ_RDDM_DUMP_SIZE;
+	const size_t len = sc->hw_params.rddm_size;
 	const size_t chunk_size = MHI_DMA_VEC_CHUNK_SIZE;
 	size_t nseg, remain, vec_size;
 	int i;
@@ -3358,9 +3357,9 @@ qwz_rddm_task(void *arg)
 	struct qwz_pci_softc *psc = arg;
 	struct qwz_softc *sc = &psc->sc_sc;
 	uint32_t reg, state = MHI_BHIE_RXVECSTATUS_STATUS_RESET;
-	const size_t len = QWZ_RDDM_DUMP_SIZE;
+	const size_t len = sc->hw_params.rddm_size;
 	int i, timeout;
-	const uint32_t msecs = 100, retries = 20;
+	const uint32_t msecs = 2000, retries = 1000;
 	uint8_t *rddm;
 	struct nameidata nd;
 	struct vnode *vp = NULL;