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:

committed by
Johannes Berg

parent
b8042b3da9
commit
41cbb0f5a2
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user