From: Marcus Glocker Subject: Re: sys/qwz: initialize on Snapdragon but fails on scan To: "Kirill A. Korinsky" Cc: tech@openbsd.org Date: Sun, 12 Apr 2026 19:25:50 +0200 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;