Re: qwx: add 40MHz channels and fix 11n phy mode

Mike Larkin <[email protected]>
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Thu, May 28, 2026 at 09:36:21PM +0200, Stefan Sperling wrote:
> Make 40 MHz channels work on qwx, and also fix phy mode settings
> configured in firmware for 11n mode.
>
> When qwx_peer_assoc_h_phymode() is called via qwx_assoc() we always
> set an 11a/b/g phy mode because 11n negotiation happens later.
> We need to update the peer in qwx_run() to configure 11n mode.
> For reference, compare how iwx does things.
>
> This depends on the the other two qwx diffs I sent earlier today.
>
> ok?
>

this reads ok to me, so ok mlarkin. I didn't test though; my qwx machine
can't be rebooted at the moment. Will let you know if i see fallout later.

> M  sys/dev/ic/qwx.c          |  176+  17-
> M  sys/dev/ic/qwxvar.h       |    3+   0-
> M  sys/dev/pci/if_qwx_pci.c  |    3+   3-
>
> 3 files changed, 182 insertions(+), 20 deletions(-)
>
> commit - be9cd6068db445307653bc9502bba152b433ba16
> commit + 915aa15cc7c682e6d6225fdd64fef45064086ee4
> blob - 469af2edef2bbe969142e9423e98007b0ac623fe
> blob + 248c6c569b7eb7db7dc78c743adf60fed6c3eb8e
> --- sys/dev/ic/qwx.c
> +++ sys/dev/ic/qwx.c
> @@ -12837,6 +12837,8 @@ qwx_init_channels(struct qwx_softc *sc, struct cur_reg
>  				    IEEE80211_CHAN_DYN |
>  				    IEEE80211_CHAN_2GHZ |
>  				    IEEE80211_CHAN_HT;
> +				if (!(rule->flags & REGULATORY_CHAN_NO_HT40))
> +					chan->ic_flags |= IEEE80211_CHAN_40MHZ;
>  			}
>  			chnum++;
>  			freq = ieee80211_ieee2mhz(chnum, IEEE80211_CHAN_2GHZ);
> @@ -12872,6 +12874,8 @@ qwx_init_channels(struct qwx_softc *sc, struct cur_reg
>  				chan->ic_freq = freq;
>  				chan->ic_flags = IEEE80211_CHAN_A |
>  				    IEEE80211_CHAN_HT;
> +				if (!(rule->flags & REGULATORY_CHAN_NO_HT40))
> +					chan->ic_flags |= IEEE80211_CHAN_40MHZ;
>  				if (rule->flags & (REGULATORY_CHAN_RADAR |
>  				    REGULATORY_CHAN_NO_IR |
>  				    REGULATORY_CHAN_INDOOR_ONLY)) {
> @@ -23696,6 +23700,7 @@ qwx_mac_vdev_start_restart(struct qwx_softc *sc, struc
>  	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);
> @@ -23712,7 +23717,25 @@ qwx_mac_vdev_start_restart(struct qwx_softc *sc, struc
>  	arg.channel.band_center_freq2 = chan->ic_freq;
>
>  	/* Deduce a legacy mode based on the channel characteristics. */
> -	if (IEEE80211_IS_CHAN_5GHZ(chan))
> +	if (ieee80211_node_supports_ht(ic->ic_bss) &&
> +	    (chan->ic_flags & IEEE80211_CHAN_HT)) {
> +		arg.channel.allow_ht = 1;
> +		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;
> @@ -26419,6 +26442,7 @@ qwx_peer_assoc_h_phymode(struct qwx_softc *sc, struct
>  {
>  	struct ieee80211com *ic = &sc->sc_ic;
>  	enum wmi_phy_mode phymode;
> +	uint8_t sco;
>
>  	switch (ic->ic_curmode) {
>  	case IEEE80211_MODE_11A:
> @@ -26431,10 +26455,17 @@ qwx_peer_assoc_h_phymode(struct qwx_softc *sc, struct
>  		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;
> @@ -26490,7 +26521,7 @@ qwx_peer_assoc_h_ht(struct qwx_softc *sc, struct qwx_v
>  {
>  	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);
> @@ -26498,6 +26529,11 @@ qwx_peer_assoc_h_ht(struct qwx_softc *sc, struct qwx_v
>  	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);
> @@ -26511,12 +26547,13 @@ qwx_peer_assoc_h_ht(struct qwx_softc *sc, struct qwx_v
>
>  	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;
> @@ -26605,6 +26642,94 @@ qwx_peer_assoc_prepare(struct qwx_softc *sc, struct qw
>  }
>
>  void
> +qwx_updatechan(struct ieee80211com *ic)
> +{
> +	struct ifnet *ifp = &ic->ic_if;
> +	struct qwx_softc *sc = ifp->if_softc;
> +	struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list);
> +	struct ieee80211_node *ni = ic->ic_bss;
> +	struct qwx_node *nq = (struct qwx_node *)ic->ic_bss;
> +	int pdev_id = 0; /* TODO: derive pdev ID somehow? */
> +	struct peer_assoc_params peer_arg;
> +	uint32_t old_phymode;
> +	enum wmi_peer_chwidth chwidth;
> +	int ret;
> +
> +	old_phymode = nq->phymode;
> +
> +	qwx_peer_assoc_h_phymode(sc, ni, &peer_arg);
> +	qwx_peer_assoc_h_ht(sc, arvif, ni, &peer_arg);
> +
> +	if (peer_arg.bw_40)
> +		chwidth = WMI_PEER_CHWIDTH_40MHZ;
> +	else
> +		chwidth = WMI_PEER_CHWIDTH_20MHZ;
> +
> +	if (nq->chwidth == chwidth)
> +		return;
> +
> +	if (nq->chwidth < chwidth) {
> +		/*
> +		 * BW is upgraded. In this case we send WMI_PEER_PHYMODE
> +		 * followed by WMI_PEER_CHWIDTH.
> +		 */
> +		ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr,
> +		    arvif->vdev_id, pdev_id, WMI_PEER_PHYMODE,
> +		    peer_arg.peer_phymode);
> +		if (ret) {
> +			printf("%s: failed to update phymode for peer %s "
> +			    "vdev_id %d\n",
> +			    sc->sc_dev.dv_xname,
> +			    ether_sprintf(ni->ni_macaddr),
> +			    arvif->vdev_id);
> +			return;
> +		}
> +
> +		ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr,
> +		    arvif->vdev_id, pdev_id, WMI_PEER_CHWIDTH, chwidth);
> +		if (ret) {
> +			printf("%s: failed to update channel width for peer %s "
> +			    "vdev_id %d\n",
> +			    sc->sc_dev.dv_xname,
> +			    ether_sprintf(ni->ni_macaddr),
> +			    arvif->vdev_id);
> +			return;
> +		}
> +	} else {
> +		/*
> +		 * BW is downgraded. In this case we send WMI_PEER_CHWIDTH
> +		 * followed by WMI_PEER_PHYMODE.
> +		 */
> +		ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr,
> +		    arvif->vdev_id, pdev_id, WMI_PEER_CHWIDTH, chwidth);
> +		if (ret) {
> +			printf("%s: failed to update channel width for peer %s "
> +			    "vdev_id %d\n",
> +			    sc->sc_dev.dv_xname,
> +			    ether_sprintf(ni->ni_macaddr),
> +			    arvif->vdev_id);
> +			return;
> +		}
> +
> +		ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr,
> +		    arvif->vdev_id, pdev_id, WMI_PEER_PHYMODE,
> +		    peer_arg.peer_phymode);
> +		if (ret) {
> +			printf("%s: failed to update phymode for peer %s "
> +			    "vdev_id %d\n",
> +			    sc->sc_dev.dv_xname,
> +			    ether_sprintf(ni->ni_macaddr),
> +			    arvif->vdev_id);
> +			return;
> +		}
> +
> +	}
> +
> +	nq->phymode = peer_arg.peer_phymode;
> +	nq->chwidth = chwidth;
> +}
> +
> +void
>  qwx_rx_agg_start(struct qwx_softc *sc, struct ieee80211_node *ni, uint8_t tid,
>      uint16_t ssn, uint16_t winsize)
>  {
> @@ -26784,15 +26909,6 @@ qwx_assoc(struct qwx_softc *sc)
>  		}
>  	}
>
> -	if (ni->ni_flags & IEEE80211_NODE_HT) {
> -		ret = qwx_setup_peer_smps(sc, pdev_id, arvif, ni->ni_macaddr,
> -		    ni->ni_htcaps);
> -		if (ret) {
> -			printf("%s: failed to setup SMPS for vdev %d: %d\n",
> -			    sc->sc_dev.dv_xname, arvif->vdev_id, ret);
> -			return ret;
> -		}
> -	}
>  #if 0
>  	if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) {
>  		ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n",
> @@ -26822,10 +26938,53 @@ qwx_run(struct qwx_softc *sc)
>  {
>  	struct ieee80211com *ic = &sc->sc_ic;
>  	struct ieee80211_node *ni = ic->ic_bss;
> +	struct qwx_node *nq = (struct qwx_node *)ni;
>  	struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
>  	uint8_t pdev_id = 0; /* TODO: derive pdev ID somehow? */
> +	struct peer_assoc_params peer_arg;
>  	int ret;
>
> +	/* Update peer again to apply HT and VHT settings. */
> +	qwx_peer_assoc_prepare(sc, arvif, ni, &peer_arg, 1);
> +
> +	peer_arg.is_assoc = 1;
> +
> +	sc->peer_assoc_done = 0;
> +	ret = qwx_wmi_send_peer_assoc_cmd(sc, pdev_id, &peer_arg);
> +	if (ret) {
> +		printf("%s: failed to run peer assoc for %s vdev %i: %d\n",
> +		    sc->sc_dev.dv_xname, ether_sprintf(ni->ni_macaddr),
> +		    arvif->vdev_id, ret);
> +		return ret;
> +	}
> +
> +	while (!sc->peer_assoc_done) {
> +		ret = tsleep_nsec(&sc->peer_assoc_done, 0, "qwxassoc",
> +		    SEC_TO_NSEC(1));
> +		if (ret) {
> +			printf("%s: failed to get peer assoc conf event "
> +			    "for %s vdev %i\n", sc->sc_dev.dv_xname,
> +			    ether_sprintf(ni->ni_macaddr), arvif->vdev_id);
> +			return ret;
> +		}
> +	}
> +
> +	if (ni->ni_flags & IEEE80211_NODE_HT) {
> +		ret = qwx_setup_peer_smps(sc, pdev_id, arvif, ni->ni_macaddr,
> +		    ni->ni_htcaps);
> +		if (ret) {
> +			printf("%s: failed to setup SMPS for vdev %d: %d\n",
> +			    sc->sc_dev.dv_xname, arvif->vdev_id, ret);
> +			return ret;
> +		}
> +	}
> +
> +	nq->phymode = peer_arg.peer_phymode;
> +	if (peer_arg.bw_40)
> +		nq->chwidth = WMI_PEER_CHWIDTH_40MHZ;
> +	else
> +		nq->chwidth = WMI_PEER_CHWIDTH_20MHZ;
> +
>  	ret = qwx_wmi_vdev_up(sc, arvif->vdev_id, pdev_id, arvif->aid,
>  	    arvif->bssid, NULL, 0, 0);
>  	if (ret) {
> blob - 3d4c9865bd00034c5491dcdf9c224a8fb252f9fd
> blob + 4e82920be3e083e48eb40eb45c9b515543476608
> --- sys/dev/ic/qwxvar.h
> +++ sys/dev/ic/qwxvar.h
> @@ -2036,6 +2036,7 @@ void	qwx_init_task(void *);
>  int	qwx_newstate(struct ieee80211com *, enum ieee80211_state, int);
>  void	qwx_newstate_task(void *);
>  int	qwx_bgscan(struct ieee80211com *);
> +void	qwx_updatechan(struct ieee80211com *);
>
>  struct qwx_node {
>  	struct ieee80211_node ni;
> @@ -2043,6 +2044,8 @@ struct qwx_node {
>  	unsigned int flags;
>  #define QWX_NODE_FLAG_HAVE_PAIRWISE_KEY	0x01
>  #define QWX_NODE_FLAG_HAVE_GROUP_KEY	0x02
> +	uint32_t phymode;
> +	enum wmi_peer_chwidth chwidth;
>  };
>
>  struct ieee80211_node *qwx_node_alloc(struct ieee80211com *);
> blob - c98c5c63b5bf02a4312ff08acdf23467e7b717a1
> blob + 674e87b64a305c082f146504eb9be99bb27637d4
> --- sys/dev/pci/if_qwx_pci.c
> +++ sys/dev/pci/if_qwx_pci.c
> @@ -1091,8 +1091,8 @@ unsupported_wcn6855_soc:
>  	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;
> @@ -1122,8 +1122,8 @@ unsupported_wcn6855_soc:
>  	ic->ic_newstate = qwx_newstate;
>  	ic->ic_set_key = qwx_set_key;
>  	ic->ic_delete_key = qwx_delete_key;
> -#if 0
>  	ic->ic_updatechan = qwx_updatechan;
> +#if 0
>  	ic->ic_updateprot = qwx_updateprot;
>  	ic->ic_updateslot = qwx_updateslot;
>  	ic->ic_updateedca = qwx_updateedca;
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.