/* Allow user to decrease AMPDU length exponent */ if (sdata->u.mgd.vht_capa_mask.vht_cap_info &
cpu_to_le32(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK)) {
u32 cap, n;
n = le32_to_cpu(sdata->u.mgd.vht_capa.vht_cap_info) &
IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
n >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
cap = vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
cap >>= IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
if (n < cap) {
vht_cap->cap &=
~IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK;
vht_cap->cap |=
n << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
}
}
/* Allow the user to decrease MCSes */
rxmcs_mask =
le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.rx_mcs_map);
rxmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.rx_mcs_map);
rxmcs_n &= rxmcs_mask;
rxmcs_cap = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map);
txmcs_mask =
le16_to_cpu(sdata->u.mgd.vht_capa_mask.supp_mcs.tx_mcs_map);
txmcs_n = le16_to_cpu(sdata->u.mgd.vht_capa.supp_mcs.tx_mcs_map);
txmcs_n &= txmcs_mask;
txmcs_cap = le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); for (i = 0; i < 8; i++) {
u8 m, n, c;
m = (rxmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
n = (rxmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
c = (rxmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
rxmcs_cap &= ~(3 << 2*i);
rxmcs_cap |= (rxmcs_n & (3 << 2*i));
}
m = (txmcs_mask >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
n = (txmcs_n >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
c = (txmcs_cap >> 2*i) & IEEE80211_VHT_MCS_NOT_SUPPORTED;
if (m && ((c != IEEE80211_VHT_MCS_NOT_SUPPORTED && n < c) ||
n == IEEE80211_VHT_MCS_NOT_SUPPORTED)) {
txmcs_cap &= ~(3 << 2*i);
txmcs_cap |= (txmcs_n & (3 << 2*i));
}
}
vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(rxmcs_cap);
vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(txmcs_cap);
}
if (!vht_cap_ie || !sband->vht_cap.vht_supported) return;
/* Allow VHT if at least one channel on the sband supports 80 MHz */
have_80mhz = false; for (i = 0; i < sband->n_channels; i++) { if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_NO_80MHZ)) continue;
have_80mhz = true; break;
}
if (!have_80mhz) return;
/* * A VHT STA must support 40 MHz, but if we verify that here * then we break a few things - some APs (e.g. Netgear R6300v2 * and others based on the BCM4360 chipset) will unset this * capability bit when operating in 20 MHz.
*/
vht_cap->vht_supported = true;
own_cap = sband->vht_cap; /* * If user has specified capability overrides, take care * of that if the station we're setting up is the AP that * we advertised a restricted capability set to. Override * our own capabilities and then use those below.
*/ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
!test_sta_flag(link_sta->sta, WLAN_STA_TDLS_PEER))
ieee80211_apply_vhtcap_overrides(sdata, &own_cap);
if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
vht_cap->cap |= cap_info &
IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE)
vht_cap->cap |= cap_info &
IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
if (own_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_RXSTBC_MASK;
if (own_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK)
vht_cap->cap |= cap_info & IEEE80211_VHT_CAP_TXSTBC;
/* Copy peer MCS info, the driver might need them. */
memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs, sizeof(struct ieee80211_vht_mcs_info));
/* copy EXT_NSS_BW Support value or remove the capability */ if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_VHT_EXT_NSS_BW))
vht_cap->cap |= (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK); else
vht_cap->vht_mcs.tx_highest &=
~cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE);
/* but also restrict MCSes */ for (i = 0; i < 8; i++) {
u16 own_rx, own_tx, peer_rx, peer_tx;
if (peer_tx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { if (own_rx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
peer_tx = IEEE80211_VHT_MCS_NOT_SUPPORTED; elseif (own_rx < peer_tx)
peer_tx = own_rx;
}
if (peer_rx != IEEE80211_VHT_MCS_NOT_SUPPORTED) { if (own_tx == IEEE80211_VHT_MCS_NOT_SUPPORTED)
peer_rx = IEEE80211_VHT_MCS_NOT_SUPPORTED; elseif (own_tx < peer_rx)
peer_rx = own_tx;
}
vht_cap->vht_mcs.rx_mcs_map &=
~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(peer_rx << i * 2);
vht_cap->vht_mcs.tx_mcs_map &=
~cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << i * 2);
vht_cap->vht_mcs.tx_mcs_map |= cpu_to_le16(peer_tx << i * 2);
}
/* * This is a workaround for VHT-enabled STAs which break the spec * and have the VHT-MCS Rx map filled in with value 3 for all eight * spatial streams, an example is AR9462. * * As per spec, in section 22.1.1 Introduction to the VHT PHY * A VHT STA shall support at least single spatial stream VHT-MCSs * 0 to 7 (transmit and receive) in all supported channel widths.
*/ if (vht_cap->vht_mcs.rx_mcs_map == cpu_to_le16(0xFFFF)) {
vht_cap->vht_supported = false;
sdata_info(sdata, "Ignoring VHT IE from %pM (link:%pM) due to invalid rx_mcs_map\n",
link_sta->sta->addr, link_sta->addr); return;
}
/* finally set up the bandwidth */ switch (vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK) { case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: case IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; break; default:
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
if (!(vht_cap->vht_mcs.tx_highest &
cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE))) break;
/* * If this is non-zero, then it does support 160 MHz after all, * in one form or the other. We don't distinguish here (or even * above) between 160 and 80+80 yet.
*/ if (cap_info & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK)
link_sta->cur_max_bandwidth =
IEEE80211_STA_RX_BW_160;
}
/* * Work around the Cisco 9115 FW 17.3 bug by taking the min of * both reported MPDU lengths.
*/
mpdu_len = vht_cap->cap & IEEE80211_VHT_CAP_MAX_MPDU_MASK; if (vht_cap_ie2)
mpdu_len = min_t(u32, mpdu_len,
le32_get_bits(vht_cap_ie2->vht_cap_info,
IEEE80211_VHT_CAP_MAX_MPDU_MASK));
/* * FIXME - should the amsdu len be per link? store per link * and maintain a minimum?
*/ switch (mpdu_len) { case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_11454; break; case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_7991; break; case IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895: default:
link_sta->pub->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_VHT_3895; break;
}
if (he_cap->has_he) { enum nl80211_band band;
u8 info;
if (chandef) {
band = chandef->chan->band;
} else { struct ieee80211_bss_conf *link_conf;
rcu_read_lock();
link_conf = rcu_dereference(sdata->vif.link_conf[link_id]);
band = link_conf->chanreq.oper.chan->band;
rcu_read_unlock();
}
if (eht_cap->has_eht && band == NL80211_BAND_6GHZ) {
info = eht_cap->eht_cap_elem.phy_cap_info[0];
if (info & IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ) return IEEE80211_STA_RX_BW_320;
}
info = he_cap->he_cap_elem.phy_cap_info[0];
if (band == NL80211_BAND_2GHZ) { if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G) return IEEE80211_STA_RX_BW_40; return IEEE80211_STA_RX_BW_20;
}
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G ||
info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) return IEEE80211_STA_RX_BW_160;
if (info & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G) return IEEE80211_STA_RX_BW_80;
return IEEE80211_STA_RX_BW_20;
}
if (!vht_cap->vht_supported) return link_sta->pub->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
IEEE80211_STA_RX_BW_40 :
IEEE80211_STA_RX_BW_20;
if (cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
cap_width == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) return IEEE80211_STA_RX_BW_160;
/* * If this is non-zero, then it does support 160 MHz after all, * in one form or the other. We don't distinguish here (or even * above) between 160 and 80+80 yet.
*/ if (vht_cap->cap & IEEE80211_VHT_CAP_EXT_NSS_BW_MASK) return IEEE80211_STA_RX_BW_160;
return IEEE80211_STA_RX_BW_80;
}
enum ieee80211_sta_rx_bandwidth
_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, struct cfg80211_chan_def *chandef)
{ /* * With RX OMI, also pretend that the STA's capability changed. * Of course this isn't really true, it didn't change, only our * RX capability was changed by notifying RX OMI to the STA. * The purpose, however, is to save power, and that requires * changing also transmissions to the AP and the chanctx. The * transmissions depend on link_sta->bandwidth which is set in * _ieee80211_sta_cur_vht_bw() below, but the chanctx depends * on the result of this function which is also called by * _ieee80211_sta_cur_vht_bw(), so we need to do that here as * well. This is sufficient for the steady state, but during * the transition we already need to change TX/RX separately, * so _ieee80211_sta_cur_vht_bw() below applies the _tx one.
*/ return min(__ieee80211_sta_cap_rx_bw(link_sta, chandef),
link_sta->rx_omi_bw_rx);
}
/* intentionally do not take rx_bw_omi_rx into account */
bw = __ieee80211_sta_cap_rx_bw(link_sta, chandef);
bw = min(bw, link_sta->cur_max_bandwidth); /* but do apply rx_omi_bw_tx */
bw = min(bw, link_sta->rx_omi_bw_tx);
/* Don't consider AP's bandwidth for TDLS peers, section 11.23.1 of * IEEE80211-2016 specification makes higher bandwidth operation * possible on the TDLS link if the peers have wider bandwidth * capability. * * However, in this case, and only if the TDLS peer is authorized, * limit to the tdls_chandef so that the configuration here isn't * wider than what's actually requested on the channel context.
*/ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
test_sta_flag(sta, WLAN_STA_TDLS_WIDER_BW) &&
test_sta_flag(sta, WLAN_STA_AUTHORIZED) &&
sta->tdls_chandef.chan)
bw = min(bw, ieee80211_chan_width_to_rx_bw(sta->tdls_chandef.width)); else
bw = min(bw, ieee80211_chan_width_to_rx_bw(bss_width));
if (link_sta->pub->eht_cap.has_eht) { int i; const u8 *rx_nss_mcs = (void *)&link_sta->pub->eht_cap.eht_mcs_nss_supp;
/* get the max nss for EHT over all possible bandwidths and mcs */ for (i = 0; i < sizeof(struct ieee80211_eht_mcs_nss_supp); i++)
eht_rx_nss = max_t(u8, eht_rx_nss,
u8_get_bits(rx_nss_mcs[i],
IEEE80211_EHT_MCS_NSS_RX));
}
if (link_sta->pub->ht_cap.ht_supported) { if (link_sta->pub->ht_cap.mcs.rx_mask[0])
ht_rx_nss++; if (link_sta->pub->ht_cap.mcs.rx_mask[1])
ht_rx_nss++; if (link_sta->pub->ht_cap.mcs.rx_mask[2])
ht_rx_nss++; if (link_sta->pub->ht_cap.mcs.rx_mask[3])
ht_rx_nss++; /* FIXME: consider rx_highest? */
}
if (link_sta->pub->vht_cap.vht_supported) { int i;
u16 rx_mcs_map;
/* that shouldn't be set yet, but we can handle it anyway */ if (link_sta->op_mode_nss)
link_sta->pub->rx_nss =
min_t(u8, rx_nss, link_sta->op_mode_nss); else
link_sta->pub->rx_nss = rx_nss;
}
if (link_sta->op_mode_nss != nss) { if (nss <= link_sta->capa_nss) {
link_sta->op_mode_nss = nss;
if (nss != link_sta->pub->rx_nss) {
link_sta->pub->rx_nss = nss;
changed |= IEEE80211_RC_NSS_CHANGED;
sta_opmode.rx_nss = link_sta->pub->rx_nss;
sta_opmode.changed |= STA_OPMODE_N_SS_CHANGED;
}
} else {
sdata_dbg(sdata, "Ignore NSS change to invalid %d in VHT opmode notif from %pM",
nss, link_sta->pub->addr);
}
}
switch (opmode & IEEE80211_OPMODE_NOTIF_CHANWIDTH_MASK) { case IEEE80211_OPMODE_NOTIF_CHANWIDTH_20MHZ: /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_20; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_40MHZ: /* ignore IEEE80211_OPMODE_NOTIF_BW_160_80P80 must not be set */
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_40; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_80MHZ: if (opmode & IEEE80211_OPMODE_NOTIF_BW_160_80P80)
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; else
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80; break; case IEEE80211_OPMODE_NOTIF_CHANWIDTH_160MHZ: /* legacy only, no longer used by newer spec */
link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_160; break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.