--- /dev/null
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Mon, 24 Feb 2020 00:00:00 +0100
+Subject: [PATCH] mac80211: Allow IBSS mode and different beacon intervals
+
+ath10k-ct supports the combination to select IBSS (ADHOC) mode and
+different beacon intervals together. mac80211 does not like this
+combination, but Ben says this is ok, so remove this check.
+
+ath10k-ct starting with version 5.2 allows the combination of
+NL80211_IFTYPE_ADHOC and beacon_int_min_gcd in ath10k_10x_ct_if_comb
+which triggers this warning. Ben told me that this is not a big problem
+and we should ignore this.
+---
+ net/wireless/core.c | 15 ---------------
+ 1 file changed, 15 deletions(-)
+
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -654,21 +654,6 @@ static int wiphy_verify_combinations(str
+ c->limits[j].max > 1))
+ return -EINVAL;
+
+- /*
+- * This isn't well-defined right now. If you have an
+- * IBSS interface, then its beacon interval may change
+- * by joining other networks, and nothing prevents it
+- * from doing that.
+- * So technically we probably shouldn't even allow AP
+- * and IBSS in the same interface, but it seems that
+- * some drivers support that, possibly only with fixed
+- * beacon intervals for IBSS.
+- */
+- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
+- c->beacon_int_min_gcd)) {
+- return -EINVAL;
+- }
+-
+ cnt += c->limits[j].max;
+ /*
+ * Don't advertise an unsupported type
--- /dev/null
+From: David Bauer <mail@david-bauer.net>
+Date: Thu, 30 Nov 2023 07:32:52 +0100
+Subject: [PATCH] mac80211: avoid crashing on invalid band info
+
+Frequent crashes have been observed on MT7916 based platforms. While the
+root of these crashes are currently unknown, they happen when decoding
+rate information of connected STAs in AP mode. The rate-information is
+associated with a band which is not available on the PHY.
+
+Check for this condition in order to avoid crashing the whole system.
+This patch should be removed once the roout cause has been found and
+fixed.
+
+Link: https://github.com/freifunk-gluon/gluon/issues/2980
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -2439,6 +2439,13 @@ static void sta_stats_decode_rate(struct
+
+ sband = local->hw.wiphy->bands[band];
+
++ if (!sband) {
++ wiphy_warn(local->hw.wiphy,
++ "Invalid band %d\n",
++ band);
++ break;
++ }
++
+ if (WARN_ON_ONCE(!sband->bitrates))
+ break;
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 14 Sep 2023 13:17:16 +0200
+Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
+ shutdown
+
+Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
+
+Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -187,6 +187,8 @@ enum ieee80211_channel_flags {
+ * @dfs_state: current state of this channel. Only relevant if radar is required
+ * on this channel.
+ * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
++ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
++ * channel was available.
+ * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
+ * @psd: power spectral density (in dBm)
+ */
+@@ -204,6 +206,7 @@ struct ieee80211_channel {
+ int orig_mag, orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned long dfs_state_entered;
++ unsigned long dfs_state_last_available;
+ unsigned int dfs_cac_ms;
+ s8 psd;
+ };
+--- a/net/wireless/ap.c
++++ b/net/wireless/ap.c
+@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cf
+ if (!wdev->links[link_id].ap.beacon_interval)
+ return -ENOENT;
+
++ cfg80211_update_last_available(wdev->wiphy,
++ &wdev->links[link_id].ap.chandef);
++
+ err = rdev_stop_ap(rdev, dev, link_id);
+ if (!err) {
+ wdev->conn_owner_nlportid = 0;
+@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cf
+ if (notify)
+ nl80211_send_ap_stopped(wdev, link_id);
+
+- /* Should we apply the grace period during beaconing interface
+- * shutdown also?
+- */
+ cfg80211_sched_dfs_chan_update(rdev);
+ }
+
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -598,6 +598,8 @@ static void cfg80211_set_chans_dfs_state
+
+ c->dfs_state = dfs_state;
+ c->dfs_state_entered = jiffies;
++ if (dfs_state == NL80211_DFS_AVAILABLE)
++ c->dfs_state_last_available = jiffies;
+ }
+ }
+
+@@ -1087,6 +1089,49 @@ static bool cfg80211_get_chans_dfs_avail
+ return true;
+ }
+
++static void
++__cfg80211_update_last_available(struct wiphy *wiphy,
++ u32 center_freq,
++ u32 bandwidth)
++{
++ struct ieee80211_channel *c;
++ u32 freq, start_freq, end_freq;
++
++ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
++ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
++
++ /*
++ * Check entire range of channels for the bandwidth.
++ * If any channel in between is disabled or has not
++ * had gone through CAC return false
++ */
++ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
++ c = ieee80211_get_channel_khz(wiphy, freq);
++ if (!c)
++ return;
++
++ c->dfs_state_last_available = jiffies;
++ }
++}
++
++void cfg80211_update_last_available(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef)
++{
++ int width;
++
++ width = cfg80211_chandef_get_width(chandef);
++ if (width < 0)
++ return;
++
++ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
++ width);
++ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
++ return;
++
++ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
++ width);
++}
++
+ static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+ {
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy
+ enum nl80211_dfs_state dfs_state);
+
+ void cfg80211_dfs_channels_update_work(struct work_struct *work);
++void cfg80211_update_last_available(struct wiphy *wiphy,
++ const struct cfg80211_chan_def *chandef);
+
+ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
+
+--- a/net/wireless/mlme.c
++++ b/net/wireless/mlme.c
+@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(s
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
+ time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
+ radar_event = NL80211_RADAR_NOP_FINISHED;
++ timeout = c->dfs_state_entered +
++ msecs_to_jiffies(time_dfs_update);
+ } else {
+ if (regulatory_pre_cac_allowed(wiphy) ||
+ cfg80211_any_wiphy_oper_chan(wiphy, c))
+@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(s
+
+ time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
+ radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
++ timeout = c->dfs_state_last_available +
++ msecs_to_jiffies(time_dfs_update);
+ }
+
+- timeout = c->dfs_state_entered +
+- msecs_to_jiffies(time_dfs_update);
+-
+ if (time_after_eq(jiffies, timeout)) {
+ c->dfs_state = NL80211_DFS_USABLE;
+ c->dfs_state_entered = jiffies;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 14 Sep 2023 13:17:16 +0200
-Subject: [PATCH] cfg80211: allow grace period for DFS available after beacon
- shutdown
-
-Fixes reconfiguring an AP on a DFS channel in non-ETSI regdomain
-
-Fixes: b35a51c7dd25 ("cfg80211: Make pre-CAC results valid only for ETSI domain")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -187,6 +187,8 @@ enum ieee80211_channel_flags {
- * @dfs_state: current state of this channel. Only relevant if radar is required
- * on this channel.
- * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
-+ * @dfs_state_last_available: timestamp (jiffies) of the last time when the
-+ * channel was available.
- * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
- * @psd: power spectral density (in dBm)
- */
-@@ -204,6 +206,7 @@ struct ieee80211_channel {
- int orig_mag, orig_mpwr;
- enum nl80211_dfs_state dfs_state;
- unsigned long dfs_state_entered;
-+ unsigned long dfs_state_last_available;
- unsigned int dfs_cac_ms;
- s8 psd;
- };
---- a/net/wireless/ap.c
-+++ b/net/wireless/ap.c
-@@ -30,6 +30,9 @@ static int ___cfg80211_stop_ap(struct cf
- if (!wdev->links[link_id].ap.beacon_interval)
- return -ENOENT;
-
-+ cfg80211_update_last_available(wdev->wiphy,
-+ &wdev->links[link_id].ap.chandef);
-+
- err = rdev_stop_ap(rdev, dev, link_id);
- if (!err) {
- wdev->conn_owner_nlportid = 0;
-@@ -41,9 +44,6 @@ static int ___cfg80211_stop_ap(struct cf
- if (notify)
- nl80211_send_ap_stopped(wdev, link_id);
-
-- /* Should we apply the grace period during beaconing interface
-- * shutdown also?
-- */
- cfg80211_sched_dfs_chan_update(rdev);
- }
-
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -598,6 +598,8 @@ static void cfg80211_set_chans_dfs_state
-
- c->dfs_state = dfs_state;
- c->dfs_state_entered = jiffies;
-+ if (dfs_state == NL80211_DFS_AVAILABLE)
-+ c->dfs_state_last_available = jiffies;
- }
- }
-
-@@ -1087,6 +1089,49 @@ static bool cfg80211_get_chans_dfs_avail
- return true;
- }
-
-+static void
-+__cfg80211_update_last_available(struct wiphy *wiphy,
-+ u32 center_freq,
-+ u32 bandwidth)
-+{
-+ struct ieee80211_channel *c;
-+ u32 freq, start_freq, end_freq;
-+
-+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
-+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
-+
-+ /*
-+ * Check entire range of channels for the bandwidth.
-+ * If any channel in between is disabled or has not
-+ * had gone through CAC return false
-+ */
-+ for (freq = start_freq; freq <= end_freq; freq += MHZ_TO_KHZ(20)) {
-+ c = ieee80211_get_channel_khz(wiphy, freq);
-+ if (!c)
-+ return;
-+
-+ c->dfs_state_last_available = jiffies;
-+ }
-+}
-+
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef)
-+{
-+ int width;
-+
-+ width = cfg80211_chandef_get_width(chandef);
-+ if (width < 0)
-+ return;
-+
-+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq1),
-+ width);
-+ if (chandef->width != NL80211_CHAN_WIDTH_80P80)
-+ return;
-+
-+ __cfg80211_update_last_available(wiphy, MHZ_TO_KHZ(chandef->center_freq2),
-+ width);
-+}
-+
- static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
- const struct cfg80211_chan_def *chandef)
- {
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy
- enum nl80211_dfs_state dfs_state);
-
- void cfg80211_dfs_channels_update_work(struct work_struct *work);
-+void cfg80211_update_last_available(struct wiphy *wiphy,
-+ const struct cfg80211_chan_def *chandef);
-
- void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
-
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(s
- if (c->dfs_state == NL80211_DFS_UNAVAILABLE) {
- time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS;
- radar_event = NL80211_RADAR_NOP_FINISHED;
-+ timeout = c->dfs_state_entered +
-+ msecs_to_jiffies(time_dfs_update);
- } else {
- if (regulatory_pre_cac_allowed(wiphy) ||
- cfg80211_any_wiphy_oper_chan(wiphy, c))
-@@ -1044,11 +1046,10 @@ void cfg80211_dfs_channels_update_work(s
-
- time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
- radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
-+ timeout = c->dfs_state_last_available +
-+ msecs_to_jiffies(time_dfs_update);
- }
-
-- timeout = c->dfs_state_entered +
-- msecs_to_jiffies(time_dfs_update);
--
- if (time_after_eq(jiffies, timeout)) {
- c->dfs_state = NL80211_DFS_USABLE;
- c->dfs_state_entered = jiffies;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 9 Feb 2024 19:43:40 +0100
+Subject: [PATCH] mac80211: add AQL support for broadcast packets
+
+Excessive broadcast traffic with little competing unicast traffic can easily
+flood hardware queues, leading to throughput issues. Additionally, filling
+the hardware queues with too many packets breaks FQ for broadcast data.
+Fix this by enabling AQL for broadcast packets.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -3423,6 +3423,7 @@ enum wiphy_params_flags {
+ /* The per TXQ device queue limit in airtime */
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
+ #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
++#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000
+
+ /* The per interface airtime threshold to switch to lower queue limit */
+ #define IEEE80211_AQL_THRESHOLD 24000
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f
+ "VI %u us\n"
+ "BE %u us\n"
+ "BK %u us\n"
++ "BC/MC %u us\n"
+ "total %u us\n",
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
+ atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
++ atomic_read(&local->aql_bc_pending_airtime),
+ atomic_read(&local->aql_total_pending_airtime));
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, len);
+@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct
+ "VO %u %u\n"
+ "VI %u %u\n"
+ "BE %u %u\n"
+- "BK %u %u\n",
++ "BK %u %u\n"
++ "BC/MC %u\n",
+ local->aql_txq_limit_low[IEEE80211_AC_VO],
+ local->aql_txq_limit_high[IEEE80211_AC_VO],
+ local->aql_txq_limit_low[IEEE80211_AC_VI],
+@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct
+ local->aql_txq_limit_low[IEEE80211_AC_BE],
+ local->aql_txq_limit_high[IEEE80211_AC_BE],
+ local->aql_txq_limit_low[IEEE80211_AC_BK],
+- local->aql_txq_limit_high[IEEE80211_AC_BK]);
++ local->aql_txq_limit_high[IEEE80211_AC_BK],
++ local->aql_txq_limit_bc);
+ return simple_read_from_buffer(user_buf, count, ppos,
+ buf, len);
+ }
+@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc
+ else
+ buf[count] = '\0';
+
++ if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
++ local->aql_txq_limit_bc = q_limit_low;
++ return count;
++ }
++
+ if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
+ return -EINVAL;
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1351,10 +1351,12 @@ struct ieee80211_local {
+ spinlock_t handle_wake_tx_queue_lock;
+
+ u16 airtime_flags;
++ u32 aql_txq_limit_bc;
+ u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
+ u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
+ u32 aql_threshold;
+ atomic_t aql_total_pending_airtime;
++ atomic_t aql_bc_pending_airtime;
+ atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
+
+ const struct ieee80211_ops *ops;
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -952,6 +952,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
+ spin_lock_init(&local->rx_path_lock);
+ spin_lock_init(&local->queue_stop_reason_lock);
+
++ local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ INIT_LIST_HEAD(&local->active_txqs[i]);
+ spin_lock_init(&local->active_txq_lock[i]);
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -2353,13 +2353,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
+
+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ struct sta_info *sta, u8 ac,
+- u16 tx_airtime, bool tx_completed)
++ u16 tx_airtime, bool tx_completed,
++ bool mcast)
+ {
+ int tx_pending;
+
+ if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
+ return;
+
++ if (mcast) {
++ if (!tx_completed) {
++ atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
++ return;
++ }
++
++ tx_pending = atomic_sub_return(tx_airtime,
++ &local->aql_bc_pending_airtime);
++ if (tx_pending < 0)
++ atomic_cmpxchg(&local->aql_bc_pending_airtime,
++ tx_pending, 0);
++ return;
++ }
++
+ if (!tx_completed) {
+ if (sta)
+ atomic_add(tx_airtime,
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -2554,7 +2554,7 @@ static u16 ieee80211_store_ack_skb(struc
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ id = idr_alloc(&local->ack_status_frames, ack_skb,
+- 1, 0x2000, GFP_ATOMIC);
++ 1, 0x1000, GFP_ATOMIC);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (id >= 0) {
+@@ -3982,20 +3982,20 @@ begin:
+ encap_out:
+ info->control.vif = vif;
+
+- if (tx.sta &&
+- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
+- bool ampdu = txq->ac != IEEE80211_AC_VO;
++ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
++ bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
+ u32 airtime;
+
+ airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
+ skb->len, ampdu);
+- if (airtime) {
+- airtime = ieee80211_info_set_tx_time_est(info, airtime);
+- ieee80211_sta_update_pending_airtime(local, tx.sta,
+- txq->ac,
+- airtime,
+- false);
+- }
++ if (!airtime)
++ return skb;
++
++ airtime = ieee80211_info_set_tx_time_est(info, airtime);
++ info->tx_time_mc = !tx.sta;
++ ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
++ airtime, false,
++ info->tx_time_mc);
+ }
+
+ return skb;
+@@ -4047,6 +4047,7 @@ struct ieee80211_txq *ieee80211_next_txq
+ struct ieee80211_txq *ret = NULL;
+ struct txq_info *txqi = NULL, *head = NULL;
+ bool found_eligible_txq = false;
++ bool aql_check;
+
+ spin_lock_bh(&local->active_txq_lock[ac]);
+
+@@ -4070,26 +4071,26 @@ struct ieee80211_txq *ieee80211_next_txq
+ if (!head)
+ head = txqi;
+
++ aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
++ if (aql_check)
++ found_eligible_txq = true;
++
+ if (txqi->txq.sta) {
+ struct sta_info *sta = container_of(txqi->txq.sta,
+ struct sta_info, sta);
+- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
+- s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
+-
+- if (aql_check)
+- found_eligible_txq = true;
+-
+- if (deficit < 0)
++ if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
+ sta->airtime[txqi->txq.ac].deficit +=
+ sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
+-
+- if (deficit < 0 || !aql_check) {
+- list_move_tail(&txqi->schedule_order,
+- &local->active_txqs[txqi->txq.ac]);
+- goto begin;
++ aql_check = false;
+ }
+ }
+
++ if (!aql_check) {
++ list_move_tail(&txqi->schedule_order,
++ &local->active_txqs[txqi->txq.ac]);
++ goto begin;
++ }
++
+ if (txqi->schedule_round == local->schedule_round[ac])
+ goto out;
+
+@@ -4154,7 +4155,8 @@ bool ieee80211_txq_airtime_check(struct
+ return true;
+
+ if (!txq->sta)
+- return true;
++ return atomic_read(&local->aql_bc_pending_airtime) <
++ local->aql_txq_limit_bc;
+
+ if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
+ return true;
+@@ -4203,15 +4205,15 @@ bool ieee80211_txq_may_transmit(struct i
+
+ spin_lock_bh(&local->active_txq_lock[ac]);
+
+- if (!txqi->txq.sta)
+- goto out;
+-
+ if (list_empty(&txqi->schedule_order))
+ goto out;
+
+ if (!ieee80211_txq_schedule_airtime_check(local, ac))
+ goto out;
+
++ if (!txqi->txq.sta)
++ goto out;
++
+ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
+ schedule_order) {
+ if (iter == txqi)
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1221,8 +1221,8 @@ struct ieee80211_tx_info {
+ status_data_idr:1,
+ status_data:13,
+ hw_queue:4,
++ tx_time_mc:1,
+ tx_time_est:10;
+- /* 1 free bit */
+
+ union {
+ struct {
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -147,7 +147,8 @@ struct airtime_info {
+
+ void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
+ struct sta_info *sta, u8 ac,
+- u16 tx_airtime, bool tx_completed);
++ u16 tx_airtime, bool tx_completed,
++ bool mcast);
+
+ struct sta_info;
+
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -734,7 +734,7 @@ static void ieee80211_report_used_skb(st
+ ieee80211_sta_update_pending_airtime(local, sta,
+ skb_get_queue_mapping(skb),
+ tx_time_est,
+- true);
++ true, info->tx_time_mc);
+ rcu_read_unlock();
+ }
+
+@@ -1158,10 +1158,11 @@ void ieee80211_tx_status_ext(struct ieee
+ /* Do this here to avoid the expensive lookup of the sta
+ * in ieee80211_report_used_skb().
+ */
++ bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
+ ieee80211_sta_update_pending_airtime(local, sta,
+ skb_get_queue_mapping(skb),
+ tx_time_est,
+- true);
++ true, mcast);
+ ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
+ }
+
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 9 Feb 2024 19:43:40 +0100
-Subject: [PATCH] mac80211: add AQL support for broadcast packets
-
-Excessive broadcast traffic with little competing unicast traffic can easily
-flood hardware queues, leading to throughput issues. Additionally, filling
-the hardware queues with too many packets breaks FQ for broadcast data.
-Fix this by enabling AQL for broadcast packets.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -3423,6 +3423,7 @@ enum wiphy_params_flags {
- /* The per TXQ device queue limit in airtime */
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_L 5000
- #define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_H 12000
-+#define IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC 50000
-
- /* The per interface airtime threshold to switch to lower queue limit */
- #define IEEE80211_AQL_THRESHOLD 24000
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -215,11 +215,13 @@ static ssize_t aql_pending_read(struct f
- "VI %u us\n"
- "BE %u us\n"
- "BK %u us\n"
-+ "BC/MC %u us\n"
- "total %u us\n",
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VO]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_VI]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BE]),
- atomic_read(&local->aql_ac_pending_airtime[IEEE80211_AC_BK]),
-+ atomic_read(&local->aql_bc_pending_airtime),
- atomic_read(&local->aql_total_pending_airtime));
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
-@@ -245,7 +247,8 @@ static ssize_t aql_txq_limit_read(struct
- "VO %u %u\n"
- "VI %u %u\n"
- "BE %u %u\n"
-- "BK %u %u\n",
-+ "BK %u %u\n"
-+ "BC/MC %u\n",
- local->aql_txq_limit_low[IEEE80211_AC_VO],
- local->aql_txq_limit_high[IEEE80211_AC_VO],
- local->aql_txq_limit_low[IEEE80211_AC_VI],
-@@ -253,7 +256,8 @@ static ssize_t aql_txq_limit_read(struct
- local->aql_txq_limit_low[IEEE80211_AC_BE],
- local->aql_txq_limit_high[IEEE80211_AC_BE],
- local->aql_txq_limit_low[IEEE80211_AC_BK],
-- local->aql_txq_limit_high[IEEE80211_AC_BK]);
-+ local->aql_txq_limit_high[IEEE80211_AC_BK],
-+ local->aql_txq_limit_bc);
- return simple_read_from_buffer(user_buf, count, ppos,
- buf, len);
- }
-@@ -279,6 +283,11 @@ static ssize_t aql_txq_limit_write(struc
- else
- buf[count] = '\0';
-
-+ if (sscanf(buf, "mcast %u", &q_limit_low) == 1) {
-+ local->aql_txq_limit_bc = q_limit_low;
-+ return count;
-+ }
-+
- if (sscanf(buf, "%u %u %u", &ac, &q_limit_low, &q_limit_high) != 3)
- return -EINVAL;
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1351,10 +1351,12 @@ struct ieee80211_local {
- spinlock_t handle_wake_tx_queue_lock;
-
- u16 airtime_flags;
-+ u32 aql_txq_limit_bc;
- u32 aql_txq_limit_low[IEEE80211_NUM_ACS];
- u32 aql_txq_limit_high[IEEE80211_NUM_ACS];
- u32 aql_threshold;
- atomic_t aql_total_pending_airtime;
-+ atomic_t aql_bc_pending_airtime;
- atomic_t aql_ac_pending_airtime[IEEE80211_NUM_ACS];
-
- const struct ieee80211_ops *ops;
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -952,6 +952,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
- spin_lock_init(&local->rx_path_lock);
- spin_lock_init(&local->queue_stop_reason_lock);
-
-+ local->aql_txq_limit_bc = IEEE80211_DEFAULT_AQL_TXQ_LIMIT_BC;
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- INIT_LIST_HEAD(&local->active_txqs[i]);
- spin_lock_init(&local->active_txq_lock[i]);
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -2353,13 +2353,28 @@ EXPORT_SYMBOL(ieee80211_sta_recalc_aggre
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
-- u16 tx_airtime, bool tx_completed)
-+ u16 tx_airtime, bool tx_completed,
-+ bool mcast)
- {
- int tx_pending;
-
- if (!wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL))
- return;
-
-+ if (mcast) {
-+ if (!tx_completed) {
-+ atomic_add(tx_airtime, &local->aql_bc_pending_airtime);
-+ return;
-+ }
-+
-+ tx_pending = atomic_sub_return(tx_airtime,
-+ &local->aql_bc_pending_airtime);
-+ if (tx_pending < 0)
-+ atomic_cmpxchg(&local->aql_bc_pending_airtime,
-+ tx_pending, 0);
-+ return;
-+ }
-+
- if (!tx_completed) {
- if (sta)
- atomic_add(tx_airtime,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -2554,7 +2554,7 @@ static u16 ieee80211_store_ack_skb(struc
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- id = idr_alloc(&local->ack_status_frames, ack_skb,
-- 1, 0x2000, GFP_ATOMIC);
-+ 1, 0x1000, GFP_ATOMIC);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- if (id >= 0) {
-@@ -3982,20 +3982,20 @@ begin:
- encap_out:
- info->control.vif = vif;
-
-- if (tx.sta &&
-- wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-- bool ampdu = txq->ac != IEEE80211_AC_VO;
-+ if (wiphy_ext_feature_isset(local->hw.wiphy, NL80211_EXT_FEATURE_AQL)) {
-+ bool ampdu = txq->sta && txq->ac != IEEE80211_AC_VO;
- u32 airtime;
-
- airtime = ieee80211_calc_expected_tx_airtime(hw, vif, txq->sta,
- skb->len, ampdu);
-- if (airtime) {
-- airtime = ieee80211_info_set_tx_time_est(info, airtime);
-- ieee80211_sta_update_pending_airtime(local, tx.sta,
-- txq->ac,
-- airtime,
-- false);
-- }
-+ if (!airtime)
-+ return skb;
-+
-+ airtime = ieee80211_info_set_tx_time_est(info, airtime);
-+ info->tx_time_mc = !tx.sta;
-+ ieee80211_sta_update_pending_airtime(local, tx.sta, txq->ac,
-+ airtime, false,
-+ info->tx_time_mc);
- }
-
- return skb;
-@@ -4047,6 +4047,7 @@ struct ieee80211_txq *ieee80211_next_txq
- struct ieee80211_txq *ret = NULL;
- struct txq_info *txqi = NULL, *head = NULL;
- bool found_eligible_txq = false;
-+ bool aql_check;
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-
-@@ -4070,26 +4071,26 @@ struct ieee80211_txq *ieee80211_next_txq
- if (!head)
- head = txqi;
-
-+ aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-+ if (aql_check)
-+ found_eligible_txq = true;
-+
- if (txqi->txq.sta) {
- struct sta_info *sta = container_of(txqi->txq.sta,
- struct sta_info, sta);
-- bool aql_check = ieee80211_txq_airtime_check(hw, &txqi->txq);
-- s32 deficit = ieee80211_sta_deficit(sta, txqi->txq.ac);
--
-- if (aql_check)
-- found_eligible_txq = true;
--
-- if (deficit < 0)
-+ if (ieee80211_sta_deficit(sta, txqi->txq.ac) < 0) {
- sta->airtime[txqi->txq.ac].deficit +=
- sta->airtime_weight << AIRTIME_QUANTUM_SHIFT;
--
-- if (deficit < 0 || !aql_check) {
-- list_move_tail(&txqi->schedule_order,
-- &local->active_txqs[txqi->txq.ac]);
-- goto begin;
-+ aql_check = false;
- }
- }
-
-+ if (!aql_check) {
-+ list_move_tail(&txqi->schedule_order,
-+ &local->active_txqs[txqi->txq.ac]);
-+ goto begin;
-+ }
-+
- if (txqi->schedule_round == local->schedule_round[ac])
- goto out;
-
-@@ -4154,7 +4155,8 @@ bool ieee80211_txq_airtime_check(struct
- return true;
-
- if (!txq->sta)
-- return true;
-+ return atomic_read(&local->aql_bc_pending_airtime) <
-+ local->aql_txq_limit_bc;
-
- if (unlikely(txq->tid == IEEE80211_NUM_TIDS))
- return true;
-@@ -4203,15 +4205,15 @@ bool ieee80211_txq_may_transmit(struct i
-
- spin_lock_bh(&local->active_txq_lock[ac]);
-
-- if (!txqi->txq.sta)
-- goto out;
--
- if (list_empty(&txqi->schedule_order))
- goto out;
-
- if (!ieee80211_txq_schedule_airtime_check(local, ac))
- goto out;
-
-+ if (!txqi->txq.sta)
-+ goto out;
-+
- list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
- schedule_order) {
- if (iter == txqi)
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1221,8 +1221,8 @@ struct ieee80211_tx_info {
- status_data_idr:1,
- status_data:13,
- hw_queue:4,
-+ tx_time_mc:1,
- tx_time_est:10;
-- /* 1 free bit */
-
- union {
- struct {
---- a/net/mac80211/sta_info.h
-+++ b/net/mac80211/sta_info.h
-@@ -147,7 +147,8 @@ struct airtime_info {
-
- void ieee80211_sta_update_pending_airtime(struct ieee80211_local *local,
- struct sta_info *sta, u8 ac,
-- u16 tx_airtime, bool tx_completed);
-+ u16 tx_airtime, bool tx_completed,
-+ bool mcast);
-
- struct sta_info;
-
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -734,7 +734,7 @@ static void ieee80211_report_used_skb(st
- ieee80211_sta_update_pending_airtime(local, sta,
- skb_get_queue_mapping(skb),
- tx_time_est,
-- true);
-+ true, info->tx_time_mc);
- rcu_read_unlock();
- }
-
-@@ -1158,10 +1158,11 @@ void ieee80211_tx_status_ext(struct ieee
- /* Do this here to avoid the expensive lookup of the sta
- * in ieee80211_report_used_skb().
- */
-+ bool mcast = IEEE80211_SKB_CB(skb)->tx_time_mc;
- ieee80211_sta_update_pending_airtime(local, sta,
- skb_get_queue_mapping(skb),
- tx_time_est,
-- true);
-+ true, mcast);
- ieee80211_info_set_tx_time_est(IEEE80211_SKB_CB(skb), 0);
- }
-
--- /dev/null
+From: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
+Date: Wed, 4 Sep 2024 19:12:56 +0800
+Subject: [PATCH] wifi: mac80211: introduce EHT rate support in AQL airtime
+
+Add definitions related to EHT mode and airtime calculation
+according to the 802.11BE_D4.0.
+
+Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
+Signed-off-by: Deren Wu <deren.wu@mediatek.com>
+Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
+Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
+Link: https://patch.msgid.link/20240904111256.11734-1-mingyen.hsieh@mediatek.com
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/airtime.c
++++ b/net/mac80211/airtime.c
+@@ -55,10 +55,21 @@
+ #define HE_DURATION_S(shift, streams, gi, bps) \
+ (HE_DURATION(streams, gi, bps) >> shift)
+
++/* gi in HE/EHT is identical. It matches enum nl80211_eht_gi as well */
++#define EHT_GI_08 HE_GI_08
++#define EHT_GI_16 HE_GI_16
++#define EHT_GI_32 HE_GI_32
++
++#define EHT_DURATION(streams, gi, bps) \
++ HE_DURATION(streams, gi, bps)
++#define EHT_DURATION_S(shift, streams, gi, bps) \
++ HE_DURATION_S(shift, streams, gi, bps)
++
+ #define BW_20 0
+ #define BW_40 1
+ #define BW_80 2
+ #define BW_160 3
++#define BW_320 4
+
+ /*
+ * Define group sort order: HT40 -> SGI -> #streams
+@@ -68,17 +79,26 @@
+ #define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */
+
+ #define IEEE80211_HE_MAX_STREAMS 8
++#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */
++
++#define IEEE80211_EHT_MAX_STREAMS 8
++#define IEEE80211_EHT_STREAM_GROUPS 15 /* BW(=5) * GI(=3) */
+
+ #define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
+ IEEE80211_HT_STREAM_GROUPS)
+ #define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
+ IEEE80211_VHT_STREAM_GROUPS)
++#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \
++ IEEE80211_HE_STREAM_GROUPS)
++#define IEEE80211_EHT_GROUPS_NB (IEEE80211_EHT_MAX_STREAMS * \
++ IEEE80211_EHT_STREAM_GROUPS)
+
+ #define IEEE80211_HT_GROUP_0 0
+ #define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
+ #define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
++#define IEEE80211_EHT_GROUP_0 (IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
+
+-#define MCS_GROUP_RATES 12
++#define MCS_GROUP_RATES 14
+
+ #define HT_GROUP_IDX(_streams, _sgi, _ht40) \
+ IEEE80211_HT_GROUP_0 + \
+@@ -203,6 +223,69 @@
+ #define HE_GROUP(_streams, _gi, _bw) \
+ __HE_GROUP(_streams, _gi, _bw, \
+ HE_GROUP_SHIFT(_streams, _gi, _bw))
++
++#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1) \
++ ((_bw) == BW_320 ? r5 : BW2VBPS(_bw, r4, r3, r2, r1))
++
++#define EHT_GROUP_IDX(_streams, _gi, _bw) \
++ (IEEE80211_EHT_GROUP_0 + \
++ IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) + \
++ IEEE80211_EHT_MAX_STREAMS * (_gi) + \
++ (_streams) - 1)
++
++#define __EHT_GROUP(_streams, _gi, _bw, _s) \
++ [EHT_GROUP_IDX(_streams, _gi, _bw)] = { \
++ .shift = _s, \
++ .duration = { \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 1960, 980, 490, 234, 117)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 3920, 1960, 980, 468, 234)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 5880, 2937, 1470, 702, 351)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 7840, 3920, 1960, 936, 468)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 11760, 5880, 2940, 1404, 702)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 15680, 7840, 3920, 1872, 936)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 17640, 8820, 4410, 2106, 1053)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 19600, 9800, 4900, 2340, 1170)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 23520, 11760, 5880, 2808, 1404)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 26133, 13066, 6533, 3120, 1560)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 29400, 14700, 7350, 3510, 1755)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 32666, 16333, 8166, 3900, 1950)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 35280, 17640, 8820, 4212, 2106)), \
++ EHT_DURATION_S(_s, _streams, _gi, \
++ EHT_BW2VBPS(_bw, 39200, 19600, 9800, 4680, 2340)) \
++ } \
++}
++
++#define EHT_GROUP_SHIFT(_streams, _gi, _bw) \
++ GROUP_SHIFT(EHT_DURATION(_streams, _gi, \
++ EHT_BW2VBPS(_bw, 1960, 980, 490, 234, 117)))
++
++#define EHT_GROUP(_streams, _gi, _bw) \
++ __EHT_GROUP(_streams, _gi, _bw, \
++ EHT_GROUP_SHIFT(_streams, _gi, _bw))
++
++#define EHT_GROUP_RANGE(_gi, _bw) \
++ EHT_GROUP(1, _gi, _bw), \
++ EHT_GROUP(2, _gi, _bw), \
++ EHT_GROUP(3, _gi, _bw), \
++ EHT_GROUP(4, _gi, _bw), \
++ EHT_GROUP(5, _gi, _bw), \
++ EHT_GROUP(6, _gi, _bw), \
++ EHT_GROUP(7, _gi, _bw), \
++ EHT_GROUP(8, _gi, _bw)
++
+ struct mcs_group {
+ u8 shift;
+ u16 duration[MCS_GROUP_RATES];
+@@ -376,6 +459,26 @@ static const struct mcs_group airtime_mc
+ HE_GROUP(6, HE_GI_32, BW_160),
+ HE_GROUP(7, HE_GI_32, BW_160),
+ HE_GROUP(8, HE_GI_32, BW_160),
++
++ EHT_GROUP_RANGE(EHT_GI_08, BW_20),
++ EHT_GROUP_RANGE(EHT_GI_16, BW_20),
++ EHT_GROUP_RANGE(EHT_GI_32, BW_20),
++
++ EHT_GROUP_RANGE(EHT_GI_08, BW_40),
++ EHT_GROUP_RANGE(EHT_GI_16, BW_40),
++ EHT_GROUP_RANGE(EHT_GI_32, BW_40),
++
++ EHT_GROUP_RANGE(EHT_GI_08, BW_80),
++ EHT_GROUP_RANGE(EHT_GI_16, BW_80),
++ EHT_GROUP_RANGE(EHT_GI_32, BW_80),
++
++ EHT_GROUP_RANGE(EHT_GI_08, BW_160),
++ EHT_GROUP_RANGE(EHT_GI_16, BW_160),
++ EHT_GROUP_RANGE(EHT_GI_32, BW_160),
++
++ EHT_GROUP_RANGE(EHT_GI_08, BW_320),
++ EHT_GROUP_RANGE(EHT_GI_16, BW_320),
++ EHT_GROUP_RANGE(EHT_GI_32, BW_320),
+ };
+
+ static u32
+@@ -422,6 +525,9 @@ static u32 ieee80211_get_rate_duration(s
+ case RATE_INFO_BW_160:
+ bw = BW_160;
+ break;
++ case RATE_INFO_BW_320:
++ bw = BW_320;
++ break;
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+@@ -443,14 +549,27 @@ static u32 ieee80211_get_rate_duration(s
+ idx = status->rate_idx;
+ group = HE_GROUP_IDX(streams, status->he_gi, bw);
+ break;
++ case RX_ENC_EHT:
++ streams = status->nss;
++ idx = status->rate_idx;
++ group = EHT_GROUP_IDX(streams, status->eht.gi, bw);
++ break;
+ default:
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+
+- if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
+- (status->encoding == RX_ENC_HE && streams > 8)))
+- return 0;
++ switch (status->encoding) {
++ case RX_ENC_EHT:
++ case RX_ENC_HE:
++ if (WARN_ON_ONCE(streams > 8))
++ return 0;
++ break;
++ default:
++ if (WARN_ON_ONCE(streams > 4))
++ return 0;
++ break;
++ }
+
+ if (idx >= MCS_GROUP_RATES)
+ return 0;
+@@ -517,7 +636,9 @@ static bool ieee80211_fill_rate_info(str
+ stat->nss = ri->nss;
+ stat->rate_idx = ri->mcs;
+
+- if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
++ if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
++ stat->encoding = RX_ENC_EHT;
++ else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
+ stat->encoding = RX_ENC_HE;
+ else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
+ stat->encoding = RX_ENC_VHT;
+@@ -529,7 +650,14 @@ static bool ieee80211_fill_rate_info(str
+ if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
+ stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
+
+- stat->he_gi = ri->he_gi;
++ switch (stat->encoding) {
++ case RX_ENC_EHT:
++ stat->eht.gi = ri->eht_gi;
++ break;
++ default:
++ stat->he_gi = ri->he_gi;
++ break;
++ }
+
+ if (stat->encoding != RX_ENC_LEGACY)
+ return true;
+++ /dev/null
-From: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
-Date: Wed, 4 Sep 2024 19:12:56 +0800
-Subject: [PATCH] wifi: mac80211: introduce EHT rate support in AQL airtime
-
-Add definitions related to EHT mode and airtime calculation
-according to the 802.11BE_D4.0.
-
-Co-developed-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Bo Jiao <Bo.Jiao@mediatek.com>
-Signed-off-by: Deren Wu <deren.wu@mediatek.com>
-Signed-off-by: Quan Zhou <quan.zhou@mediatek.com>
-Signed-off-by: Ming Yen Hsieh <mingyen.hsieh@mediatek.com>
-Link: https://patch.msgid.link/20240904111256.11734-1-mingyen.hsieh@mediatek.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/airtime.c
-+++ b/net/mac80211/airtime.c
-@@ -55,10 +55,21 @@
- #define HE_DURATION_S(shift, streams, gi, bps) \
- (HE_DURATION(streams, gi, bps) >> shift)
-
-+/* gi in HE/EHT is identical. It matches enum nl80211_eht_gi as well */
-+#define EHT_GI_08 HE_GI_08
-+#define EHT_GI_16 HE_GI_16
-+#define EHT_GI_32 HE_GI_32
-+
-+#define EHT_DURATION(streams, gi, bps) \
-+ HE_DURATION(streams, gi, bps)
-+#define EHT_DURATION_S(shift, streams, gi, bps) \
-+ HE_DURATION_S(shift, streams, gi, bps)
-+
- #define BW_20 0
- #define BW_40 1
- #define BW_80 2
- #define BW_160 3
-+#define BW_320 4
-
- /*
- * Define group sort order: HT40 -> SGI -> #streams
-@@ -68,17 +79,26 @@
- #define IEEE80211_VHT_STREAM_GROUPS 8 /* BW(=4) * SGI(=2) */
-
- #define IEEE80211_HE_MAX_STREAMS 8
-+#define IEEE80211_HE_STREAM_GROUPS 12 /* BW(=4) * GI(=3) */
-+
-+#define IEEE80211_EHT_MAX_STREAMS 8
-+#define IEEE80211_EHT_STREAM_GROUPS 15 /* BW(=5) * GI(=3) */
-
- #define IEEE80211_HT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
- IEEE80211_HT_STREAM_GROUPS)
- #define IEEE80211_VHT_GROUPS_NB (IEEE80211_MAX_STREAMS * \
- IEEE80211_VHT_STREAM_GROUPS)
-+#define IEEE80211_HE_GROUPS_NB (IEEE80211_HE_MAX_STREAMS * \
-+ IEEE80211_HE_STREAM_GROUPS)
-+#define IEEE80211_EHT_GROUPS_NB (IEEE80211_EHT_MAX_STREAMS * \
-+ IEEE80211_EHT_STREAM_GROUPS)
-
- #define IEEE80211_HT_GROUP_0 0
- #define IEEE80211_VHT_GROUP_0 (IEEE80211_HT_GROUP_0 + IEEE80211_HT_GROUPS_NB)
- #define IEEE80211_HE_GROUP_0 (IEEE80211_VHT_GROUP_0 + IEEE80211_VHT_GROUPS_NB)
-+#define IEEE80211_EHT_GROUP_0 (IEEE80211_HE_GROUP_0 + IEEE80211_HE_GROUPS_NB)
-
--#define MCS_GROUP_RATES 12
-+#define MCS_GROUP_RATES 14
-
- #define HT_GROUP_IDX(_streams, _sgi, _ht40) \
- IEEE80211_HT_GROUP_0 + \
-@@ -203,6 +223,69 @@
- #define HE_GROUP(_streams, _gi, _bw) \
- __HE_GROUP(_streams, _gi, _bw, \
- HE_GROUP_SHIFT(_streams, _gi, _bw))
-+
-+#define EHT_BW2VBPS(_bw, r5, r4, r3, r2, r1) \
-+ ((_bw) == BW_320 ? r5 : BW2VBPS(_bw, r4, r3, r2, r1))
-+
-+#define EHT_GROUP_IDX(_streams, _gi, _bw) \
-+ (IEEE80211_EHT_GROUP_0 + \
-+ IEEE80211_EHT_MAX_STREAMS * 3 * (_bw) + \
-+ IEEE80211_EHT_MAX_STREAMS * (_gi) + \
-+ (_streams) - 1)
-+
-+#define __EHT_GROUP(_streams, _gi, _bw, _s) \
-+ [EHT_GROUP_IDX(_streams, _gi, _bw)] = { \
-+ .shift = _s, \
-+ .duration = { \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 1960, 980, 490, 234, 117)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 3920, 1960, 980, 468, 234)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 5880, 2937, 1470, 702, 351)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 7840, 3920, 1960, 936, 468)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 11760, 5880, 2940, 1404, 702)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 15680, 7840, 3920, 1872, 936)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 17640, 8820, 4410, 2106, 1053)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 19600, 9800, 4900, 2340, 1170)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 23520, 11760, 5880, 2808, 1404)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 26133, 13066, 6533, 3120, 1560)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 29400, 14700, 7350, 3510, 1755)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 32666, 16333, 8166, 3900, 1950)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 35280, 17640, 8820, 4212, 2106)), \
-+ EHT_DURATION_S(_s, _streams, _gi, \
-+ EHT_BW2VBPS(_bw, 39200, 19600, 9800, 4680, 2340)) \
-+ } \
-+}
-+
-+#define EHT_GROUP_SHIFT(_streams, _gi, _bw) \
-+ GROUP_SHIFT(EHT_DURATION(_streams, _gi, \
-+ EHT_BW2VBPS(_bw, 1960, 980, 490, 234, 117)))
-+
-+#define EHT_GROUP(_streams, _gi, _bw) \
-+ __EHT_GROUP(_streams, _gi, _bw, \
-+ EHT_GROUP_SHIFT(_streams, _gi, _bw))
-+
-+#define EHT_GROUP_RANGE(_gi, _bw) \
-+ EHT_GROUP(1, _gi, _bw), \
-+ EHT_GROUP(2, _gi, _bw), \
-+ EHT_GROUP(3, _gi, _bw), \
-+ EHT_GROUP(4, _gi, _bw), \
-+ EHT_GROUP(5, _gi, _bw), \
-+ EHT_GROUP(6, _gi, _bw), \
-+ EHT_GROUP(7, _gi, _bw), \
-+ EHT_GROUP(8, _gi, _bw)
-+
- struct mcs_group {
- u8 shift;
- u16 duration[MCS_GROUP_RATES];
-@@ -376,6 +459,26 @@ static const struct mcs_group airtime_mc
- HE_GROUP(6, HE_GI_32, BW_160),
- HE_GROUP(7, HE_GI_32, BW_160),
- HE_GROUP(8, HE_GI_32, BW_160),
-+
-+ EHT_GROUP_RANGE(EHT_GI_08, BW_20),
-+ EHT_GROUP_RANGE(EHT_GI_16, BW_20),
-+ EHT_GROUP_RANGE(EHT_GI_32, BW_20),
-+
-+ EHT_GROUP_RANGE(EHT_GI_08, BW_40),
-+ EHT_GROUP_RANGE(EHT_GI_16, BW_40),
-+ EHT_GROUP_RANGE(EHT_GI_32, BW_40),
-+
-+ EHT_GROUP_RANGE(EHT_GI_08, BW_80),
-+ EHT_GROUP_RANGE(EHT_GI_16, BW_80),
-+ EHT_GROUP_RANGE(EHT_GI_32, BW_80),
-+
-+ EHT_GROUP_RANGE(EHT_GI_08, BW_160),
-+ EHT_GROUP_RANGE(EHT_GI_16, BW_160),
-+ EHT_GROUP_RANGE(EHT_GI_32, BW_160),
-+
-+ EHT_GROUP_RANGE(EHT_GI_08, BW_320),
-+ EHT_GROUP_RANGE(EHT_GI_16, BW_320),
-+ EHT_GROUP_RANGE(EHT_GI_32, BW_320),
- };
-
- static u32
-@@ -422,6 +525,9 @@ static u32 ieee80211_get_rate_duration(s
- case RATE_INFO_BW_160:
- bw = BW_160;
- break;
-+ case RATE_INFO_BW_320:
-+ bw = BW_320;
-+ break;
- default:
- WARN_ON_ONCE(1);
- return 0;
-@@ -443,14 +549,27 @@ static u32 ieee80211_get_rate_duration(s
- idx = status->rate_idx;
- group = HE_GROUP_IDX(streams, status->he_gi, bw);
- break;
-+ case RX_ENC_EHT:
-+ streams = status->nss;
-+ idx = status->rate_idx;
-+ group = EHT_GROUP_IDX(streams, status->eht.gi, bw);
-+ break;
- default:
- WARN_ON_ONCE(1);
- return 0;
- }
-
-- if (WARN_ON_ONCE((status->encoding != RX_ENC_HE && streams > 4) ||
-- (status->encoding == RX_ENC_HE && streams > 8)))
-- return 0;
-+ switch (status->encoding) {
-+ case RX_ENC_EHT:
-+ case RX_ENC_HE:
-+ if (WARN_ON_ONCE(streams > 8))
-+ return 0;
-+ break;
-+ default:
-+ if (WARN_ON_ONCE(streams > 4))
-+ return 0;
-+ break;
-+ }
-
- if (idx >= MCS_GROUP_RATES)
- return 0;
-@@ -517,7 +636,9 @@ static bool ieee80211_fill_rate_info(str
- stat->nss = ri->nss;
- stat->rate_idx = ri->mcs;
-
-- if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
-+ if (ri->flags & RATE_INFO_FLAGS_EHT_MCS)
-+ stat->encoding = RX_ENC_EHT;
-+ else if (ri->flags & RATE_INFO_FLAGS_HE_MCS)
- stat->encoding = RX_ENC_HE;
- else if (ri->flags & RATE_INFO_FLAGS_VHT_MCS)
- stat->encoding = RX_ENC_VHT;
-@@ -529,7 +650,14 @@ static bool ieee80211_fill_rate_info(str
- if (ri->flags & RATE_INFO_FLAGS_SHORT_GI)
- stat->enc_flags |= RX_ENC_FLAG_SHORT_GI;
-
-- stat->he_gi = ri->he_gi;
-+ switch (stat->encoding) {
-+ case RX_ENC_EHT:
-+ stat->eht.gi = ri->eht_gi;
-+ break;
-+ default:
-+ stat->he_gi = ri->he_gi;
-+ break;
-+ }
-
- if (stat->encoding != RX_ENC_LEGACY)
- return true;
+++ /dev/null
-From: Hauke Mehrtens <hauke@hauke-m.de>
-Date: Mon, 24 Feb 2020 00:00:00 +0100
-Subject: [PATCH] mac80211: Allow IBSS mode and different beacon intervals
-
-ath10k-ct supports the combination to select IBSS (ADHOC) mode and
-different beacon intervals together. mac80211 does not like this
-combination, but Ben says this is ok, so remove this check.
-
-ath10k-ct starting with version 5.2 allows the combination of
-NL80211_IFTYPE_ADHOC and beacon_int_min_gcd in ath10k_10x_ct_if_comb
-which triggers this warning. Ben told me that this is not a big problem
-and we should ignore this.
----
- net/wireless/core.c | 15 ---------------
- 1 file changed, 15 deletions(-)
-
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -654,21 +654,6 @@ static int wiphy_verify_combinations(str
- c->limits[j].max > 1))
- return -EINVAL;
-
-- /*
-- * This isn't well-defined right now. If you have an
-- * IBSS interface, then its beacon interval may change
-- * by joining other networks, and nothing prevents it
-- * from doing that.
-- * So technically we probably shouldn't even allow AP
-- * and IBSS in the same interface, but it seems that
-- * some drivers support that, possibly only with fixed
-- * beacon intervals for IBSS.
-- */
-- if (WARN_ON(types & BIT(NL80211_IFTYPE_ADHOC) &&
-- c->beacon_int_min_gcd)) {
-- return -EINVAL;
-- }
--
- cnt += c->limits[j].max;
- /*
- * Don't advertise an unsupported type
+++ /dev/null
-From: David Bauer <mail@david-bauer.net>
-Date: Thu, 30 Nov 2023 07:32:52 +0100
-Subject: [PATCH] mac80211: avoid crashing on invalid band info
-
-Frequent crashes have been observed on MT7916 based platforms. While the
-root of these crashes are currently unknown, they happen when decoding
-rate information of connected STAs in AP mode. The rate-information is
-associated with a band which is not available on the PHY.
-
-Check for this condition in order to avoid crashing the whole system.
-This patch should be removed once the roout cause has been found and
-fixed.
-
-Link: https://github.com/freifunk-gluon/gluon/issues/2980
-
-Signed-off-by: David Bauer <mail@david-bauer.net>
----
-
---- a/net/mac80211/sta_info.c
-+++ b/net/mac80211/sta_info.c
-@@ -2455,6 +2455,13 @@ static void sta_stats_decode_rate(struct
-
- sband = local->hw.wiphy->bands[band];
-
-+ if (!sband) {
-+ wiphy_warn(local->hw.wiphy,
-+ "Invalid band %d\n",
-+ band);
-+ break;
-+ }
-+
- if (WARN_ON_ONCE(!sband->bitrates))
- break;
-