From e7d0e97bdbdc996564f06b382af3d5a5164a8fb3 Mon Sep 17 00:00:00 2001 From: Yanbo Li Date: Mon, 10 Nov 2014 23:12:29 +0800 Subject: [PATCH] hostapd: Add vendor specific VHT extension for the 2.4 GHz band This allows vendor specific information element to be used to advertise support for VHT on 2.4 GHz band. In practice, this is used to enable use of 256 QAM rates (VHT-MCS 8 and 9) on 2.4 GHz band. This functionality is disabled by default, but can be enabled with vendor_vht=1 parameter in hostapd.conf if the driver advertises support for VHT on either 2.4 or 5 GHz bands. Signed-off-by: Yanbo Li --- hostapd/config_file.c | 2 + src/ap/ap_config.h | 2 + src/ap/beacon.c | 28 ++++++++++-- src/ap/hw_features.c | 21 ++++++++- src/ap/ieee802_11.c | 18 +++++++- src/ap/ieee802_11.h | 4 ++ src/ap/ieee802_11_vht.c | 83 +++++++++++++++++++++++++++++++--- src/ap/sta_info.c | 3 +- src/ap/sta_info.h | 1 + src/common/ieee802_11_common.c | 9 ++++ src/common/ieee802_11_common.h | 2 + src/common/ieee802_11_defs.h | 3 ++ 12 files changed, 161 insertions(+), 15 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index e30efbe31..99cd05286 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2690,6 +2690,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->vht_oper_centr_freq_seg0_idx = atoi(pos); } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) { conf->vht_oper_centr_freq_seg1_idx = atoi(pos); + } else if (os_strcmp(buf, "vendor_vht") == 0) { + bss->vendor_vht = atoi(pos); #endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 58af6cb19..e5215c529 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -551,6 +551,8 @@ struct hostapd_bss_config { int mesh; int radio_measurements; + + int vendor_vht; }; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 4a8703acc..b0a74e01d 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -379,6 +379,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) buflen += wpabuf_len(hapd->conf->vendor_elements); + if (hapd->conf->vendor_vht) { + buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } resp = os_zalloc(buflen); if (resp == NULL) return NULL; @@ -446,8 +450,12 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp, &hapd->cs_c_off_proberesp); #ifdef CONFIG_IEEE80211AC - pos = hostapd_eid_vht_capabilities(hapd, pos); - pos = hostapd_eid_vht_operation(hapd, pos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + } + if (hapd->conf->vendor_vht) + pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ @@ -776,6 +784,14 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_P2P */ if (hapd->conf->vendor_elements) tail_len += wpabuf_len(hapd->conf->vendor_elements); + +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht) { + tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation); + } +#endif /* CONFIG_IEEE80211AC */ + tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { wpa_printf(MSG_ERROR, "Failed to set beacon data"); @@ -865,8 +881,12 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_add_csa_elems(hapd, tailpos, tail, &hapd->cs_c_off_beacon); #ifdef CONFIG_IEEE80211AC - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); - tailpos = hostapd_eid_vht_operation(hapd, tailpos); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_operation(hapd, tailpos); + } + if (hapd->conf->vendor_vht) + tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ /* Wi-Fi Alliance WMM */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 6b0a72d83..f6d79ea6b 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -641,12 +641,31 @@ static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { - u32 hw = iface->current_mode->vht_capab; + struct hostapd_hw_modes *mode = iface->current_mode; + u32 hw = mode->vht_capab; u32 conf = iface->conf->vht_capab; wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x", hw, conf); + if (mode->mode == HOSTAPD_MODE_IEEE80211G && + iface->conf->bss[0]->vendor_vht && + mode->vht_capab == 0 && iface->hw_features) { + int i; + + for (i = 0; i < iface->num_hw_features; i++) { + if (iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &iface->hw_features[i]; + hw = mode->vht_capab; + wpa_printf(MSG_DEBUG, + "update hw vht capab based on 5 GHz band: 0x%x", + hw); + break; + } + } + } + #define VHT_CAP_CHECK(cap) \ do { \ if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 3d4488a1c..89911b1fd 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1327,6 +1327,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, "mandatory VHT PHY - reject association"); return WLAN_STATUS_ASSOC_DENIED_NO_VHT; } + + if (hapd->conf->vendor_vht && !elems.vht_capabilities) { + resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht, + elems.vendor_vht_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_P2P @@ -1616,8 +1623,10 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_IEEE80211N */ #ifdef CONFIG_IEEE80211AC - p = hostapd_eid_vht_capabilities(hapd, p); - p = hostapd_eid_vht_operation(hapd, p); + if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { + p = hostapd_eid_vht_capabilities(hapd, p); + p = hostapd_eid_vht_operation(hapd, p); + } #endif /* CONFIG_IEEE80211AC */ p = hostapd_eid_ext_capab(hapd, p); @@ -1625,6 +1634,11 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); +#ifdef CONFIG_IEEE80211AC + if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + p = hostapd_eid_vendor_vht(hapd, p); +#endif /* CONFIG_IEEE80211AC */ + if (sta->flags & WLAN_STA_WMM) p = hostapd_eid_wmm(hapd, p); diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index beaeac500..41c27d906 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -51,6 +51,7 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, const u8 *addr, const u8 *trans_id); @@ -62,6 +63,9 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd, struct ieee80211_vht_capabilities *neg_vht_cap); u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ht_capab, size_t ht_capab_len); +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len); + void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta); void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta); diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 437cf5031..89c14ec87 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -22,12 +22,25 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) { struct ieee80211_vht_capabilities *cap; + struct hostapd_hw_modes *mode = hapd->iface->current_mode; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode || - hapd->conf->disable_11ac) + if (!mode) return eid; + if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && + mode->vht_capab == 0 && hapd->iface->hw_features) { + int i; + + for (i = 0; i < hapd->iface->num_hw_features; i++) { + if (hapd->iface->hw_features[i].mode == + HOSTAPD_MODE_IEEE80211A) { + mode = &hapd->iface->hw_features[i]; + break; + } + } + } + *pos++ = WLAN_EID_VHT_CAP; *pos++ = sizeof(*cap); @@ -37,8 +50,7 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) hapd->iface->conf->vht_capab); /* Supported MCS set comes from hw */ - os_memcpy(&cap->vht_supported_mcs_set, - hapd->iface->current_mode->vht_mcs_set, 8); + os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); pos += sizeof(*cap); @@ -51,9 +63,6 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) struct ieee80211_vht_operation *oper; u8 *pos = eid; - if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac) - return eid; - *pos++ = WLAN_EID_VHT_OPERATION; *pos++ = sizeof(*oper); @@ -109,6 +118,66 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, } +u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ie, size_t len) +{ + const u8 *vht_capab; + unsigned int vht_capab_len; + + if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) || + hapd->conf->disable_11ac) + goto no_capab; + + /* The VHT Capabilities element embedded in vendor VHT */ + vht_capab = ie + 5; + if (vht_capab[0] != WLAN_EID_VHT_CAP) + goto no_capab; + vht_capab_len = vht_capab[1]; + if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || + vht_capab_len > ie + len - vht_capab - 2) + goto no_capab; + vht_capab += 2; + + if (sta->vht_capabilities == NULL) { + sta->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (sta->vht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT; + os_memcpy(sta->vht_capabilities, vht_capab, + sizeof(struct ieee80211_vht_capabilities)); + return WLAN_STATUS_SUCCESS; + +no_capab: + sta->flags &= ~WLAN_STA_VENDOR_VHT; + return WLAN_STATUS_SUCCESS; +} + + +u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = (5 + /* The Vendor OUI, type and subtype */ + 2 + sizeof(struct ieee80211_vht_capabilities) + + 2 + sizeof(struct ieee80211_vht_operation)); + + WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); + pos += 4; + *pos++ = VENDOR_VHT_SUBTYPE; + pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_operation(hapd, pos); + + return pos; +} + + u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, const u8 *vht_oper_notif) { diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 1c2197a42..f4de14093 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1101,7 +1101,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1119,6 +1119,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), (flags & WLAN_STA_GAS ? "[GAS]" : ""), (flags & WLAN_STA_VHT ? "[VHT]" : ""), + (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? "[WNM_SLEEP_MODE]" : "")); if (os_snprintf_error(buflen, res)) diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 588a9e2f2..59d0e2915 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -35,6 +35,7 @@ #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_VENDOR_VHT BIT(21) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index e1d45cf96..ed8d46619 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -128,6 +128,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->vendor_ht_cap = pos; elems->vendor_ht_cap_len = elen; break; + case VENDOR_VHT_TYPE: + if (elen > 4 && + (pos[4] == VENDOR_VHT_SUBTYPE || + pos[4] == VENDOR_VHT_SUBTYPE2)) { + elems->vendor_vht = pos; + elems->vendor_vht_len = elen; + } else + return -1; + break; default: wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom " "information element ignored " diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 2357afc5c..05fe32b45 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -35,6 +35,7 @@ struct ieee802_11_elems { const u8 *vht_operation; const u8 *vht_opmode_notif; const u8 *vendor_ht_cap; + const u8 *vendor_vht; const u8 *p2p; const u8 *wfd; const u8 *link_id; @@ -71,6 +72,7 @@ struct ieee802_11_elems { u8 vht_capabilities_len; u8 vht_operation_len; u8 vendor_ht_cap_len; + u8 vendor_vht_len; u8 p2p_len; u8 wfd_len; u8 interworking_len; diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index dfe0fafd3..803b8ccc8 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1155,6 +1155,9 @@ enum plink_action_field { }; #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ +#define VENDOR_VHT_TYPE 0x04 +#define VENDOR_VHT_SUBTYPE 0x08 +#define VENDOR_VHT_SUBTYPE2 0x00 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */