Index | Thread | Search

From:
Peter Hessler <phessler@theapt.org>
Subject:
Re: sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
To:
tech@openbsd.org
Date:
Wed, 25 Mar 2026 20:20:10 +0100

Download raw body.

Thread
On 2026 Mar 25 (Wed) at 01:50:37 +0100 (+0100), Kirill A. Korinsky wrote:
:On Fri, 20 Mar 2026 15:40:03 +0100,
:Kirill A. Korinsky <kirill@korins.ky> wrote:
:> 
:> tech@,
:> 
:> here two diff in one where I implemented support of uAPSD and enabled it
:> for iwx driver. When AP is misbehaving, uAPSD is disabled till the
:> reconnect, and I don't keep per AP state like linux for simplicity.
:> 
:> Tested on uAPSD enabled and disabled networks 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
:> 
:
:here updated version of the diff. Changes:
:
:1. Fixed a copy and paste typo with IEEE80211 AC_XX bits
:2. Never use uAPSD when firmware doesn't announce support it.
:
:Ok?
:


This version works for me on AX211, OK



iwx0 at pci0 dev 20 function 3 "Intel Wi-Fi 6 AX211" rev 0x01, msix
iwx0: hw rev 0x370, fw 77.f92b5fed.0, pnvm 285b3568, address XXX

AP: TP-Link AX3000 Gigabit Wi-Fi 6 Router





:
: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	25 Mar 2026 00:33:11 -0000
:@@ -6551,6 +6551,56 @@ iwx_update_beacon_abort(struct iwx_softc
: 	return iwx_beacon_filter_send_cmd(sc, &cmd);
: }
: 
:+static uint8_t
:+iwx_uapsd_qndp_tid(struct ieee80211com *ic, uint8_t acs)
:+{
:+	if ((acs & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) &&
:+	    !ic->ic_edca_ac[EDCA_AC_VO].ac_acm)
:+		return 6;
:+	if ((acs & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) &&
:+	    !ic->ic_edca_ac[EDCA_AC_VI].ac_acm)
:+		return 5;
:+	if ((acs & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) &&
:+	    !ic->ic_edca_ac[EDCA_AC_BE].ac_acm)
:+		return 0;
:+	if ((acs & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) &&
:+	    !ic->ic_edca_ac[EDCA_AC_BK].ac_acm)
:+		return 1;
:+	return 0;
:+}
:+
:+static uint8_t
:+iwx_uapsd_acs(struct ieee80211_node *ni)
:+{
:+	uint8_t acs = 0;
:+
:+	if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
:+		acs |= 1 << IWX_AC_VO;
:+	if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
:+		acs |= 1 << IWX_AC_VI;
:+	if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
:+		acs |= 1 << IWX_AC_BE;
:+	if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
:+		acs |= 1 << IWX_AC_BK;
:+
:+	return acs | (acs << IWX_AC_NUM);
:+}
:+
:+static uint8_t
:+iwx_uapsd_sp_length(struct ieee80211_node *ni)
:+{
:+	switch (ni->ni_uapsd_maxsp & IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) {
:+	case IEEE80211_WMM_IE_STA_QOSINFO_SP_2:
:+		return 2;
:+	case IEEE80211_WMM_IE_STA_QOSINFO_SP_4:
:+		return 4;
:+	case IEEE80211_WMM_IE_STA_QOSINFO_SP_6:
:+		return 6;
:+	default:
:+		return 128;
:+	}
:+}
:+
: int
: iwx_set_pslevel(struct iwx_softc *sc, int dtim, int level, int async)
: {
:@@ -6612,6 +6662,23 @@ iwx_set_pslevel(struct iwx_softc *sc, in
: 		    IWX_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
: 		mcmd.rx_data_timeout = htole32(pmgt->rxtimeout * 1024);
: 		mcmd.tx_data_timeout = htole32(pmgt->txtimeout * 1024);
:+		if (ni->ni_uapsd &&
:+		    (sc->sc_capaflags & IWX_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) {
:+			mcmd.flags |=
:+			    htole16(IWX_POWER_FLAGS_ADVANCE_PM_ENA_MSK);
:+			mcmd.flags |=
:+			    htole16(IWX_POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
:+			mcmd.rx_data_timeout_uapsd =
:+			    htole32(IWX_UAPSD_PS_RX_DATA_TIMEOUT);
:+			mcmd.tx_data_timeout_uapsd =
:+			    htole32(IWX_UAPSD_PS_TX_DATA_TIMEOUT);
:+			mcmd.qndp_tid = iwx_uapsd_qndp_tid(ic, ni->ni_uapsd_ac);
:+			mcmd.uapsd_ac_flags =
:+			    ni->ni_uapsd_ac &
:+			    IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK;
:+			mcmd.uapsd_max_sp = ni->ni_uapsd_maxsp &
:+			    IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK;
:+		}
: 		if (skip_dtim != 0) {
: 			mcmd.flags |= htole16(IWX_POWER_FLAGS_SKIP_OVER_DTIM_MSK);
: 			mcmd.skip_dtim_periods = skip_dtim + 1;
:@@ -6770,6 +6837,12 @@ iwx_add_sta_cmd(struct iwx_softc *sc, st
: 			break;
: 		}
: 	}
:+	if (in->in_ni.ni_uapsd &&
:+	    (sc->sc_capaflags & IWX_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) {
:+		add_sta_cmd.modify_mask |= IWX_STA_MODIFY_UAPSD_ACS;
:+		add_sta_cmd.uapsd_acs = iwx_uapsd_acs(&in->in_ni);
:+		add_sta_cmd.sp_length = iwx_uapsd_sp_length(&in->in_ni);
:+	}
: 
: 	status = IWX_ADD_STA_SUCCESS;
: 	err = iwx_send_cmd_pdu_status(sc, IWX_ADD_STA, sizeof(add_sta_cmd),
:@@ -6976,6 +7049,12 @@ iwx_mld_add_sta_cmd(struct iwx_softc *sc
: 
: 	if (in->in_ni.ni_flags & IEEE80211_NODE_MFP)
: 		sta_cmd.mfp = htole32(1);
:+	if (in->in_ni.ni_uapsd &&
:+	    (sc->sc_capaflags & IWX_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) {
:+		sta_cmd.sp_length =
:+		    htole32(iwx_uapsd_sp_length(&in->in_ni));
:+		sta_cmd.uapsd_acs = htole32(iwx_uapsd_acs(&in->in_ni));
:+	}
: 
: 	return iwx_send_cmd_pdu(sc,
: 	    IWX_WIDE_ID(IWX_MAC_CONF_GROUP, IWX_STA_CONFIG_CMD),
:@@ -10977,6 +11056,32 @@ iwx_rx_pkt(struct iwx_softc *sc, struct 
: 			break;
: 		}
: 
:+		case IWX_PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION: {
:+			struct ieee80211com *ic = &sc->sc_ic;
:+			struct ifnet *ifp = &ic->ic_if;
:+			struct ieee80211_node *ni = ic->ic_bss;
:+			struct iwx_uapsd_misbehaving_ap_notif *notif;
:+
:+			SYNC_RESP_STRUCT(notif, pkt);
:+
:+			if (ni == NULL || !ni->ni_uapsd)
:+				break;
:+
:+			if (ifp->if_flags & IFF_DEBUG)
:+				printf("%s: firmware reported uAPSD "
:+				    "misbehaving AP on sta %u, disabling "
:+				    "uAPSD\n", ifp->if_xname,
:+				    le32toh(notif->sta_id));
:+
:+			ni->ni_uapsd = 0;
:+			ni->ni_uapsd_ac = 0;
:+			ni->ni_uapsd_maxsp = 0;
:+
:+			if (ic->ic_flags & IEEE80211_F_PMGTON)
:+				(void)iwx_set_pslevel(sc, 0, 3, 1);
:+			break;
:+		}
:+
: 		case IWX_WIDE_ID(IWX_MAC_CONF_GROUP,
: 		    IWX_SESSION_PROTECTION_NOTIF): {
: 			struct iwx_session_prot_notif *notif;
:@@ -12367,6 +12472,9 @@ iwx_attach(struct device *parent, struct
: 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
: 	    IEEE80211_C_MFP;		/* management frame protection */
: 	ic->ic_flags |= IEEE80211_F_PMGTON;
:+	ic->ic_uapsd_ac = IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |
:+	    IEEE80211_WMM_IE_STA_QOSINFO_AC_VO;
:+	ic->ic_uapsd_maxsp = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL;
: 
: 	ic->ic_htcaps = IEEE80211_HTCAP_SGI20 | IEEE80211_HTCAP_SGI40;
: 	ic->ic_htcaps |= IEEE80211_HTCAP_CBW20_40;
:Index: sys/dev/pci/if_iwxreg.h
:===================================================================
:RCS file: /home/cvs/src/sys/dev/pci/if_iwxreg.h,v
:diff -u -p -r1.73 if_iwxreg.h
:--- sys/dev/pci/if_iwxreg.h	13 Mar 2026 11:11:02 -0000	1.73
:+++ sys/dev/pci/if_iwxreg.h	25 Mar 2026 00:25:41 -0000
:@@ -5472,6 +5472,8 @@ struct iwx_mac_power_cmd {
: 
: #define IWX_DEFAULT_PS_TX_DATA_TIMEOUT      (100 * 1000)
: #define IWX_DEFAULT_PS_RX_DATA_TIMEOUT      (100 * 1000)
:+#define IWX_UAPSD_PS_TX_DATA_TIMEOUT       (50 * 1000)
:+#define IWX_UAPSD_PS_RX_DATA_TIMEOUT       (50 * 1000)
: 
: #define IWX_NDTIMRANGES		3
: #define IWX_NPOWERLEVELS	6
:@@ -8311,6 +8313,7 @@ struct iwx_umac_scan_iter_complete_notif
:  * @IWX_STA_MODIFY_QUEUE_REMOVAL: this command removes a queue
:  * @IWX_STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx
:  * @IWX_STA_MODIFY_TX_RATE: unused
:+ * @IWX_STA_MODIFY_UAPSD_ACS: this command modifies %uapsd_acs
:  * @IWX_STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid
:  * @IWX_STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid
:  * @IWX_STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count
:@@ -8320,6 +8323,7 @@ struct iwx_umac_scan_iter_complete_notif
: #define IWX_STA_MODIFY_QUEUE_REMOVAL		(1 << 0)
: #define IWX_STA_MODIFY_TID_DISABLE_TX		(1 << 1)
: #define IWX_STA_MODIFY_TX_RATE			(1 << 2)
:+#define IWX_STA_MODIFY_UAPSD_ACS		IWX_STA_MODIFY_TX_RATE
: #define IWX_STA_MODIFY_ADD_BA_TID		(1 << 3)
: #define IWX_STA_MODIFY_REMOVE_BA_TID		(1 << 4)
: #define IWX_STA_MODIFY_SLEEPING_STA_TX_COUNT	(1 << 5)
: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	25 Mar 2026 00:25:34 -0000
:@@ -193,6 +193,20 @@ struct ieee80211_htframe_addr4 {	/* 11n 
: #define IEEE80211_QOS_EOSP			0x0010
: #define IEEE80211_QOS_TID			0x000f
: 
:+#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD	0x80
:+
:+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO	0x01
:+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI	0x02
:+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK	0x04
:+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE	0x08
:+#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK	0x0f
:+
:+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL	0x00
:+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2	0x20
:+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4	0x40
:+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6	0x60
:+#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK	0x60
:+
: /*
:  * Control frames.
:  */
:Index: sys/net80211/ieee80211_input.c
:===================================================================
:RCS file: /home/cvs/src/sys/net80211/ieee80211_input.c,v
:diff -u -p -r1.260 ieee80211_input.c
:--- sys/net80211/ieee80211_input.c	19 Mar 2026 16:50:32 -0000	1.260
:+++ sys/net80211/ieee80211_input.c	25 Mar 2026 00:25:41 -0000
:@@ -87,6 +87,7 @@ int	ieee80211_parse_edca_params_body(str
: 	    const u_int8_t *);
: int	ieee80211_parse_edca_params(struct ieee80211com *, const u_int8_t *);
: int	ieee80211_parse_wmm_params(struct ieee80211com *, const u_int8_t *);
:+int	ieee80211_parse_wmm_qosinfo(const u_int8_t *, u_int8_t *);
: enum	ieee80211_cipher ieee80211_parse_rsn_cipher(const u_int8_t *);
: enum	ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t *);
: int	ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *,
:@@ -1387,6 +1388,30 @@ ieee80211_parse_wmm_params(struct ieee80
: 	return ieee80211_parse_edca_params_body(ic, frm + 8);
: }
: 
:+int
:+ieee80211_parse_wmm_qosinfo(const u_int8_t *frm, u_int8_t *qosinfo)
:+{
:+	if (frm[1] < 7)
:+		return IEEE80211_REASON_IE_INVALID;
:+
:+	*qosinfo = frm[8];
:+	return 0;
:+}
:+
:+void
:+ieee80211_setup_uapsd(struct ieee80211com *ic, struct ieee80211_node *ni,
:+    int peer_uapsd)
:+{
:+	ni->ni_uapsd = !!peer_uapsd;
:+	if (ni->ni_uapsd && (ni->ni_flags & IEEE80211_NODE_QOS)) {
:+		ni->ni_uapsd_ac = ic->ic_uapsd_ac;
:+		ni->ni_uapsd_maxsp = ic->ic_uapsd_maxsp;
:+	} else {
:+		ni->ni_uapsd_ac = 0;
:+		ni->ni_uapsd_maxsp = 0;
:+	}
:+}
:+
: enum ieee80211_cipher
: ieee80211_parse_rsn_cipher(const u_int8_t selector[4])
: {
:@@ -1627,7 +1652,8 @@ ieee80211_recv_probe_resp(struct ieee802
: 	const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
: 	const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop, *hecaps, *heop;
: 	u_int16_t capinfo, bintval;
:-	u_int8_t chan, bchan, erp;
:+	u_int8_t chan, bchan, erp, wmm_qosinfo;
:+	int has_wmm_qosinfo = 0;
: 	int is_new;
: 
: 	/*
:@@ -1845,6 +1871,9 @@ ieee80211_recv_probe_resp(struct ieee802
: 		ni->ni_dtimcount = tim[2];
: 		ni->ni_dtimperiod = tim[3];
: 	}
:+	if (wmmie != NULL &&
:+	    ieee80211_parse_wmm_qosinfo(wmmie, &wmm_qosinfo) == 0)
:+		has_wmm_qosinfo = 1;
: 
: 	/*
: 	 * When operating in station mode, check for state updates
:@@ -1955,11 +1984,18 @@ ieee80211_recv_probe_resp(struct ieee802
: 		else
: 			ni->ni_flags &= ~IEEE80211_NODE_QOS;
: 	}
:+	ieee80211_setup_uapsd(ic, ni, has_wmm_qosinfo &&
:+	    (wmm_qosinfo & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD));
: 
: 	if (ic->ic_state == IEEE80211_S_SCAN ||
: 	    (ic->ic_flags & IEEE80211_F_BGSCAN)) {
: 		struct ieee80211_rsnparams rsn, wpa;
: 
:+		if (edcaie != NULL || wmmie != NULL)
:+			ni->ni_flags |= IEEE80211_NODE_QOS;
:+		else
:+			ni->ni_flags &= ~IEEE80211_NODE_QOS;
:+
: 		ni->ni_rsnprotos = IEEE80211_PROTO_NONE;
: 		ni->ni_supported_rsnprotos = IEEE80211_PROTO_NONE;
: 		ni->ni_rsnakms = 0;
:@@ -1980,6 +2016,9 @@ ieee80211_recv_probe_resp(struct ieee802
: 			ni->ni_supported_rsnakms |= wpa.rsn_akms;
: 		}
: 
:+		ieee80211_setup_uapsd(ic, ni, has_wmm_qosinfo &&
:+		    (wmm_qosinfo & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD));
:+
: 		/*
: 		 * If the AP advertises both WPA and RSN IEs (WPA1+WPA2),
: 		 * we only use the highest protocol version we support.
:@@ -2608,7 +2647,8 @@ ieee80211_recv_assoc_resp(struct ieee802
: 	const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
: 	const u_int8_t *vhtcaps, *vhtop, *hecaps, *heop;
: 	u_int16_t capinfo, status, associd;
:-	u_int8_t rate;
:+	u_int8_t rate, wmm_qosinfo;
:+	int has_wmm_qosinfo = 0;
: 
: 	if (ic->ic_opmode != IEEE80211_M_STA ||
: 	    ic->ic_state != IEEE80211_S_ASSOC) {
:@@ -2696,6 +2736,9 @@ ieee80211_recv_assoc_resp(struct ieee802
: 		}
: 		frm += 2 + frm[1];
: 	}
:+	if (wmmie != NULL &&
:+	    ieee80211_parse_wmm_qosinfo(wmmie, &wmm_qosinfo) == 0)
:+		has_wmm_qosinfo = 1;
: 	/* supported rates element is mandatory */
: 	if (rates == NULL || rates[1] > IEEE80211_RATE_MAXSIZE) {
: 		DPRINTF(("invalid supported rates element\n"));
:@@ -2724,6 +2767,8 @@ ieee80211_recv_assoc_resp(struct ieee802
: 		else	/* for Reassociation */
: 			ni->ni_flags &= ~IEEE80211_NODE_QOS;
: 	}
:+	ieee80211_setup_uapsd(ic, ni, has_wmm_qosinfo &&
:+	    (wmm_qosinfo & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD));
: 	if (htcaps)
: 		ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
: 	if (htop)
:Index: sys/net80211/ieee80211_node.h
:===================================================================
:RCS file: /home/cvs/src/sys/net80211/ieee80211_node.h,v
:diff -u -p -r1.99 ieee80211_node.h
:--- sys/net80211/ieee80211_node.h	19 Mar 2026 16:50:32 -0000	1.99
:+++ sys/net80211/ieee80211_node.h	25 Mar 2026 00:25:42 -0000
:@@ -306,6 +306,9 @@ struct ieee80211_node {
: 	/* power saving mode */
: 	u_int8_t		ni_pwrsave;
: 	struct mbuf_queue	ni_savedq;	/* packets queued for pspoll */
:+	u_int8_t		ni_uapsd;
:+	u_int8_t		ni_uapsd_ac;
:+	u_int8_t		ni_uapsd_maxsp;
: 
: 	/* RSN */
: 	struct timeout		ni_eapol_to;
:Index: sys/net80211/ieee80211_output.c
:===================================================================
:RCS file: /home/cvs/src/sys/net80211/ieee80211_output.c,v
:diff -u -p -r1.147 ieee80211_output.c
:--- sys/net80211/ieee80211_output.c	19 Mar 2026 16:50:32 -0000	1.147
:+++ sys/net80211/ieee80211_output.c	24 Mar 2026 23:47:29 -0000
:@@ -985,6 +985,13 @@ ieee80211_add_erp(u_int8_t *frm, struct 
: }
: #endif	/* IEEE80211_STA_ONLY */
: 
:+uint8_t
:+ieee80211_uapsd_qosinfo(struct ieee80211com *ic)
:+{
:+	return (ic->ic_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) |
:+	    (ic->ic_uapsd_maxsp & IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK);
:+}
:+
: /*
:  * Add a QoS Capability element to a frame (see 7.3.2.35).
:  */
:@@ -993,7 +1000,7 @@ ieee80211_add_qos_capability(u_int8_t *f
: {
: 	*frm++ = IEEE80211_ELEMID_QOS_CAP;
: 	*frm++ = 1;
:-	*frm++ = 0;	/* QoS Info */
:+	*frm++ = ieee80211_uapsd_qosinfo(ic);
: 	return frm;
: }
: 
:@@ -1011,7 +1018,7 @@ ieee80211_add_wme_info(uint8_t *frm, str
: 	*frm++ = 2; /* OUI type */
: 	*frm++ = 0; /* OUI subtype */
: 	*frm++ = 1; /* version */
:-	*frm++ = 0; /* info */
:+	*frm++ = ieee80211_uapsd_qosinfo(ic);
: 
: 	return frm;
: }
:@@ -1585,7 +1592,7 @@ ieee80211_get_assoc_req(struct ieee80211
: 	struct mbuf *m;
: 	u_int8_t *frm;
: 	u_int16_t capinfo;
:-	int addvht = 0;
:+	int addvht = 0, addwme;
: 	u_int hecapslen = 0;
: 
: 	if ((ic->ic_flags & IEEE80211_F_VHTON) && ni->ni_chan != NULL &&
:@@ -1601,6 +1608,9 @@ ieee80211_get_assoc_req(struct ieee80211
: 		    IEEE80211_HE_MCS_NSS_SIZE(ic->ic_he_phy_cap[0]);
: 	}
: 
:+	addwme = (ni->ni_flags & IEEE80211_NODE_QOS) ||
:+	    (ic->ic_flags & IEEE80211_F_HTON);
:+
: 	m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA,
: 	    2 + 2 +
: 	    ((type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) ?
:@@ -1616,7 +1626,8 @@ ieee80211_get_assoc_req(struct ieee80211
: 	    (((ic->ic_flags & IEEE80211_F_RSNON) &&
: 	      (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
: 		2 + IEEE80211_WPAIE_MAXLEN : 0) +
:-	    ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
:+	    ((ic->ic_flags & IEEE80211_F_HTON) ? 28 : 0) +
:+	    (addwme ? 9 : 0) +
: 	    (addvht ? 14 : 0) +
: 	    hecapslen);
: 	if (m == NULL)
:@@ -1649,10 +1660,10 @@ ieee80211_get_assoc_req(struct ieee80211
: 	if ((ic->ic_flags & IEEE80211_F_RSNON) &&
: 	    (ni->ni_rsnprotos & IEEE80211_PROTO_WPA))
: 		frm = ieee80211_add_wpa(frm, ic, ni);
:-	if (ic->ic_flags & IEEE80211_F_HTON) {
:+	if (ic->ic_flags & IEEE80211_F_HTON)
: 		frm = ieee80211_add_htcaps(frm, ic);
:+	if (addwme)
: 		frm = ieee80211_add_wme_info(frm, ic);
:-	}
: 	if (addvht)
: 		frm = ieee80211_add_vhtcaps(frm, ic);
: 	if (hecapslen)
:Index: sys/net80211/ieee80211_proto.c
:===================================================================
:RCS file: /home/cvs/src/sys/net80211/ieee80211_proto.c,v
:diff -u -p -r1.112 ieee80211_proto.c
:--- sys/net80211/ieee80211_proto.c	19 Mar 2026 16:50:32 -0000	1.112
:+++ sys/net80211/ieee80211_proto.c	24 Mar 2026 23:47:29 -0000
:@@ -1376,7 +1376,7 @@ justcleanup:
: 				else
: 					printf(" start %u%sMb",
: 					    rate / 2, (rate & 1) ? ".5" : "");
:-				printf(" %s preamble %s slot time%s%s%s\n",
:+				printf(" %s preamble %s slot time%s%s%s%s\n",
: 				    (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
: 					"short" : "long",
: 				    (ic->ic_flags & IEEE80211_F_SHSLOT) ?
:@@ -1386,7 +1386,12 @@ justcleanup:
: 				    (ni->ni_flags & IEEE80211_NODE_HT) ?
: 					" HT enabled" : "",
: 				    (ni->ni_flags & IEEE80211_NODE_VHT) ?
:-					" VHT enabled" : "");
:+					" VHT enabled" : "",
:+				    ic->ic_uapsd_ac != 0 ?
:+					(ni->ni_uapsd ?
:+					    " uAPSD enabled" :
:+					    " uAPSD disabled") :
:+					"");
: 			}
: 			if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
: 				/*
:Index: sys/net80211/ieee80211_var.h
:===================================================================
:RCS file: /home/cvs/src/sys/net80211/ieee80211_var.h,v
:diff -u -p -r1.114 ieee80211_var.h
:--- sys/net80211/ieee80211_var.h	19 Mar 2026 16:50:32 -0000	1.114
:+++ sys/net80211/ieee80211_var.h	24 Mar 2026 23:47:29 -0000
:@@ -344,6 +344,8 @@ struct ieee80211com {
: 	u_int			ic_edca_txop_count[EDCA_NUM_AC];
: 	struct timeval		ic_edca_txop_time[EDCA_NUM_AC];
: 	u_int16_t		ic_tid_noack;
:+	u_int8_t		ic_uapsd_ac;
:+	u_int8_t		ic_uapsd_maxsp;
: 	u_int8_t		ic_globalcnt[EAPOL_KEY_NONCE_LEN];
: 	u_int8_t		ic_nonce[EAPOL_KEY_NONCE_LEN];
: 	u_int8_t		ic_psk[IEEE80211_PMK_LEN];
:
:
:
:-- 
:wbr, Kirill
:

-- 
New crypt.  See /usr/news/crypt.