diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index 893e6d9ee..e998fc620 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -346,7 +346,7 @@ 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, - u32 flags, u8 qosinfo) + u32 flags, u8 qosinfo, u8 vht_opmode) { struct hostapd_sta_add_params params; @@ -364,6 +364,8 @@ int hostapd_sta_add(struct hostapd_data *hapd, params.listen_interval = listen_interval; params.ht_capabilities = ht_capab; params.vht_capabilities = vht_capab; + params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED); + params.vht_opmode = vht_opmode; params.flags = hostapd_sta_flags_to_drv(flags); params.qosinfo = qosinfo; return hapd->driver->sta_add(hapd->drv_priv, ¶ms); diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 15a4b2678..9edaf7d74 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -40,7 +40,7 @@ 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, - u32 flags, u8 qosinfo); + u32 flags, u8 qosinfo, u8 vht_opmode); int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, size_t elem_len); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index c755265c8..9251ac3a5 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -895,6 +895,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems.vht_capabilities_len); if (resp != WLAN_STATUS_SUCCESS) return resp; + + resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht && !(sta->flags & WLAN_STA_VHT)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -1937,7 +1942,7 @@ static void handle_assoc_cb(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, sta->qosinfo)) { + sta->flags, sta->qosinfo, sta->vht_opmode)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE, "Could not add STA to kernel driver"); diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 566a65ad6..809b4ca6c 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -61,6 +61,8 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_capab, size_t vht_capab_len); +u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_opmode); 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_vht.c b/src/ap/ieee802_11_vht.c index f2ab182db..221d9c207 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -108,6 +108,35 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, return WLAN_STATUS_SUCCESS; } + +u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *vht_oper_notif) +{ + u8 channel_width; + + if (!vht_oper_notif) { + sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED; + return WLAN_STATUS_SUCCESS; + } + + channel_width = *vht_oper_notif & VHT_OPMODE_CHANNEL_WIDTH_MASK; + + if (channel_width != VHT_CHANWIDTH_USE_HT && + channel_width != VHT_CHANWIDTH_80MHZ && + channel_width != VHT_CHANWIDTH_160MHZ && + channel_width != VHT_CHANWIDTH_80P80MHZ && + ((*vht_oper_notif & VHT_OPMODE_CHANNEL_RxNSS_MASK) >> + VHT_OPMODE_NOTIF_RX_NSS_SHIFT) > VHT_RX_NSS_MAX_STREAMS - 1) { + sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED; + sta->vht_opmode = *vht_oper_notif; + return WLAN_STATUS_SUCCESS; +} + + void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *vht_cap, struct ieee80211_vht_capabilities *neg_vht_cap) diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 9b77e0609..240b92634 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -27,6 +27,7 @@ #define WLAN_STA_GAS BIT(17) #define WLAN_STA_VHT BIT(18) #define WLAN_STA_WNM_SLEEP_MODE BIT(19) +#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -103,6 +104,7 @@ struct sta_info { struct ieee80211_ht_capabilities *ht_capabilities; struct ieee80211_vht_capabilities *vht_capabilities; + u8 vht_opmode; #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 809089fac..50bdc0147 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -252,6 +252,11 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->vht_operation = pos; elems->vht_operation_len = elen; break; + case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION: + if (elen != 1) + break; + elems->vht_opmode_notif = pos; + break; case WLAN_EID_LINK_ID: if (elen < 18) break; diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index b84dd9e75..4fb2e842a 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -30,6 +30,7 @@ struct ieee802_11_elems { const u8 *ht_operation; const u8 *vht_capabilities; const u8 *vht_operation; + const u8 *vht_opmode_notif; const u8 *vendor_ht_cap; const u8 *p2p; const u8 *wfd; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 6f7f7779d..0e39caf56 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -764,6 +764,13 @@ struct ieee80211_vht_operation { #define VHT_CAP_RX_ANTENNA_PATTERN ((u32) BIT(28)) #define VHT_CAP_TX_ANTENNA_PATTERN ((u32) BIT(29)) +#define VHT_OPMODE_CHANNEL_WIDTH_MASK ((u8) BIT(0) | BIT(1)) +#define VHT_OPMODE_CHANNEL_RxNSS_MASK ((u8) BIT(4) | BIT(5) | \ + BIT(6)) +#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT 4 + +#define VHT_RX_NSS_MAX_STREAMS 8 + /* VHT channel widths */ #define VHT_CHANWIDTH_USE_HT 0 #define VHT_CHANWIDTH_80MHZ 1 diff --git a/src/drivers/driver.h b/src/drivers/driver.h index d4136d7f8..632ae3a77 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1028,6 +1028,8 @@ struct hostapd_sta_add_params { u16 listen_interval; const struct ieee80211_ht_capabilities *ht_capabilities; const struct ieee80211_vht_capabilities *vht_capabilities; + int vht_opmode_enabled; + u8 vht_opmode; u32 flags; /* bitmask of WPA_STA_* flags */ int set; /* Set STA parameters instead of add */ u8 qosinfo; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index f4bb31c6b..e96c0c22a 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -7351,6 +7351,12 @@ static int wpa_driver_nl80211_sta_add(void *priv, params->vht_capabilities); } + if (params->vht_opmode_enabled) { + wpa_printf(MSG_DEBUG, " * opmode=%u", params->vht_opmode); + NLA_PUT_U8(msg, NL80211_ATTR_OPMODE_NOTIF, + params->vht_opmode); + } + wpa_printf(MSG_DEBUG, " * capability=0x%x", params->capability); NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);