From ba719c391d972a697b74c574a4aceeb4c61ed7b2 Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 16 Jun 2020 00:27:32 +0000 Subject: [PATCH] [net80211] Add initial U-APSD negotiation support. U-APSD (unscheduled automatic power save delivery) is a power save method that's a bit better than legacy PS-POLL - stations can mark frames with an extra flag that tells the AP to leak out more frames after it sends its own frames rather than needing to send a PS-POLL to get another frame from the AP. Now, this code just handles the negotiation bits; it doesn't actually implement U-APSD. That's up to drivers, and nothing in the tree yet implements this. I /may/ implement this for ath(4) if I eventually care enough but right now I plan on just implementing it for firmware offload based NICs that handle this in the NIC. I'll commit the ifconfig bit after this and I may have some follow-up commits as this gets used more by me in local testing. This should be a glorious no-op for everyone else. If things change for anyone that isn't fixed by a complete recompile then please reach out to me. --- sys/net80211/_ieee80211.h | 1 + sys/net80211/ieee80211.c | 6 +++ sys/net80211/ieee80211_hostap.c | 14 ++++++- sys/net80211/ieee80211_ioctl.c | 15 +++++++ sys/net80211/ieee80211_ioctl.h | 2 + sys/net80211/ieee80211_node.h | 4 ++ sys/net80211/ieee80211_output.c | 68 ++++++++++++++++++++++--------- sys/net80211/ieee80211_proto.c | 5 ++- sys/net80211/ieee80211_sta.c | 72 ++++++++++++++++++++++++++++----- sys/net80211/ieee80211_sta.h | 9 ++++- sys/net80211/ieee80211_var.h | 4 ++ 11 files changed, 167 insertions(+), 33 deletions(-) diff --git a/sys/net80211/_ieee80211.h b/sys/net80211/_ieee80211.h index 0208c0cdbe8..a0f7823adb5 100644 --- a/sys/net80211/_ieee80211.h +++ b/sys/net80211/_ieee80211.h @@ -488,6 +488,7 @@ struct ieee80211_mimo_info { #define IEEE80211_C_MBSS 0x00040000 /* CAPABILITY: MBSS available */ #define IEEE80211_C_SWSLEEP 0x00080000 /* CAPABILITY: do sleep here */ #define IEEE80211_C_SWAMSDUTX 0x00100000 /* CAPABILITY: software A-MSDU TX */ +#define IEEE80211_C_UAPSD 0x00200000 /* CAPABILITY: U-APSD */ /* 0x7c0000 available */ #define IEEE80211_C_WPA1 0x00800000 /* CAPABILITY: WPA1 avail */ #define IEEE80211_C_WPA2 0x01000000 /* CAPABILITY: WPA2 avail */ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 391780ddab9..f4a3a59b219 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -616,6 +616,12 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, if (vap->iv_opmode == IEEE80211_M_HOSTAP && (vap->iv_caps & IEEE80211_C_DFS)) vap->iv_flags_ext |= IEEE80211_FEXT_DFS; + /* NB: only flip on U-APSD for hostap/sta for now */ + if ((vap->iv_opmode == IEEE80211_M_STA) + || (vap->iv_opmode == IEEE80211_M_HOSTAP)) { + if (vap->iv_caps & IEEE80211_C_UAPSD) + vap->iv_flags_ext |= IEEE80211_FEXT_UAPSD; + } vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 263e0c3a2b6..a8a976a035c 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include /* for parse_wmeie */ #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) @@ -2253,8 +2254,18 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, * Mark node as capable of QoS. */ ni->ni_flags |= IEEE80211_NODE_QOS; + if (ieee80211_parse_wmeie(wme, wh, ni) > 0) { + if (ni->ni_uapsd != 0) + ni->ni_flags |= + IEEE80211_NODE_UAPSD; + else + ni->ni_flags &= + ~IEEE80211_NODE_UAPSD; + } } else - ni->ni_flags &= ~IEEE80211_NODE_QOS; + ni->ni_flags &= + ~(IEEE80211_NODE_QOS | + IEEE80211_NODE_UAPSD); #ifdef IEEE80211_SUPPORT_SUPERG if (ath != NULL) { setie(ath_ie, ath - sfrm); @@ -2268,6 +2279,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, #undef setie } else { ni->ni_flags &= ~IEEE80211_NODE_QOS; + ni->ni_flags &= ~IEEE80211_NODE_UAPSD; ni->ni_ath_flags = 0; } ieee80211_node_join(ni, resp); diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 2798657d874..d84abc1ae89 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -1145,6 +1145,11 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, if (vap->iv_flags_ht & IEEE80211_FHT_LDPC_RX) ireq->i_val |= 2; break; + case IEEE80211_IOC_UAPSD: + ireq->i_val = 0; + if (vap->iv_flags_ext & IEEE80211_FEXT_UAPSD) + ireq->i_val = 1; + break; /* VHT */ case IEEE80211_IOC_VHTCONF: @@ -3462,6 +3467,16 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r if (isvapht(vap)) error = ERESTART; break; + case IEEE80211_IOC_UAPSD: + if ((vap->iv_caps & IEEE80211_C_UAPSD) == 0) + return EOPNOTSUPP; + if (ireq->i_val == 0) + vap->iv_flags_ext &= ~IEEE80211_FEXT_UAPSD; + else if (ireq->i_val == 1) + vap->iv_flags_ext |= IEEE80211_FEXT_UAPSD; + else + return EINVAL; + break; /* VHT */ case IEEE80211_IOC_VHTCONF: diff --git a/sys/net80211/ieee80211_ioctl.h b/sys/net80211/ieee80211_ioctl.h index 5021285bad1..eb933a5c053 100644 --- a/sys/net80211/ieee80211_ioctl.h +++ b/sys/net80211/ieee80211_ioctl.h @@ -710,6 +710,8 @@ struct ieee80211req { #define IEEE80211_IOC_GREENFIELD 112 /* Greenfield (on, off) */ #define IEEE80211_IOC_STBC 113 /* STBC Tx/RX (on, off) */ #define IEEE80211_IOC_LDPC 114 /* LDPC Tx/RX (on, off) */ +#define IEEE80211_IOC_UAPSD 115 /* UAPSD (on, off) */ +#define IEEE80211_IOC_UAPSD_INFO 116 /* UAPSD (SP, per-AC enable) */ /* VHT */ #define IEEE80211_IOC_VHTCONF 130 /* VHT config (off, on; widths) */ diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 3e21cdda210..6e9fa5f168a 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -146,6 +146,7 @@ struct ieee80211_node { #define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */ #define IEEE80211_NODE_VHT 0x100000 /* VHT enabled */ #define IEEE80211_NODE_LDPC 0x200000 /* LDPC enabled */ +#define IEEE80211_NODE_UAPSD 0x400000 /* U-APSD power save enabled */ uint16_t ni_associd; /* association ID */ uint16_t ni_vlan; /* vlan tag */ uint16_t ni_txpower; /* current transmit power */ @@ -256,6 +257,9 @@ struct ieee80211_node { uint32_t ni_quiet_ie_set; /* Quiet time IE was seen */ struct ieee80211_quiet_ie ni_quiet_ie; /* last seen quiet IE */ + /* U-APSD */ + uint8_t ni_uapsd; /* U-APSD per-node flags matching WMM STA QoS Info field */ + uint64_t ni_spare[3]; }; MALLOC_DECLARE(M_80211_NODE); diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index de9416b1777..c53911604ef 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -2156,26 +2156,48 @@ add_ie(uint8_t *frm, const uint8_t *ie) * Add a WME information element to a frame. */ uint8_t * -ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme) +ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme, + struct ieee80211_node *ni) { - static const struct ieee80211_wme_info info = { - .wme_id = IEEE80211_ELEMID_VENDOR, - .wme_len = sizeof(struct ieee80211_wme_info) - 2, - .wme_oui = { WME_OUI_BYTES }, - .wme_type = WME_OUI_TYPE, - .wme_subtype = WME_INFO_OUI_SUBTYPE, - .wme_version = WME_VERSION, - .wme_info = 0, - }; - memcpy(frm, &info, sizeof(info)); - return frm + sizeof(info); + static const uint8_t oui[4] = { WME_OUI_BYTES, WME_OUI_TYPE }; + struct ieee80211vap *vap = ni->ni_vap; + + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = sizeof(struct ieee80211_wme_info) - 2; + memcpy(frm, oui, sizeof(oui)); + frm += sizeof(oui); + *frm++ = WME_INFO_OUI_SUBTYPE; + *frm++ = WME_VERSION; + + /* QoS info field depends upon operating mode */ + switch (vap->iv_opmode) { + case IEEE80211_M_HOSTAP: + *frm = wme->wme_bssChanParams.cap_info; + if (vap->iv_flags_ext & IEEE80211_FEXT_UAPSD) + *frm |= WME_CAPINFO_UAPSD_EN; + frm++; + break; + case IEEE80211_M_STA: + /* + * NB: UAPSD drivers must set this up in their + * VAP creation method. + */ + *frm++ = vap->iv_uapsdinfo; + break; + default: + *frm++ = 0; + break; + } + + return frm; } /* * Add a WME parameters element to a frame. */ static uint8_t * -ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) +ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme, + int uapsd_enable) { #define SM(_v, _f) (((_v) << _f##_S) & _f) #define ADDSHORT(frm, v) do { \ @@ -2195,8 +2217,12 @@ ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme) memcpy(frm, ¶m, sizeof(param)); frm += __offsetof(struct ieee80211_wme_info, wme_info); - *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */ + *frm = wme->wme_bssChanParams.cap_info; /* AC info */ + if (uapsd_enable) + *frm |= WME_CAPINFO_UAPSD_EN; + frm++; *frm++ = 0; /* reserved field */ + /* XXX TODO - U-APSD bits - SP, flags below */ for (i = 0; i < WME_NUM_AC; i++) { const struct wmeParams *ac = &wme->wme_bssChanParams.cap_wmeParams[i]; @@ -2789,7 +2815,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) frm = ieee80211_add_wpa(frm, vap); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) - frm = ieee80211_add_wme_info(frm, &ic->ic_wme); + frm = ieee80211_add_wme_info(frm, &ic->ic_wme, ni); /* * Same deal - only send HT info if we're on an 11n @@ -2881,7 +2907,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) } if ((vap->iv_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) - frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + frm = ieee80211_add_wme_param(frm, &ic->ic_wme, + !! (vap->iv_flags_ext & IEEE80211_FEXT_UAPSD)); if ((ni->ni_flags & HTFLAGS) == HTFLAGS) { frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); @@ -3092,7 +3119,8 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) } frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) - frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + frm = ieee80211_add_wme_param(frm, &ic->ic_wme, + !! (vap->iv_flags_ext & IEEE80211_FEXT_UAPSD)); if (IEEE80211_IS_CHAN_HT(bss->ni_chan) && (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT) && legacy != IEEE80211_SEND_LEGACY_11B) { @@ -3490,7 +3518,8 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; - frm = ieee80211_add_wme_param(frm, &ic->ic_wme); + frm = ieee80211_add_wme_param(frm, &ic->ic_wme, + !! (vap->iv_flags_ext & IEEE80211_FEXT_UAPSD)); } if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && (vap->iv_flags_ht & IEEE80211_FHT_HTCOMPAT)) { @@ -3782,7 +3811,8 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) wme->wme_hipri_switch_hysteresis; } if (isset(bo->bo_flags, IEEE80211_BEACON_WME)) { - (void) ieee80211_add_wme_param(bo->bo_wme, wme); + (void) ieee80211_add_wme_param(bo->bo_wme, wme, + vap->iv_flags_ext & IEEE80211_FEXT_UAPSD); clrbit(bo->bo_flags, IEEE80211_BEACON_WME); } } diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 9862cb1da45..be7bb1430c0 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1147,8 +1147,11 @@ ieee80211_wme_initparams_locked(struct ieee80211vap *vap) * field and updates hardware when said field changes. * Otherwise the hardware is programmed with defaults, not what * the beacon actually announces. + * + * Note that we can't ever have 0xff as an actual value; + * the only valid values are 0..15. */ - wme->wme_wmeChanParams.cap_info = 0; + wme->wme_wmeChanParams.cap_info = 0xfe; /* * Select mode; we can be called early in which case we diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index e855e1d437e..ab446d94b08 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -1129,25 +1129,56 @@ sta_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh, IEEE80211_SCAN_FAIL_STATUS); } +/* + * Parse the WME IE for QoS and U-APSD information. + * + * Returns -1 if the IE isn't found, 1 if it's found. + */ +int +ieee80211_parse_wmeie(uint8_t *frm, const struct ieee80211_frame *wh, + struct ieee80211_node *ni) +{ + u_int len = frm[1]; + + ni->ni_uapsd = 0; + + if (len < sizeof(struct ieee80211_wme_param)-2) { + IEEE80211_DISCARD_IE(ni->ni_vap, + IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, + wh, "WME", "too short, len %u", len); + return -1; + } + + ni->ni_uapsd = frm[WME_CAPINFO_IE_OFFSET]; + + IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_POWER | IEEE80211_MSG_ASSOC, + ni, "U-APSD settings from STA: 0x%02x", ni->ni_uapsd); + + return 1; +} + int ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, - const struct ieee80211_frame *wh) + const struct ieee80211_frame *wh, uint8_t *qosinfo) { #define MS(_v, _f) (((_v) & _f) >> _f##_S) struct ieee80211_wme_state *wme = &vap->iv_ic->ic_wme; - u_int len = frm[1], qosinfo; + u_int len = frm[1], qosinfo_count; int i; + *qosinfo = 0; + if (len < sizeof(struct ieee80211_wme_param)-2) { IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME, wh, "WME", "too short, len %u", len); return -1; } - qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; - qosinfo &= WME_QOSINFO_COUNT; + *qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)]; + qosinfo_count = *qosinfo & WME_QOSINFO_COUNT; + /* XXX do proper check for wraparound */ - if (qosinfo == wme->wme_wmeChanParams.cap_info) + if (qosinfo_count == wme->wme_wmeChanParams.cap_info) return 0; frm += __offsetof(struct ieee80211_wme_param, params_acParams); for (i = 0; i < WME_NUM_AC; i++) { @@ -1159,9 +1190,18 @@ ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN); wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX); wmep->wmep_txopLimit = le16dec(frm+2); + IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, + "%s: WME: %d: acm=%d aifsn=%d logcwmin=%d logcwmax=%d txopLimit=%d\n", + __func__, + i, + wmep->wmep_acm, + wmep->wmep_aifsn, + wmep->wmep_logcwmin, + wmep->wmep_logcwmax, + wmep->wmep_txopLimit); frm += 4; } - wme->wme_wmeChanParams.cap_info = qosinfo; + wme->wme_wmeChanParams.cap_info = qosinfo_count; return 1; #undef MS } @@ -1350,11 +1390,12 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, struct ieee80211com *ic = ni->ni_ic; struct ieee80211_channel *rxchan = ic->ic_curchan; struct ieee80211_frame *wh; + int ht_state_change = 0, do_ht = 0; uint8_t *frm, *efrm; uint8_t *rates, *xrates, *wme, *htcap, *htinfo; uint8_t *vhtcap, *vhtopmode; uint8_t rate; - int ht_state_change = 0, do_ht = 0; + uint8_t qosinfo; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; @@ -1443,9 +1484,18 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, /* XXX statistic */ } if (scan.wme != NULL && - (ni->ni_flags & IEEE80211_NODE_QOS) && - ieee80211_parse_wmeparams(vap, scan.wme, wh) > 0) - ieee80211_wme_updateparams(vap); + (ni->ni_flags & IEEE80211_NODE_QOS)) { + int _retval; + if ((_retval = ieee80211_parse_wmeparams(vap, + scan.wme, wh, &qosinfo)) >= 0) { + if (qosinfo & WME_CAPINFO_UAPSD_EN) + ni->ni_flags |= + IEEE80211_NODE_UAPSD; + if (_retval > 0) + ieee80211_wme_updateparams(vap); + } + } else + ni->ni_flags &= ~IEEE80211_NODE_UAPSD; #ifdef IEEE80211_SUPPORT_SUPERG if (scan.ath != NULL) ieee80211_parse_athparams(ni, scan.ath, wh); @@ -1782,7 +1832,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, if (ni->ni_jointime == 0) ni->ni_jointime = time_uptime; if (wme != NULL && - ieee80211_parse_wmeparams(vap, wme, wh) >= 0) { + ieee80211_parse_wmeparams(vap, wme, wh, &qosinfo) >= 0) { ni->ni_flags |= IEEE80211_NODE_QOS; ieee80211_wme_updateparams(vap); } else diff --git a/sys/net80211/ieee80211_sta.h b/sys/net80211/ieee80211_sta.h index 79c576d140b..5f953c7a4dd 100644 --- a/sys/net80211/ieee80211_sta.h +++ b/sys/net80211/ieee80211_sta.h @@ -40,5 +40,12 @@ void ieee80211_sta_vattach(struct ieee80211vap *); * Used by the adhoc/mesh/tdma paths. */ extern int ieee80211_parse_wmeparams(struct ieee80211vap *vap, uint8_t *frm, - const struct ieee80211_frame *wh); + const struct ieee80211_frame *wh, uint8_t *qosinfo); + +/* + * Used in the hostap path. + */ +extern int ieee80211_parse_wmeie(uint8_t *frm, + const struct ieee80211_frame *wh, struct ieee80211_node *ni); + #endif /* !_NET80211_IEEE80211_STA_H_ */ diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 6bdbd609a41..84b69da7ec2 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -581,6 +581,9 @@ struct ieee80211vap { void (*iv_updateslot)(struct ieee80211vap *); struct task iv_slot_task; /* deferred slot time update */ + /* per-vap U-APSD state */ + uint8_t iv_uapsdinfo; /* sta mode QoS Info flags */ + uint64_t iv_spare[6]; }; MALLOC_DECLARE(M_80211_VAP); @@ -662,6 +665,7 @@ MALLOC_DECLARE(M_80211_VAP); #define IEEE80211_FEXT_FRAG_OFFLOAD 0x00200000 /* CONF: hardware does 802.11 fragmentation + assignment */ #define IEEE80211_FEXT_VHT 0x00400000 /* CONF: VHT support */ #define IEEE80211_FEXT_QUIET_IE 0x00800000 /* STATUS: quiet IE in a beacon has been added */ +#define IEEE80211_FEXT_UAPSD 0x01000000 /* CONF: enable U-APSD */ #define IEEE80211_FEXT_BITS \ "\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \ -- 2.45.0