nl80211: Add driver multi iftype HE capability parsing

The HE capabilities are no longer per PHY but per iftype on this
specific PHY. It is therefore no longer enough to just parse the AP
capabilities.

The he_capabilities are now duplicated to store all information for
IEEE80211_MODE_* which hostap cares about. The nl80211 driver fills in
this information when the iftype supports HE. The rest of the code still
only uses the IEEE80211_HE_AP portion but can be extended later to also
use other HE capabilities.

Signed-off-by: Sven Eckelmann <seckelmann@datto.com>
This commit is contained in:
Sven Eckelmann 2019-06-14 16:49:20 +02:00 committed by Jouni Malinen
parent f74618cb92
commit 29d8bd1dec
9 changed files with 99 additions and 52 deletions

View file

@ -546,14 +546,15 @@ int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
int center_segment0, int center_segment1)
{
struct hostapd_freq_params data;
struct hostapd_hw_modes *cmode = hapd->iface->current_mode;
if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
vht_enabled, he_enabled, sec_channel_offset,
oper_chwidth,
center_segment0, center_segment1,
hapd->iface->current_mode ?
hapd->iface->current_mode->vht_capab : 0,
&hapd->iface->current_mode->he_capab))
cmode ? cmode->vht_capab : 0,
cmode ?
&cmode->he_capab[IEEE80211_MODE_AP] : NULL))
return -1;
if (hapd->driver == NULL)
@ -798,8 +799,9 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
struct hostapd_data *hapd = iface->bss[0];
struct hostapd_freq_params data;
int res;
struct hostapd_hw_modes *cmode = iface->current_mode;
if (!hapd->driver || !hapd->driver->start_dfs_cac)
if (!hapd->driver || !hapd->driver->start_dfs_cac || !cmode)
return 0;
if (!iface->conf->ieee80211h) {
@ -812,8 +814,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
vht_enabled, he_enabled, sec_channel_offset,
oper_chwidth, center_segment0,
center_segment1,
iface->current_mode->vht_capab,
&iface->current_mode->he_capab)) {
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP])) {
wpa_printf(MSG_ERROR, "Can't set freq params");
return -1;
}

View file

@ -510,7 +510,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
pos = hostapd_eid_he_capab(hapd, pos);
pos = hostapd_eid_he_capab(hapd, pos, IEEE80211_MODE_AP);
pos = hostapd_eid_he_operation(hapd, pos);
pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
pos = hostapd_eid_spatial_reuse(hapd, pos);
@ -1226,7 +1226,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
tailpos = hostapd_eid_he_capab(hapd, tailpos);
tailpos = hostapd_eid_he_capab(hapd, tailpos,
IEEE80211_MODE_AP);
tailpos = hostapd_eid_he_operation(hapd, tailpos);
tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
tailpos = hostapd_eid_spatial_reuse(hapd, tailpos);
@ -1398,6 +1399,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
struct hostapd_freq_params freq;
struct hostapd_iface *iface = hapd->iface;
struct hostapd_config *iconf = iface->conf;
struct hostapd_hw_modes *cmode = iface->current_mode;
struct wpabuf *beacon, *proberesp, *assocresp;
int res, ret = -1;
@ -1421,7 +1423,7 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
params.reenable = hapd->reenable_beacon;
hapd->reenable_beacon = 0;
if (iface->current_mode &&
if (cmode &&
hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
iconf->channel, iconf->ieee80211n,
iconf->ieee80211ac, iconf->ieee80211ax,
@ -1429,8 +1431,8 @@ int ieee802_11_set_beacon(struct hostapd_data *hapd)
hostapd_get_oper_chwidth(iconf),
hostapd_get_oper_centr_freq_seg0_idx(iconf),
hostapd_get_oper_centr_freq_seg1_idx(iconf),
iface->current_mode->vht_capab,
&iface->current_mode->he_capab) == 0)
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP]) == 0)
params.freq = &freq;
res = hostapd_drv_set_ap(hapd, &params);

View file

@ -888,6 +888,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
struct csa_settings csa_settings;
unsigned int i;
int err = 1;
struct hostapd_hw_modes *cmode = iface->current_mode;
wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
__func__, iface->cac_started ? "yes" : "no",
@ -966,8 +967,8 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
hostapd_get_oper_chwidth(iface->conf),
oper_centr_freq_seg0_idx,
oper_centr_freq_seg1_idx,
iface->current_mode->vht_capab,
&iface->current_mode->he_capab);
cmode->vht_capab,
&cmode->he_capab[IEEE80211_MODE_AP]);
if (err) {
wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");

View file

@ -3241,7 +3241,8 @@ static int hostapd_change_config_freq(struct hostapd_data *hapd,
hostapd_get_oper_centr_freq_seg0_idx(conf),
hostapd_get_oper_centr_freq_seg1_idx(conf),
conf->vht_capab,
mode ? &mode->he_capab : NULL))
mode ? &mode->he_capab[IEEE80211_MODE_AP] :
NULL))
return -1;
switch (params->bandwidth) {

View file

@ -2990,7 +2990,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
#endif /* CONFIG_IEEE80211AC */
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
resp = copy_sta_he_capab(hapd, sta, elems.he_capabilities,
resp = copy_sta_he_capab(hapd, sta, IEEE80211_MODE_AP,
elems.he_capabilities,
elems.he_capabilities_len);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@ -3583,7 +3584,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
p = hostapd_eid_he_capab(hapd, p);
p = hostapd_eid_he_capab(hapd, p, IEEE80211_MODE_AP);
p = hostapd_eid_he_operation(hapd, p);
p = hostapd_eid_spatial_reuse(hapd, p);
p = hostapd_eid_he_mu_edca_parameter_set(hapd, p);

View file

@ -18,6 +18,7 @@ struct ieee80211_vht_capabilities;
struct ieee80211_mgmt;
struct vlan_description;
struct hostapd_sta_wpa_psk_short;
enum ieee80211_op_mode;
int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
struct hostapd_frame_info *fi);
@ -57,7 +58,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode);
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid);
@ -91,7 +93,8 @@ u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
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);
enum ieee80211_op_mode opmode, 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,

View file

@ -44,7 +44,8 @@ static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
}
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
enum ieee80211_op_mode opmode)
{
struct ieee80211_he_capabilities *cap;
struct hostapd_hw_modes *mode = hapd->iface->current_mode;
@ -56,8 +57,8 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
return eid;
ie_size = sizeof(struct ieee80211_he_capabilities);
ppet_size = ieee80211_he_ppet_size(mode->he_capab.ppet[0],
mode->he_capab.phy_cap);
ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
mode->he_capab[opmode].phy_cap);
switch (hapd->iface->conf->he_oper_chwidth) {
case CHANWIDTH_80P80MHZ:
@ -86,14 +87,14 @@ u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
cap = (struct ieee80211_he_capabilities *) pos;
os_memset(cap, 0, sizeof(*cap));
os_memcpy(cap->he_mac_capab_info, mode->he_capab.mac_cap,
os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
HE_MAX_MAC_CAPAB_SIZE);
os_memcpy(cap->he_phy_capab_info, mode->he_capab.phy_cap,
os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
HE_MAX_PHY_CAPAB_SIZE);
os_memcpy(cap->optional, mode->he_capab.mcs, mcs_nss_size);
os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
if (ppet_size)
os_memcpy(&cap->optional[mcs_nss_size], mode->he_capab.ppet,
ppet_size);
os_memcpy(&cap->optional[mcs_nss_size],
mode->he_capab[opmode].ppet, ppet_size);
if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
@ -260,7 +261,8 @@ void hostapd_get_he_capab(struct hostapd_data *hapd,
}
static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab)
static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
enum ieee80211_op_mode opmode)
{
u16 sta_rx_mcs_set, ap_tx_mcs_set;
u8 mcs_count = 0;
@ -269,7 +271,7 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab)
if (!hapd->iface->current_mode)
return 1;
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab.mcs;
ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
sta_he_capab)->optional;
@ -318,10 +320,11 @@ static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab)
u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *he_capab, size_t he_capab_len)
enum ieee80211_op_mode opmode, const u8 *he_capab,
size_t he_capab_len)
{
if (!he_capab || !hapd->iconf->ieee80211ax ||
!check_valid_he_mcs(hapd, he_capab) ||
!check_valid_he_mcs(hapd, he_capab, opmode) ||
he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
sta->flags &= ~WLAN_STA_HE;
os_free(sta->he_capab);

View file

@ -200,6 +200,17 @@ struct he_capabilities {
#define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
enum ieee80211_op_mode {
IEEE80211_MODE_INFRA = 0,
IEEE80211_MODE_IBSS = 1,
IEEE80211_MODE_AP = 2,
IEEE80211_MODE_MESH = 5,
/* only add new entries before IEEE80211_MODE_NUM */
IEEE80211_MODE_NUM
};
/**
* struct hostapd_hw_modes - Supported hardware mode information
*/
@ -259,15 +270,10 @@ struct hostapd_hw_modes {
/**
* he_capab - HE (IEEE 802.11ax) capabilities
*/
struct he_capabilities he_capab;
struct he_capabilities he_capab[IEEE80211_MODE_NUM];
};
#define IEEE80211_MODE_INFRA 0
#define IEEE80211_MODE_IBSS 1
#define IEEE80211_MODE_AP 2
#define IEEE80211_MODE_MESH 5
#define IEEE80211_CAP_ESS 0x0001
#define IEEE80211_CAP_IBSS 0x0002
#define IEEE80211_CAP_PRIVACY 0x0010

View file

@ -1551,26 +1551,32 @@ static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
}
static int phy_info_iftype(struct hostapd_hw_modes *mode,
struct nlattr *nl_iftype)
static void phy_info_iftype_copy(struct he_capabilities *he_capab,
enum ieee80211_op_mode opmode,
struct nlattr **tb, struct nlattr **tb_flags)
{
struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
struct he_capabilities *he_capab = &mode->he_capab;
struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
enum nl80211_iftype iftype;
size_t len;
nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
nla_data(nl_iftype), nla_len(nl_iftype), NULL);
switch (opmode) {
case IEEE80211_MODE_INFRA:
iftype = NL80211_IFTYPE_STATION;
break;
case IEEE80211_MODE_IBSS:
iftype = NL80211_IFTYPE_ADHOC;
break;
case IEEE80211_MODE_AP:
iftype = NL80211_IFTYPE_AP;
break;
case IEEE80211_MODE_MESH:
iftype = NL80211_IFTYPE_MESH_POINT;
break;
default:
return;
}
if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
return NL_STOP;
if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
return NL_STOP;
if (!nla_get_flag(tb_flags[NL80211_IFTYPE_AP]))
return NL_OK;
if (!nla_get_flag(tb_flags[iftype]))
return;
he_capab->he_supported = 1;
@ -1613,6 +1619,28 @@ static int phy_info_iftype(struct hostapd_hw_modes *mode,
nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE]),
len);
}
}
static int phy_info_iftype(struct hostapd_hw_modes *mode,
struct nlattr *nl_iftype)
{
struct nlattr *tb[NL80211_BAND_IFTYPE_ATTR_MAX + 1];
struct nlattr *tb_flags[NL80211_IFTYPE_MAX + 1];
unsigned int i;
nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
nla_data(nl_iftype), nla_len(nl_iftype), NULL);
if (!tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES])
return NL_STOP;
if (nla_parse_nested(tb_flags, NL80211_IFTYPE_MAX,
tb[NL80211_BAND_IFTYPE_ATTR_IFTYPES], NULL))
return NL_STOP;
for (i = 0; i < IEEE80211_MODE_NUM; i++)
phy_info_iftype_copy(&mode->he_capab[i], i, tb, tb_flags);
return NL_OK;
}