u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab,
+ size_t he_capab_len,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set)
{
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
params.vht_capabilities = vht_capab;
+ params.he_capab = he_capab;
+ params.he_capab_len = he_capab_len;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab,
+ size_t he_capab_len,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
WLAN_STA_AUTHORIZED);
if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0,
- NULL, NULL, sta->flags, 0, 0, 0, 0)) {
+ NULL, NULL, NULL, 0,
+ sta->flags, 0, 0, 0, 0)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
return resp;
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ resp = copy_sta_he_capab(hapd, sta, elems.he_capabilities,
+ elems.he_capabilities_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_P2P
if (elems.p2p) {
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
+ struct ieee80211_he_capabilities he_cap;
int set = 1;
/*
if (sta->flags & WLAN_STA_VHT)
hostapd_get_vht_capab(hapd, sta->vht_capabilities, &vht_cap);
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (sta->flags & WLAN_STA_HE) {
+ hostapd_get_he_capab(hapd, sta->he_capab, &he_cap,
+ sta->he_capab_len);
+ }
+#endif /* CONFIG_IEEE80211AX */
/*
* Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
sta->listen_interval,
sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
+ sta->flags & WLAN_STA_HE ? &he_cap : NULL,
+ sta->flags & WLAN_STA_HE ? sta->he_capab_len : 0,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set)) {
}
#endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+ if (hapd->iconf->ieee80211ax) {
+ p = hostapd_eid_he_capab(hapd, p);
+ p = hostapd_eid_he_operation(hapd, p);
+ p = hostapd_eid_spatial_reuse(hapd, p);
+ p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);
+ }
+#endif /* CONFIG_IEEE80211AX */
+
p = hostapd_eid_ext_capab(hapd, p);
p = hostapd_eid_bss_max_idle_period(hapd, p);
if (sta && sta->qos_map_enabled)
void hostapd_get_vht_capab(struct hostapd_data *hapd,
struct ieee80211_vht_capabilities *vht_cap,
struct ieee80211_vht_capabilities *neg_vht_cap);
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+ const struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ size_t he_capab_len);
int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ht_capab);
const u8 *vht_oper);
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_opmode);
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_capab, size_t he_capab_len);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
/*
* hostapd / IEEE 802.11ax HE
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@phrozen.org>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
+#include "sta_info.h"
#include "ieee802_11.h"
#include "dfs.h"
return pos;
}
+
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+ const struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ size_t he_capab_len)
+{
+ if (!he_cap)
+ return;
+
+ if (he_capab_len > sizeof(*neg_he_cap))
+ he_capab_len = sizeof(*neg_he_cap);
+ /* TODO: mask out unsupported features */
+
+ os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab)
+{
+ u16 sta_rx_mcs_set, ap_tx_mcs_set;
+ u8 mcs_count = 0;
+ const u16 *ap_mcs_set, *sta_mcs_set;
+ int i;
+
+ if (!hapd->iface->current_mode)
+ return 1;
+ ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab.mcs;
+ sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
+ sta_he_capab)->optional;
+
+ /*
+ * Disable HE capabilities for STAs for which there is not even a single
+ * allowed MCS in any supported number of streams, i.e., STA is
+ * advertising 3 (not supported) as HE MCS rates for all supported
+ * band/stream cases.
+ */
+ switch (hapd->iface->conf->he_oper_chwidth) {
+ case CHANWIDTH_80P80MHZ:
+ mcs_count = 3;
+ break;
+ case CHANWIDTH_160MHZ:
+ mcs_count = 2;
+ break;
+ default:
+ mcs_count = 1;
+ break;
+ }
+
+ for (i = 0; i < mcs_count; i++) {
+ int j;
+
+ /* AP Tx MCS map vs. STA Rx MCS map */
+ sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
+ ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
+ &ap_mcs_set[(i * 2) + 1]);
+
+ for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+ if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
+ continue;
+
+ if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
+ continue;
+
+ return 1;
+ }
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "No matching HE MCS found between AP TX and STA RX");
+
+ return 0;
+}
+
+
+u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_capab, size_t he_capab_len)
+{
+ if (!he_capab || !hapd->iconf->ieee80211ax ||
+ !check_valid_he_mcs(hapd, he_capab) ||
+ he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
+ sta->flags &= ~WLAN_STA_HE;
+ os_free(sta->he_capab);
+ sta->he_capab = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_capab) {
+ sta->he_capab =
+ os_zalloc(sizeof(struct ieee80211_he_capabilities));
+ if (!sta->he_capab)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_HE;
+ os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
+ os_memcpy(sta->he_capab, he_capab, he_capab_len);
+ sta->he_capab_len = he_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
os_free(sta->ht_capabilities);
os_free(sta->vht_capabilities);
os_free(sta->vht_operation);
+ os_free(sta->he_capab);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
#define WLAN_STA_MULTI_AP BIT(23)
+#define WLAN_STA_HE BIT(24)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
struct ieee80211_vht_capabilities *vht_capabilities;
struct ieee80211_vht_operation *vht_operation;
u8 vht_opmode;
+ struct ieee80211_he_capabilities *he_capab;
+ size_t he_capab_len;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
#define CHANWIDTH_160MHZ 2
#define CHANWIDTH_80P80MHZ 3
+#define HE_NSS_MAX_STREAMS 8
+
#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
* 00:50:F2 */
#define WPA_IE_VENDOR_TYPE 0x0050f201