* We assume that TX/RX might be asymmetric (so e.g. VHT operating
* mode notification changes what a STA wants to receive, but not
* necessarily what it will transmit to us), and therefore use the
- * capabilities here. Calling it RX bandwidth capability is a bit
- * wrong though, since capabilities are in fact symmetric.
+ * "from station" bandwidth here.
*/
- width = ieee80211_sta_cap_rx_bw(link_sta, &link->conf->chanreq.oper);
+ width = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
+ IEEE80211_STA_BW_RX_FROM_STA);
if (width == IEEE80211_STA_RX_BW_20 &&
!link_sta->pub->ht_cap.ht_supported &&
else
new_chandef = &link_conf->chanreq.oper;
- new_sta_bw = ieee80211_sta_cur_vht_bw(link_sta,
- new_chandef);
+ new_sta_bw = ieee80211_sta_current_bw(link_sta,
+ new_chandef,
+ IEEE80211_STA_BW_TX_TO_STA);
/* nothing change */
if (new_sta_bw == link_sta->pub->bandwidth)
band = link->conf->chanreq.oper.chan->band;
sband = sdata->local->hw.wiphy->bands[band];
- new_bw = ieee80211_sta_cur_vht_bw(link_sta, &link->conf->chanreq.oper);
+ new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
+ IEEE80211_STA_BW_TX_TO_STA);
if (link_sta->pub->bandwidth == new_bw)
return;
else
max_bw = IEEE80211_STA_RX_BW_MAX;
- /* set cur_max_bandwidth and recalc sta bw */
- link_sta->cur_max_bandwidth = max_bw;
- new_bw = ieee80211_sta_cur_vht_bw(link_sta, &link->conf->chanreq.oper);
+ /* set op_mode_bw and recalc sta bw */
+ link_sta->op_mode_bw = max_bw;
+ new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
+ IEEE80211_STA_BW_TX_TO_STA);
if (link_sta->pub->bandwidth == new_bw)
return;
const struct ieee80211_vht_cap *vht_cap_ie,
const struct ieee80211_vht_cap *vht_cap_ie2,
struct link_sta_info *link_sta);
-enum ieee80211_sta_rx_bandwidth
-ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta,
- struct cfg80211_chan_def *chandef);
-enum ieee80211_sta_rx_bandwidth
-ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta,
- struct cfg80211_chan_def *chandef);
-
void ieee80211_process_mu_groups(struct ieee80211_sub_if_data *sdata,
struct ieee80211_link_data *link,
struct ieee80211_mgmt *mgmt);
return;
link_sta->pub->bandwidth =
- ieee80211_sta_cur_vht_bw(link_sta,
- &link->csa.chanreq.oper);
+ ieee80211_sta_current_bw(link_sta,
+ &link->csa.chanreq.oper,
+ IEEE80211_STA_BW_TX_TO_STA);
return;
}
link_info->rx_omi_bw_tx = IEEE80211_STA_RX_BW_MAX;
link_info->rx_omi_bw_staging = IEEE80211_STA_RX_BW_MAX;
- link_info->cur_max_bandwidth = IEEE80211_STA_RX_BW_MAX;
+ link_info->op_mode_bw = IEEE80211_STA_RX_BW_MAX;
/*
* Cause (a) warning(s) if IEEE80211_STA_RX_BW_MAX != 320
* or if new values are added to the enum.
*/
- switch (link_info->cur_max_bandwidth) {
+ switch (link_info->op_mode_bw) {
case IEEE80211_STA_RX_BW_20:
case IEEE80211_STA_RX_BW_40:
case IEEE80211_STA_RX_BW_80:
link_sta->capa_nss = ieee80211_sta_nss_capability(link_sta);
link_sta->pub->rx_nss = link_sta->capa_nss;
- link_sta->pub->bandwidth = ieee80211_sta_cur_vht_bw(link_sta, chandef);
+ link_sta->pub->bandwidth =
+ ieee80211_sta_current_bw(link_sta, chandef,
+ IEEE80211_STA_BW_TX_TO_STA);
}
void ieee80211_sta_set_max_amsdu_subframes(struct sta_info *sta,
}
EXPORT_SYMBOL(lockdep_sta_mutex_held);
#endif
+
+/**
+ * ieee80211_sta_bw_capability - get STA's bandwidth capability
+ * @link_sta: the (link) STA to get the capability for
+ * @band: the band to get the capability on
+ *
+ * Return: the maximum bandwidth supported by the STA
+ */
+static enum ieee80211_sta_rx_bandwidth
+ieee80211_sta_bw_capability(struct link_sta_info *link_sta,
+ enum nl80211_band band)
+{
+ struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
+ struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
+ struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
+ u32 cap_width;
+
+ if (he_cap->has_he) {
+ u8 info;
+
+ 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;
+
+ cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+ 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;
+}
+
+static enum ieee80211_sta_rx_bandwidth
+ieee80211_sta_current_bw_rx_from_sta(struct link_sta_info *link_sta,
+ struct cfg80211_chan_def *chandef)
+{
+ /*
+ * Take RX OMI into account. The value "rx_omi_bw_rx" is what
+ * we've indicated to the STA we can currently receive.
+ *
+ * This is needed since the RX OMI is done by us to save power,
+ * requiring changing both our TX (rate control) and RX (chanctx),
+ * which in turn needs to be done in the right order (stop TX
+ * at a higher bandwidth first while reducing bandwidth, and
+ * change the chanctx only after the peer accepts, etc.)
+ */
+ return min(ieee80211_sta_bw_capability(link_sta, chandef->chan->band),
+ link_sta->rx_omi_bw_rx);
+}
+
+static enum ieee80211_sta_rx_bandwidth
+ieee80211_sta_current_bw_tx_to_sta(struct link_sta_info *link_sta,
+ struct cfg80211_chan_def *chandef)
+{
+ struct sta_info *sta = link_sta->sta;
+ enum nl80211_chan_width bss_width;
+ enum ieee80211_sta_rx_bandwidth bw;
+ enum nl80211_band band;
+
+ bss_width = chandef->width;
+ band = chandef->chan->band;
+
+ bw = ieee80211_sta_bw_capability(link_sta, band);
+ bw = min(bw, link_sta->op_mode_bw);
+ /* also limit to RX OMI bandwidth we TX to the STA */
+ 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));
+
+ return bw;
+}
+
+/**
+ * ieee80211_sta_current_bw - get STA's current usable bandwidth
+ * @link_sta: the (link) STA to get the bandwidth for
+ * @chandef: the chandef for the channel the STA is on
+ * @direction: the direction (to or from STA)
+ *
+ * Return: the maximum bandwidth that the station can/may
+ * (currently) use in the given direction
+ */
+enum ieee80211_sta_rx_bandwidth
+ieee80211_sta_current_bw(struct link_sta_info *link_sta,
+ struct cfg80211_chan_def *chandef,
+ enum ieee80211_sta_bw_direction direction)
+{
+ if (WARN_ON(!chandef))
+ return IEEE80211_STA_RX_BW_20;
+
+ switch (direction) {
+ case IEEE80211_STA_BW_RX_FROM_STA:
+ return ieee80211_sta_current_bw_rx_from_sta(link_sta, chandef);
+ case IEEE80211_STA_BW_TX_TO_STA:
+ return ieee80211_sta_current_bw_tx_to_sta(link_sta, chandef);
+ }
+
+ /* unreachable */
+ return IEEE80211_STA_RX_BW_20;
+}
* @status_stats.last_ack_signal: last ACK signal
* @status_stats.ack_signal_filled: last ACK signal validity
* @status_stats.avg_ack_signal: average ACK signal
- * @cur_max_bandwidth: dynamic bandwidth limit for the station,
+ * @op_mode_bw: dynamic bandwidth limit to transmit to the STA,
* taken from HT/VHT capabilities or VHT operating mode notification.
* Invalid for NAN since that is operating on multiple bands.
* @rx_omi_bw_rx: RX OMI bandwidth restriction to apply for RX
u64 msdu[IEEE80211_NUM_TIDS + 1];
} tx_stats;
- enum ieee80211_sta_rx_bandwidth cur_max_bandwidth;
+ enum ieee80211_sta_rx_bandwidth op_mode_bw;
enum ieee80211_sta_rx_bandwidth rx_omi_bw_rx,
rx_omi_bw_tx,
rx_omi_bw_staging;
void __ieee80211_sta_recalc_aggregates(struct sta_info *sta, u16 active_links);
+enum ieee80211_sta_bw_direction {
+ IEEE80211_STA_BW_RX_FROM_STA,
+ IEEE80211_STA_BW_TX_TO_STA,
+};
+
+enum ieee80211_sta_rx_bandwidth
+ieee80211_sta_current_bw(struct link_sta_info *link_sta,
+ struct cfg80211_chan_def *chandef,
+ enum ieee80211_sta_bw_direction direction);
+
enum sta_stats_type {
STA_STATS_RATE_TYPE_INVALID = 0,
STA_STATS_RATE_TYPE_LEGACY,
enum nl80211_chan_width max_width;
int i;
- switch (ieee80211_sta_cap_rx_bw(&sta->deflink, &uc)) {
+ switch (ieee80211_sta_current_bw(&sta->deflink, &uc,
+ IEEE80211_STA_BW_RX_FROM_STA)) {
case IEEE80211_STA_RX_BW_20:
max_width = NL80211_CHAN_WIDTH_20;
break;
enum ieee80211_sta_rx_bandwidth bw;
bw = ieee80211_chan_width_to_rx_bw(conf->def.width);
- bw = min(bw, ieee80211_sta_cap_rx_bw(&sta->deflink,
- &conf->def));
+ bw = min(bw, ieee80211_sta_current_bw(&sta->deflink,
+ &conf->def,
+ IEEE80211_STA_BW_RX_FROM_STA));
if (bw != sta->sta.deflink.bandwidth) {
sta->sta.deflink.bandwidth = bw;
rate_control_rate_update(local, sband,
ieee80211_sta_recalc_aggregates(&link_sta->sta->sta);
}
-/* FIXME: move this to some better location - parses HE/EHT now */
-static enum ieee80211_sta_rx_bandwidth
-_ieee80211_sta_cap_rx_bw(struct link_sta_info *link_sta, enum nl80211_band band)
-{
- struct ieee80211_sta_vht_cap *vht_cap = &link_sta->pub->vht_cap;
- struct ieee80211_sta_he_cap *he_cap = &link_sta->pub->he_cap;
- struct ieee80211_sta_eht_cap *eht_cap = &link_sta->pub->eht_cap;
- u32 cap_width;
-
- if (he_cap->has_he) {
- u8 info;
-
- 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;
-
- cap_width = vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
-
- 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->chan->band),
- link_sta->rx_omi_bw_rx);
-}
-
-/* FIXME: rename/move - this deals with everything not just VHT */
-enum ieee80211_sta_rx_bandwidth
-ieee80211_sta_cur_vht_bw(struct link_sta_info *link_sta,
- struct cfg80211_chan_def *chandef)
-{
- struct sta_info *sta = link_sta->sta;
- enum nl80211_chan_width bss_width;
- enum ieee80211_sta_rx_bandwidth bw;
- enum nl80211_band band;
-
- if (WARN_ON(!chandef))
- return IEEE80211_STA_RX_BW_20;
-
- bss_width = chandef->width;
- band = chandef->chan->band;
-
- /* intentionally do not take rx_bw_omi_rx into account */
- bw = _ieee80211_sta_cap_rx_bw(link_sta, band);
- 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));
-
- return bw;
-}
-
u32 __ieee80211_vht_handle_opmode(struct ieee80211_sub_if_data *sdata,
struct link_sta_info *link_sta,
u8 opmode, enum nl80211_band band)
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;
+ link_sta->op_mode_bw = 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;
+ link_sta->op_mode_bw = 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;
+ link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160;
else
- link_sta->cur_max_bandwidth = IEEE80211_STA_RX_BW_80;
+ link_sta->op_mode_bw = 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;
+ link_sta->op_mode_bw = IEEE80211_STA_RX_BW_160;
break;
}
- new_bw = ieee80211_sta_cur_vht_bw(link_sta, &link->conf->chanreq.oper);
+ new_bw = ieee80211_sta_current_bw(link_sta, &link->conf->chanreq.oper,
+ IEEE80211_STA_BW_TX_TO_STA);
if (new_bw != link_sta->pub->bandwidth) {
link_sta->pub->bandwidth = new_bw;
sta_opmode.bw = ieee80211_sta_rx_bw_to_chan_width(new_bw);