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 <yanbol@qti.qualcomm.com>
This commit is contained in:
Yanbo Li 2014-11-10 23:12:29 +08:00 committed by Jouni Malinen
parent 3e7f1c7980
commit e7d0e97bdb
12 changed files with 161 additions and 15 deletions

View File

@ -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);

View File

@ -551,6 +551,8 @@ struct hostapd_bss_config {
int mesh;
int radio_measurements;
int vendor_vht;
};

View File

@ -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 */

View File

@ -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)) \

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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))

View File

@ -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)

View File

@ -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 "

View File

@ -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;

View File

@ -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 */