Download raw body.
sys/ieee80211: support of uAPSD; sys/iwx: enable uAPSD when supported by AP
On Sun, 05 Apr 2026 00:22:10 +0200,
Kirill A. Korinsky <kirill@korins.ky> wrote:
>
> On Wed, 25 Mar 2026 01:50:37 +0100,
> Kirill A. Korinsky <kirill@korins.ky> 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.
> >
>
> Here the third version of that diff which contains fixed a few more typos
> near encoding and decoding AC and Lenght bits, and now it more like linux.
>
> I also moved uAPSD under nwflag which is off by default because on my
> testing it is quite clear that it introduce some latency in spare traffic,
> and the same behaviour is reproduced by Linux on the same hardware, and
> Linux has uAPSD disabled by default as well.
>
> Feedback? Tests? Ok?
>
I would like to go this patch as well.
Anyone?
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 4 Apr 2026 22:03:41 -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/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 4 Apr 2026 22:03:47 -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_VO)
+ ac_flags |= 1 << 0;
+ if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
+ ac_flags |= 1 << 1;
+ if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
+ ac_flags |= 1 << 2;
+ if (ni->ni_uapsd_ac & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
+ ac_flags |= 1 << 3;
+
+ 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_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_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_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,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;
@@ -12510,6 +12630,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 4 Apr 2026 22:03:41 -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: 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 4 Apr 2026 22:03:41 -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 4 Apr 2026 22:03: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,31 @@ 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 &&
+ (ic->ic_userflags & IEEE80211_F_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 +1653,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 +1889,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 +2002,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 +2034,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 +2665,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 +2754,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 +2785,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 4 Apr 2026 22:03:41 -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 4 Apr 2026 22:03:41 -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 4 Apr 2026 22:03:41 -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,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 +1629,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 +1663,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 4 Apr 2026 22:03:41 -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 4 Apr 2026 22:03:41 -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