Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: sys/iwx: support of 160Mhz window at 5Ghz
To:
tech@openbsd.org
Date:
Sun, 29 Mar 2026 15:15:06 +0200

Download raw body.

Thread
On Wed, 25 Mar 2026 11:50:04 +0100,
Stefan Sperling <stsp@stsp.name> wrote:
> 
> On Tue, Mar 24, 2026 at 10:03:20PM +0100, Kirill A. Korinsky wrote:
> > I think it is strong proof that 160Mhz actually works as expected
> 
> This is nice overall. I am happy the Tx side is working well now.
> 
> Can we please try to reduce the amount of unrelated changes in the diff,
> even if they happen to increase performance in your tests?
> 
> Specifically, enabling support for LDPC, STBC, beam-forming, and announcing
> support for larger aggregates in VHT-caps are all unrelated changes.
> 
> I don't know exactly what the consequences of enabling these features are
> and unfortunately I lack the time to dig into them now. But I'm quite sure
> more changes will be required than simply flipping feature bits in VHT caps.
> These features will trigger new code paths in firmware which the driver
> may not yet be prepared to handle, potentially resulting in firmware
> crashes or other instabilities in some setups.
> For STBC we will likely need to configure related parameters in the rate
> selection firmware command, for example. Beamforming might require us
> to handle sounding frames perhaps. Not sure about the others, they will
> also require some consideration.
> 
> I would prefer to enable all of these features one-by-one in isolation
> to make it easier to review the changes and spot any potential regressions.
> 

Yes, moving by small steps is the right way.

Here updated diff which includes only 160MHz, I also spoted one missed point
which related to last non-MIMO fix.

With only this diff my iperf test shows with -P 16:

[SUM]   0.00-30.05  sec  1.76 GBytes   504 Mbits/sec  268540             sender
[SUM]   0.00-30.01  sec  1.72 GBytes   493 Mbits/sec                  receiver

and unifi confirms that laptop is connected with 160Mhz window and RX/Tx
Rate 1.3 / 1.2 Gbps

Ok?

Index: sys/dev/pci/if_iwx.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_iwx.c,v
diff -u -p -r1.225 if_iwx.c
--- sys/dev/pci/if_iwx.c	26 Mar 2026 15:39:04 -0000	1.225
+++ sys/dev/pci/if_iwx.c	29 Mar 2026 13:02:54 -0000
@@ -3207,6 +3207,8 @@ iwx_init_channel_map(struct iwx_softc *s
 			    IEEE80211_CHAN_A;
 		}
 		channel->ic_freq = ieee80211_ieee2mhz(hw_value, flags);
+		channel->ic_xflags &= ~(IEEE80211_CHANX_80MHZ |
+		    IEEE80211_CHANX_160MHZ);
 
 		if (!(ch_flags & IWX_NVM_CHANNEL_ACTIVE))
 			channel->ic_flags |= IEEE80211_CHAN_PASSIVE;
@@ -3221,6 +3223,8 @@ iwx_init_channel_map(struct iwx_softc *s
 			channel->ic_flags |= IEEE80211_CHAN_VHT;
 			if (ch_flags & IWX_NVM_CHANNEL_80MHZ)
 				channel->ic_xflags |= IEEE80211_CHANX_80MHZ;
+			if (ch_flags & IWX_NVM_CHANNEL_160MHZ)
+				channel->ic_xflags |= IEEE80211_CHANX_160MHZ;
 		}
 	}
 }
@@ -3620,6 +3624,10 @@ iwx_phy_ctxt_task(void *arg)
 	else
 		sco = IEEE80211_HTOP0_SCO_SCN;
 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+	    IEEE80211_CHAN_160MHZ_ALLOWED(in->in_ni.ni_chan) &&
+	    ieee80211_node_supports_vht_chan160(ni))
+		vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_160;
+	else if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 	    IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
 	    ieee80211_node_supports_vht_chan80(ni))
 		vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
@@ -5573,6 +5581,12 @@ iwx_get_vht_ctrl_pos(struct ieee80211com
 	uint8_t pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
 
 	switch (primary_idx - center_idx) {
+	case -14:
+		pos = IWX_PHY_VHT_CTRL_POS_4_BELOW;
+		break;
+	case -10:
+		pos = IWX_PHY_VHT_CTRL_POS_3_BELOW;
+		break;
 	case -6:
 		pos = IWX_PHY_VHT_CTRL_POS_2_BELOW;
 		break;
@@ -5585,6 +5599,12 @@ iwx_get_vht_ctrl_pos(struct ieee80211com
 	case 6:
 		pos = IWX_PHY_VHT_CTRL_POS_2_ABOVE;
 		break;
+	case 10:
+		pos = IWX_PHY_VHT_CTRL_POS_3_ABOVE;
+		break;
+	case 14:
+		pos = IWX_PHY_VHT_CTRL_POS_4_ABOVE;
+		break;
 	default:
 		break;
 	}
@@ -5616,7 +5636,10 @@ iwx_phy_ctxt_cmd_uhb_v3_v4(struct iwx_so
 	cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
 	    IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
 	cmd.ci.channel = htole32(ieee80211_chan2ieee(ic, chan));
-	if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+	if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE160;
+	} else if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
 		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
 	} else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
@@ -5676,7 +5699,10 @@ iwx_phy_ctxt_cmd_v3_v4(struct iwx_softc 
 	cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
 	    IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
 	cmd.ci.channel = ieee80211_chan2ieee(ic, chan);
-	if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+	if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE160;
+	} else if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
 		cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
 		cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
 	} else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
@@ -6731,7 +6757,11 @@ iwx_add_sta_cmd(struct iwx_softc *sc, st
 		}
 
 		if (in->in_ni.ni_flags & IEEE80211_NODE_VHT) {
-			if (IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+			if (IEEE80211_CHAN_160MHZ_ALLOWED(in->in_ni.ni_chan) &&
+			    ieee80211_node_supports_vht_chan160(&in->in_ni)) {
+				add_sta_cmd.station_flags |= htole32(
+				    IWX_STA_FLG_FAT_EN_160MHZ);
+			} else if (IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
 			    ieee80211_node_supports_vht_chan80(&in->in_ni)) {
 				add_sta_cmd.station_flags |= htole32(
 				    IWX_STA_FLG_FAT_EN_80MHZ);
@@ -8431,9 +8461,19 @@ iwx_rs_init_v3(struct iwx_softc *sc, str
 		cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
+		if (in->in_phyctxt->vht_chan_width ==
+		    IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+			cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_160] =
+			    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80];
+		}
 		if (iwx_mimo_enabled(sc)) {
 			cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
 			    htole16(iwx_rs_vht_rates(sc, ni, 2));
+			if (in->in_phyctxt->vht_chan_width ==
+			    IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+				cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_160] =
+				    cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80];
+			}
 		}
 	} else if (ni->ni_flags & IEEE80211_NODE_HT) {
 		cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
@@ -8450,6 +8490,9 @@ iwx_rs_init_v3(struct iwx_softc *sc, str
 
 	cfg_cmd.sta_id = IWX_STATION_ID;
 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160)
+		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_160MHZ;
+	else if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
 	else if ((ni->ni_flags & IEEE80211_NODE_HT) &&
@@ -8481,6 +8524,10 @@ iwx_rs_init_v3(struct iwx_softc *sc, str
 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 	    ieee80211_node_supports_vht_sgi80(ni))
 		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
+	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+	    ieee80211_node_supports_vht_sgi160(ni))
+		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_160MHZ);
 
 	cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
 	return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
@@ -8510,9 +8557,19 @@ iwx_rs_init_v4(struct iwx_softc *sc, str
 		cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
+		if (in->in_phyctxt->vht_chan_width ==
+		    IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+			cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_160] =
+			    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80];
+		}
 		if (iwx_mimo_enabled(sc)) {
 			cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80] =
 			    htole16(iwx_rs_vht_rates(sc, ni, 2));
+			if (in->in_phyctxt->vht_chan_width ==
+			    IEEE80211_VHTOP0_CHAN_WIDTH_160) {
+				cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_160] =
+				    cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_MCS_PER_BW_80];
+			}
 		}
 	} else if (ni->ni_flags & IEEE80211_NODE_HT) {
 		cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
@@ -8529,6 +8586,9 @@ iwx_rs_init_v4(struct iwx_softc *sc, str
 
 	cfg_cmd.sta_id = IWX_STATION_ID;
 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160)
+		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_160MHZ;
+	else if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
 		cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
 	else if ((ni->ni_flags & IEEE80211_NODE_HT) &&
@@ -8560,6 +8620,10 @@ iwx_rs_init_v4(struct iwx_softc *sc, str
 	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 	    ieee80211_node_supports_vht_sgi80(ni))
 		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
+	if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+	    in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+	    ieee80211_node_supports_vht_sgi160(ni))
+		cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_160MHZ);
 
 	cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
 	return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
@@ -8944,6 +9008,10 @@ iwx_run(struct iwx_softc *sc)
 		else
 			sco = IEEE80211_HTOP0_SCO_SCN;
 		if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+		    IEEE80211_CHAN_160MHZ_ALLOWED(in->in_ni.ni_chan) &&
+		    ieee80211_node_supports_vht_chan160(ni))
+			vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_160;
+		else if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
 		    IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
 		    ieee80211_node_supports_vht_chan80(ni))
 			vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
@@ -12404,8 +12472,9 @@ iwx_attach(struct device *parent, struct
 	ic->ic_vhtcaps = IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 |
 	    (IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K <<
 	    IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT) |
-	    (IEEE80211_VHTCAP_CHAN_WIDTH_80 <<
-	     IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT) | IEEE80211_VHTCAP_SGI80 |
+	    (IEEE80211_VHTCAP_CHAN_WIDTH_160 <<
+		IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT) |
+	    IEEE80211_VHTCAP_SGI80 | IEEE80211_VHTCAP_SGI160 |
 	    IEEE80211_VHTCAP_RX_ANT_PATTERN | IEEE80211_VHTCAP_TX_ANT_PATTERN;
 
 	ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
Index: sys/net80211/ieee80211.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211.h,v
diff -u -p -r1.65 ieee80211.h
--- sys/net80211/ieee80211.h	19 Mar 2026 16:50:32 -0000	1.65
+++ sys/net80211/ieee80211.h	29 Mar 2026 11:17:09 -0000
@@ -720,7 +720,8 @@ enum {
 #define IEEE80211_HTOP1_NONGF_STA	0x0004
 /* Bit 3 is reserved. */
 #define IEEE80211_HTOP1_OBSS_NONHT_STA	0x0010
-/* Bits 5-15 are reserved. */
+#define IEEE80211_HTOP1_CCFS2_SHIFT	5
+#define IEEE80211_HTOP1_CCFS2_MASK	0x1fe0
 /* Bytes 4-5. */
 /* Bits 0-5 are reserved. */
 #define IEEE80211_HTOP2_DUALBEACON	0x0040
@@ -776,6 +777,8 @@ enum {
 #define IEEE80211_VHTCAP_LINK_ADAPT_MRQ_MFB	3
 #define IEEE80211_VHTCAP_RX_ANT_PATTERN		0x10000000
 #define IEEE80211_VHTCAP_TX_ANT_PATTERN		0x20000000
+#define IEEE80211_VHTCAP_EXT_NSS_BW_SHIFT	30
+#define IEEE80211_VHTCAP_EXT_NSS_BW_MASK	0xc0000000
 
 /*
  * VHT-MCS and NSS map (see 802.11ac-2013 8.4.2.160.3, Figure 8-401bs).
@@ -792,6 +795,7 @@ enum {
 
 #define IEEE80211_VHT_MAX_LGI_MBIT_S_MASK	0x1fff
 #define IEEE80211_VHT_MAX_LGI_MBIT_S_SHIFT	0
+#define IEEE80211_VHT_EXT_NSS_BW_CAPABLE	(1 << 13)
 
 /* The highest number of spatial streams supported by VHT. */
 #define IEEE80211_VHT_NUM_SS	8
Index: sys/net80211/ieee80211_node.c
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.c,v
diff -u -p -r1.212 ieee80211_node.c
--- sys/net80211/ieee80211_node.c	26 Mar 2026 12:15:01 -0000	1.212
+++ sys/net80211/ieee80211_node.c	29 Mar 2026 11:17:09 -0000
@@ -2536,7 +2536,7 @@ ieee80211_setup_htop(struct ieee80211_no
 	if (!ieee80211_40mhz_center_freq_valid(data[0], data[1]))
 		ni->ni_htop0 &= ~IEEE80211_HTOP0_SCO_MASK;
 	ni->ni_htop1 = (data[2] | (data[3] << 8));
-	ni->ni_htop2 = (data[3] | (data[4] << 8));
+	ni->ni_htop2 = (data[4] | (data[5] << 8));
 
 	/*
 	 * According to 802.11-2012 Table 8-130 the Basic MCS set is
@@ -2565,8 +2565,7 @@ ieee80211_setup_vhtcaps(struct ieee80211
 	ni->ni_vht_rx_max_lgi_mbit_s = ((data[6] | (data[7] << 8)) &
 	    IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
 	ni->ni_vht_txmcs = (data[8] | (data[9] << 8));
-	ni->ni_vht_tx_max_lgi_mbit_s = ((data[10] | (data[11] << 8)) &
-	    IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+	ni->ni_vht_tx_max_lgi_mbit_s = (data[10] | (data[11] << 8));
 
 	ni->ni_flags |= IEEE80211_NODE_VHTCAP;
 }
@@ -2603,8 +2602,8 @@ int
 ieee80211_setup_vhtop(struct ieee80211_node *ni, const uint8_t *data,
     uint8_t len, int isprobe)
 {
-	uint8_t sco;
-	int have_40mhz;
+	uint8_t sco, ccfs0, ccfs1, ccfs2, supp_chwidth, ext_nss_bw_supp;
+	int have_40mhz, width, ccf1;
 
 	if (len != 5)
 		return 0;
@@ -2621,14 +2620,66 @@ ieee80211_setup_vhtop(struct ieee80211_n
 	    sco == IEEE80211_HTOP0_SCO_SCB);
 
 	if (have_40mhz && ieee80211_80mhz_center_freq_valid(data[1])) {
-		ni->ni_vht_chan_width = data[0];
-		ni->ni_vht_chan_center_freq_idx0 = data[1];
-
-		/* Only used in non-consecutive 80-80 160MHz configs. */
+		width = data[0];
+		ccfs0 = data[1];
 		if (data[2] && ieee80211_80mhz_center_freq_valid(data[2]))
-			ni->ni_vht_chan_center_freq_idx1 = data[2];
+			ccfs1 = data[2];
 		else
+			ccfs1 = 0;
+		ccfs2 = (ni->ni_htop1 & IEEE80211_HTOP1_CCFS2_MASK) >>
+		    IEEE80211_HTOP1_CCFS2_SHIFT;
+		if (!ieee80211_80mhz_center_freq_valid(ccfs2))
+			ccfs2 = 0;
+
+		supp_chwidth = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
+		    IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
+		ext_nss_bw_supp =
+		    (ni->ni_vhtcaps & IEEE80211_VHTCAP_EXT_NSS_BW_MASK) >>
+		    IEEE80211_VHTCAP_EXT_NSS_BW_SHIFT;
+
+		switch ((supp_chwidth << 4) | ext_nss_bw_supp) {
+		case 0x01:
+		case 0x02:
+		case 0x03:
+			ccf1 = ccfs2;
+			break;
+		case 0x10:
+			ccf1 = ccfs1;
+			break;
+		case 0x11:
+		case 0x12:
+			if (ccfs1 != 0)
+				ccf1 = ccfs1;
+			else
+				ccf1 = ccfs2;
+			break;
+		case 0x13:
+		case 0x20:
+		case 0x23:
+			ccf1 = ccfs1;
+			break;
+		default:
+			ccf1 = 0;
+			break;
+		}
+
+		ni->ni_vht_chan_center_freq_idx0 = ccfs0;
+		ni->ni_vht_chan_center_freq_idx1 = ccfs1;
+
+		if (width == IEEE80211_VHTOP0_CHAN_WIDTH_80 && ccf1 != 0) {
+			int diff;
+
+			diff = abs(ccf1 - ccfs0);
+			if (diff == 8) {
+				ni->ni_vht_chan_center_freq_idx0 = ccf1;
+				ni->ni_vht_chan_center_freq_idx1 = 0;
+				width = IEEE80211_VHTOP0_CHAN_WIDTH_160;
+			}
+		} else if (width == IEEE80211_VHTOP0_CHAN_WIDTH_160) {
 			ni->ni_vht_chan_center_freq_idx1 = 0;
+		}
+
+		ni->ni_vht_chan_width = width;
 	} else {
 		ni->ni_vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
 		ni->ni_vht_chan_center_freq_idx0 = 0;
Index: sys/net80211/ieee80211_node.h
===================================================================
RCS file: /home/cvs/src/sys/net80211/ieee80211_node.h,v
diff -u -p -r1.100 ieee80211_node.h
--- sys/net80211/ieee80211_node.h	26 Mar 2026 12:15:01 -0000	1.100
+++ sys/net80211/ieee80211_node.h	29 Mar 2026 11:17:09 -0000
@@ -609,7 +609,11 @@ ieee80211_node_supports_vht_chan160(stru
 
 	cap_chan_width = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
 	    IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
-	if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160)
+	if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160 &&
+	    cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160_8080 &&
+	    ((ni->ni_vhtcaps & IEEE80211_VHTCAP_EXT_NSS_BW_MASK) == 0 ||
+	    (ni->ni_vht_tx_max_lgi_mbit_s &
+	    IEEE80211_VHT_EXT_NSS_BW_CAPABLE) == 0))
 		return 0;
 
 	op_chan_width = (ni->ni_vht_chan_width &


-- 
wbr, Kirill