---
Changes in V2
* fix the comment for the basic NSS rates was wrong
* add SPR IE to assoc responses
src/ap/ap_drv_ops.c | 6 ++
src/ap/ap_drv_ops.h | 4 ++
src/ap/ieee802_11.c | 35 ++++++++++-
src/ap/ieee802_11.h | 12 ++++
src/ap/ieee802_11_he.c | 130 +++++++++++++++++++++++++++++++++++++++++
src/ap/sta_info.c | 2 +
src/ap/sta_info.h | 5 ++
src/common/ieee802_11_common.c | 4 ++
src/common/ieee802_11_common.h | 2 +
src/common/ieee802_11_defs.h | 5 +-
src/drivers/driver.h | 4 ++
src/drivers/driver_nl80211.c | 10 ++++
12 files changed, 217 insertions(+), 2 deletions(-)
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 067cf863e..3628f3b22 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -413,6 +413,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab, int he_capab_len,
+ const struct ieee80211_he_operation *he_oper, int he_oper_len,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set)
{
@@ -432,6 +434,10 @@ int hostapd_sta_add(struct hostapd_data *hapd,
params.listen_interval = listen_interval;
params.ht_capabilities = ht_capab;
params.vht_capabilities = vht_capab;
+ params.he_capabilities = he_capab;
+ params.he_capabilities_len = he_capab_len;
+ params.he_operation = he_oper;
+ params.he_operation_len = he_oper_len;
params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
params.vht_opmode = vht_opmode;
params.flags = hostapd_sta_flags_to_drv(flags);
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index de40171e1..db9653868 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,6 +41,10 @@ int hostapd_sta_add(struct hostapd_data *hapd,
u16 listen_interval,
const struct ieee80211_ht_capabilities *ht_capab,
const struct ieee80211_vht_capabilities *vht_capab,
+ const struct ieee80211_he_capabilities *he_capab,
+ int he_capab_len,
+ const struct ieee80211_he_operation *he_oper,
+ int he_oper_len,
u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
int set);
int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 56f6363a3..82917ebea 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2325,7 +2325,8 @@ static void handle_auth(struct hostapd_data *hapd,
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, NULL, 0,
+ sta->flags, 0, 0, 0, 0)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_NOTICE,
@@ -2870,6 +2871,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
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;
+
+ resp = copy_sta_he_oper(hapd, sta, elems.he_operation, elems.he_operation_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+ }
+#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_P2P
if (elems.p2p) {
@@ -3232,6 +3244,8 @@ static int add_associated_sta(struct hostapd_data *hapd,
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
+ struct ieee80211_he_capabilities he_cap;
+ struct ieee80211_he_operation he_oper;
int set = 1;
/*
@@ -3284,6 +3298,12 @@ static int add_associated_sta(struct hostapd_data *hapd,
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_capabilities, &he_cap, sta->he_capabilities_len);
+ hostapd_get_he_oper(hapd, sta->he_operation, &he_oper, sta->he_operation_len);
+ }
+#endif /* CONFIG_IEEE80211AX */
/*
* Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
@@ -3295,6 +3315,10 @@ static int add_associated_sta(struct hostapd_data *hapd,
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_capabilities_len : 0,
+ sta->flags & WLAN_STA_HE ? &he_oper : NULL,
+ sta->flags & WLAN_STA_HE ? sta->he_operation_len : 0,
sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
sta->vht_opmode, sta->p2p_ie ? 1 : 0,
set)) {
@@ -3435,6 +3459,15 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#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_he_mu_edca_parameter_set(hapd, p);
+ p = hostapd_eid_spatial_reuse(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)
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 3699246b3..830d0586f 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -71,6 +71,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
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,
+ struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ int he_capab_len);
+void hostapd_get_he_oper(struct hostapd_data *hapd,
+ struct ieee80211_he_operation *he_oper,
+ struct ieee80211_he_operation *neg_he_oper,
+ int he_oper_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);
@@ -86,6 +94,10 @@ u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
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, int he_capab_len);
+u16 copy_sta_he_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_oper, int he_oper_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,
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 72d88b0a2..b32e5aa2f 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -1,6 +1,7 @@
/*
* hostapd / IEEE 802.11ax HE
* Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2019 John Crispin <john@xxxxxxxxxxx>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,6 +14,7 @@
#include "hostapd.h"
#include "ap_config.h"
#include "beacon.h"
+#include "sta_info.h"
#include "ieee802_11.h"
#include "dfs.h"
@@ -91,6 +93,10 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
(hapd->iface->conf->he_op.he_bss_color <<
HE_OPERATION_BSS_COLOR_OFFSET);
+ /* Basic NSS rates. Make no special requirements */
+ oper->he_mcs_nss_set[0] = 0xff;
+ oper->he_mcs_nss_set[1] = 0xff;
+
/* TODO: conditional MaxBSSID Indicator subfield */
pos += oper_size;
@@ -170,3 +176,127 @@ u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
return pos;
}
+
+void hostapd_get_he_capab(struct hostapd_data *hapd,
+ struct ieee80211_he_capabilities *he_cap,
+ struct ieee80211_he_capabilities *neg_he_cap,
+ int he_capab_len)
+{
+ if (he_cap == NULL)
+ return;
+
+ /* TODO: mask out unsupported features */
+
+ os_memcpy(neg_he_cap, he_cap, he_capab_len);
+}
+
+
+void hostapd_get_he_oper(struct hostapd_data *hapd,
+ struct ieee80211_he_operation *he_oper,
+ struct ieee80211_he_operation *neg_he_oper,
+ int he_oper_len)
+{
+ if (he_oper == NULL)
+ return;
+
+ /* TODO: mask out unsupported features */
+
+ os_memcpy(neg_he_oper, he_oper, he_oper_len);
+}
+
+
+static int check_valid_he_mcs(struct hostapd_hw_modes *mode,
+ const u8 *sta_he_capab)
+{
+ const struct ieee80211_he_capabilities *he_cap;
+ struct ieee80211_he_capabilities ap_he_cap;
+ u16 sta_rx_mcs_set, ap_tx_mcs_set;
+ int i;
+
+ if (!mode)
+ return 1;
+
+ /*
+ * Disable HE caps 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.
+ */
+ os_memcpy(&ap_he_cap.he_txrx_mcs_support, mode->he_capab.mcs,
+ HE_MAX_MCS_CAPAB_SIZE);
+ he_cap = (const struct ieee80211_he_capabilities *) sta_he_capab;
+
+ for (i = 0; i < HE_NSS_MAX_BANDS; i++) {
+ int j;
+
+ /* AP Tx MCS map vs. STA Rx MCS map */
+ sta_rx_mcs_set = le_to_host16(he_cap->he_txrx_mcs_support[i * 2]);
+ ap_tx_mcs_set = le_to_host16(ap_he_cap.he_txrx_mcs_support[(i * 2) + 1]);
+
+ for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
+ if ((ap_tx_mcs_set & (0x3 << (j * 2))) == 3)
+ continue;
+
+ if ((sta_rx_mcs_set & (0x3 << (j * 2))) == 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, int he_capab_len)
+{
+ if (!he_capab || !hapd->iconf->ieee80211ax ||
+ !check_valid_he_mcs(hapd->iface->current_mode, he_capab)) {
+ sta->flags &= ~WLAN_STA_HE;
+ os_free(sta->he_capabilities);
+ sta->he_capabilities = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (sta->he_capabilities == NULL) {
+ sta->he_capabilities =
+ os_zalloc(sizeof(struct ieee80211_he_capabilities));
+ if (sta->he_capabilities == NULL)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ sta->flags |= WLAN_STA_HE;
+ os_memset(sta->he_capabilities, 0, sizeof(struct ieee80211_he_capabilities));
+ os_memcpy(sta->he_capabilities, he_capab, he_capab_len);
+ sta->he_capabilities_len = he_capab_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
+u16 copy_sta_he_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *he_oper, int he_oper_len)
+{
+ if (!he_oper) {
+ os_free(sta->he_operation);
+ sta->he_operation = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->he_operation) {
+ sta->he_operation =
+ os_zalloc(sizeof(struct ieee80211_he_operation));
+ if (!sta->he_operation)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memset(sta->he_operation, 0, sizeof(struct ieee80211_he_operation));
+ os_memcpy(sta->he_operation, he_oper, he_oper_len);
+ sta->he_operation_len = he_oper_len;
+
+ return WLAN_STATUS_SUCCESS;
+}
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 4f9eae847..6de858449 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -330,6 +330,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->ht_capabilities);
os_free(sta->vht_capabilities);
os_free(sta->vht_operation);
+ os_free(sta->he_capabilities);
+ os_free(sta->he_operation);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index ece0c60ab..2d28c2589 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -37,6 +37,7 @@
#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)
@@ -166,6 +167,10 @@ struct sta_info {
struct ieee80211_vht_capabilities *vht_capabilities;
struct ieee80211_vht_operation *vht_operation;
u8 vht_opmode;
+ struct ieee80211_he_capabilities *he_capabilities;
+ int he_capabilities_len;
+ struct ieee80211_he_operation *he_operation;
+ int he_operation_len;
#ifdef CONFIG_IEEE80211W
int sa_query_count; /* number of pending SA Query requests;
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index c02b99dbc..4de539935 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -274,6 +274,10 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->he_capabilities = pos;
elems->he_capabilities_len = elen;
break;
+ case WLAN_EID_EXT_HE_OPERATION:
+ elems->he_operation = pos;
+ elems->he_operation_len = elen;
+ break;
case WLAN_EID_EXT_OCV_OCI:
elems->oci = pos;
elems->oci_len = elen;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 930d45420..9b045b41a 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -94,6 +94,7 @@ struct ieee802_11_elems {
const u8 *oci;
const u8 *multi_ap;
const u8 *he_capabilities;
+ const u8 *he_operation;
u8 ssid_len;
u8 supp_rates_len;
@@ -143,6 +144,7 @@ struct ieee802_11_elems {
u8 oci_len;
u8 multi_ap_len;
u8 he_capabilities_len;
+ u8 he_operation_len;
struct mb_ies_info mb_ies;
};
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 90615ece6..54bb35520 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1280,6 +1280,9 @@ struct ieee80211_ampe_ie {
#define VHT_CHANWIDTH_160MHZ 2
#define VHT_CHANWIDTH_80P80MHZ 3
+#define HE_NSS_MAX_BANDS 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
@@ -2105,7 +2108,7 @@ enum nr_chan_width {
struct ieee80211_he_capabilities {
u8 he_mac_capab_info[6];
u8 he_phy_capab_info[11];
- u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
+ u16 he_txrx_mcs_support[6]; /* TODO: 2, 4, or 6 words */
/* PPE Thresholds (optional) */
} STRUCT_PACKED;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 0acc0958a..376e65c4d 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1788,6 +1788,10 @@ struct hostapd_sta_add_params {
const struct ieee80211_vht_capabilities *vht_capabilities;
int vht_opmode_enabled;
u8 vht_opmode;
+ const struct ieee80211_he_capabilities *he_capabilities;
+ int he_capabilities_len;
+ const struct ieee80211_he_operation *he_operation;
+ int he_operation_len;
u32 flags; /* bitmask of WPA_STA_* flags */
u32 flags_mask; /* unset bits in flags */
#ifdef CONFIG_MESH
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5da8a793b..94c77b470 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -4563,6 +4563,16 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
}
+ if (params->he_capabilities) {
+ wpa_hexdump(MSG_DEBUG, " * he_capabilities",
+ (u8 *) params->he_capabilities,
+ params->he_capabilities_len);
+ if (nla_put(msg, NL80211_ATTR_HE_CAPABILITY,
+ params->he_capabilities_len,
+ params->he_capabilities))
+ goto fail;
+ }
+
if (params->ext_capab) {
wpa_hexdump(MSG_DEBUG, " * ext_capab",
params->ext_capab, params->ext_capab_len);