Download raw body.
sys/qwz: add 802.11n support
On Mon, May 18, 2026 at 01:28:15AM +0200, Kirill A. Korinsky wrote:
> tech@,
>
> I'd like to bring support of 802.11n for qwz.
>
> It needs jus advertise HT capabilities and pass HT/QoS peer association.
>
> Tested on my honor, works.
>
> Ok?
Looks good to me but I cannot test.
This is mostly lifted from qwx, right?
ok stsp@
>
> Index: sys/dev/ic/qwz.c
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v
> diff -u -p -r1.28 qwz.c
> --- sys/dev/ic/qwz.c 15 May 2026 19:02:12 -0000 1.28
> +++ sys/dev/ic/qwz.c 17 May 2026 23:15:32 -0000
> @@ -10676,7 +10676,8 @@ qwz_init_channels_world(struct qwz_softc
> chan->ic_flags = IEEE80211_CHAN_CCK |
> IEEE80211_CHAN_OFDM |
> IEEE80211_CHAN_DYN |
> - IEEE80211_CHAN_2GHZ;
> + IEEE80211_CHAN_2GHZ |
> + IEEE80211_CHAN_HT;
> }
> }
>
> @@ -10686,6 +10687,7 @@ qwz_init_channels_world(struct qwz_softc
> chan->ic_freq = ieee80211_ieee2mhz(channels_5ghz[i],
> IEEE80211_CHAN_5GHZ);
> chan->ic_flags = IEEE80211_CHAN_A |
> + IEEE80211_CHAN_HT |
> IEEE80211_CHAN_PASSIVE;
> }
> }
> @@ -10730,7 +10732,8 @@ qwz_init_channels(struct qwz_softc *sc,
> chan->ic_flags = IEEE80211_CHAN_CCK |
> IEEE80211_CHAN_OFDM |
> IEEE80211_CHAN_DYN |
> - IEEE80211_CHAN_2GHZ;
> + IEEE80211_CHAN_2GHZ |
> + IEEE80211_CHAN_HT;
> }
> chnum++;
> freq = ieee80211_ieee2mhz(chnum, IEEE80211_CHAN_2GHZ);
> @@ -10764,7 +10767,8 @@ qwz_init_channels(struct qwz_softc *sc,
> chan->ic_flags = 0;
> } else {
> chan->ic_freq = freq;
> - chan->ic_flags = IEEE80211_CHAN_A;
> + chan->ic_flags = IEEE80211_CHAN_A |
> + IEEE80211_CHAN_HT;
> if (rule->flags & (REGULATORY_CHAN_RADAR |
> REGULATORY_CHAN_NO_IR |
> REGULATORY_CHAN_INDOOR_ONLY)) {
> @@ -20857,14 +20861,30 @@ qwz_reg_update_chan_list(struct qwz_soft
> int num_channels = 0;
> size_t params_size;
> int ret;
> + int scan_2ghz = 1, scan_5ghz = 1;
> #if 0
> if (ar->state == ATH12K_STATE_RESTARTING)
> return 0;
> #endif
> + /*
> + * Scan an appropriate subset of channels if we are running
> + * in a fixed, user-specified phy mode.
> + */
> + if (IFM_MODE(ic->ic_media.ifm_cur->ifm_media) != IFM_AUTO) {
> + if (ic->ic_curmode == IEEE80211_MODE_11A)
> + scan_2ghz = 0;
> + if (ic->ic_curmode == IEEE80211_MODE_11B ||
> + ic->ic_curmode == IEEE80211_MODE_11G)
> + scan_5ghz = 0;
> + }
> +
> lastc = &ic->ic_channels[IEEE80211_CHAN_MAX];
> for (channel = &ic->ic_channels[1]; channel <= lastc; channel++) {
> if (channel->ic_flags == 0)
> continue;
> + if ((!scan_2ghz && IEEE80211_IS_CHAN_2GHZ(channel)) ||
> + (!scan_5ghz && IEEE80211_IS_CHAN_5GHZ(channel)))
> + continue;
> num_channels++;
> }
>
> @@ -20890,6 +20910,9 @@ qwz_reg_update_chan_list(struct qwz_soft
> for (channel = &ic->ic_channels[1]; channel <= lastc; channel++) {
> if (channel->ic_flags == 0)
> continue;
> + if ((!scan_2ghz && IEEE80211_IS_CHAN_2GHZ(channel)) ||
> + (!scan_5ghz && IEEE80211_IS_CHAN_5GHZ(channel)))
> + continue;
> /*
> * XXX We do not populate 6 GHz channels here yet.
> * Linux sets these scan capability bits unconditionally too.
> @@ -20910,13 +20933,24 @@ qwz_reg_update_chan_list(struct qwz_soft
> ch->maxregpower = ch->maxpower;
> ch->antennamax = 0;
>
> - /* TODO: Use appropriate phymodes */
> - if (IEEE80211_IS_CHAN_A(channel))
> + switch (IFM_MODE(ic->ic_media.ifm_cur->ifm_media)) {
> + case IFM_IEEE80211_11A:
> ch->phy_mode = MODE_11A;
> - else if (IEEE80211_IS_CHAN_G(channel))
> + break;
> + case IFM_IEEE80211_11G:
> ch->phy_mode = MODE_11G;
> - else
> + break;
> + case IFM_IEEE80211_11B:
> ch->phy_mode = MODE_11B;
> + break;
> + case IFM_IEEE80211_11N:
> + default:
> + if (IEEE80211_IS_CHAN_A(channel))
> + ch->phy_mode = MODE_11NA_HT20;
> + else
> + ch->phy_mode = MODE_11NG_HT20;
> + break;
> + }
> #ifdef notyet
> if (channel->band == NL80211_BAND_6GHZ &&
> cfg80211_channel_is_psc(channel))
> @@ -21052,6 +21086,7 @@ int
> qwz_mac_op_start(struct qwz_pdev *pdev)
> {
> struct qwz_softc *sc = pdev->sc;
> + struct ieee80211com *ic = &sc->sc_ic;
> int ret;
>
> ret = qwz_wmi_pdev_set_param(sc, WMI_PDEV_PARAM_PMF_QOS, 1,
> @@ -21103,6 +21138,15 @@ qwz_mac_op_start(struct qwz_pdev *pdev)
>
> qwz_set_antenna(pdev, pdev->cap.tx_chain_mask, pdev->cap.rx_chain_mask);
>
> + memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs));
> + ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */
> + if (sc->num_rx_chains > 1)
> + ic->ic_sup_mcs[1] = 0xff; /* MCS 8-15 */
> + if (sc->num_rx_chains > 2)
> + ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */
> + if (sc->num_rx_chains > 3)
> + ic->ic_sup_mcs[3] = 0xff; /* MCS 24-31 */
> +
> /* TODO: Do we need to enable ANI? */
>
> ret = qwz_reg_update_chan_list(sc, pdev->pdev_id);
> @@ -21340,21 +21384,12 @@ qwz_mac_vdev_start_restart(struct qwz_so
> arg.channel.band_center_freq1 = chan->ic_freq;
> arg.channel.band_center_freq2 = chan->ic_freq;
>
> - switch (ic->ic_curmode) {
> - case IEEE80211_MODE_11A:
> + if (IEEE80211_IS_CHAN_5GHZ(chan))
> arg.channel.mode = MODE_11A;
> - break;
> - case IEEE80211_MODE_11B:
> - arg.channel.mode = MODE_11B;
> - break;
> - case IEEE80211_MODE_11G:
> + else if (ic->ic_bss->ni_flags & IEEE80211_NODE_ERP)
> arg.channel.mode = MODE_11G;
> - break;
> - default:
> - printf("%s: unsupported phy mode %d\n",
> - sc->sc_dev.dv_xname, ic->ic_curmode);
> - return ENOTSUP;
> - }
> + else
> + arg.channel.mode = MODE_11B;
>
> arg.channel.min_power = 0;
> arg.channel.max_power = 20; /* XXX */
> @@ -23993,6 +24028,12 @@ qwz_peer_assoc_h_phymode(struct qwz_soft
> case IEEE80211_MODE_11G:
> phymode = MODE_11G;
> break;
> + case IEEE80211_MODE_11N:
> + if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan))
> + phymode = MODE_11NA_HT20;
> + else
> + phymode = MODE_11NG_HT20;
> + break;
> default:
> phymode = MODE_UNKNOWN;
> break;
> @@ -24004,6 +24045,114 @@ qwz_peer_assoc_h_phymode(struct qwz_soft
> arg->peer_phymode = phymode;
> }
>
> +uint8_t
> +qwz_parse_mpdudensity(uint8_t mpdudensity)
> +{
> + switch (mpdudensity) {
> + case 0:
> + return 0;
> + case 1:
> + case 2:
> + case 3:
> + return 1;
> + case 4:
> + return 2;
> + case 5:
> + return 4;
> + case 6:
> + return 8;
> + case 7:
> + return 16;
> + default:
> + return 0;
> + }
> +}
> +
> +void
> +qwz_peer_assoc_h_ht(struct qwz_softc *sc, struct ieee80211_node *ni,
> + struct peer_assoc_params *arg)
> +{
> + struct ieee80211com *ic = &sc->sc_ic;
> + int i, n;
> + uint8_t max_nss;
> + uint32_t stbc, aggsize, mpdu_density;
> +#ifdef notyet
> + lockdep_assert_held(&ar->conf_mutex);
> +#endif
> + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
> + return;
> +
> + arg->ht_flag = true;
> +
> + aggsize = (ni->ni_ampdu_param & IEEE80211_AMPDU_PARAM_LE);
> + arg->peer_max_mpdu = (1 << (13 + aggsize)) - 1;
> +
> + mpdu_density = (ni->ni_ampdu_param & IEEE80211_AMPDU_PARAM_SS) >> 2;
> + arg->peer_mpdu_density = qwz_parse_mpdudensity(mpdu_density);
> +
> + arg->peer_ht_caps = ni->ni_htcaps;
> + arg->peer_rate_caps |= WMI_HOST_RC_HT_FLAG;
> +
> + if (ni->ni_htcaps & IEEE80211_HTCAP_LDPC)
> + arg->ldpc_flag = true;
> +#if 0
> + if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) {
> + arg->bw_40 = true;
> + arg->peer_rate_caps |= WMI_HOST_RC_CW40_FLAG;
> + }
> +#endif
> + if (ieee80211_node_supports_ht_sgi20(ni) ||
> + ieee80211_node_supports_ht_sgi40(ni))
> + arg->peer_rate_caps |= WMI_HOST_RC_SGI_FLAG;
> +
> + if (ni->ni_htcaps & IEEE80211_HTCAP_TXSTBC) {
> + arg->peer_rate_caps |= WMI_HOST_RC_TX_STBC_FLAG;
> + arg->stbc_flag = true;
> + }
> +
> + if (ni->ni_htcaps & IEEE80211_HTCAP_TXSTBC) {
> + stbc = ni->ni_htcaps & IEEE80211_HTCAP_RXSTBC_MASK;
> + stbc = stbc >> IEEE80211_HTCAP_RXSTBC_SHIFT;
> + stbc = stbc << WMI_HOST_RC_RX_STBC_FLAG_S;
> + arg->peer_rate_caps |= stbc;
> + arg->stbc_flag = true;
> + }
> +
> + if (ni->ni_rxmcs[1] && ni->ni_rxmcs[2])
> + arg->peer_rate_caps |= WMI_HOST_RC_TS_FLAG;
> + else if (ni->ni_rxmcs[1])
> + arg->peer_rate_caps |= WMI_HOST_RC_DS_FLAG;
> +
> + for (i = 0, n = 0, max_nss = 0; i < nitems(ni->ni_rxmcs) * 8; i++)
> + if ((ic->ic_sup_mcs[i / 8] & BIT(i % 8)) &&
> + (ni->ni_rxmcs[i / 8] & BIT(i % 8))) {
> + max_nss = (i / 8) + 1;
> + arg->peer_ht_rates.rates[n++] = i;
> + }
> +
> + if (n == 0) {
> + arg->peer_ht_rates.num_rates = 8;
> + for (i = 0; i < arg->peer_ht_rates.num_rates; i++)
> + arg->peer_ht_rates.rates[i] = i;
> + } else {
> + arg->peer_ht_rates.num_rates = n;
> + arg->peer_nss = max_nss;
> + }
> +
> + DNPRINTF(QWZ_D_MAC, "%s: ht peer %s mcs cnt %d nss %d\n", __func__,
> + ether_sprintf(arg->peer_mac), arg->peer_ht_rates.num_rates,
> + arg->peer_nss);
> +}
> +
> +void
> +qwz_peer_assoc_h_qos(struct ieee80211_node *ni, struct peer_assoc_params *arg)
> +{
> + if (ni->ni_flags & IEEE80211_NODE_QOS) {
> + arg->is_wme_set = 1;
> + arg->qos_flag = 1;
> + }
> +}
> +
> void
> qwz_peer_assoc_prepare(struct qwz_softc *sc, struct qwz_vif *arvif,
> struct ieee80211_node *ni, struct peer_assoc_params *arg, int reassoc)
> @@ -24015,12 +24164,12 @@ qwz_peer_assoc_prepare(struct qwz_softc
> qwz_peer_assoc_h_crypto(sc, arvif, ni, arg);
> qwz_peer_assoc_h_rates(ni, arg);
> qwz_peer_assoc_h_phymode(sc, ni, arg);
> + qwz_peer_assoc_h_ht(sc, ni, arg);
> + qwz_peer_assoc_h_qos(ni, arg);
> #if 0
> - qwz_peer_assoc_h_ht(sc, arvif, ni, arg);
> qwz_peer_assoc_h_vht(sc, arvif, ni, arg);
> qwz_peer_assoc_h_he(sc, arvif, ni, arg);
> qwz_peer_assoc_h_he_6ghz(sc, arvif, ni, arg);
> - qwz_peer_assoc_h_qos(sc, arvif, ni, arg);
> qwz_peer_assoc_h_smps(ni, arg);
> #endif
> #if 0
> Index: sys/dev/ic/qwzreg.h
> ===================================================================
> RCS file: /home/cvs/src/sys/dev/ic/qwzreg.h,v
> diff -u -p -r1.14 qwzreg.h
> --- sys/dev/ic/qwzreg.h 14 May 2026 16:17:21 -0000 1.14
> +++ sys/dev/ic/qwzreg.h 17 May 2026 23:17:27 -0000
> @@ -13251,6 +13251,13 @@ struct htt_rx_full_monitor_mode_cfg_cmd
> uint32_t cfg;
> } __packed;
>
> +#define WMI_HOST_RC_DS_FLAG 0x01
> +#define WMI_HOST_RC_SGI_FLAG 0x04
> +#define WMI_HOST_RC_HT_FLAG 0x08
> +#define WMI_HOST_RC_TX_STBC_FLAG 0x20
> +#define WMI_HOST_RC_RX_STBC_FLAG_S 6
> +#define WMI_HOST_RC_TS_FLAG 0x200
> +
> /* HTT message target->host */
>
> enum htt_t2h_msg_type {
> 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.10 if_qwz_pci.c
> --- sys/dev/pci/if_qwz_pci.c 15 May 2026 19:02:12 -0000 1.10
> +++ sys/dev/pci/if_qwz_pci.c 17 May 2026 23:15:10 -0000
> @@ -953,9 +953,7 @@ qwz_pci_attach(struct device *parent, st
>
> /* Set device capabilities. */
> ic->ic_caps =
> -#if 0
> IEEE80211_C_QOS | IEEE80211_C_TX_AMPDU | /* A-MPDU */
> -#endif
> IEEE80211_C_ADDBA_OFFLOAD | /* device sends ADDBA/DELBA frames */
> IEEE80211_C_WEP | /* WEP */
> IEEE80211_C_RSN | /* WPA/RSN */
> @@ -970,6 +968,17 @@ qwz_pci_attach(struct device *parent, st
> ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
> ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
> ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
> +
> + ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_AMSDU7935;
> + ic->ic_htcaps |=
> + (IEEE80211_HTCAP_SMPS_DIS << IEEE80211_HTCAP_SMPS_SHIFT);
> + ic->ic_htxcaps = 0;
> + ic->ic_txbfcaps = 0;
> + ic->ic_aselcaps = 0;
> + ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_NONE | 0x3 /* 64k */);
> +
> + memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs));
> + ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */
>
> /* IBSS channel undefined for now. */
> ic->ic_ibss_chan = &ic->ic_channels[1];
>
> --
> wbr, Kirill
>
>
sys/qwz: add 802.11n support