Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
sys/iwx: support of 160Mhz window at 5Ghz
To:
OpenBSD tech <tech@openbsd.org>
Date:
Sat, 21 Mar 2026 03:06:50 +0100

Download raw body.

Thread
  • Kirill A. Korinsky:

    sys/iwx: support of 160Mhz window at 5Ghz

tech@,

here a trivial diff which brings supports of 160Mhz windows.

Tested with 80Mhz and 160Mhz windows on

iwx0 at pci0 dev 20 function 3 "Intel Wi-Fi 6 AX201" rev 0x00, msix
iwx0: hw rev 0x350, fw 77.30b1cbd8.0, address 98:8d:46:21:2b:6d

with unifi nano AP with enabled roaming with firmware 6.7.41.15623

Ok?


Index: sys/dev/pci/if_iwx.c
===================================================================
RCS file: /home/cvs/src/sys/dev/pci/if_iwx.c,v
diff -u -p -r1.223 if_iwx.c
--- sys/dev/pci/if_iwx.c	14 Mar 2026 15:37:44 -0000	1.223
+++ sys/dev/pci/if_iwx.c	20 Mar 2026 23:32:57 -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);
@@ -8433,6 +8463,13 @@ iwx_rs_init_v3(struct iwx_softc *sc, str
 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
 		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_1][IWX_TLC_MCS_PER_BW_160] =
+			    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80];
+			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;
 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
@@ -8446,6 +8483,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) &&
@@ -8477,6 +8517,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);
@@ -8508,6 +8552,13 @@ iwx_rs_init_v4(struct iwx_softc *sc, str
 		    htole16(iwx_rs_vht_rates(sc, ni, 1));
 		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_1][IWX_TLC_MCS_PER_BW_160] =
+			    cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80];
+			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;
 		cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_MCS_PER_BW_80] =
@@ -8521,6 +8572,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) &&
@@ -8552,6 +8606,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);
@@ -8936,6 +8994,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;
@@ -12380,8 +12442,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;


-- 
wbr, Kirill