mac80211: add support for HE

Add support for HE in mac80211 conforming with P802.11ax_D1.4.

Johannes: Fix another bug with the buf_size comparison in agg-rx.c.

Signed-off-by: Liad Kaufman <liad.kaufman@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
Signed-off-by: Ido Yariv <idox.yariv@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Luca Coelho
2018-06-09 09:14:44 +03:00
committed by Johannes Berg
parent b8042b3da9
commit 41cbb0f5a2
14 changed files with 716 additions and 45 deletions

View File

@@ -149,6 +149,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
struct cfg80211_chan_def *chandef, bool tracking)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -207,7 +208,27 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
}
vht_chandef = *chandef;
if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) && he_oper &&
(le32_to_cpu(he_oper->he_oper_params) &
IEEE80211_HE_OPERATION_VHT_OPER_INFO)) {
struct ieee80211_vht_operation he_oper_vht_cap;
/*
* Set only first 3 bytes (other 2 aren't used in
* ieee80211_chandef_vht_oper() anyway)
*/
memcpy(&he_oper_vht_cap, he_oper->optional, 3);
he_oper_vht_cap.basic_mcs_set = cpu_to_le16(0);
if (!ieee80211_chandef_vht_oper(&he_oper_vht_cap,
&vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
sdata_info(sdata,
"HE AP VHT information is invalid, disable HE\n");
ret = IEEE80211_STA_DISABLE_HE;
goto out;
}
} else if (!ieee80211_chandef_vht_oper(vht_oper, &vht_chandef)) {
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
sdata_info(sdata,
"AP VHT information is invalid, disable VHT\n");
@@ -300,12 +321,14 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_cap *ht_cap,
const struct ieee80211_ht_operation *ht_oper,
const struct ieee80211_vht_operation *vht_oper,
const struct ieee80211_he_operation *he_oper,
const u8 *bssid, u32 *changed)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct ieee80211_channel *chan = sdata->vif.bss_conf.chandef.chan;
struct ieee80211_supported_band *sband =
local->hw.wiphy->bands[chan->band];
struct cfg80211_chan_def chandef;
u16 ht_opmode;
u32 flags;
@@ -320,6 +343,11 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
vht_oper = NULL;
/* don't check HE if we associated as non-HE station */
if (ifmgd->flags & IEEE80211_STA_DISABLE_HE ||
!ieee80211_get_he_sta_cap(sband))
he_oper = NULL;
if (WARN_ON_ONCE(!sta))
return -EINVAL;
@@ -333,12 +361,9 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
}
chan = sdata->vif.bss_conf.chandef.chan;
sband = local->hw.wiphy->bands[chan->band];
/* calculate new channel (type) based on HT/VHT operation IEs */
/* calculate new channel (type) based on HT/VHT/HE operation IEs */
flags = ieee80211_determine_chantype(sdata, sband, chan,
ht_oper, vht_oper,
ht_oper, vht_oper, he_oper,
&chandef, true);
/*
@@ -582,6 +607,34 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
}
/* This function determines HE capability flags for the association
* and builds the IE.
*/
static void ieee80211_add_he_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct ieee80211_supported_band *sband)
{
u8 *pos;
const struct ieee80211_sta_he_cap *he_cap = NULL;
u8 he_cap_size;
he_cap = ieee80211_get_he_sta_cap(sband);
if (!he_cap)
return;
/*
* TODO: the 1 added is because this temporarily is under the EXTENSION
* IE. Get rid of it when it moves.
*/
he_cap_size =
2 + 1 + sizeof(he_cap->he_cap_elem) +
ieee80211_he_mcs_nss_size(&he_cap->he_cap_elem) +
ieee80211_he_ppe_size(he_cap->ppe_thres[0],
he_cap->he_cap_elem.phy_cap_info);
pos = skb_put(skb, he_cap_size);
ieee80211_ie_build_he_cap(pos, he_cap, pos + he_cap_size);
}
static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -643,6 +696,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
2 + 2 * sband->n_channels + /* supported channels */
2 + sizeof(struct ieee80211_ht_cap) + /* HT */
2 + sizeof(struct ieee80211_vht_cap) + /* VHT */
2 + 1 + sizeof(struct ieee80211_he_cap_elem) + /* HE */
sizeof(struct ieee80211_he_mcs_nss_supp) +
IEEE80211_HE_PPE_THRES_MAX_LEN +
assoc_data->ie_len + /* extra IEs */
(assoc_data->fils_kek_len ? 16 /* AES-SIV */ : 0) +
9, /* WMM */
@@ -827,11 +883,41 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
/* if present, add any custom IEs that go before HE */
if (assoc_data->ie_len) {
static const u8 before_he[] = {
/*
* no need to list the ones split off before VHT
* or generated here
*/
WLAN_EID_OPMODE_NOTIF,
WLAN_EID_EXTENSION, WLAN_EID_EXT_FUTURE_CHAN_GUIDANCE,
/* 11ai elements */
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_SESSION,
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_PUBLIC_KEY,
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_KEY_CONFIRM,
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_HLP_CONTAINER,
WLAN_EID_EXTENSION, WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN,
/* TODO: add 11ah/11aj/11ak elements */
};
/* RIC already taken above, so no need to handle here anymore */
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
before_he, ARRAY_SIZE(before_he),
offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, assoc_data->ie + offset, noffset - offset);
offset = noffset;
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband,
&assoc_data->ap_vht_cap);
/* if present, add any custom non-vendor IEs that go after HT */
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
ieee80211_add_he_ie(sdata, skb, sband);
/* if present, add any custom non-vendor IEs that go after HE */
if (assoc_data->ie_len) {
noffset = ieee80211_ie_split_vendor(assoc_data->ie,
assoc_data->ie_len,
@@ -898,6 +984,11 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
struct ieee80211_hdr_3addr *nullfunc;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
/* Don't send NDPs when STA is connected HE */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!(ifmgd->flags & IEEE80211_STA_DISABLE_HE))
return;
skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif,
!ieee80211_hw_check(&local->hw, DOESNT_SUPPORT_QOS_NDP));
if (!skb)
@@ -929,6 +1020,10 @@ static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local,
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
return;
/* Don't send NDPs when connected HE */
if (!(sdata->u.mgd.flags & IEEE80211_STA_DISABLE_HE))
return;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30);
if (!skb)
return;
@@ -1700,9 +1795,11 @@ static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
}
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const u8 *wmm_param, size_t wmm_param_len)
static bool
ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
const u8 *wmm_param, size_t wmm_param_len,
const struct ieee80211_mu_edca_param_set *mu_edca)
{
struct ieee80211_tx_queue_params params[IEEE80211_NUM_ACS];
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1749,6 +1846,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
sdata->wmm_acm |= BIT(1) | BIT(2); /* BK/- */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK)
uapsd = true;
params[ac].mu_edca = !!mu_edca;
if (mu_edca)
params[ac].mu_edca_param_rec = mu_edca->ac_bk;
break;
case 2: /* AC_VI */
ac = IEEE80211_AC_VI;
@@ -1756,6 +1856,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
sdata->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI)
uapsd = true;
params[ac].mu_edca = !!mu_edca;
if (mu_edca)
params[ac].mu_edca_param_rec = mu_edca->ac_vi;
break;
case 3: /* AC_VO */
ac = IEEE80211_AC_VO;
@@ -1763,6 +1866,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
sdata->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO)
uapsd = true;
params[ac].mu_edca = !!mu_edca;
if (mu_edca)
params[ac].mu_edca_param_rec = mu_edca->ac_vo;
break;
case 0: /* AC_BE */
default:
@@ -1771,6 +1877,9 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
sdata->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE)
uapsd = true;
params[ac].mu_edca = !!mu_edca;
if (mu_edca)
params[ac].mu_edca_param_rec = mu_edca->ac_be;
break;
}
@@ -3021,6 +3130,25 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
goto out;
}
/*
* If AP doesn't support HT, or it doesn't have HE mandatory IEs, mark
* HE as disabled. If on the 5GHz band, make sure it supports VHT.
*/
if (ifmgd->flags & IEEE80211_STA_DISABLE_HT ||
(sband->band == NL80211_BAND_5GHZ &&
ifmgd->flags & IEEE80211_STA_DISABLE_VHT) ||
(!elems.he_cap && !elems.he_operation))
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
(!elems.he_cap || !elems.he_operation)) {
mutex_unlock(&sdata->local->sta_mtx);
sdata_info(sdata,
"HE AP is missing HE capability/operation\n");
ret = false;
goto out;
}
/* Set up internal HT/VHT capabilities */
if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
@@ -3030,6 +3158,48 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
elems.vht_cap_elem, sta);
if (elems.he_operation && !(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
elems.he_cap) {
ieee80211_he_cap_ie_to_sta_he_cap(sdata, sband,
elems.he_cap,
elems.he_cap_len,
sta);
bss_conf->he_support = sta->sta.he_cap.has_he;
} else {
bss_conf->he_support = false;
}
if (bss_conf->he_support) {
u32 he_oper_params =
le32_to_cpu(elems.he_operation->he_oper_params);
bss_conf->bss_color = he_oper_params &
IEEE80211_HE_OPERATION_BSS_COLOR_MASK;
bss_conf->htc_trig_based_pkt_ext =
(he_oper_params &
IEEE80211_HE_OPERATION_DFLT_PE_DURATION_MASK) <<
IEEE80211_HE_OPERATION_DFLT_PE_DURATION_OFFSET;
bss_conf->frame_time_rts_th =
(he_oper_params &
IEEE80211_HE_OPERATION_RTS_THRESHOLD_MASK) <<
IEEE80211_HE_OPERATION_RTS_THRESHOLD_OFFSET;
bss_conf->multi_sta_back_32bit =
sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
IEEE80211_HE_MAC_CAP2_32BIT_BA_BITMAP;
bss_conf->ack_enabled =
sta->sta.he_cap.he_cap_elem.mac_cap_info[2] &
IEEE80211_HE_MAC_CAP2_ACK_EN;
bss_conf->uora_exists = !!elems.uora_element;
if (elems.uora_element)
bss_conf->uora_ocw_range = elems.uora_element[0];
/* TODO: OPEN: what happens if BSS color disable is set? */
}
/*
* Some APs, e.g. Netgear WNDR3700, report invalid HT operation data
* in their association response, so ignore that data for our own
@@ -3089,7 +3259,8 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_DISABLE_WMM) {
ieee80211_set_wmm_default(sdata, false, false);
} else if (!ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
elems.wmm_param_len)) {
elems.wmm_param_len,
elems.mu_edca_param_set)) {
/* still enable QoS since we might have HT/VHT */
ieee80211_set_wmm_default(sdata, false, true);
/* set the disable-WMM flag in this case to disable
@@ -3603,7 +3774,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
ieee80211_sta_wmm_params(local, sdata, elems.wmm_param,
elems.wmm_param_len))
elems.wmm_param_len,
elems.mu_edca_param_set))
changed |= BSS_CHANGED_QOS;
/*
@@ -3642,7 +3814,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (ieee80211_config_bw(sdata, sta,
elems.ht_cap_elem, elems.ht_operation,
elems.vht_operation, bssid, &changed)) {
elems.vht_operation, elems.he_operation,
bssid, &changed)) {
mutex_unlock(&local->sta_mtx);
sdata_info(sdata,
"failed to follow AP %pM bandwidth change, disconnect\n",
@@ -4279,6 +4452,66 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
return chains;
}
static bool
ieee80211_verify_sta_he_mcs_support(struct ieee80211_supported_band *sband,
const struct ieee80211_he_operation *he_op)
{
const struct ieee80211_sta_he_cap *sta_he_cap =
ieee80211_get_he_sta_cap(sband);
u16 ap_min_req_set = le16_to_cpu(he_op->he_mcs_nss_set);
int i;
if (!sta_he_cap || !he_op)
return false;
/* Need to go over for 80MHz, 160MHz and for 80+80 */
for (i = 0; i < 3; i++) {
const struct ieee80211_he_mcs_nss_supp *sta_mcs_nss_supp =
&sta_he_cap->he_mcs_nss_supp;
u16 sta_mcs_map_rx =
le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i]);
u16 sta_mcs_map_tx =
le16_to_cpu(((__le16 *)sta_mcs_nss_supp)[2 * i + 1]);
u8 nss;
bool verified = true;
/*
* For each band there is a maximum of 8 spatial streams
* possible. Each of the sta_mcs_map_* is a 16-bit struct built
* of 2 bits per NSS (1-8), with the values defined in enum
* ieee80211_he_mcs_support. Need to make sure STA TX and RX
* capabilities aren't less than the AP's minimum requirements
* for this HE BSS per SS.
* It is enough to find one such band that meets the reqs.
*/
for (nss = 8; nss > 0; nss--) {
u8 sta_rx_val = (sta_mcs_map_rx >> (2 * (nss - 1))) & 3;
u8 sta_tx_val = (sta_mcs_map_tx >> (2 * (nss - 1))) & 3;
u8 ap_val = (ap_min_req_set >> (2 * (nss - 1))) & 3;
if (ap_val == IEEE80211_HE_MCS_NOT_SUPPORTED)
continue;
/*
* Make sure the HE AP doesn't require MCSs that aren't
* supported by the client
*/
if (sta_rx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
sta_tx_val == IEEE80211_HE_MCS_NOT_SUPPORTED ||
(ap_val > sta_rx_val) || (ap_val > sta_tx_val)) {
verified = false;
break;
}
}
if (verified)
return true;
}
/* If here, STA doesn't meet AP's HE min requirements */
return false;
}
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss)
{
@@ -4287,6 +4520,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
const struct ieee80211_ht_cap *ht_cap = NULL;
const struct ieee80211_ht_operation *ht_oper = NULL;
const struct ieee80211_vht_operation *vht_oper = NULL;
const struct ieee80211_he_operation *he_oper = NULL;
struct ieee80211_supported_band *sband;
struct cfg80211_chan_def chandef;
int ret;
@@ -4342,6 +4576,25 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
}
}
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HE) &&
ieee80211_get_he_sta_cap(sband)) {
const struct cfg80211_bss_ies *ies;
const u8 *he_oper_ie;
ies = rcu_dereference(cbss->ies);
he_oper_ie = cfg80211_find_ext_ie(WLAN_EID_EXT_HE_OPERATION,
ies->data, ies->len);
if (he_oper_ie &&
he_oper_ie[1] == ieee80211_he_oper_size(&he_oper_ie[3]))
he_oper = (void *)(he_oper_ie + 3);
else
he_oper = NULL;
if (!he_oper ||
!ieee80211_verify_sta_he_mcs_support(sband, he_oper))
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
}
/* Allow VHT if at least one channel on the sband supports 80 MHz */
have_80mhz = false;
for (i = 0; i < sband->n_channels; i++) {
@@ -4358,7 +4611,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
cbss->channel,
ht_oper, vht_oper,
ht_oper, vht_oper, he_oper,
&chandef, false);
sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
@@ -4764,8 +5017,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
ifmgd->flags |= IEEE80211_STA_DISABLE_HE;
netdev_info(sdata->dev,
"disabling HT/VHT due to WEP/TKIP use\n");
"disabling HE/HT/VHT due to WEP/TKIP use\n");
}
}