From: Stefan Sperling Subject: Re: sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP To: tech@openbsd.org Date: Thu, 28 May 2026 11:55:13 +0200 On Thu, May 28, 2026 at 11:13:07AM +0200, Kirill A. Korinsky wrote: > Just a ping. Still ok by me. > Index: sys/dev/pci/if_iwx.c > =================================================================== > RCS file: /home/cvs/src/sys/dev/pci/if_iwx.c,v > diff -u -p -r1.228 if_iwx.c > --- sys/dev/pci/if_iwx.c 2 Apr 2026 11:19:45 -0000 1.228 > +++ sys/dev/pci/if_iwx.c 19 May 2026 22:43:26 -0000 > @@ -6612,6 +6612,73 @@ 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_ac_flags(struct ieee80211_node *ni) > +{ > + uint8_t ac_flags = 0; > + > + if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) > + ac_flags |= 1 << EDCA_AC_BE; > + if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) > + ac_flags |= 1 << EDCA_AC_BK; > + if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) > + ac_flags |= 1 << EDCA_AC_VI; > + if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) > + ac_flags |= 1 << EDCA_AC_VO; > + > + return ac_flags; > +} > + > +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) > { > @@ -6673,6 +6740,21 @@ 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_flags & IEEE80211_NODE_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 = iwx_uapsd_ac_flags(ni); > + 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; > @@ -6835,6 +6917,12 @@ iwx_add_sta_cmd(struct iwx_softc *sc, st > break; > } > } > + if ((in->in_ni.ni_flags & IEEE80211_NODE_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), > @@ -7041,6 +7129,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_flags & IEEE80211_NODE_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), > @@ -11104,6 +11198,33 @@ 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_flags & IEEE80211_NODE_UAPSD) == 0) > + 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_flags &= ~IEEE80211_NODE_UAPSD; > + 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; > @@ -12510,6 +12631,11 @@ 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_VO | > + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | > + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | > + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE; > + 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.74 if_iwxreg.h > --- sys/dev/pci/if_iwxreg.h 26 Mar 2026 12:15:48 -0000 1.74 > +++ sys/dev/pci/if_iwxreg.h 19 May 2026 22:22:58 -0000 > @@ -5473,6 +5473,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 > @@ -8312,6 +8314,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 > @@ -8321,6 +8324,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: sbin/ifconfig/ifconfig.8 > =================================================================== > RCS file: /home/cvs/src/sbin/ifconfig/ifconfig.8,v > diff -u -p -r1.413 ifconfig.8 > --- sbin/ifconfig/ifconfig.8 3 Dec 2025 10:19:27 -0000 1.413 > +++ sbin/ifconfig/ifconfig.8 19 May 2026 22:22:58 -0000 > @@ -1111,6 +1111,14 @@ flag will disable MIMO reception and tra > and wireless network device support MIMO. > This flag can be used to work around packet loss in 11n mode if the > wireless network device has unused antenna connectors. > +.It uapsd > +The > +.Ql uapsd > +flag will enable Unscheduled Automatic Power Save Delivery > +(u-APSD) support. > +This can reduce power consumption while the interface is idle or > +carrying little traffic, but may increase latency for sparse traffic. > +This flag is disabled by default. > .It stayauth > The > .Ql stayauth > Index: sys/net80211/ieee80211.h > =================================================================== > RCS file: /home/cvs/src/sys/net80211/ieee80211.h,v > diff -u -p -r1.66 ieee80211.h > --- sys/net80211/ieee80211.h 29 Mar 2026 21:16:21 -0000 1.66 > +++ sys/net80211/ieee80211.h 19 May 2026 22:22:58 -0000 > @@ -193,6 +193,21 @@ 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 0x01 > +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 > +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 > +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 > +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 > + > /* > * Control frames. > */ > Index: sys/net80211/ieee80211_input.c > =================================================================== > RCS file: /home/cvs/src/sys/net80211/ieee80211_input.c,v > diff -u -p -r1.261 ieee80211_input.c > --- sys/net80211/ieee80211_input.c 26 Mar 2026 12:15:01 -0000 1.261 > +++ sys/net80211/ieee80211_input.c 19 May 2026 22:43:26 -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,32 @@ 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) > +{ > + if (peer_uapsd && (ic->ic_userflags & IEEE80211_F_UAPSD) && > + (ni->ni_flags & IEEE80211_NODE_QOS)) { > + ni->ni_flags |= IEEE80211_NODE_UAPSD; > + ni->ni_uapsd_ac = ic->ic_uapsd_ac; > + ni->ni_uapsd_maxsp = ic->ic_uapsd_maxsp; > + } else { > + ni->ni_flags &= ~IEEE80211_NODE_UAPSD; > + 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 +1654,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; > > /* > @@ -1862,6 +1890,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 > @@ -1972,11 +2003,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; > @@ -1997,6 +2035,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. > @@ -2625,7 +2666,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) { > @@ -2713,6 +2755,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")); > @@ -2741,6 +2786,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_ioctl.h > =================================================================== > RCS file: /home/cvs/src/sys/net80211/ieee80211_ioctl.h,v > diff -u -p -r1.45 ieee80211_ioctl.h > --- sys/net80211/ieee80211_ioctl.h 26 Mar 2026 12:15:01 -0000 1.45 > +++ sys/net80211/ieee80211_ioctl.h 19 May 2026 22:22:58 -0000 > @@ -410,7 +410,9 @@ struct ieee80211_nodereq_all { > #define IEEE80211_F_HOSTAPMASK 0x00000003 > #define IEEE80211_F_STAYAUTH 0x00000004 /* CONF: ignore deauth */ > #define IEEE80211_F_NOMIMO 0x00000008 /* CONF: disable MIMO */ > -#define IEEE80211_F_USERBITS "\20\01HIDENWID\02NOBRIDGE\03STAYAUTH\04NOMIMO" > +#define IEEE80211_F_UAPSD 0x00000010 /* CONF: enable u-APSD */ > +#define IEEE80211_F_USERBITS "\20\01HIDENWID\02NOBRIDGE\03STAYAUTH\04NOMIMO" \ > + "\05UAPSD" > > struct ieee80211_flags { > const char *f_name; > @@ -421,7 +423,8 @@ struct ieee80211_flags { > { "hidenwid", IEEE80211_F_HIDENWID }, \ > { "nobridge", IEEE80211_F_NOBRIDGE }, \ > { "stayauth", IEEE80211_F_STAYAUTH }, \ > - { "nomimo", IEEE80211_F_NOMIMO } \ > + { "nomimo", IEEE80211_F_NOMIMO }, \ > + { "uapsd", IEEE80211_F_UAPSD } \ > } > > #define SIOCG80211FLAGS _IOWR('i', 216, struct ifreq) > Index: sys/net80211/ieee80211_node.h > =================================================================== > RCS file: /home/cvs/src/sys/net80211/ieee80211_node.h,v > diff -u -p -r1.101 ieee80211_node.h > --- sys/net80211/ieee80211_node.h 29 Mar 2026 21:16:21 -0000 1.101 > +++ sys/net80211/ieee80211_node.h 19 May 2026 22:43:26 -0000 > @@ -306,6 +306,8 @@ 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_ac; > + u_int8_t ni_uapsd_maxsp; > > /* RSN */ > struct timeout ni_eapol_to; > @@ -446,6 +448,7 @@ struct ieee80211_node { > #define IEEE80211_NODE_HE 0x200000 /* HE negotiated */ > #define IEEE80211_NODE_HECAP 0x400000 /* claims to support HE */ > #define IEEE80211_NODE_CSA 0x800000 /* channel switch announced */ > +#define IEEE80211_NODE_UAPSD 0x1000000 /* uAPSD negotiated */ > > /* If not NULL, this function gets called when ni_refcnt hits zero. */ > void (*ni_unref_cb)(struct ieee80211com *, > 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 19 May 2026 23:11:15 -0000 > @@ -985,6 +985,16 @@ ieee80211_add_erp(u_int8_t *frm, struct > } > #endif /* IEEE80211_STA_ONLY */ > > +uint8_t > +ieee80211_uapsd_qosinfo(struct ieee80211com *ic) > +{ > + if ((ic->ic_userflags & IEEE80211_F_UAPSD) == 0) > + return 0; > + return (ic->ic_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) | > + ((ic->ic_uapsd_maxsp & IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) << > + IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); > +} > + > /* > * Add a QoS Capability element to a frame (see 7.3.2.35). > */ > @@ -993,7 +1003,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 +1021,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 +1595,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 +1611,10 @@ ieee80211_get_assoc_req(struct ieee80211 > IEEE80211_HE_MCS_NSS_SIZE(ic->ic_he_phy_cap[0]); > } > > + /* Keep QoS Capability aligned with ieee80211_add_wme_info(). */ > + 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) ? > @@ -1612,11 +1626,12 @@ ieee80211_get_assoc_req(struct ieee80211 > (((ic->ic_flags & IEEE80211_F_RSNON) && > (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) ? > 2 + IEEE80211_RSNIE_MAXLEN : 0) + > - ((ni->ni_flags & IEEE80211_NODE_QOS) ? 2 + 1 : 0) + > + (addwme ? 2 + 1 : 0) + > (((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) > @@ -1644,15 +1659,15 @@ ieee80211_get_assoc_req(struct ieee80211 > if ((ic->ic_flags & IEEE80211_F_RSNON) && > (ni->ni_rsnprotos & IEEE80211_PROTO_RSN)) > frm = ieee80211_add_rsn(frm, ic, ni); > - if (ni->ni_flags & IEEE80211_NODE_QOS) > + if (addwme) > frm = ieee80211_add_qos_capability(frm, ic); > 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 19 May 2026 22:43:26 -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_flags & IEEE80211_NODE_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 19 May 2026 22:22:58 -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 > >