From: Kirill A. Korinsky Subject: sys/qwz: initialize on Snapdragon but fails on scan To: OpenBSD tech Date: Sun, 29 Mar 2026 21:23:36 +0200 tech@, I'd like to share some work which I pocking around with attempt to move forward with qwz driver. I've attached 3 diffs where: 1. fixed a copy and typo in DTS for my Honor machine; 2. updated qwz firmware to the last version; 3. a bit of work in qwz. Right now, after boot qwz reads like this: qwz0: flags=8802 mtu 1500 lladdr 3c:0a:f3:d3:55:c5 index 1 priority 4 llprio 3 groups: wlan media: IEEE802.11 autoselect status: no network ieee80211: nwid "" and attempt to up an interface leads to firmware crash. Scope of qwz work: 1. Fixed a crash on my machine which was suggested as: https://marc.info/?l=openbsd-tech&m=177410294328488&w=2 2. Implemented world reg domain like ath12k 3. Fully filled flags and follow ath12k logic with peer-metadata 4. Increased RDDM size and synced timeouts with ath12k This work was like read how ath12k works, and figure out what we do the wrong way. Right now qwz works more or less the same way, and I run out of ideas why it crashing. Any suggestion and ideas are welcome. Also, I'd like to commit all three diffs, because it safe and qwz is disabled anyway. Anyone willing to OK it? -- wbr, Kirill 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; Index: sysutils/firmware/qwz/Makefile =================================================================== RCS file: /home/cvs/ports/sysutils/firmware/qwz/Makefile,v diff -u -p -r1.4 Makefile --- sysutils/firmware/qwz/Makefile 24 Jul 2025 20:10:52 -0000 1.4 +++ sysutils/firmware/qwz/Makefile 21 Mar 2026 20:31:40 -0000 @@ -1,6 +1,6 @@ FW_DRIVER= qwz -FW_VER= 20250708 +FW_VER= 20260309 DISTNAME= linux-firmware-${FW_VER} EXTRACT_SUFX= .tar.xz Index: sysutils/firmware/qwz/distinfo =================================================================== RCS file: /home/cvs/ports/sysutils/firmware/qwz/distinfo,v diff -u -p -r1.4 distinfo --- sysutils/firmware/qwz/distinfo 24 Jul 2025 20:10:52 -0000 1.4 +++ sysutils/firmware/qwz/distinfo 21 Mar 2026 20:39:28 -0000 @@ -1,2 +1,2 @@ -SHA256 (firmware/linux-firmware-20250708.tar.xz) = bz7+5/YAwgH5stZ1iJpMzbjP5W4NKDZBeW7RDmTHIEc= -SIZE (firmware/linux-firmware-20250708.tar.xz) = 530333772 +SHA256 (firmware/linux-firmware-20260309.tar.xz) = x0zG9WK1itW8aysAphq8KcnkngYSbnujT7ypko4HqWw= +SIZE (firmware/linux-firmware-20260309.tar.xz) = 610973936 Index: sysutils/firmware/arm64-qcom-dtb/Makefile =================================================================== RCS file: /home/cvs/ports/sysutils/firmware/arm64-qcom-dtb/Makefile,v diff -u -p -r1.27 Makefile --- sysutils/firmware/arm64-qcom-dtb/Makefile 11 Jan 2026 13:36:05 -0000 1.27 +++ sysutils/firmware/arm64-qcom-dtb/Makefile 23 Mar 2026 16:20:35 -0000 @@ -1,5 +1,6 @@ FW_DRIVER= arm64-qcom-dtb FW_VER= 2.7 +REVISION= 0 DISTNAME= devicetree-rebasing-6.17-dts Index: sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-honor-magicbook-art-14_dts =================================================================== RCS file: /home/cvs/ports/sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-honor-magicbook-art-14_dts,v diff -u -p -r1.2 patch-src_arm64_qcom_x1e80100-honor-magicbook-art-14_dts --- sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-honor-magicbook-art-14_dts 21 Sep 2025 11:11:21 -0000 1.2 +++ sysutils/firmware/arm64-qcom-dtb/patches/patch-src_arm64_qcom_x1e80100-honor-magicbook-art-14_dts 28 Mar 2026 23:37:40 -0000 @@ -1,7 +1,7 @@ Index: src/arm64/qcom/x1e80100-honor-magicbook-art-14.dts --- src/arm64/qcom/x1e80100-honor-magicbook-art-14.dts.orig +++ src/arm64/qcom/x1e80100-honor-magicbook-art-14.dts -@@ -0,0 +1,1517 @@ +@@ -0,0 +1,1516 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. @@ -1030,7 +1030,7 @@ Index: src/arm64/qcom/x1e80100-honor-mag + compatible = "pci17cb,1107"; + reg = <0x10000 0x0 0x0 0x0 0x0>; + -+ qcom,ath12k-calibration-variant = "LES790"; ++ qcom,ath12k-calibration-variant = "HO_MoorO"; + }; +}; + @@ -1518,4 +1518,3 @@ Index: src/arm64/qcom/x1e80100-honor-mag +&usb_2_dwc3 { + dr_mode = "host"; +}; -+