Download raw body.
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
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
>
>
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP