From: Kirill A. Korinsky Subject: sys/qwz: add 802.11n with 40Mhz width To: OpenBSD tech Date: Mon, 25 May 2026 23:39:18 +0200 tech@, here support of 40Mhz width for qwz at 802.11n, tested against Unifi with 2.4Ghz and 5Ghz, and AP reports RX and TX as 300 Mbps. 30 seconds iperf shows ~140 Mbits/sec and with -R ~171 Mbits/sec. I don't like +/- 10 for channel center, but better apporach is helper in net80211 which I plan to add as the next step with 11ac and 80Mhz width. Ok? Index: sys/dev/ic/qwz.c =================================================================== RCS file: /home/cvs/src/sys/dev/ic/qwz.c,v diff -u -p -r1.36 qwz.c --- sys/dev/ic/qwz.c 25 May 2026 20:33:58 -0000 1.36 +++ sys/dev/ic/qwz.c 25 May 2026 21:27:51 -0000 @@ -10874,6 +10874,7 @@ qwz_init_channels_world(struct qwz_softc IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ | + IEEE80211_CHAN_40MHZ | IEEE80211_CHAN_HT; } } @@ -10884,6 +10885,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_40MHZ | IEEE80211_CHAN_HT | IEEE80211_CHAN_PASSIVE; } @@ -10931,6 +10933,9 @@ qwz_init_channels(struct qwz_softc *sc, IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_HT; + if ((rule->flags & REGULATORY_CHAN_NO_HT40) == 0) + chan->ic_flags |= + IEEE80211_CHAN_40MHZ; } chnum++; freq = ieee80211_ieee2mhz(chnum, IEEE80211_CHAN_2GHZ); @@ -10966,6 +10971,9 @@ qwz_init_channels(struct qwz_softc *sc, chan->ic_freq = freq; chan->ic_flags = IEEE80211_CHAN_A | IEEE80211_CHAN_HT; + if ((rule->flags & REGULATORY_CHAN_NO_HT40) == 0) + chan->ic_flags |= + IEEE80211_CHAN_40MHZ; if (rule->flags & (REGULATORY_CHAN_RADAR | REGULATORY_CHAN_NO_IR | REGULATORY_CHAN_INDOOR_ONLY)) { @@ -21614,6 +21622,7 @@ qwz_mac_vdev_start_restart(struct qwz_so struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *chan = ic->ic_bss->ni_chan; struct wmi_vdev_start_req_arg arg = {}; + uint8_t sco = IEEE80211_HTOP0_SCO_SCN; int ret = 0; #ifdef notyet lockdep_assert_held(&ar->conf_mutex); @@ -21629,7 +21638,25 @@ qwz_mac_vdev_start_restart(struct qwz_so arg.channel.band_center_freq1 = chan->ic_freq; arg.channel.band_center_freq2 = chan->ic_freq; - if (IEEE80211_IS_CHAN_5GHZ(chan)) + if (ieee80211_node_supports_ht(ic->ic_bss) && + (chan->ic_flags & IEEE80211_CHAN_HT)) { + arg.channel.allow_ht = true; + if (IEEE80211_CHAN_40MHZ_ALLOWED(chan) && + ieee80211_node_supports_ht_chan40(ic->ic_bss)) + sco = ic->ic_bss->ni_htop0 & + IEEE80211_HTOP0_SCO_MASK; + if (sco == IEEE80211_HTOP0_SCO_SCA) { + arg.channel.band_center_freq1 = chan->ic_freq + 10; + arg.channel.mode = IEEE80211_IS_CHAN_5GHZ(chan) ? + MODE_11NA_HT40 : MODE_11NG_HT40; + } else if (sco == IEEE80211_HTOP0_SCO_SCB) { + arg.channel.band_center_freq1 = chan->ic_freq - 10; + arg.channel.mode = IEEE80211_IS_CHAN_5GHZ(chan) ? + MODE_11NA_HT40 : MODE_11NG_HT40; + } else + arg.channel.mode = IEEE80211_IS_CHAN_5GHZ(chan) ? + MODE_11NA_HT20 : MODE_11NG_HT20; + } else if (IEEE80211_IS_CHAN_5GHZ(chan)) arg.channel.mode = MODE_11A; else if (ic->ic_bss->ni_flags & IEEE80211_NODE_ERP) arg.channel.mode = MODE_11G; @@ -24271,6 +24298,7 @@ qwz_peer_assoc_h_phymode(struct qwz_soft { struct ieee80211com *ic = &sc->sc_ic; enum wmi_phy_mode phymode; + uint8_t sco; switch (ic->ic_curmode) { case IEEE80211_MODE_11A: @@ -24283,10 +24311,17 @@ qwz_peer_assoc_h_phymode(struct qwz_soft phymode = MODE_11G; break; case IEEE80211_MODE_11N: - if (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) - phymode = MODE_11NA_HT20; + sco = IEEE80211_HTOP0_SCO_SCN; + if (IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) && + ieee80211_node_supports_ht_chan40(ni)) + sco = ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK; + if (sco == IEEE80211_HTOP0_SCO_SCA || + sco == IEEE80211_HTOP0_SCO_SCB) + phymode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? + MODE_11NA_HT40 : MODE_11NG_HT40; else - phymode = MODE_11NG_HT20; + phymode = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? + MODE_11NA_HT20 : MODE_11NG_HT20; break; default: phymode = MODE_UNKNOWN; @@ -24328,7 +24363,7 @@ qwz_peer_assoc_h_ht(struct qwz_softc *sc { struct ieee80211com *ic = &sc->sc_ic; int i, n; - uint8_t max_nss; + uint8_t max_nss, sco; uint32_t stbc, aggsize, mpdu_density; #ifdef notyet lockdep_assert_held(&ar->conf_mutex); @@ -24336,6 +24371,11 @@ qwz_peer_assoc_h_ht(struct qwz_softc *sc if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) return; + sco = IEEE80211_HTOP0_SCO_SCN; + if (IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) && + ieee80211_node_supports_ht_chan40(ni)) + sco = ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK; + arg->ht_flag = true; aggsize = (ni->ni_ampdu_param & IEEE80211_AMPDU_PARAM_LE); @@ -24349,12 +24389,11 @@ qwz_peer_assoc_h_ht(struct qwz_softc *sc if (ni->ni_htcaps & IEEE80211_HTCAP_LDPC) arg->ldpc_flag = true; -#if 0 - if (sta->deflink.bandwidth >= IEEE80211_STA_RX_BW_40) { + if (sco == IEEE80211_HTOP0_SCO_SCA || + sco == IEEE80211_HTOP0_SCO_SCB) { 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; Index: sys/dev/ic/qwzreg.h =================================================================== RCS file: /home/cvs/src/sys/dev/ic/qwzreg.h,v diff -u -p -r1.15 qwzreg.h --- sys/dev/ic/qwzreg.h 18 May 2026 13:47:32 -0000 1.15 +++ sys/dev/ic/qwzreg.h 25 May 2026 20:54:14 -0000 @@ -13252,6 +13252,7 @@ struct htt_rx_full_monitor_mode_cfg_cmd } __packed; #define WMI_HOST_RC_DS_FLAG 0x01 +#define WMI_HOST_RC_CW40_FLAG 0x02 #define WMI_HOST_RC_SGI_FLAG 0x04 #define WMI_HOST_RC_HT_FLAG 0x08 #define WMI_HOST_RC_TX_STBC_FLAG 0x20 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.11 if_qwz_pci.c --- sys/dev/pci/if_qwz_pci.c 18 May 2026 13:47:32 -0000 1.11 +++ sys/dev/pci/if_qwz_pci.c 25 May 2026 20:54:14 -0000 @@ -969,13 +969,14 @@ qwz_pci_attach(struct device *parent, st 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 |= + ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_SGI40 | + IEEE80211_HTCAP_CBW20_40 | IEEE80211_HTCAP_AMSDU7935 | (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 */); + ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED; memset(ic->ic_sup_mcs, 0, sizeof(ic->ic_sup_mcs)); ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */ -- wbr, Kirill