From: Kirill A. Korinsky Subject: Re: sys/qwz: handle QoS in native WiFi frames To: Marcus Glocker Cc: tech@openbsd.org Date: Sat, 23 May 2026 12:29:27 +0200 On Fri, 22 May 2026 21:13:04 +0200, Marcus Glocker wrote: > > > + memcpy(decap_hdr, wh, hdrlen); > > + wh = (struct ieee80211_frame *)decap_hdr; > > + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; > > + wh->i_fc[1] &= ~IEEE80211_FC1_ORDER; > > + qos_ctl = mpdu->qos_ctrl; > > The Linux ath12k driver is only rebuilding the qos_ctl field from the > TID, plus applying the optional mesh bit: > > qos_ctl = rxcb->tid; > if (rx_info->mesh_ctrl_present) > qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT; > > While you are copying the entire hardware captured QoS field. > Is there a specific reason for that? > > For example, the hardware should already de-aggregate A-MSDU in to > separate MSDUs, and a copied over A-MSDU bit could make net80211 > mis-parse a plain MSDU as aggregated. Thanks! It was leftover from very agressive tests, cleaned up. I also added missed *mp = NULL, thanks. And on my setup it Unifi stop says that that AP/Client signal balance is poor, and I don't see this errors on boot anymore: qwz0: failed to setup rxd tid queue for tid 2: 12 qwz0: failed to setup dp for peer b6:fb:e4:9b:30:cd on vdev 0 (12) Ok? Index: sys/dev/ic/qwz.c =================================================================== RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v diff -u -p -r1.33 qwz.c --- sys/dev/ic/qwz.c 19 May 2026 21:15:21 -0000 1.33 +++ sys/dev/ic/qwz.c 23 May 2026 10:27:30 -0000 @@ -15116,13 +15116,68 @@ void qwz_dp_rx_h_undecap_nwifi(struct qwz_softc *sc, struct qwz_rx_msdu *msdu, uint8_t *first_hdr, enum hal_encrypt_type enctype) { - /* - * This function will need to do some work once we are receiving - * aggregated frames. For now, it needs to do nothing. - */ + struct rx_mpdu_start_qcn9274 *mpdu; + struct ieee80211_frame *wh; + struct mbuf *m = msdu->m; + uint8_t decap_hdr[IEEE80211_MAX_FRAME_HDR_LEN]; + size_t hdrlen; + uint16_t qos_ctl; + + if (m == NULL) + return; + + if (m->m_len < sizeof(*wh) && + (m = m_pullup(m, sizeof(*wh))) == NULL) { + msdu->m = NULL; + return; + } + msdu->m = m; + + mpdu = &msdu->rx_desc->u.wcn7850.mpdu_start; + wh = mtod(m, struct ieee80211_frame *); + if ((mpdu->info6 & RX_MPDU_START_INFO6_NON_QOS) || + ieee80211_has_qos(wh)) + return; + + hdrlen = ieee80211_get_hdrlen(wh); + if (hdrlen > sizeof(decap_hdr)) + return; + + if (m->m_len < hdrlen && + (m = m_pullup(m, hdrlen)) == NULL) { + msdu->m = NULL; + return; + } + msdu->m = m; - if (!msdu->is_first_msdu) - printf("%s: not implemented\n", __func__); + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + if (hdrlen > sizeof(decap_hdr)) + return; + + memcpy(decap_hdr, wh, hdrlen); + wh = (struct ieee80211_frame *)decap_hdr; + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS; + wh->i_fc[1] &= ~IEEE80211_FC1_ORDER; + qos_ctl = htole16(sc->hal_rx_ops->rx_desc_get_mpdu_tid(msdu->rx_desc) & + IEEE80211_QOS_TID); + + m_adj(m, hdrlen); + + M_PREPEND(m, sizeof(qos_ctl), M_DONTWAIT); + if (m == NULL) { + msdu->m = NULL; + return; + } + memcpy(mtod(m, void *), &qos_ctl, sizeof(qos_ctl)); + + M_PREPEND(m, hdrlen, M_DONTWAIT); + if (m == NULL) { + msdu->m = NULL; + return; + } + msdu->m = m; + memcpy(mtod(m, void *), decap_hdr, hdrlen); } void @@ -23076,6 +23131,50 @@ qwz_dp_tx_get_tid(struct mbuf *m) return ieee80211_get_qos(wh) & IEEE80211_QOS_TID; } +int +qwz_dp_tx_encap_nwifi(struct mbuf **mp) +{ + struct mbuf *m = *mp; + struct ieee80211_frame *wh; + uint8_t *qos_ctl; + int hdrlen; + + if (m->m_len < sizeof(*wh) && + (m = m_pullup(m, sizeof(*wh))) == NULL) { + *mp = NULL; + return ENOBUFS; + } + + wh = mtod(m, struct ieee80211_frame *); + if (!ieee80211_has_qos(wh)) { + *mp = m; + return 0; + } + + hdrlen = ieee80211_get_hdrlen(wh); + if (m->m_len < hdrlen && + (m = m_pullup(m, hdrlen)) == NULL) { + *mp = NULL; + return ENOBUFS; + } + + wh = mtod(m, struct ieee80211_frame *); + if (ieee80211_has_addr4(wh)) + qos_ctl = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos; + else + qos_ctl = ((struct ieee80211_qosframe *)wh)->i_qos; + + memmove(mtod(m, uint8_t *) + sizeof(uint16_t), mtod(m, uint8_t *), + qos_ctl - mtod(m, uint8_t *)); + m_adj(m, sizeof(uint16_t)); + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] &= ~IEEE80211_FC0_SUBTYPE_QOS; + + *mp = m; + return 0; +} + /* * Build a WCN7850 / ath12k WiFi7 TCL data descriptor. Encap/encrypt/ * search settings live in the bank addressed by ti->bank_id; the @@ -23272,28 +23371,14 @@ qwz_dp_tx(struct qwz_softc *sc, struct q ti.flags1 |= FIELD_PREP(HAL_TCL_DATA_CMD_INFO3_TID_OVERWRITE, 1); ti.tid = qwz_dp_tx_get_tid(m); -#if 0 - switch (ti.encap_type) { - case HAL_TCL_ENCAP_TYPE_NATIVE_WIFI: - ath12k_dp_tx_encap_nwifi(skb); - break; - case HAL_TCL_ENCAP_TYPE_RAW: - if (!test_bit(ATH12K_FLAG_RAW_MODE, &ab->dev_flags)) { - ret = -EINVAL; - goto fail_remove_idr; + if (ti.encap_type == HAL_TCL_ENCAP_TYPE_NATIVE_WIFI) { + ret = qwz_dp_tx_encap_nwifi(&m); + if (ret) { + m_freem(m); + return ret; } - break; - case HAL_TCL_ENCAP_TYPE_ETHERNET: - /* no need to encap */ - break; - case HAL_TCL_ENCAP_TYPE_802_3: - default: - /* TODO: Take care of other encap modes as well */ - ret = -EINVAL; - atomic_inc(&ab->soc_stats.tx_err.misc_fail); - goto fail_remove_idr; } -#endif + ret = bus_dmamap_load_mbuf(sc->sc_dmat, tx_data->map, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT); if (ret && ret != EFBIG) { -- wbr, Kirill