From: Kirill A. Korinsky Subject: sys/iwx: support of 160Mhz window at 5Ghz To: OpenBSD tech Date: Sat, 21 Mar 2026 03:06:50 +0100 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