summaryrefslogtreecommitdiff
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2024-01-29 21:34:42 +0300
committerJohannes Berg <johannes.berg@intel.com>2024-02-08 15:07:37 +0300
commit761748f001800d925c2ee8b04407e7aee12c3ffb (patch)
treec27f1469ed37f340073d9b8b6ccc48c6fb8cd3f5 /net/mac80211/mlme.c
parentd1256c1546a0e03f103e2f0381103dc747ea7f81 (diff)
downloadlinux-761748f001800d925c2ee8b04407e7aee12c3ffb.tar.xz
wifi: mac80211: support wider bandwidth OFDMA config
EHT requires that stations are able to participate in wider bandwidth OFDMA, i.e. parse downlink OFDMA and uplink OFDMA triggers when they're not capable of (or not connected at) the (wider) bandwidth that the AP is using. This requires hardware configuration, since the entity responsible for parsing (possibly hardware) needs to know the AP bandwidth. To support this, change the channel request to have the AP's bandwidth for clients, and track that in the channel context in mac80211. This means that the same chandef might need to be split up into two different contexts, if the APs are different. Interfaces other than client are not participating in OFDMA the same way, so they don't request any AP setting. Note that this doesn't introduce any API to split a channel context, so that there are cases where this might lead to a disconnect, e.g. if there are two client interfaces using the same channel context, e.g. both 160 MHz connected to different 320 MHz APs, and one of the APs switches to 160 MHz. Note also there are possible cases where this can be optimised, e.g. when using the upper or lower 160 Mhz, but I haven't been able to really fully understand the spec and/or hardware limitations. If, for some reason, there are no hardware limits on this because the OFDMA (downlink/trigger) parsing is done in firmware and can take the transmitter into account, then drivers can set the new flag IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW on interfaces to not have them request any AP bandwidth in the channel context and ignore this issue entirely. The bss_conf still contains the AP configuration (if any, i.e. EHT) in the chanreq. Link: https://msgid.link/20240129194108.d3d5b35dd783.I939d04674f4ff06f39934b1591c8d36a30ce74c2@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c60
1 files changed, 35 insertions, 25 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e9d720f25ddf..31d6a5603582 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -665,7 +665,7 @@ static struct ieee802_11_elems *
ieee80211_determine_chan_mode(struct ieee80211_sub_if_data *sdata,
struct ieee80211_conn_settings *conn,
struct cfg80211_bss *cbss, int link_id,
- struct cfg80211_chan_def *chandef)
+ struct ieee80211_chan_req *chanreq)
{
struct ieee80211_local *local = sdata->local;
const struct cfg80211_bss_ies *ies = rcu_dereference(cbss->ies);
@@ -752,20 +752,27 @@ again:
}
conn->mode = ap_mode;
- *chandef = ap_chandef;
+ chanreq->oper = ap_chandef;
- while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ /* wider-bandwidth OFDMA is only done in EHT */
+ if (conn->mode >= IEEE80211_CONN_MODE_EHT &&
+ !(sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW))
+ chanreq->ap = ap_chandef;
+ else
+ chanreq->ap.chan = NULL;
+
+ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, &chanreq->oper,
IEEE80211_CHAN_DISABLED)) {
- if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
+ if (WARN_ON(chanreq->oper.width == NL80211_CHAN_WIDTH_20_NOHT)) {
ret = -EINVAL;
goto free;
}
- ieee80211_chandef_downgrade(chandef, conn);
+ ieee80211_chanreq_downgrade(chanreq, conn);
}
if (conn->mode >= IEEE80211_CONN_MODE_HE &&
- !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
+ !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper,
IEEE80211_CHAN_NO_HE)) {
conn->mode = IEEE80211_CONN_MODE_VHT;
conn->bw_limit = min_t(enum ieee80211_conn_bw_limit,
@@ -774,7 +781,7 @@ again:
}
if (conn->mode >= IEEE80211_CONN_MODE_EHT &&
- !cfg80211_chandef_usable(sdata->wdev.wiphy, chandef,
+ !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper,
IEEE80211_CHAN_NO_EHT)) {
conn->mode = IEEE80211_CONN_MODE_HE;
conn->bw_limit = min_t(enum ieee80211_conn_bw_limit,
@@ -782,7 +789,7 @@ again:
IEEE80211_CONN_BW_LIMIT_160);
}
- if (chandef->width != ap_chandef.width || ap_mode != conn->mode)
+ if (chanreq->oper.width != ap_chandef.width || ap_mode != conn->mode)
sdata_info(sdata,
"regulatory prevented using AP config, downgraded\n");
@@ -847,7 +854,7 @@ again:
ieee80211_conn_mode_str(conn->mode),
20 * (1 << conn->bw_limit));
- if (WARN_ON_ONCE(!cfg80211_chandef_valid(chandef))) {
+ if (WARN_ON_ONCE(!cfg80211_chandef_valid(&chanreq->oper))) {
ret = -EINVAL;
goto free;
}
@@ -865,7 +872,6 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
struct ieee80211_channel *channel = link->conf->chanreq.oper.chan;
struct ieee80211_sub_if_data *sdata = link->sdata;
struct ieee80211_chan_req chanreq = {};
- struct cfg80211_chan_def ap_chandef;
enum ieee80211_conn_mode ap_mode;
u32 vht_cap_info = 0;
u16 ht_opmode;
@@ -881,7 +887,7 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
ap_mode = ieee80211_determine_ap_chan(sdata, channel, vht_cap_info,
elems, true, &link->u.mgd.conn,
- &ap_chandef);
+ &chanreq.ap);
if (ap_mode != link->u.mgd.conn.mode) {
link_info(link,
@@ -891,6 +897,11 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
return -EINVAL;
}
+ chanreq.oper = chanreq.ap;
+ if (link->u.mgd.conn.mode < IEEE80211_CONN_MODE_EHT ||
+ sdata->vif.driver_flags & IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW)
+ chanreq.ap.chan = NULL;
+
/*
* if HT operation mode changed store the new one -
* this may be applicable even if channel is identical
@@ -911,20 +922,20 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
* won't do us any good -- we couldn't use it with the AP.
*/
while (link->u.mgd.conn.bw_limit <
- ieee80211_min_bw_limit_from_chandef(&ap_chandef))
- ieee80211_chandef_downgrade(&ap_chandef, NULL);
+ ieee80211_min_bw_limit_from_chandef(&chanreq.oper))
+ ieee80211_chandef_downgrade(&chanreq.oper, NULL);
- if (cfg80211_chandef_identical(&ap_chandef, &link->conf->chanreq.oper))
+ if (ieee80211_chanreq_identical(&chanreq, &link->conf->chanreq))
return 0;
link_info(link,
- "AP %pM changed bandwidth, new config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n",
- link->u.mgd.bssid, ap_chandef.chan->center_freq,
- ap_chandef.chan->freq_offset, ap_chandef.width,
- ap_chandef.center_freq1, ap_chandef.freq1_offset,
- ap_chandef.center_freq2);
+ "AP %pM changed bandwidth, new used config is %d.%03d MHz, width %d (%d.%03d/%d MHz)\n",
+ link->u.mgd.bssid, chanreq.oper.chan->center_freq,
+ chanreq.oper.chan->freq_offset, chanreq.oper.width,
+ chanreq.oper.center_freq1, chanreq.oper.freq1_offset,
+ chanreq.oper.center_freq2);
- if (!cfg80211_chandef_valid(&ap_chandef)) {
+ if (!cfg80211_chandef_valid(&chanreq.oper)) {
sdata_info(sdata,
"AP %pM changed caps/bw in a way we can't support - disconnect\n",
link->u.mgd.bssid);
@@ -947,7 +958,6 @@ static int ieee80211_config_bw(struct ieee80211_link_data *link,
* bandwidth changes where a this could happen, but those cases are
* less common and wouldn't completely prevent using the AP.
*/
- chanreq.oper = ap_chandef;
ret = ieee80211_link_change_chanreq(link, &chanreq, changed);
if (ret) {
@@ -2071,8 +2081,8 @@ static void ieee80211_chswitch_work(struct wiphy *wiphy,
return;
}
- if (!cfg80211_chandef_identical(&link->conf->chanreq.oper,
- &link->csa_chanreq.oper)) {
+ if (!ieee80211_chanreq_identical(&link->conf->chanreq,
+ &link->csa_chanreq)) {
sdata_info(sdata,
"failed to finalize channel switch, disconnecting\n");
wiphy_work_queue(sdata->local->hw.wiphy,
@@ -5146,7 +5156,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
elems = ieee80211_determine_chan_mode(sdata, conn, cbss, link_id,
- &chanreq.oper);
+ &chanreq);
if (IS_ERR(elems)) {
rcu_read_unlock();
@@ -5209,7 +5219,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret;
while (ret && chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) {
- ieee80211_chandef_downgrade(&chanreq.oper, conn);
+ ieee80211_chanreq_downgrade(&chanreq, conn);
ret = ieee80211_link_use_channel(link, &chanreq,
IEEE80211_CHANCTX_SHARED);