PKG_NAME:=mac80211
-PKG_VERSION:=6.11.2
+PKG_VERSION:=6.12.6
PKG_RELEASE:=1
PKG_LICENSE:=GPL-2.0-only
PKG_LICENSE_FILES:=COPYING
PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources/
-PKG_HASH:=700ea5abef8dde9e3c6df2acd32ff443da735d773d56db9a80269e2237549b34
+PKG_HASH:=28ec39425a1b3270e1422d92a8131a6a3d8919cc13e8ee250c315e55d922ba68
PKG_SOURCE:=backports-$(PKG_VERSION).tar.xz
PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(if $(BUILD_VARIANT),$(PKG_NAME)-$(BUILD_VARIANT)/)backports-$(PKG_VERSION)
#include "../ath.h"
#include "ath5k.h"
#include "debug.h"
-@@ -71,7 +72,7 @@ static void ath5k_pci_read_cachesize(str
+@@ -73,7 +74,7 @@ static void ath5k_pci_read_cachesize(str
}
/*
*/
static bool
ath5k_pci_eeprom_read(struct ath_common *common, u32 offset, u16 *data)
-@@ -79,6 +80,19 @@ ath5k_pci_eeprom_read(struct ath_common
+@@ -81,6 +82,19 @@ ath5k_pci_eeprom_read(struct ath_common
struct ath5k_hw *ah = common->ah;
u32 status, timeout;
/*
* Initialize EEPROM access
*/
-@@ -122,6 +136,16 @@ static int ath5k_pci_eeprom_read_mac(str
+@@ -124,6 +138,16 @@ static int ath5k_pci_eeprom_read_mac(str
u16 data;
int octet;
enum ath12k_dev_flags {
ATH12K_CAC_RUNNING,
ATH12K_FLAG_CRASH_FLUSH,
-@@ -313,6 +319,8 @@ struct ath12k_vif_iter {
+@@ -319,6 +325,8 @@ struct ath12k_vif_iter {
#define ATH12K_RX_RATE_TABLE_11AX_NUM 576
#define ATH12K_RX_RATE_TABLE_NUM 320
struct ath12k_rx_peer_rate_stats {
u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1];
u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1];
-@@ -648,6 +656,13 @@ struct ath12k {
+@@ -654,6 +662,13 @@ struct ath12k {
u32 freq_low;
u32 freq_high;
bool nlo_enabled;
};
-@@ -880,6 +895,8 @@ struct ath12k_base {
+@@ -886,6 +901,8 @@ struct ath12k_base {
/* continuous recovery fail count */
atomic_t fail_cont_count;
unsigned long reset_fail_timeout;
u32 fw_crash_counter;
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
-@@ -2946,6 +2946,11 @@ static void ath12k_bss_assoc(struct ath1
+@@ -2949,6 +2949,11 @@ static void ath12k_bss_assoc(struct ath1
if (ret)
ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n",
arvif->vdev_id, ret);
}
static void ath12k_bss_disassoc(struct ath12k *ar,
-@@ -3521,7 +3526,7 @@ void __ath12k_mac_scan_finish(struct ath
+@@ -3524,7 +3529,7 @@ void __ath12k_mac_scan_finish(struct ath
ar->scan_channel = NULL;
ar->scan.roc_freq = 0;
cancel_delayed_work(&ar->scan.timeout);
break;
}
}
-@@ -3782,7 +3787,12 @@ scan:
+@@ -3790,7 +3795,12 @@ scan:
- ret = ath12k_start_scan(ar, &arg);
+ ret = ath12k_start_scan(ar, arg);
if (ret) {
- ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret);
+ if (ret == -EBUSY)
spin_lock_bh(&ar->data_lock);
ar->scan.state = ATH12K_SCAN_IDLE;
spin_unlock_bh(&ar->data_lock);
-@@ -3801,6 +3811,11 @@ exit:
+@@ -3810,6 +3820,11 @@ exit:
mutex_unlock(&ar->conf_mutex);
return ret;
}
-@@ -5985,7 +6000,7 @@ static int ath12k_mac_start(struct ath12
+@@ -5994,7 +6009,7 @@ static int ath12k_mac_start(struct ath12
/* TODO: Do we need to enable ANI? */
ar->num_started_vdevs = 0;
ar->num_created_vdevs = 0;
-@@ -6165,6 +6180,9 @@ static void ath12k_mac_stop(struct ath12
+@@ -6174,6 +6189,9 @@ static void ath12k_mac_stop(struct ath12
cancel_delayed_work_sync(&ar->scan.timeout);
cancel_work_sync(&ar->regd_update_work);
cancel_work_sync(&ar->ab->rfkill_work);
spin_lock_bh(&ar->data_lock);
list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) {
-@@ -6411,6 +6429,117 @@ static void ath12k_mac_op_update_vif_off
+@@ -6420,6 +6438,117 @@ static void ath12k_mac_op_update_vif_off
ath12k_mac_update_vif_offload(arvif);
}
static int ath12k_mac_vdev_create(struct ath12k *ar, struct ieee80211_vif *vif)
{
struct ath12k_hw *ah = ar->ah;
-@@ -6525,6 +6654,7 @@ static int ath12k_mac_vdev_create(struct
+@@ -6534,6 +6663,7 @@ static int ath12k_mac_vdev_create(struct
arvif->vdev_id, ret);
goto err_peer_del;
}
break;
case WMI_VDEV_TYPE_STA:
param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY;
-@@ -6563,6 +6693,13 @@ static int ath12k_mac_vdev_create(struct
+@@ -6572,6 +6702,13 @@ static int ath12k_mac_vdev_create(struct
arvif->vdev_id, ret);
goto err_peer_del;
}
break;
default:
break;
-@@ -6903,6 +7040,11 @@ static void ath12k_mac_op_remove_interfa
+@@ -6912,6 +7049,11 @@ static void ath12k_mac_op_remove_interfa
ath12k_dbg(ab, ATH12K_DBG_MAC, "mac remove interface (vdev %d)\n",
arvif->vdev_id);
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath12k_peer_delete(ar, arvif->vdev_id, vif->addr);
if (ret)
-@@ -7743,6 +7885,14 @@ ath12k_mac_op_unassign_vif_chanctx(struc
+@@ -7752,6 +7894,14 @@ ath12k_mac_op_unassign_vif_chanctx(struc
ar->num_started_vdevs == 1 && ar->monitor_vdev_created)
ath12k_mac_monitor_stop(ar);
mutex_unlock(&ar->conf_mutex);
}
-@@ -8281,6 +8431,14 @@ ath12k_mac_op_reconfig_complete(struct i
+@@ -8290,6 +8440,14 @@ ath12k_mac_op_reconfig_complete(struct i
ath12k_warn(ar->ab, "pdev %d successfully recovered\n",
ar->pdev->pdev_id);
if (ab->is_reset) {
recovery_count = atomic_inc_return(&ab->recovery_count);
-@@ -9330,6 +9488,9 @@ static void ath12k_mac_setup(struct ath1
+@@ -9339,6 +9497,9 @@ static void ath12k_mac_setup(struct ath1
INIT_WORK(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work);
skb_queue_head_init(&ar->wmi_mgmt_tx_queue);
#endif
--- a/drivers/net/wireless/ath/ath12k/hw.c
+++ b/drivers/net/wireless/ath/ath12k/hw.c
-@@ -926,6 +926,7 @@ static const struct ath12k_hw_params ath
- .supports_dynamic_smps_6ghz = true,
-
+@@ -928,6 +928,7 @@ static const struct ath12k_hw_params ath
.iova_mask = 0,
+
+ .supports_aspm = false,
+ .current_cc_support = false,
},
{
.name = "wcn7850 hw2.0",
-@@ -1004,6 +1005,7 @@ static const struct ath12k_hw_params ath
- .supports_dynamic_smps_6ghz = false,
-
+@@ -1008,6 +1009,7 @@ static const struct ath12k_hw_params ath
.iova_mask = ATH12K_PCIE_MAX_PAYLOAD_SIZE - 1,
+
+ .supports_aspm = true,
+ .current_cc_support = true,
},
{
.name = "qcn9274 hw2.0",
-@@ -1078,6 +1080,7 @@ static const struct ath12k_hw_params ath
- .supports_dynamic_smps_6ghz = true,
-
+@@ -1084,6 +1086,7 @@ static const struct ath12k_hw_params ath
.iova_mask = 0,
+
+ .supports_aspm = false,
+ .current_cc_support = false,
},
};
--- a/drivers/net/wireless/ath/ath12k/hw.h
+++ b/drivers/net/wireless/ath/ath12k/hw.h
-@@ -189,6 +189,7 @@ struct ath12k_hw_params {
- bool tcl_ring_retry:1;
+@@ -190,6 +190,7 @@ struct ath12k_hw_params {
bool reoq_lut_support:1;
bool supports_shadow_regs:1;
+ bool supports_aspm:1;
+ bool current_cc_support:1;
u32 num_tcl_banks;
+++ /dev/null
---- a/drivers/net/wireless/ath/ath5k/pci.c
-+++ b/drivers/net/wireless/ath/ath5k/pci.c
-@@ -47,6 +47,8 @@ static const struct pci_device_id ath5k_
- { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */
- { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */
- { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */
-+ { PCI_VDEVICE(ATHEROS, 0xff16) }, /* 2413,2414 sx76x on lantiq_danube */
-+ { PCI_VDEVICE(ATHEROS, 0xff1a) }, /* 2417 arv45xx on lantiq_danube */
- { PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */
- { 0 }
- };
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -2977,7 +2977,8 @@ void ath9k_hw_apply_txpower(struct ath_h
+@@ -2975,7 +2975,8 @@ void ath9k_hw_apply_txpower(struct ath_h
{
struct ath_regulatory *reg = ath9k_hw_regulatory(ah);
struct ieee80211_channel *channel;
u16 ctl = NO_CTL;
if (!chan)
-@@ -2989,9 +2990,14 @@ void ath9k_hw_apply_txpower(struct ath_h
+@@ -2987,9 +2988,14 @@ void ath9k_hw_apply_txpower(struct ath_h
channel = chan->chan;
chan_pwr = min_t(int, channel->max_power * 2, MAX_COMBINED_POWER);
new_pwr = min_t(int, chan_pwr, reg->power_limit);
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
-@@ -2996,6 +2996,10 @@ void ath9k_hw_apply_txpower(struct ath_h
+@@ -2994,6 +2994,10 @@ void ath9k_hw_apply_txpower(struct ath_h
if (ant_gain > max_gain)
ant_reduction = ant_gain - max_gain;
void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
struct brcmf_mp_device *settings)
{
-@@ -114,6 +144,8 @@ void brcmf_of_probe(struct device *dev,
- of_node_put(root);
+@@ -113,6 +143,8 @@ void brcmf_of_probe(struct device *dev,
}
+ of_node_put(root);
+ brcmf_of_probe_cc(dev, settings);
+
+++ /dev/null
---- a/drivers/staging/rtl8723bs/Kconfig
-+++ b/drivers/staging/rtl8723bs/Kconfig
-@@ -5,7 +5,6 @@ config RTL8723BS
- depends on m
- depends on WLAN && MMC && CFG80211
- depends on m
-- select CFG80211_WEXT
- depends on CRYPTO
- select BPAUTO_CRYPTO_LIB_ARC4
- help
+++ /dev/null
---- a/backport-include/net/dropreason.h
-+++ b/backport-include/net/dropreason.h
-@@ -3,10 +3,9 @@
-
- #include <linux/version.h>
-
-+#include <net/dropreason-core.h>
- #if LINUX_VERSION_IS_GEQ(6,0,0)
- #include_next <net/dropreason.h>
--#else
--#include <net/dropreason-core.h>
- #endif
-
- #if LINUX_VERSION_IS_LESS(6,4,0)
--- a/drivers/bus/mhi/host/trace.h
+++ b/drivers/bus/mhi/host/trace.h
-@@ -103,7 +103,7 @@ TRACE_EVENT(mhi_gen_tre,
+@@ -104,7 +104,7 @@ TRACE_EVENT(mhi_gen_tre,
),
TP_fast_assign(
+ __assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->ch_num = mhi_chan->chan;
__entry->wp = mhi_tre;
- __entry->tre_ptr = mhi_tre->ptr;
-@@ -131,7 +131,7 @@ TRACE_EVENT(mhi_intvec_states,
+ __entry->tre_ptr = le64_to_cpu(mhi_tre->ptr);
+@@ -132,7 +132,7 @@ TRACE_EVENT(mhi_intvec_states,
),
TP_fast_assign(
__entry->local_ee = mhi_cntrl->ee;
__entry->state = mhi_cntrl->dev_state;
__entry->dev_ee = dev_ee;
-@@ -158,7 +158,7 @@ TRACE_EVENT(mhi_tryset_pm_state,
+@@ -159,7 +159,7 @@ TRACE_EVENT(mhi_tryset_pm_state,
),
TP_fast_assign(
if (pm_state)
pm_state = __fls(pm_state);
__entry->pm_state = pm_state;
-@@ -184,7 +184,7 @@ DECLARE_EVENT_CLASS(mhi_process_event_ri
+@@ -185,7 +185,7 @@ DECLARE_EVENT_CLASS(mhi_process_event_ri
),
TP_fast_assign(
- __assign_str(name);
+ __assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->rp = rp;
- __entry->ptr = rp->ptr;
- __entry->dword0 = rp->dword[0];
-@@ -226,7 +226,7 @@ DECLARE_EVENT_CLASS(mhi_update_channel_s
+ __entry->ptr = le64_to_cpu(rp->ptr);
+ __entry->dword0 = le32_to_cpu(rp->dword[0]);
+@@ -227,7 +227,7 @@ DECLARE_EVENT_CLASS(mhi_update_channel_s
),
TP_fast_assign(
__entry->ch_num = mhi_chan->chan;
__entry->state = state;
__entry->reason = reason;
-@@ -265,7 +265,7 @@ TRACE_EVENT(mhi_pm_st_transition,
+@@ -266,7 +266,7 @@ TRACE_EVENT(mhi_pm_st_transition,
),
TP_fast_assign(
--- /dev/null
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -165,11 +165,15 @@ int cfg80211_switch_netns(struct cfg8021
+ list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
+ if (!wdev->netdev)
+ continue;
++#if LINUX_VERSION_IS_GEQ(6,12,0)
+ wdev->netdev->netns_local = false;
++#endif
+ err = dev_change_net_namespace(wdev->netdev, net, "wlan%d");
+ if (err)
+ break;
++#if LINUX_VERSION_IS_GEQ(6,12,0)
+ wdev->netdev->netns_local = true;
++#endif
+ }
+
+ if (err) {
+@@ -181,11 +185,15 @@ int cfg80211_switch_netns(struct cfg8021
+ list) {
+ if (!wdev->netdev)
+ continue;
++#if LINUX_VERSION_IS_GEQ(6,12,0)
+ wdev->netdev->netns_local = false;
++#endif
+ err = dev_change_net_namespace(wdev->netdev, net,
+ "wlan%d");
+ WARN_ON(err);
++#if LINUX_VERSION_IS_GEQ(6,12,0)
+ wdev->netdev->netns_local = true;
++#endif
+ }
+
+ return err;
+@@ -1518,7 +1526,9 @@ static int cfg80211_netdev_notifier_call
+ SET_NETDEV_DEVTYPE(dev, &wiphy_type);
+ wdev->netdev = dev;
+ /* can only change netns with wiphy */
++#if LINUX_VERSION_IS_GEQ(6,12,0)
+ dev->netns_local = true;
++#endif
+
+ cfg80211_init_wdev(wdev);
+ break;
--- /dev/null
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
+@@ -1574,7 +1574,11 @@ static int brcmf_usb_reset_device(struct
+
+ void brcmf_usb_exit(void)
+ {
++#if LINUX_VERSION_IS_GEQ(6,8,0)
+ struct device_driver *drv = &brcmf_usbdrvr.driver;
++#else
++ struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
++#endif
+ int ret;
+
+ brcmf_dbg(USB, "Enter\n");
+++ /dev/null
---- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
-+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c
-@@ -1574,7 +1574,11 @@ static int brcmf_usb_reset_device(struct
-
- void brcmf_usb_exit(void)
- {
-+#if LINUX_VERSION_IS_GEQ(6,8,0)
- struct device_driver *drv = &brcmf_usbdrvr.driver;
-+#else
-+ struct device_driver *drv = &brcmf_usbdrvr.drvwrap.driver;
-+#endif
- int ret;
-
- brcmf_dbg(USB, "Enter\n");
--- /dev/null
+--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
++++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
+@@ -6629,6 +6629,7 @@ static void hwsim_virtio_rx_done(struct
+
+ static int init_vqs(struct virtio_device *vdev)
+ {
++#if LINUX_VERSION_IS_GEQ(6,11,0)
+ struct virtqueue_info vqs_info[HWSIM_NUM_VQS] = {
+ [HWSIM_VQ_TX] = { "tx", hwsim_virtio_tx_done },
+ [HWSIM_VQ_RX] = { "rx", hwsim_virtio_rx_done },
+@@ -6636,6 +6637,19 @@ static int init_vqs(struct virtio_device
+
+ return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
+ hwsim_vqs, vqs_info, NULL);
++#else /* Using the old ABI, copied from kernel 6.6 */
++ vq_callback_t *callbacks[HWSIM_NUM_VQS] = {
++ [HWSIM_VQ_TX] = hwsim_virtio_tx_done,
++ [HWSIM_VQ_RX] = hwsim_virtio_rx_done,
++ };
++ const char *names[HWSIM_NUM_VQS] = {
++ [HWSIM_VQ_TX] = "tx",
++ [HWSIM_VQ_RX] = "rx",
++ };
++
++ return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
++ hwsim_vqs, callbacks, names, NULL);
++#endif
+ }
+
+ static int fill_vq(struct virtqueue *vq)
+++ /dev/null
---- a/drivers/net/wireless/virtual/mac80211_hwsim.c
-+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
-@@ -6629,6 +6629,7 @@ static void hwsim_virtio_rx_done(struct
-
- static int init_vqs(struct virtio_device *vdev)
- {
-+#if LINUX_VERSION_IS_GEQ(6,11,0)
- struct virtqueue_info vqs_info[HWSIM_NUM_VQS] = {
- [HWSIM_VQ_TX] = { "tx", hwsim_virtio_tx_done },
- [HWSIM_VQ_RX] = { "rx", hwsim_virtio_rx_done },
-@@ -6636,6 +6637,19 @@ static int init_vqs(struct virtio_device
-
- return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
- hwsim_vqs, vqs_info, NULL);
-+#else /* Using the old ABI, copied from kernel 6.6 */
-+ vq_callback_t *callbacks[HWSIM_NUM_VQS] = {
-+ [HWSIM_VQ_TX] = hwsim_virtio_tx_done,
-+ [HWSIM_VQ_RX] = hwsim_virtio_rx_done,
-+ };
-+ const char *names[HWSIM_NUM_VQS] = {
-+ [HWSIM_VQ_TX] = "tx",
-+ [HWSIM_VQ_RX] = "rx",
-+ };
-+
-+ return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
-+ hwsim_vqs, callbacks, names, NULL);
-+#endif
- }
-
- static int fill_vq(struct virtqueue *vq)
+++ /dev/null
-From e0f83d268974dab0361d11904dfc9acec53f96a6 Mon Sep 17 00:00:00 2001
-From: Eric Dumazet <edumazet@google.com>
-Date: Fri, 11 Oct 2024 17:12:17 +0000
-Subject: [PATCH] genetlink: hold RCU in genlmsg_mcast()
-
-[ Upstream commit 56440d7ec28d60f8da3bfa09062b3368ff9b16db ]
-
-While running net selftests with CONFIG_PROVE_RCU_LIST=y I saw
-one lockdep splat [1].
-
-genlmsg_mcast() uses for_each_net_rcu(), and must therefore hold RCU.
-
-Instead of letting all callers guard genlmsg_multicast_allns()
-with a rcu_read_lock()/rcu_read_unlock() pair, do it in genlmsg_mcast().
-
-This also means the @flags parameter is useless, we need to always use
-GFP_ATOMIC.
-
-[1]
-[10882.424136] =============================
-[10882.424166] WARNING: suspicious RCU usage
-[10882.424309] 6.12.0-rc2-virtme #1156 Not tainted
-[10882.424400] -----------------------------
-[10882.424423] net/netlink/genetlink.c:1940 RCU-list traversed in non-reader section!!
-[10882.424469]
-other info that might help us debug this:
-
-[10882.424500]
-rcu_scheduler_active = 2, debug_locks = 1
-[10882.424744] 2 locks held by ip/15677:
-[10882.424791] #0: ffffffffb6b491b0 (cb_lock){++++}-{3:3}, at: genl_rcv (net/netlink/genetlink.c:1219)
-[10882.426334] #1: ffffffffb6b49248 (genl_mutex){+.+.}-{3:3}, at: genl_rcv_msg (net/netlink/genetlink.c:61 net/netlink/genetlink.c:57 net/netlink/genetlink.c:1209)
-[10882.426465]
-stack backtrace:
-[10882.426805] CPU: 14 UID: 0 PID: 15677 Comm: ip Not tainted 6.12.0-rc2-virtme #1156
-[10882.426919] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
-[10882.427046] Call Trace:
-[10882.427131] <TASK>
-[10882.427244] dump_stack_lvl (lib/dump_stack.c:123)
-[10882.427335] lockdep_rcu_suspicious (kernel/locking/lockdep.c:6822)
-[10882.427387] genlmsg_multicast_allns (net/netlink/genetlink.c:1940 (discriminator 7) net/netlink/genetlink.c:1977 (discriminator 7))
-[10882.427436] l2tp_tunnel_notify.constprop.0 (net/l2tp/l2tp_netlink.c:119) l2tp_netlink
-[10882.427683] l2tp_nl_cmd_tunnel_create (net/l2tp/l2tp_netlink.c:253) l2tp_netlink
-[10882.427748] genl_family_rcv_msg_doit (net/netlink/genetlink.c:1115)
-[10882.427834] genl_rcv_msg (net/netlink/genetlink.c:1195 net/netlink/genetlink.c:1210)
-[10882.427877] ? __pfx_l2tp_nl_cmd_tunnel_create (net/l2tp/l2tp_netlink.c:186) l2tp_netlink
-[10882.427927] ? __pfx_genl_rcv_msg (net/netlink/genetlink.c:1201)
-[10882.427959] netlink_rcv_skb (net/netlink/af_netlink.c:2551)
-[10882.428069] genl_rcv (net/netlink/genetlink.c:1220)
-[10882.428095] netlink_unicast (net/netlink/af_netlink.c:1332 net/netlink/af_netlink.c:1357)
-[10882.428140] netlink_sendmsg (net/netlink/af_netlink.c:1901)
-[10882.428210] ____sys_sendmsg (net/socket.c:729 (discriminator 1) net/socket.c:744 (discriminator 1) net/socket.c:2607 (discriminator 1))
-
-Fixes: 33f72e6f0c67 ("l2tp : multicast notification to the registered listeners")
-Signed-off-by: Eric Dumazet <edumazet@google.com>
-Cc: James Chapman <jchapman@katalix.com>
-Cc: Tom Parkin <tparkin@katalix.com>
-Cc: Johannes Berg <johannes.berg@intel.com>
-Link: https://patch.msgid.link/20241011171217.3166614-1-edumazet@google.com
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- drivers/target/target_core_user.c | 2 +-
- include/net/genetlink.h | 3 +--
- net/l2tp/l2tp_netlink.c | 4 ++--
- net/netlink/genetlink.c | 28 ++++++++++++++--------------
- net/wireless/nl80211.c | 8 ++------
- 5 files changed, 20 insertions(+), 25 deletions(-)
-
---- a/backport-include/net/genetlink.h
-+++ b/backport-include/net/genetlink.h
-@@ -150,7 +150,7 @@ int genlmsg_multicast(const struct genl_
- #define genlmsg_multicast_allns LINUX_BACKPORT(genlmsg_multicast_allns)
- int backport_genlmsg_multicast_allns(const struct genl_family *family,
- struct sk_buff *skb, u32 portid,
-- unsigned int group, gfp_t flags);
-+ unsigned int group);
-
- #define genl_family_attrbuf LINUX_BACKPORT(genl_family_attrbuf)
- static inline struct nlattr **genl_family_attrbuf(struct genl_family *family)
---- a/compat/backport-genetlink.c
-+++ b/compat/backport-genetlink.c
-@@ -198,23 +198,23 @@ int genlmsg_multicast(const struct genl_
- }
- EXPORT_SYMBOL_GPL(genlmsg_multicast);
-
--static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group,
-- gfp_t flags)
-+static int genlmsg_mcast(struct sk_buff *skb, u32 portid, unsigned long group)
- {
- struct sk_buff *tmp;
- struct net *net, *prev = NULL;
- bool delivered = false;
- int err;
-
-+ rcu_read_lock();
- for_each_net_rcu(net) {
- if (prev) {
-- tmp = skb_clone(skb, flags);
-+ tmp = skb_clone(skb, GFP_ATOMIC);
- if (!tmp) {
- err = -ENOMEM;
- goto error;
- }
- err = nlmsg_multicast(prev->genl_sock, tmp,
-- portid, group, flags);
-+ portid, group, GFP_ATOMIC);
- if (!err)
- delivered = true;
- else if (err != -ESRCH)
-@@ -223,25 +223,29 @@ static int genlmsg_mcast(struct sk_buff
-
- prev = net;
- }
-+ err = nlmsg_multicast(prev->genl_sock, skb, portid, group, GFP_ATOMIC);
-+
-+ rcu_read_unlock();
-
-- err = nlmsg_multicast(prev->genl_sock, skb, portid, group, flags);
- if (!err)
- delivered = true;
- else if (err != -ESRCH)
- return err;
- return delivered ? 0 : -ESRCH;
- error:
-+ rcu_read_unlock();
-+
- kfree_skb(skb);
- return err;
- }
-
- int backport_genlmsg_multicast_allns(const struct genl_family *family,
- struct sk_buff *skb, u32 portid,
-- unsigned int group, gfp_t flags)
-+ unsigned int group)
- {
- group = __backport_genl_group(family, group);
- if (group == INVALID_GROUP)
- return -EINVAL;
-- return genlmsg_mcast(skb, portid, group, flags);
-+ return genlmsg_mcast(skb, portid, group);
- }
- EXPORT_SYMBOL_GPL(backport_genlmsg_multicast_allns);
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -17956,10 +17956,8 @@ void nl80211_common_reg_change_event(enu
-
- genlmsg_end(msg, hdr);
-
-- rcu_read_lock();
- genlmsg_multicast_allns(&nl80211_fam, msg, 0,
-- NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
-- rcu_read_unlock();
-+ NL80211_MCGRP_REGULATORY);
-
- return;
-
-@@ -18692,10 +18690,8 @@ void nl80211_send_beacon_hint_event(stru
-
- genlmsg_end(msg, hdr);
-
-- rcu_read_lock();
- genlmsg_multicast_allns(&nl80211_fam, msg, 0,
-- NL80211_MCGRP_REGULATORY, GFP_ATOMIC);
-- rcu_read_unlock();
-+ NL80211_MCGRP_REGULATORY);
-
- return;
-
--- a/drivers/net/wireless/marvell/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
-@@ -5709,6 +5709,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw")
+@@ -5712,6 +5712,7 @@ MODULE_FIRMWARE("mwl8k/fmimage_8366.fw")
MODULE_FIRMWARE(MWL8K_8366_AP_FW(MWL8K_8366_AP_FW_API));
static const struct pci_device_id mwl8k_pci_id_table[] = {
#define MWIFIEX_DMA_ALIGN_SZ 64
#define MWIFIEX_RX_HEADROOM 64
-@@ -100,7 +100,7 @@
+@@ -123,7 +123,7 @@
#define MWIFIEX_RATE_INDEX_OFDM0 4
#define MWIFIEX_MAX_STA_NUM 3
--- a/drivers/net/wireless/marvell/mwl8k.c
+++ b/drivers/net/wireless/marvell/mwl8k.c
-@@ -6295,6 +6295,8 @@ static int mwl8k_probe(struct pci_dev *p
+@@ -6298,6 +6298,8 @@ static int mwl8k_probe(struct pci_dev *p
priv->running_bsses = 0;
return rc;
err_stop_firmware:
-@@ -6328,8 +6330,6 @@ static void mwl8k_remove(struct pci_dev
+@@ -6331,8 +6333,6 @@ static void mwl8k_remove(struct pci_dev
return;
priv = hw->priv;
mwifiex_recycle_cmd_node(adapter, cmd_node);
queue_work(adapter->workqueue, &adapter->main_work);
return -1;
-@@ -653,8 +732,8 @@ int mwifiex_send_cmd(struct mwifiex_priv
+@@ -655,8 +734,8 @@ int mwifiex_send_cmd(struct mwifiex_priv
/* Return error, since the command preparation failed */
if (ret) {
mwifiex_dbg(adapter, ERROR,
mwifiex_insert_cmd_to_free_q(adapter, cmd_node);
return -1;
}
-@@ -902,8 +981,9 @@ int mwifiex_process_cmdresp(struct mwifi
+@@ -904,8 +983,9 @@ int mwifiex_process_cmdresp(struct mwifi
if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) {
if (ret) {
mwifiex_dbg(adapter, ERROR,
mwifiex_init_fw_complete(adapter);
return -1;
} else if (adapter->last_init_cmd == cmdresp_no)
-@@ -1265,8 +1345,8 @@ mwifiex_process_sleep_confirm_resp(struc
+@@ -1287,8 +1367,8 @@ mwifiex_process_sleep_confirm_resp(struc
if (command != HostCmd_CMD_802_11_PS_MODE_ENH) {
mwifiex_dbg(adapter, ERROR,
--- a/drivers/net/wireless/marvell/mwifiex/main.h
+++ b/drivers/net/wireless/marvell/mwifiex/main.h
-@@ -1084,6 +1084,8 @@ void mwifiex_cancel_all_pending_cmd(stru
+@@ -1100,6 +1100,8 @@ void mwifiex_cancel_all_pending_cmd(stru
void mwifiex_cancel_pending_scan_cmd(struct mwifiex_adapter *adapter);
void mwifiex_cancel_scan(struct mwifiex_adapter *adapter);
adapter->cmd_wait_q.status = -1;
--- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
-@@ -802,7 +802,8 @@ int mwifiex_uap_prepare_cmd(struct mwifi
+@@ -964,7 +964,8 @@ int mwifiex_uap_prepare_cmd(struct mwifi
break;
default:
mwifiex_dbg(priv->adapter, ERROR,
+++ /dev/null
-From 8fbcaa308591b91e9037ab6a8d733873b749a70d Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Sat, 6 Jul 2024 01:40:58 +0300
-Subject: [PATCH] wifi: rtw88: Set efuse->ext_lna_5g - fix typo
-
-efuse->ext_lna_2g is set twice and efuse->ext_lna_5g is not set at all.
-Set each one once.
-
-Nothing uses these members right now. They will be used by the RTL8821AU
-and RTL8812AU drivers.
-
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Acked-by: Ping-Ke Shih <pkshih@realtek.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/8ccc9e13-0d45-417d-8f88-93a0ad294f77@gmail.com
----
- drivers/net/wireless/realtek/rtw88/main.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/net/wireless/realtek/rtw88/main.c
-+++ b/drivers/net/wireless/realtek/rtw88/main.c
-@@ -2006,7 +2006,7 @@ static int rtw_chip_efuse_info_setup(str
- efuse->ext_pa_2g = efuse->pa_type_2g & BIT(4) ? 1 : 0;
- efuse->ext_lna_2g = efuse->lna_type_2g & BIT(3) ? 1 : 0;
- efuse->ext_pa_5g = efuse->pa_type_5g & BIT(0) ? 1 : 0;
-- efuse->ext_lna_2g = efuse->lna_type_5g & BIT(3) ? 1 : 0;
-+ efuse->ext_lna_5g = efuse->lna_type_5g & BIT(3) ? 1 : 0;
-
- if (!is_valid_ether_addr(efuse->addr)) {
- eth_random_addr(efuse->addr);
+++ /dev/null
-From 315c23a64e99552502dd4d18d6ddc073fad9a7c3 Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Thu, 11 Jul 2024 01:11:33 +0300
-Subject: [PATCH] wifi: rtw88: usb: Support USB 3 with RTL8822CU/RTL8822BU
-
-The Realtek wifi 5 devices which support USB 3 are weird: when first
-plugged in, they pretend to be USB 2. The driver needs to send some
-commands to the device, which make it disappear and come back as a
-USB 3 device.
-
-Implement the required commands in rtw88.
-
-When a USB 3 device is plugged into a USB 2 port, rtw88 will try to
-switch it to USB 3 mode only once. The device will disappear and come
-back still in USB 2 mode, of course.
-
-Some people experience heavy interference in the 2.4 GHz band in
-USB 3 mode, so add a module parameter switch_usb_mode with the
-default value 1 to let people disable the switching.
-
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Acked-by: Ping-Ke Shih <pkshih@realtek.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/77906c62-5674-426f-bde1-1b2a12a0339d@gmail.com
----
- drivers/net/wireless/realtek/rtw88/debug.h | 1 +
- drivers/net/wireless/realtek/rtw88/main.h | 2 +
- drivers/net/wireless/realtek/rtw88/reg.h | 11 +++
- drivers/net/wireless/realtek/rtw88/rtw8822b.c | 1 +
- drivers/net/wireless/realtek/rtw88/rtw8822b.h | 4 +-
- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 1 +
- drivers/net/wireless/realtek/rtw88/rtw8822c.h | 24 +++---
- drivers/net/wireless/realtek/rtw88/usb.c | 84 +++++++++++++++++++
- 8 files changed, 116 insertions(+), 12 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/debug.h
-+++ b/drivers/net/wireless/realtek/rtw88/debug.h
-@@ -25,6 +25,7 @@ enum rtw_debug_mask {
- RTW_DBG_HW_SCAN = 0x00010000,
- RTW_DBG_STATE = 0x00020000,
- RTW_DBG_SDIO = 0x00040000,
-+ RTW_DBG_USB = 0x00080000,
-
- RTW_DBG_UNEXP = 0x80000000,
- RTW_DBG_ALL = 0xffffffff
---- a/drivers/net/wireless/realtek/rtw88/main.h
-+++ b/drivers/net/wireless/realtek/rtw88/main.h
-@@ -1785,6 +1785,8 @@ struct rtw_efuse {
- bool share_ant;
- u8 bt_setting;
-
-+ u8 usb_mode_switch;
-+
- struct {
- u8 hci;
- u8 bw;
---- a/drivers/net/wireless/realtek/rtw88/reg.h
-+++ b/drivers/net/wireless/realtek/rtw88/reg.h
-@@ -15,6 +15,7 @@
- #define BIT_WLOCK_1C_B6 BIT(5)
- #define REG_SYS_PW_CTRL 0x0004
- #define BIT_PFM_WOWL BIT(3)
-+#define BIT_APFM_OFFMAC BIT(9)
- #define REG_SYS_CLK_CTRL 0x0008
- #define BIT_CPU_CLK_EN BIT(14)
-
-@@ -133,6 +134,14 @@
- #define REG_PMC_DBG_CTRL1 0xa8
- #define BITS_PMC_BT_IQK_STS GENMASK(22, 21)
-
-+#define REG_PAD_CTRL2 0x00C4
-+#define BIT_RSM_EN_V1 BIT(16)
-+#define BIT_NO_PDN_CHIPOFF_V1 BIT(17)
-+#define BIT_MASK_USB23_SW_MODE_V1 GENMASK(19, 18)
-+#define BIT_USB3_USB2_TRANSITION BIT(20)
-+#define BIT_USB_MODE_U2 1
-+#define BIT_USB_MODE_U3 2
-+
- #define REG_EFUSE_ACCESS 0x00CF
- #define EFUSE_ACCESS_ON 0x69
- #define EFUSE_ACCESS_OFF 0x00
-@@ -568,6 +577,8 @@
- #define BIT_WL_SECURITY_CLK BIT(15)
- #define BIT_DDMA_EN BIT(8)
-
-+#define REG_SW_MDIO 0x10C0
-+
- #define REG_H2C_PKT_READADDR 0x10D0
- #define REG_H2C_PKT_WRITEADDR 0x10D4
- #define REG_FW_DBG6 0x10F8
---- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
-+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
-@@ -46,6 +46,7 @@ static int rtw8822b_read_efuse(struct rt
-
- map = (struct rtw8822b_efuse *)log_map;
-
-+ efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(7));
- efuse->rfe_option = map->rfe_option;
- efuse->rf_board_option = map->rf_board_option;
- efuse->crystal_cap = map->xtal_k;
---- a/drivers/net/wireless/realtek/rtw88/rtw8822b.h
-+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.h
-@@ -72,7 +72,9 @@ struct rtw8822bs_efuse {
-
- struct rtw8822b_efuse {
- __le16 rtl_id;
-- u8 res0[0x0e];
-+ u8 res0[4];
-+ u8 usb_mode;
-+ u8 res1[0x09];
-
- /* power index for four RF paths */
- struct rtw_txpwr_idx txpwr_idx_table[4];
---- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
-+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
-@@ -49,6 +49,7 @@ static int rtw8822c_read_efuse(struct rt
-
- map = (struct rtw8822c_efuse *)log_map;
-
-+ efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(7));
- efuse->rfe_option = map->rfe_option;
- efuse->rf_board_option = map->rf_board_option;
- efuse->crystal_cap = map->xtal_k & XCAP_MASK;
---- a/drivers/net/wireless/realtek/rtw88/rtw8822c.h
-+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.h
-@@ -59,16 +59,18 @@ struct rtw8822ce_efuse {
-
- struct rtw8822c_efuse {
- __le16 rtl_id;
-- u8 res0[0x0e];
-+ u8 res0[4];
-+ u8 usb_mode;
-+ u8 res1[0x09];
-
- /* power index for four RF paths */
- struct rtw_txpwr_idx txpwr_idx_table[4];
-
- u8 channel_plan; /* 0xb8 */
- u8 xtal_k;
-- u8 res1;
-+ u8 res2;
- u8 iqk_lck;
-- u8 res2[5]; /* 0xbc */
-+ u8 res3[5]; /* 0xbc */
- u8 rf_board_option;
- u8 rf_feature_option;
- u8 rf_bt_setting;
-@@ -80,21 +82,21 @@ struct rtw8822c_efuse {
- u8 rf_antenna_option; /* 0xc9 */
- u8 rfe_option;
- u8 country_code[2];
-- u8 res3[3];
-+ u8 res4[3];
- u8 path_a_thermal; /* 0xd0 */
- u8 path_b_thermal;
-- u8 res4[2];
-+ u8 res5[2];
- u8 rx_gain_gap_2g_ofdm;
-- u8 res5;
-- u8 rx_gain_gap_2g_cck;
- u8 res6;
-- u8 rx_gain_gap_5gl;
-+ u8 rx_gain_gap_2g_cck;
- u8 res7;
-- u8 rx_gain_gap_5gm;
-+ u8 rx_gain_gap_5gl;
- u8 res8;
-- u8 rx_gain_gap_5gh;
-+ u8 rx_gain_gap_5gm;
- u8 res9;
-- u8 res10[0x42];
-+ u8 rx_gain_gap_5gh;
-+ u8 res10;
-+ u8 res11[0x42];
- union {
- struct rtw8822ce_efuse e;
- struct rtw8822cu_efuse u;
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -14,6 +14,11 @@
- #include "ps.h"
- #include "usb.h"
-
-+static bool rtw_switch_usb_mode = true;
-+module_param_named(switch_usb_mode, rtw_switch_usb_mode, bool, 0644);
-+MODULE_PARM_DESC(switch_usb_mode,
-+ "Set to N to disable switching to USB 3 mode to avoid potential interference in the 2.4 GHz band (default: Y)");
-+
- #define RTW_USB_MAX_RXQ_LEN 512
-
- struct rtw_usb_txcb {
-@@ -841,6 +846,77 @@ static void rtw_usb_intf_deinit(struct r
- usb_set_intfdata(intf, NULL);
- }
-
-+static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev)
-+{
-+ enum usb_device_speed cur_speed;
-+ u8 id = rtwdev->chip->id;
-+ bool can_switch;
-+ u32 pad_ctrl2;
-+
-+ if (rtw_read8(rtwdev, REG_SYS_CFG2 + 3) == 0x20)
-+ cur_speed = USB_SPEED_SUPER;
-+ else
-+ cur_speed = USB_SPEED_HIGH;
-+
-+ if (cur_speed == USB_SPEED_SUPER)
-+ return 0;
-+
-+ pad_ctrl2 = rtw_read32(rtwdev, REG_PAD_CTRL2);
-+
-+ can_switch = !!(pad_ctrl2 & (BIT_MASK_USB23_SW_MODE_V1 |
-+ BIT_USB3_USB2_TRANSITION));
-+
-+ if (!can_switch) {
-+ rtw_dbg(rtwdev, RTW_DBG_USB,
-+ "Switching to USB 3 mode unsupported by the chip\n");
-+ return 0;
-+ }
-+
-+ /* At this point cur_speed is USB_SPEED_HIGH. If we already tried
-+ * to switch don't try again - it's a USB 2 port.
-+ */
-+ if (u32_get_bits(pad_ctrl2, BIT_MASK_USB23_SW_MODE_V1) == BIT_USB_MODE_U3)
-+ return 0;
-+
-+ /* Enable IO wrapper timeout */
-+ if (id == RTW_CHIP_TYPE_8822B || id == RTW_CHIP_TYPE_8821C)
-+ rtw_write8_clr(rtwdev, REG_SW_MDIO + 3, BIT(0));
-+
-+ u32p_replace_bits(&pad_ctrl2, BIT_USB_MODE_U3, BIT_MASK_USB23_SW_MODE_V1);
-+ pad_ctrl2 |= BIT_RSM_EN_V1;
-+
-+ rtw_write32(rtwdev, REG_PAD_CTRL2, pad_ctrl2);
-+ rtw_write8(rtwdev, REG_PAD_CTRL2 + 1, 4);
-+
-+ rtw_write16_set(rtwdev, REG_SYS_PW_CTRL, BIT_APFM_OFFMAC);
-+ usleep_range(1000, 1001);
-+ rtw_write32_set(rtwdev, REG_PAD_CTRL2, BIT_NO_PDN_CHIPOFF_V1);
-+
-+ return 1;
-+}
-+
-+static int rtw_usb_switch_mode(struct rtw_dev *rtwdev)
-+{
-+ u8 id = rtwdev->chip->id;
-+
-+ if (id != RTW_CHIP_TYPE_8822C && id != RTW_CHIP_TYPE_8822B)
-+ return 0;
-+
-+ if (!rtwdev->efuse.usb_mode_switch) {
-+ rtw_dbg(rtwdev, RTW_DBG_USB,
-+ "Switching to USB 3 mode disabled by chip's efuse\n");
-+ return 0;
-+ }
-+
-+ if (!rtw_switch_usb_mode) {
-+ rtw_dbg(rtwdev, RTW_DBG_USB,
-+ "Switching to USB 3 mode disabled by module parameter\n");
-+ return 0;
-+ }
-+
-+ return rtw_usb_switch_mode_new(rtwdev);
-+}
-+
- int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
- {
- struct rtw_dev *rtwdev;
-@@ -896,6 +972,14 @@ int rtw_usb_probe(struct usb_interface *
- goto err_destroy_rxwq;
- }
-
-+ ret = rtw_usb_switch_mode(rtwdev);
-+ if (ret) {
-+ /* Not a fail, but we do need to skip rtw_register_hw. */
-+ rtw_dbg(rtwdev, RTW_DBG_USB, "switching to USB 3 mode\n");
-+ ret = 0;
-+ goto err_destroy_rxwq;
-+ }
-+
- ret = rtw_register_hw(rtwdev, rtwdev->hw);
- if (ret) {
- rtw_err(rtwdev, "failed to register hw\n");
+++ /dev/null
-From 8db6c1ca64664ef9b071e6eeb646023ac5b240a8 Mon Sep 17 00:00:00 2001
-From: Ping-Ke Shih <pkshih@realtek.com>
-Date: Thu, 18 Jul 2024 14:41:55 +0800
-Subject: [PATCH] wifi: rtw88: debugfs: support multiple adapters debugging
-
-Originally in order to read partial registers from large area, we write
-a range value stored into a static variable and read registers according
-to the static variable.
-
-However, if we install more than one adapters supported by this driver,
-the static variables will be overwritten by latter adapters. To resolve
-the problem, move the static variables to struct rtw_dev for each adapter.
-
-With changes, smatch spends too much time to parse rtw_debugfs_init():
- debug.c:1289 rtw_debugfs_init() parse error: turning off implications
- after 60 seconds
-Move stuffs of adding debugfs entries to three rtw_debugfs_add_xxx()
-functions.
-
-Reported-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Closes: https://lore.kernel.org/linux-wireless/cd6a2acf3c2c36d938b40140b52a779516f446a9.camel@realtek.com/T/#m27662022c70d9f893ba96f6c6a8dd8fce2434dfe
-Tested-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/20240718064155.38955-1-pkshih@realtek.com
----
- drivers/net/wireless/realtek/rtw88/debug.c | 303 ++++++++++++---------
- drivers/net/wireless/realtek/rtw88/debug.h | 2 +
- drivers/net/wireless/realtek/rtw88/main.c | 1 +
- drivers/net/wireless/realtek/rtw88/main.h | 3 +-
- 4 files changed, 180 insertions(+), 129 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/debug.c
-+++ b/drivers/net/wireless/realtek/rtw88/debug.c
-@@ -43,6 +43,62 @@ struct rtw_debugfs_priv {
- };
- };
-
-+struct rtw_debugfs {
-+ struct rtw_debugfs_priv mac_0;
-+ struct rtw_debugfs_priv mac_1;
-+ struct rtw_debugfs_priv mac_2;
-+ struct rtw_debugfs_priv mac_3;
-+ struct rtw_debugfs_priv mac_4;
-+ struct rtw_debugfs_priv mac_5;
-+ struct rtw_debugfs_priv mac_6;
-+ struct rtw_debugfs_priv mac_7;
-+ struct rtw_debugfs_priv mac_10;
-+ struct rtw_debugfs_priv mac_11;
-+ struct rtw_debugfs_priv mac_12;
-+ struct rtw_debugfs_priv mac_13;
-+ struct rtw_debugfs_priv mac_14;
-+ struct rtw_debugfs_priv mac_15;
-+ struct rtw_debugfs_priv mac_16;
-+ struct rtw_debugfs_priv mac_17;
-+ struct rtw_debugfs_priv bb_8;
-+ struct rtw_debugfs_priv bb_9;
-+ struct rtw_debugfs_priv bb_a;
-+ struct rtw_debugfs_priv bb_b;
-+ struct rtw_debugfs_priv bb_c;
-+ struct rtw_debugfs_priv bb_d;
-+ struct rtw_debugfs_priv bb_e;
-+ struct rtw_debugfs_priv bb_f;
-+ struct rtw_debugfs_priv bb_18;
-+ struct rtw_debugfs_priv bb_19;
-+ struct rtw_debugfs_priv bb_1a;
-+ struct rtw_debugfs_priv bb_1b;
-+ struct rtw_debugfs_priv bb_1c;
-+ struct rtw_debugfs_priv bb_1d;
-+ struct rtw_debugfs_priv bb_1e;
-+ struct rtw_debugfs_priv bb_1f;
-+ struct rtw_debugfs_priv bb_2c;
-+ struct rtw_debugfs_priv bb_2d;
-+ struct rtw_debugfs_priv bb_40;
-+ struct rtw_debugfs_priv bb_41;
-+ struct rtw_debugfs_priv rf_dump;
-+ struct rtw_debugfs_priv tx_pwr_tbl;
-+ struct rtw_debugfs_priv write_reg;
-+ struct rtw_debugfs_priv h2c;
-+ struct rtw_debugfs_priv rf_write;
-+ struct rtw_debugfs_priv rf_read;
-+ struct rtw_debugfs_priv read_reg;
-+ struct rtw_debugfs_priv fix_rate;
-+ struct rtw_debugfs_priv dump_cam;
-+ struct rtw_debugfs_priv rsvd_page;
-+ struct rtw_debugfs_priv phy_info;
-+ struct rtw_debugfs_priv coex_enable;
-+ struct rtw_debugfs_priv coex_info;
-+ struct rtw_debugfs_priv edcca_enable;
-+ struct rtw_debugfs_priv fw_crash;
-+ struct rtw_debugfs_priv force_lowest_basic_rate;
-+ struct rtw_debugfs_priv dm_cap;
-+};
-+
- static const char * const rtw_dm_cap_strs[] = {
- [RTW_DM_CAP_NA] = "NA",
- [RTW_DM_CAP_TXGAPK] = "TXGAPK",
-@@ -524,7 +580,7 @@ static int rtw_debug_get_bb_page(struct
- return 0;
- }
-
--static int rtw_debug_get_rf_dump(struct seq_file *m, void *v)
-+static int rtw_debugfs_get_rf_dump(struct seq_file *m, void *v)
- {
- struct rtw_debugfs_priv *debugfs_priv = m->private;
- struct rtw_dev *rtwdev = debugfs_priv->rtwdev;
-@@ -1074,139 +1130,102 @@ static int rtw_debugfs_get_dm_cap(struct
- return 0;
- }
-
--#define rtw_debug_impl_mac(page, addr) \
--static struct rtw_debugfs_priv rtw_debug_priv_mac_ ##page = { \
-+#define rtw_debug_priv_mac(addr) \
-+{ \
- .cb_read = rtw_debug_get_mac_page, \
- .cb_data = addr, \
- }
-
--rtw_debug_impl_mac(0, 0x0000);
--rtw_debug_impl_mac(1, 0x0100);
--rtw_debug_impl_mac(2, 0x0200);
--rtw_debug_impl_mac(3, 0x0300);
--rtw_debug_impl_mac(4, 0x0400);
--rtw_debug_impl_mac(5, 0x0500);
--rtw_debug_impl_mac(6, 0x0600);
--rtw_debug_impl_mac(7, 0x0700);
--rtw_debug_impl_mac(10, 0x1000);
--rtw_debug_impl_mac(11, 0x1100);
--rtw_debug_impl_mac(12, 0x1200);
--rtw_debug_impl_mac(13, 0x1300);
--rtw_debug_impl_mac(14, 0x1400);
--rtw_debug_impl_mac(15, 0x1500);
--rtw_debug_impl_mac(16, 0x1600);
--rtw_debug_impl_mac(17, 0x1700);
--
--#define rtw_debug_impl_bb(page, addr) \
--static struct rtw_debugfs_priv rtw_debug_priv_bb_ ##page = { \
-+#define rtw_debug_priv_bb(addr) \
-+{ \
- .cb_read = rtw_debug_get_bb_page, \
- .cb_data = addr, \
- }
-
--rtw_debug_impl_bb(8, 0x0800);
--rtw_debug_impl_bb(9, 0x0900);
--rtw_debug_impl_bb(a, 0x0a00);
--rtw_debug_impl_bb(b, 0x0b00);
--rtw_debug_impl_bb(c, 0x0c00);
--rtw_debug_impl_bb(d, 0x0d00);
--rtw_debug_impl_bb(e, 0x0e00);
--rtw_debug_impl_bb(f, 0x0f00);
--rtw_debug_impl_bb(18, 0x1800);
--rtw_debug_impl_bb(19, 0x1900);
--rtw_debug_impl_bb(1a, 0x1a00);
--rtw_debug_impl_bb(1b, 0x1b00);
--rtw_debug_impl_bb(1c, 0x1c00);
--rtw_debug_impl_bb(1d, 0x1d00);
--rtw_debug_impl_bb(1e, 0x1e00);
--rtw_debug_impl_bb(1f, 0x1f00);
--rtw_debug_impl_bb(2c, 0x2c00);
--rtw_debug_impl_bb(2d, 0x2d00);
--rtw_debug_impl_bb(40, 0x4000);
--rtw_debug_impl_bb(41, 0x4100);
--
--static struct rtw_debugfs_priv rtw_debug_priv_rf_dump = {
-- .cb_read = rtw_debug_get_rf_dump,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_tx_pwr_tbl = {
-- .cb_read = rtw_debugfs_get_tx_pwr_tbl,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_write_reg = {
-- .cb_write = rtw_debugfs_set_write_reg,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_h2c = {
-- .cb_write = rtw_debugfs_set_h2c,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_rf_write = {
-- .cb_write = rtw_debugfs_set_rf_write,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_rf_read = {
-- .cb_write = rtw_debugfs_set_rf_read,
-- .cb_read = rtw_debugfs_get_rf_read,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_read_reg = {
-- .cb_write = rtw_debugfs_set_read_reg,
-- .cb_read = rtw_debugfs_get_read_reg,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_fix_rate = {
-- .cb_write = rtw_debugfs_set_fix_rate,
-- .cb_read = rtw_debugfs_get_fix_rate,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_dump_cam = {
-- .cb_write = rtw_debugfs_set_single_input,
-- .cb_read = rtw_debugfs_get_dump_cam,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_rsvd_page = {
-- .cb_write = rtw_debugfs_set_rsvd_page,
-- .cb_read = rtw_debugfs_get_rsvd_page,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_phy_info = {
-- .cb_read = rtw_debugfs_get_phy_info,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_coex_enable = {
-- .cb_write = rtw_debugfs_set_coex_enable,
-- .cb_read = rtw_debugfs_get_coex_enable,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_coex_info = {
-- .cb_read = rtw_debugfs_get_coex_info,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_edcca_enable = {
-- .cb_write = rtw_debugfs_set_edcca_enable,
-- .cb_read = rtw_debugfs_get_edcca_enable,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_fw_crash = {
-- .cb_write = rtw_debugfs_set_fw_crash,
-- .cb_read = rtw_debugfs_get_fw_crash,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_force_lowest_basic_rate = {
-- .cb_write = rtw_debugfs_set_force_lowest_basic_rate,
-- .cb_read = rtw_debugfs_get_force_lowest_basic_rate,
--};
--
--static struct rtw_debugfs_priv rtw_debug_priv_dm_cap = {
-- .cb_write = rtw_debugfs_set_dm_cap,
-- .cb_read = rtw_debugfs_get_dm_cap,
-+#define rtw_debug_priv_get(name) \
-+{ \
-+ .cb_read = rtw_debugfs_get_ ##name, \
-+}
-+
-+#define rtw_debug_priv_set(name) \
-+{ \
-+ .cb_write = rtw_debugfs_set_ ##name, \
-+}
-+
-+#define rtw_debug_priv_set_and_get(name) \
-+{ \
-+ .cb_write = rtw_debugfs_set_ ##name, \
-+ .cb_read = rtw_debugfs_get_ ##name, \
-+}
-+
-+#define rtw_debug_priv_set_single_and_get(name) \
-+{ \
-+ .cb_write = rtw_debugfs_set_single_input, \
-+ .cb_read = rtw_debugfs_get_ ##name, \
-+}
-+
-+static const struct rtw_debugfs rtw_debugfs_templ = {
-+ .mac_0 = rtw_debug_priv_mac(0x0000),
-+ .mac_1 = rtw_debug_priv_mac(0x0100),
-+ .mac_2 = rtw_debug_priv_mac(0x0200),
-+ .mac_3 = rtw_debug_priv_mac(0x0300),
-+ .mac_4 = rtw_debug_priv_mac(0x0400),
-+ .mac_5 = rtw_debug_priv_mac(0x0500),
-+ .mac_6 = rtw_debug_priv_mac(0x0600),
-+ .mac_7 = rtw_debug_priv_mac(0x0700),
-+ .mac_10 = rtw_debug_priv_mac(0x1000),
-+ .mac_11 = rtw_debug_priv_mac(0x1100),
-+ .mac_12 = rtw_debug_priv_mac(0x1200),
-+ .mac_13 = rtw_debug_priv_mac(0x1300),
-+ .mac_14 = rtw_debug_priv_mac(0x1400),
-+ .mac_15 = rtw_debug_priv_mac(0x1500),
-+ .mac_16 = rtw_debug_priv_mac(0x1600),
-+ .mac_17 = rtw_debug_priv_mac(0x1700),
-+ .bb_8 = rtw_debug_priv_bb(0x0800),
-+ .bb_9 = rtw_debug_priv_bb(0x0900),
-+ .bb_a = rtw_debug_priv_bb(0x0a00),
-+ .bb_b = rtw_debug_priv_bb(0x0b00),
-+ .bb_c = rtw_debug_priv_bb(0x0c00),
-+ .bb_d = rtw_debug_priv_bb(0x0d00),
-+ .bb_e = rtw_debug_priv_bb(0x0e00),
-+ .bb_f = rtw_debug_priv_bb(0x0f00),
-+ .bb_18 = rtw_debug_priv_bb(0x1800),
-+ .bb_19 = rtw_debug_priv_bb(0x1900),
-+ .bb_1a = rtw_debug_priv_bb(0x1a00),
-+ .bb_1b = rtw_debug_priv_bb(0x1b00),
-+ .bb_1c = rtw_debug_priv_bb(0x1c00),
-+ .bb_1d = rtw_debug_priv_bb(0x1d00),
-+ .bb_1e = rtw_debug_priv_bb(0x1e00),
-+ .bb_1f = rtw_debug_priv_bb(0x1f00),
-+ .bb_2c = rtw_debug_priv_bb(0x2c00),
-+ .bb_2d = rtw_debug_priv_bb(0x2d00),
-+ .bb_40 = rtw_debug_priv_bb(0x4000),
-+ .bb_41 = rtw_debug_priv_bb(0x4100),
-+ .rf_dump = rtw_debug_priv_get(rf_dump),
-+ .tx_pwr_tbl = rtw_debug_priv_get(tx_pwr_tbl),
-+ .write_reg = rtw_debug_priv_set(write_reg),
-+ .h2c = rtw_debug_priv_set(h2c),
-+ .rf_write = rtw_debug_priv_set(rf_write),
-+ .rf_read = rtw_debug_priv_set_and_get(rf_read),
-+ .read_reg = rtw_debug_priv_set_and_get(read_reg),
-+ .fix_rate = rtw_debug_priv_set_and_get(fix_rate),
-+ .dump_cam = rtw_debug_priv_set_single_and_get(dump_cam),
-+ .rsvd_page = rtw_debug_priv_set_and_get(rsvd_page),
-+ .phy_info = rtw_debug_priv_get(phy_info),
-+ .coex_enable = rtw_debug_priv_set_and_get(coex_enable),
-+ .coex_info = rtw_debug_priv_get(coex_info),
-+ .edcca_enable = rtw_debug_priv_set_and_get(edcca_enable),
-+ .fw_crash = rtw_debug_priv_set_and_get(fw_crash),
-+ .force_lowest_basic_rate = rtw_debug_priv_set_and_get(force_lowest_basic_rate),
-+ .dm_cap = rtw_debug_priv_set_and_get(dm_cap),
- };
-
- #define rtw_debugfs_add_core(name, mode, fopname, parent) \
- do { \
-- rtw_debug_priv_ ##name.rtwdev = rtwdev; \
-+ struct rtw_debugfs_priv *priv = &rtwdev->debugfs->name; \
-+ priv->rtwdev = rtwdev; \
- if (IS_ERR(debugfs_create_file(#name, mode, \
-- parent, &rtw_debug_priv_ ##name,\
-+ parent, priv, \
- &file_ops_ ##fopname))) \
- pr_debug("Unable to initialize debugfs:%s\n", \
- #name); \
-@@ -1219,12 +1238,9 @@ static struct rtw_debugfs_priv rtw_debug
- #define rtw_debugfs_add_r(name) \
- rtw_debugfs_add_core(name, S_IFREG | 0444, single_r, debugfs_topdir)
-
--void rtw_debugfs_init(struct rtw_dev *rtwdev)
-+static
-+void rtw_debugfs_add_basic(struct rtw_dev *rtwdev, struct dentry *debugfs_topdir)
- {
-- struct dentry *debugfs_topdir;
--
-- debugfs_topdir = debugfs_create_dir("rtw88",
-- rtwdev->hw->wiphy->debugfsdir);
- rtw_debugfs_add_w(write_reg);
- rtw_debugfs_add_rw(read_reg);
- rtw_debugfs_add_w(rf_write);
-@@ -1236,6 +1252,17 @@ void rtw_debugfs_init(struct rtw_dev *rt
- rtw_debugfs_add_r(coex_info);
- rtw_debugfs_add_rw(coex_enable);
- rtw_debugfs_add_w(h2c);
-+ rtw_debugfs_add_r(rf_dump);
-+ rtw_debugfs_add_r(tx_pwr_tbl);
-+ rtw_debugfs_add_rw(edcca_enable);
-+ rtw_debugfs_add_rw(fw_crash);
-+ rtw_debugfs_add_rw(force_lowest_basic_rate);
-+ rtw_debugfs_add_rw(dm_cap);
-+}
-+
-+static
-+void rtw_debugfs_add_sec0(struct rtw_dev *rtwdev, struct dentry *debugfs_topdir)
-+{
- rtw_debugfs_add_r(mac_0);
- rtw_debugfs_add_r(mac_1);
- rtw_debugfs_add_r(mac_2);
-@@ -1252,6 +1279,11 @@ void rtw_debugfs_init(struct rtw_dev *rt
- rtw_debugfs_add_r(bb_d);
- rtw_debugfs_add_r(bb_e);
- rtw_debugfs_add_r(bb_f);
-+}
-+
-+static
-+void rtw_debugfs_add_sec1(struct rtw_dev *rtwdev, struct dentry *debugfs_topdir)
-+{
- rtw_debugfs_add_r(mac_10);
- rtw_debugfs_add_r(mac_11);
- rtw_debugfs_add_r(mac_12);
-@@ -1274,14 +1306,29 @@ void rtw_debugfs_init(struct rtw_dev *rt
- rtw_debugfs_add_r(bb_40);
- rtw_debugfs_add_r(bb_41);
- }
-- rtw_debugfs_add_r(rf_dump);
-- rtw_debugfs_add_r(tx_pwr_tbl);
-- rtw_debugfs_add_rw(edcca_enable);
-- rtw_debugfs_add_rw(fw_crash);
-- rtw_debugfs_add_rw(force_lowest_basic_rate);
-- rtw_debugfs_add_rw(dm_cap);
- }
-
-+void rtw_debugfs_init(struct rtw_dev *rtwdev)
-+{
-+ struct dentry *debugfs_topdir;
-+
-+ rtwdev->debugfs = kmemdup(&rtw_debugfs_templ, sizeof(rtw_debugfs_templ),
-+ GFP_KERNEL);
-+ if (!rtwdev->debugfs)
-+ return;
-+
-+ debugfs_topdir = debugfs_create_dir("rtw88",
-+ rtwdev->hw->wiphy->debugfsdir);
-+
-+ rtw_debugfs_add_basic(rtwdev, debugfs_topdir);
-+ rtw_debugfs_add_sec0(rtwdev, debugfs_topdir);
-+ rtw_debugfs_add_sec1(rtwdev, debugfs_topdir);
-+}
-+
-+void rtw_debugfs_deinit(struct rtw_dev *rtwdev)
-+{
-+ kfree(rtwdev->debugfs);
-+}
- #endif /* CPTCFG_RTW88_DEBUGFS */
-
- #ifdef CPTCFG_RTW88_DEBUG
---- a/drivers/net/wireless/realtek/rtw88/debug.h
-+++ b/drivers/net/wireless/realtek/rtw88/debug.h
-@@ -34,11 +34,13 @@ enum rtw_debug_mask {
- #ifdef CPTCFG_RTW88_DEBUGFS
-
- void rtw_debugfs_init(struct rtw_dev *rtwdev);
-+void rtw_debugfs_deinit(struct rtw_dev *rtwdev);
- void rtw_debugfs_get_simple_phy_info(struct seq_file *m);
-
- #else
-
- static inline void rtw_debugfs_init(struct rtw_dev *rtwdev) {}
-+static inline void rtw_debugfs_deinit(struct rtw_dev *rtwdev) {}
-
- #endif /* CPTCFG_RTW88_DEBUGFS */
-
---- a/drivers/net/wireless/realtek/rtw88/main.c
-+++ b/drivers/net/wireless/realtek/rtw88/main.c
-@@ -2300,6 +2300,7 @@ void rtw_unregister_hw(struct rtw_dev *r
-
- ieee80211_unregister_hw(hw);
- rtw_unset_supported_band(hw, chip);
-+ rtw_debugfs_deinit(rtwdev);
- }
- EXPORT_SYMBOL(rtw_unregister_hw);
-
---- a/drivers/net/wireless/realtek/rtw88/main.h
-+++ b/drivers/net/wireless/realtek/rtw88/main.h
-@@ -50,6 +50,7 @@ extern const struct ieee80211_ops rtw_op
- #define RTW_MAX_CHANNEL_NUM_5G 49
-
- struct rtw_dev;
-+struct rtw_debugfs;
-
- enum rtw_hci_type {
- RTW_HCI_TYPE_PCIE,
-@@ -2053,7 +2054,7 @@ struct rtw_dev {
- bool beacon_loss;
- struct completion lps_leave_check;
-
-- struct dentry *debugfs;
-+ struct rtw_debugfs *debugfs;
-
- u8 sta_cnt;
- u32 rts_threshold;
+++ /dev/null
-From 7e989b0c1e33210c07340bf5228aa83ea52515b5 Mon Sep 17 00:00:00 2001
-From: Zong-Zhe Yang <kevin_yang@realtek.com>
-Date: Thu, 18 Jul 2024 15:06:15 +0800
-Subject: [PATCH] wifi: rtw88: select WANT_DEV_COREDUMP
-
-We have invoked device coredump when fw crash.
-Should select WANT_DEV_COREDUMP by ourselves.
-
-Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/20240718070616.42217-1-pkshih@realtek.com
----
- drivers/net/wireless/realtek/rtw88/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/wireless/realtek/rtw88/Kconfig
-+++ b/drivers/net/wireless/realtek/rtw88/Kconfig
-@@ -14,6 +14,7 @@ if RTW88
- config RTW88_CORE
- tristate
- depends on m
-+ select BPAUTO_WANT_DEV_COREDUMP
-
- config RTW88_PCI
- tristate
+++ /dev/null
-From 53ed4b25a79aeec5991c2dc579e635b136ef7676 Mon Sep 17 00:00:00 2001
-From: Po-Hao Huang <phhuang@realtek.com>
-Date: Wed, 24 Jul 2024 13:05:01 +0800
-Subject: [PATCH] wifi: rtw88: 8822c: Parse channel from IE to correct invalid
- hardware reports
-
-For CCK packets we could get incorrect reports from hardware.
-And this causes wrong frequencies being reported. Parse the channel
-information from IE if provided by AP to fix this.
-
-Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/20240724050501.7550-1-pkshih@realtek.com
----
- drivers/net/wireless/realtek/rtw88/main.h | 1 +
- drivers/net/wireless/realtek/rtw88/pci.c | 1 +
- drivers/net/wireless/realtek/rtw88/rtw8822c.c | 7 ++--
- drivers/net/wireless/realtek/rtw88/rx.c | 41 +++++++++++++++++++
- drivers/net/wireless/realtek/rtw88/rx.h | 13 ++++++
- drivers/net/wireless/realtek/rtw88/sdio.c | 1 +
- drivers/net/wireless/realtek/rtw88/usb.c | 2 +
- 7 files changed, 63 insertions(+), 3 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/main.h
-+++ b/drivers/net/wireless/realtek/rtw88/main.h
-@@ -623,6 +623,7 @@ struct rtw_rx_pkt_stat {
- bool crc_err;
- bool decrypted;
- bool is_c2h;
-+ bool channel_invalid;
-
- s32 signal_power;
- u16 pkt_len;
---- a/drivers/net/wireless/realtek/rtw88/pci.c
-+++ b/drivers/net/wireless/realtek/rtw88/pci.c
-@@ -1088,6 +1088,7 @@ static u32 rtw_pci_rx_napi(struct rtw_de
- /* remove rx_desc */
- skb_pull(new, pkt_offset);
-
-+ rtw_update_rx_freq_for_invalid(rtwdev, new, &rx_status, &pkt_stat);
- rtw_rx_stats(rtwdev, pkt_stat.vif, new);
- memcpy(new->cb, &rx_status, sizeof(rx_status));
- ieee80211_rx_napi(rtwdev->hw, NULL, new, napi);
---- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
-+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
-@@ -2576,9 +2576,10 @@ static void query_phy_status_page0(struc
- rx_power[RF_PATH_B] -= 110;
-
- channel = GET_PHY_STAT_P0_CHANNEL(phy_status);
-- if (channel == 0)
-- channel = rtwdev->hal.current_channel;
-- rtw_set_rx_freq_band(pkt_stat, channel);
-+ if (channel != 0)
-+ rtw_set_rx_freq_band(pkt_stat, channel);
-+ else
-+ pkt_stat->channel_invalid = true;
-
- pkt_stat->rx_power[RF_PATH_A] = rx_power[RF_PATH_A];
- pkt_stat->rx_power[RF_PATH_B] = rx_power[RF_PATH_B];
---- a/drivers/net/wireless/realtek/rtw88/rx.c
-+++ b/drivers/net/wireless/realtek/rtw88/rx.c
-@@ -146,6 +146,47 @@ static void rtw_set_rx_freq_by_pktstat(s
- rx_status->band = pkt_stat->band;
- }
-
-+void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
-+ struct ieee80211_rx_status *rx_status,
-+ struct rtw_rx_pkt_stat *pkt_stat)
-+{
-+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
-+ int channel = rtwdev->hal.current_channel;
-+ size_t hdr_len, ielen;
-+ int channel_number;
-+ u8 *variable;
-+
-+ if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
-+ goto fill_rx_status;
-+
-+ if (ieee80211_is_beacon(mgmt->frame_control)) {
-+ variable = mgmt->u.beacon.variable;
-+ hdr_len = offsetof(struct ieee80211_mgmt,
-+ u.beacon.variable);
-+ } else if (ieee80211_is_probe_resp(mgmt->frame_control)) {
-+ variable = mgmt->u.probe_resp.variable;
-+ hdr_len = offsetof(struct ieee80211_mgmt,
-+ u.probe_resp.variable);
-+ } else {
-+ goto fill_rx_status;
-+ }
-+
-+ if (skb->len > hdr_len)
-+ ielen = skb->len - hdr_len;
-+ else
-+ goto fill_rx_status;
-+
-+ channel_number = cfg80211_get_ies_channel_number(variable, ielen,
-+ NL80211_BAND_2GHZ);
-+ if (channel_number != -1)
-+ channel = channel_number;
-+
-+fill_rx_status:
-+ rtw_set_rx_freq_band(pkt_stat, channel);
-+ rtw_set_rx_freq_by_pktstat(pkt_stat, rx_status);
-+}
-+EXPORT_SYMBOL(rtw_update_rx_freq_from_ie);
-+
- void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
- struct rtw_rx_pkt_stat *pkt_stat,
- struct ieee80211_hdr *hdr,
---- a/drivers/net/wireless/realtek/rtw88/rx.h
-+++ b/drivers/net/wireless/realtek/rtw88/rx.h
-@@ -50,5 +50,18 @@ void rtw_rx_fill_rx_status(struct rtw_de
- struct ieee80211_hdr *hdr,
- struct ieee80211_rx_status *rx_status,
- u8 *phy_status);
-+void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
-+ struct ieee80211_rx_status *rx_status,
-+ struct rtw_rx_pkt_stat *pkt_stat);
-+
-+static inline
-+void rtw_update_rx_freq_for_invalid(struct rtw_dev *rtwdev, struct sk_buff *skb,
-+ struct ieee80211_rx_status *rx_status,
-+ struct rtw_rx_pkt_stat *pkt_stat)
-+{
-+ if (pkt_stat->channel_invalid)
-+ rtw_update_rx_freq_from_ie(rtwdev, skb, rx_status, pkt_stat);
-+}
-+
-
- #endif
---- a/drivers/net/wireless/realtek/rtw88/sdio.c
-+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
-@@ -948,6 +948,7 @@ static void rtw_sdio_rx_skb(struct rtw_d
- skb_put(skb, pkt_stat->pkt_len);
- skb_reserve(skb, pkt_offset);
-
-+ rtw_update_rx_freq_for_invalid(rtwdev, skb, rx_status, pkt_stat);
- rtw_rx_stats(rtwdev, pkt_stat->vif, skb);
-
- ieee80211_rx_irqsafe(rtwdev->hw, skb);
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -579,6 +579,8 @@ static void rtw_usb_rx_handler(struct wo
-
- skb_put(skb, pkt_stat.pkt_len);
- skb_reserve(skb, pkt_offset);
-+
-+ rtw_update_rx_freq_for_invalid(rtwdev, skb, &rx_status, &pkt_stat);
- memcpy(skb->cb, &rx_status, sizeof(rx_status));
- ieee80211_rx_irqsafe(rtwdev->hw, skb);
- }
+++ /dev/null
-From fbbd8cb347e25b68d25c4f3871821afc495ee7a9 Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Thu, 8 Aug 2024 01:19:36 +0300
-Subject: [PATCH] wifi: rtw88: usb: Init RX burst length according to USB speed
-
-This is needed in order to make USB RX aggregation work with RTL8811CU
-(and presumably RTL8822BU and RTL8822CU also).
-
-I don't know what BIT_DMA_BURST_CNT, BIT_DMA_MODE, and BIT_DROP_DATA_EN
-are doing.
-
-Tested with RTL8822CU, RTL8811CU, and RTL8723DU.
-
-The RX speed is unchanged in my tests.
-
-Tested-by: Sascha Hauer <s.hauer@pengutronix.de>
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/ac569c6f-7129-4341-b523-901fe10cabff@gmail.com
----
- drivers/net/wireless/realtek/rtw88/reg.h | 6 ++++++
- drivers/net/wireless/realtek/rtw88/usb.c | 23 ++++++++++++++++++++++-
- 2 files changed, 28 insertions(+), 1 deletion(-)
-
---- a/drivers/net/wireless/realtek/rtw88/reg.h
-+++ b/drivers/net/wireless/realtek/rtw88/reg.h
-@@ -322,6 +322,12 @@
- #define REG_RXDMA_DPR 0x028C
- #define REG_RXDMA_MODE 0x0290
- #define BIT_DMA_MODE BIT(1)
-+#define BIT_DMA_BURST_CNT GENMASK(3, 2)
-+#define BIT_DMA_BURST_SIZE GENMASK(5, 4)
-+#define BIT_DMA_BURST_SIZE_64 2
-+#define BIT_DMA_BURST_SIZE_512 1
-+#define BIT_DMA_BURST_SIZE_1024 0
-+
- #define REG_RXPKTNUM 0x02B0
-
- #define REG_INT_MIG 0x0304
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -720,9 +720,30 @@ static void rtw_usb_link_ps(struct rtw_d
- /* empty function for rtw_hci_ops */
- }
-
-+static void rtw_usb_init_burst_pkt_len(struct rtw_dev *rtwdev)
-+{
-+ struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
-+ enum usb_device_speed speed = rtwusb->udev->speed;
-+ u8 rxdma, burst_size;
-+
-+ rxdma = BIT_DMA_BURST_CNT | BIT_DMA_MODE;
-+
-+ if (speed == USB_SPEED_SUPER)
-+ burst_size = BIT_DMA_BURST_SIZE_1024;
-+ else if (speed == USB_SPEED_HIGH)
-+ burst_size = BIT_DMA_BURST_SIZE_512;
-+ else
-+ burst_size = BIT_DMA_BURST_SIZE_64;
-+
-+ u8p_replace_bits(&rxdma, burst_size, BIT_DMA_BURST_SIZE);
-+
-+ rtw_write8(rtwdev, REG_RXDMA_MODE, rxdma);
-+ rtw_write16_set(rtwdev, REG_TXDMA_OFFSET_CHK, BIT_DROP_DATA_EN);
-+}
-+
- static void rtw_usb_interface_cfg(struct rtw_dev *rtwdev)
- {
-- /* empty function for rtw_hci_ops */
-+ rtw_usb_init_burst_pkt_len(rtwdev);
- }
-
- static struct rtw_hci_ops rtw_usb_ops = {
+++ /dev/null
-From 38ea04a79ad0f8cc30bb5e9ad98d665e4ae5060c Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Thu, 8 Aug 2024 01:20:36 +0300
-Subject: [PATCH] wifi: rtw88: usb: Update the RX stats after every frame
-
-Update the number of received unicast data frames and bytes every time
-a frame is received. This is what the PCI and SDIO drivers do.
-
-This has an influence on the power saving, bluetooth coexistence, and
-(in a future patch) the use of RX aggregation.
-
-Tested with RTL8822CU, RTL8811CU, and RTL8723DU.
-
-Tested-by: Sascha Hauer <s.hauer@pengutronix.de>
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/75a2ca52-8f01-45c5-926f-d3a68ae3b284@gmail.com
----
- drivers/net/wireless/realtek/rtw88/usb.c | 1 +
- 1 file changed, 1 insertion(+)
-
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -581,6 +581,7 @@ static void rtw_usb_rx_handler(struct wo
- skb_reserve(skb, pkt_offset);
-
- rtw_update_rx_freq_for_invalid(rtwdev, skb, &rx_status, &pkt_stat);
-+ rtw_rx_stats(rtwdev, pkt_stat.vif, skb);
- memcpy(skb->cb, &rx_status, sizeof(rx_status));
- ieee80211_rx_irqsafe(rtwdev->hw, skb);
- }
+++ /dev/null
-From df3d8f463b1dfc7cb8f4fb52b1b81d290b850d03 Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Thu, 8 Aug 2024 01:21:36 +0300
-Subject: [PATCH] wifi: rtw88: usb: Support RX aggregation
-
-The chips can be configured to aggregate several frames into a single
-USB transfer. Modify rtw_usb_rx_handler() to support this case.
-
-RX aggregation improves the RX speed of RTL8811CU on certain ARM
-systems, like the NanoPi NEO Core2. It also improves the RX speed of
-RTL8822CU on some x86_64 systems.
-
-Currently none of the chips are configured to aggregate frames.
-
-Tested with RTL8822CU, RTL8811CU, and RTL8723DU.
-
-Reviewed-by: Sascha Hauer <s.hauer@pengutronix.de>
-Tested-by: Sascha Hauer <s.hauer@pengutronix.de>
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/f845826d-de71-492d-9a22-e48c07989a1f@gmail.com
----
- drivers/net/wireless/realtek/rtw88/usb.c | 61 ++++++++++++++++--------
- 1 file changed, 40 insertions(+), 21 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -546,11 +546,12 @@ static void rtw_usb_rx_handler(struct wo
- struct rtw_usb *rtwusb = container_of(work, struct rtw_usb, rx_work);
- struct rtw_dev *rtwdev = rtwusb->rtwdev;
- const struct rtw_chip_info *chip = rtwdev->chip;
-- struct rtw_rx_pkt_stat pkt_stat;
-+ u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
- struct ieee80211_rx_status rx_status;
-+ u32 pkt_offset, next_pkt, urb_len;
-+ struct rtw_rx_pkt_stat pkt_stat;
-+ struct sk_buff *next_skb;
- struct sk_buff *skb;
-- u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
-- u32 pkt_offset;
- u8 *rx_desc;
- int limit;
-
-@@ -559,31 +560,48 @@ static void rtw_usb_rx_handler(struct wo
- if (!skb)
- break;
-
-- rx_desc = skb->data;
-- chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
-- &rx_status);
-- pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
-- pkt_stat.shift;
--
-- if (pkt_stat.is_c2h) {
-- skb_put(skb, pkt_stat.pkt_len + pkt_offset);
-- rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
-- continue;
-- }
--
- if (skb_queue_len(&rtwusb->rx_queue) >= RTW_USB_MAX_RXQ_LEN) {
- dev_dbg_ratelimited(rtwdev->dev, "failed to get rx_queue, overflow\n");
- dev_kfree_skb_any(skb);
- continue;
- }
-
-- skb_put(skb, pkt_stat.pkt_len);
-- skb_reserve(skb, pkt_offset);
-+ urb_len = skb->len;
-+
-+ do {
-+ rx_desc = skb->data;
-+ chip->ops->query_rx_desc(rtwdev, rx_desc, &pkt_stat,
-+ &rx_status);
-+ pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
-+ pkt_stat.shift;
-+
-+ next_pkt = round_up(pkt_stat.pkt_len + pkt_offset, 8);
-+
-+ if (urb_len >= next_pkt + pkt_desc_sz)
-+ next_skb = skb_clone(skb, GFP_KERNEL);
-+ else
-+ next_skb = NULL;
-+
-+ if (pkt_stat.is_c2h) {
-+ skb_trim(skb, pkt_stat.pkt_len + pkt_offset);
-+ rtw_fw_c2h_cmd_rx_irqsafe(rtwdev, pkt_offset, skb);
-+ } else {
-+ skb_pull(skb, pkt_offset);
-+ skb_trim(skb, pkt_stat.pkt_len);
-+ rtw_update_rx_freq_for_invalid(rtwdev, skb,
-+ &rx_status,
-+ &pkt_stat);
-+ rtw_rx_stats(rtwdev, pkt_stat.vif, skb);
-+ memcpy(skb->cb, &rx_status, sizeof(rx_status));
-+ ieee80211_rx_irqsafe(rtwdev->hw, skb);
-+ }
-+
-+ skb = next_skb;
-+ if (skb)
-+ skb_pull(skb, next_pkt);
-
-- rtw_update_rx_freq_for_invalid(rtwdev, skb, &rx_status, &pkt_stat);
-- rtw_rx_stats(rtwdev, pkt_stat.vif, skb);
-- memcpy(skb->cb, &rx_status, sizeof(rx_status));
-- ieee80211_rx_irqsafe(rtwdev->hw, skb);
-+ urb_len -= next_pkt;
-+ } while (skb);
- }
- }
-
-@@ -627,6 +645,7 @@ static void rtw_usb_read_port_complete(s
- if (skb)
- dev_kfree_skb_any(skb);
- } else {
-+ skb_put(skb, urb->actual_length);
- skb_queue_tail(&rtwusb->rx_queue, skb);
- queue_work(rtwusb->rxwq, &rtwusb->rx_work);
- }
+++ /dev/null
-From 002a5db9a52a0e7af0fa9a450d31049748435748 Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Thu, 8 Aug 2024 01:23:06 +0300
-Subject: [PATCH] wifi: rtw88: Enable USB RX aggregation for 8822c/8822b/8821c
-
-Enable USB RX aggregation when there is at least 1 Mbps RX or TX
-traffic, otherwise disable it.
-
-USB RX aggregation improves the RX speed of RTL8811CU on certain ARM
-systems, like the NanoPi NEO Core2. Before: 28 Mbps, after: 231 Mbps.
-
-It also improves the RX speed of RTL8822CU on some x86_64 systems.
-Before: ~200 Mbps, after: ~300 Mbps.
-
-The official drivers for these chips use the same logic for SDIO, but
-for some reason the SDIO driver in rtw88 always enables RX aggregation,
-so this patch only toggles aggregation for USB devices.
-
-RTL8703B is likely not found in USB devices, and RTL8723DU doesn't like
-aggregation.
-
-Tested-by: Sascha Hauer <s.hauer@pengutronix.de>
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/b4c0d54c-6755-4b0f-9dd7-f9196fd74b68@gmail.com
----
- drivers/net/wireless/realtek/rtw88/hci.h | 7 ++++
- drivers/net/wireless/realtek/rtw88/main.c | 13 +++++---
- drivers/net/wireless/realtek/rtw88/pci.c | 1 +
- drivers/net/wireless/realtek/rtw88/sdio.c | 1 +
- drivers/net/wireless/realtek/rtw88/usb.c | 40 +++++++++++++++++++++++
- 5 files changed, 58 insertions(+), 4 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/hci.h
-+++ b/drivers/net/wireless/realtek/rtw88/hci.h
-@@ -18,6 +18,7 @@ struct rtw_hci_ops {
- void (*deep_ps)(struct rtw_dev *rtwdev, bool enter);
- void (*link_ps)(struct rtw_dev *rtwdev, bool enter);
- void (*interface_cfg)(struct rtw_dev *rtwdev);
-+ void (*dynamic_rx_agg)(struct rtw_dev *rtwdev, bool enable);
-
- int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
- int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size);
-@@ -72,6 +73,12 @@ static inline void rtw_hci_interface_cfg
- rtwdev->hci.ops->interface_cfg(rtwdev);
- }
-
-+static inline void rtw_hci_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
-+{
-+ if (rtwdev->hci.ops->dynamic_rx_agg)
-+ rtwdev->hci.ops->dynamic_rx_agg(rtwdev, enable);
-+}
-+
- static inline int
- rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
- {
---- a/drivers/net/wireless/realtek/rtw88/main.c
-+++ b/drivers/net/wireless/realtek/rtw88/main.c
-@@ -212,6 +212,7 @@ static void rtw_watch_dog_work(struct wo
- struct rtw_traffic_stats *stats = &rtwdev->stats;
- struct rtw_watch_dog_iter_data data = {};
- bool busy_traffic = test_bit(RTW_FLAG_BUSY_TRAFFIC, rtwdev->flags);
-+ u32 tx_unicast_mbps, rx_unicast_mbps;
- bool ps_active;
-
- mutex_lock(&rtwdev->mutex);
-@@ -236,10 +237,11 @@ static void rtw_watch_dog_work(struct wo
- else
- ps_active = false;
-
-- ewma_tp_add(&stats->tx_ewma_tp,
-- (u32)(stats->tx_unicast >> RTW_TP_SHIFT));
-- ewma_tp_add(&stats->rx_ewma_tp,
-- (u32)(stats->rx_unicast >> RTW_TP_SHIFT));
-+ tx_unicast_mbps = stats->tx_unicast >> RTW_TP_SHIFT;
-+ rx_unicast_mbps = stats->rx_unicast >> RTW_TP_SHIFT;
-+
-+ ewma_tp_add(&stats->tx_ewma_tp, tx_unicast_mbps);
-+ ewma_tp_add(&stats->rx_ewma_tp, rx_unicast_mbps);
- stats->tx_throughput = ewma_tp_read(&stats->tx_ewma_tp);
- stats->rx_throughput = ewma_tp_read(&stats->rx_ewma_tp);
-
-@@ -259,6 +261,9 @@ static void rtw_watch_dog_work(struct wo
-
- rtw_phy_dynamic_mechanism(rtwdev);
-
-+ rtw_hci_dynamic_rx_agg(rtwdev,
-+ tx_unicast_mbps >= 1 || rx_unicast_mbps >= 1);
-+
- data.rtwdev = rtwdev;
- /* rtw_iterate_vifs internally uses an atomic iterator which is needed
- * to avoid taking local->iflist_mtx mutex
---- a/drivers/net/wireless/realtek/rtw88/pci.c
-+++ b/drivers/net/wireless/realtek/rtw88/pci.c
-@@ -1601,6 +1601,7 @@ static struct rtw_hci_ops rtw_pci_ops =
- .deep_ps = rtw_pci_deep_ps,
- .link_ps = rtw_pci_link_ps,
- .interface_cfg = rtw_pci_interface_cfg,
-+ .dynamic_rx_agg = NULL,
-
- .read8 = rtw_pci_read8,
- .read16 = rtw_pci_read16,
---- a/drivers/net/wireless/realtek/rtw88/sdio.c
-+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
-@@ -1157,6 +1157,7 @@ static struct rtw_hci_ops rtw_sdio_ops =
- .deep_ps = rtw_sdio_deep_ps,
- .link_ps = rtw_sdio_link_ps,
- .interface_cfg = rtw_sdio_interface_cfg,
-+ .dynamic_rx_agg = NULL,
-
- .read8 = rtw_sdio_read8,
- .read16 = rtw_sdio_read16,
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -766,6 +766,45 @@ static void rtw_usb_interface_cfg(struct
- rtw_usb_init_burst_pkt_len(rtwdev);
- }
-
-+static void rtw_usb_dynamic_rx_agg_v1(struct rtw_dev *rtwdev, bool enable)
-+{
-+ u8 size, timeout;
-+ u16 val16;
-+
-+ rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
-+ rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
-+ rtw_write8_clr(rtwdev, REG_RXDMA_AGG_PG_TH + 3, BIT(7));
-+
-+ if (enable) {
-+ size = 0x5;
-+ timeout = 0x20;
-+ } else {
-+ size = 0x0;
-+ timeout = 0x1;
-+ }
-+ val16 = u16_encode_bits(size, BIT_RXDMA_AGG_PG_TH) |
-+ u16_encode_bits(timeout, BIT_DMA_AGG_TO_V1);
-+
-+ rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, val16);
-+}
-+
-+static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
-+{
-+ switch (rtwdev->chip->id) {
-+ case RTW_CHIP_TYPE_8822C:
-+ case RTW_CHIP_TYPE_8822B:
-+ case RTW_CHIP_TYPE_8821C:
-+ rtw_usb_dynamic_rx_agg_v1(rtwdev, enable);
-+ break;
-+ case RTW_CHIP_TYPE_8723D:
-+ /* Doesn't like aggregation. */
-+ break;
-+ case RTW_CHIP_TYPE_8703B:
-+ /* Likely not found in USB devices. */
-+ break;
-+ }
-+}
-+
- static struct rtw_hci_ops rtw_usb_ops = {
- .tx_write = rtw_usb_tx_write,
- .tx_kick_off = rtw_usb_tx_kick_off,
-@@ -775,6 +814,7 @@ static struct rtw_hci_ops rtw_usb_ops =
- .deep_ps = rtw_usb_deep_ps,
- .link_ps = rtw_usb_link_ps,
- .interface_cfg = rtw_usb_interface_cfg,
-+ .dynamic_rx_agg = rtw_usb_dynamic_rx_agg,
-
- .write8 = rtw_usb_write8,
- .write16 = rtw_usb_write16,
+++ /dev/null
-From 902cb7b11f9a7ff07233cc4c626b54c3e4703149 Mon Sep 17 00:00:00 2001
-From: Ping-Ke Shih <pkshih@realtek.com>
-Date: Mon, 19 Aug 2024 10:52:48 +0800
-Subject: [PATCH] wifi: rtw88: assign mac_id for vif/sta and update to TX desc
-
-A mac_id as an instance in firmware has to be assigned for each station
-including AP and connected stations. Firmware will use the mac_id to
-control TX rate and do statistics.
-
-Assignment rule is to assign mac_id to each vif when adding vif.
-For station mode, sta->mac_id will reuse vif->mac_id. For AP mode,
-dynamically allocate an sta->mac_id to a station, and vif->mac_id is
-used to send broadcast/multicast packets which are not belong to
-a station. For example,
-
- vif->mac_id sta->mac_id
-vif0 (STA mode) 0 0
-vif1 (AP mode) 1 2...
-
-By the way, remove unused RTW_BC_MC_MACID, which was planed to send
-broadcast/multicast packets on fixed mac_id.
-
-Tested-on RTL8822CE with STA + AP SCC mode.
-
-Link: https://lore.kernel.org/linux-wireless/e4be0a75-43b2-4ae5-9aab-5c4a88e78097@gmail.com/
-Cc: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
-Link: https://patch.msgid.link/20240819025248.17939-1-pkshih@realtek.com
----
- drivers/net/wireless/realtek/rtw88/mac80211.c | 13 ++++++--
- drivers/net/wireless/realtek/rtw88/main.c | 30 ++++++++-----------
- drivers/net/wireless/realtek/rtw88/main.h | 14 +++++++--
- drivers/net/wireless/realtek/rtw88/tx.c | 11 +++++--
- drivers/net/wireless/realtek/rtw88/tx.h | 1 +
- 5 files changed, 44 insertions(+), 25 deletions(-)
-
---- a/drivers/net/wireless/realtek/rtw88/mac80211.c
-+++ b/drivers/net/wireless/realtek/rtw88/mac80211.c
-@@ -167,6 +167,12 @@ static int rtw_ops_add_interface(struct
-
- mutex_lock(&rtwdev->mutex);
-
-+ rtwvif->mac_id = rtw_acquire_macid(rtwdev);
-+ if (rtwvif->mac_id >= RTW_MAX_MAC_ID_NUM) {
-+ mutex_unlock(&rtwdev->mutex);
-+ return -ENOSPC;
-+ }
-+
- port = find_first_zero_bit(rtwdev->hw_port, RTW_PORT_NUM);
- if (port >= RTW_PORT_NUM) {
- mutex_unlock(&rtwdev->mutex);
-@@ -214,7 +220,8 @@ static int rtw_ops_add_interface(struct
-
- mutex_unlock(&rtwdev->mutex);
-
-- rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %pM on port %d\n", vif->addr, rtwvif->port);
-+ rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %pM mac_id %d on port %d\n",
-+ vif->addr, rtwvif->mac_id, rtwvif->port);
- return 0;
- }
-
-@@ -225,7 +232,8 @@ static void rtw_ops_remove_interface(str
- struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
- u32 config = 0;
-
-- rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM on port %d\n", vif->addr, rtwvif->port);
-+ rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM mac_id %d on port %d\n",
-+ vif->addr, rtwvif->mac_id, rtwvif->port);
-
- mutex_lock(&rtwdev->mutex);
-
-@@ -242,6 +250,7 @@ static void rtw_ops_remove_interface(str
- config |= PORT_SET_BCN_CTRL;
- rtw_vif_port_config(rtwdev, rtwvif, config);
- clear_bit(rtwvif->port, rtwdev->hw_port);
-+ rtw_release_macid(rtwdev, rtwvif->mac_id);
- rtw_recalc_lps(rtwdev, NULL);
-
- mutex_unlock(&rtwdev->mutex);
---- a/drivers/net/wireless/realtek/rtw88/main.c
-+++ b/drivers/net/wireless/realtek/rtw88/main.c
-@@ -311,17 +311,6 @@ static void rtw_ips_work(struct work_str
- mutex_unlock(&rtwdev->mutex);
- }
-
--static u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
--{
-- unsigned long mac_id;
--
-- mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
-- if (mac_id < RTW_MAX_MAC_ID_NUM)
-- set_bit(mac_id, rtwdev->mac_id_map);
--
-- return mac_id;
--}
--
- static void rtw_sta_rc_work(struct work_struct *work)
- {
- struct rtw_sta_info *si = container_of(work, struct rtw_sta_info,
-@@ -340,12 +329,14 @@ int rtw_sta_add(struct rtw_dev *rtwdev,
- struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
- int i;
-
-- si->mac_id = rtw_acquire_macid(rtwdev);
-- if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
-- return -ENOSPC;
-+ if (vif->type == NL80211_IFTYPE_STATION) {
-+ si->mac_id = rtwvif->mac_id;
-+ } else {
-+ si->mac_id = rtw_acquire_macid(rtwdev);
-+ if (si->mac_id >= RTW_MAX_MAC_ID_NUM)
-+ return -ENOSPC;
-+ }
-
-- if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc == 0)
-- rtwvif->mac_id = si->mac_id;
- si->rtwdev = rtwdev;
- si->sta = sta;
- si->vif = vif;
-@@ -370,11 +361,13 @@ void rtw_sta_remove(struct rtw_dev *rtwd
- bool fw_exist)
- {
- struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
-+ struct ieee80211_vif *vif = si->vif;
- int i;
-
- cancel_work_sync(&si->rc_work);
-
-- rtw_release_macid(rtwdev, si->mac_id);
-+ if (vif->type != NL80211_IFTYPE_STATION)
-+ rtw_release_macid(rtwdev, si->mac_id);
- if (fw_exist)
- rtw_fw_media_status_report(rtwdev, si->mac_id, false);
-
-@@ -614,6 +607,8 @@ static void rtw_reset_vif_iter(void *dat
- rtw_bf_disassoc(rtwdev, vif, NULL);
- rtw_vif_assoc_changed(rtwvif, NULL);
- rtw_txq_cleanup(rtwdev, vif->txq);
-+
-+ rtw_release_macid(rtwdev, rtwvif->mac_id);
- }
-
- void rtw_fw_recovery(struct rtw_dev *rtwdev)
-@@ -2139,7 +2134,6 @@ int rtw_core_init(struct rtw_dev *rtwdev
- rtwdev->sec.total_cam_num = 32;
- rtwdev->hal.current_channel = 1;
- rtwdev->dm_info.fix_rate = U8_MAX;
-- set_bit(RTW_BC_MC_MACID, rtwdev->mac_id_map);
-
- rtw_stats_init(rtwdev);
-
---- a/drivers/net/wireless/realtek/rtw88/main.h
-+++ b/drivers/net/wireless/realtek/rtw88/main.h
-@@ -742,7 +742,6 @@ struct rtw_txq {
- unsigned long flags;
- };
-
--#define RTW_BC_MC_MACID 1
- DECLARE_EWMA(rssi, 10, 16);
-
- struct rtw_sta_info {
-@@ -805,7 +804,7 @@ struct rtw_bf_info {
- struct rtw_vif {
- enum rtw_net_type net_type;
- u16 aid;
-- u8 mac_id; /* for STA mode only */
-+ u8 mac_id;
- u8 mac_addr[ETH_ALEN];
- u8 bssid[ETH_ALEN];
- u8 port;
-@@ -2131,6 +2130,17 @@ static inline bool rtw_chip_has_tx_stbc(
- return rtwdev->chip->tx_stbc;
- }
-
-+static inline u8 rtw_acquire_macid(struct rtw_dev *rtwdev)
-+{
-+ unsigned long mac_id;
-+
-+ mac_id = find_first_zero_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
-+ if (mac_id < RTW_MAX_MAC_ID_NUM)
-+ set_bit(mac_id, rtwdev->mac_id_map);
-+
-+ return mac_id;
-+}
-+
- static inline void rtw_release_macid(struct rtw_dev *rtwdev, u8 mac_id)
- {
- clear_bit(mac_id, rtwdev->mac_id_map);
---- a/drivers/net/wireless/realtek/rtw88/tx.c
-+++ b/drivers/net/wireless/realtek/rtw88/tx.c
-@@ -46,7 +46,8 @@ void rtw_tx_fill_tx_desc(struct rtw_tx_p
- le32_encode_bits(pkt_info->ls, RTW_TX_DESC_W0_LS) |
- le32_encode_bits(pkt_info->dis_qselseq, RTW_TX_DESC_W0_DISQSELSEQ);
-
-- tx_desc->w1 = le32_encode_bits(pkt_info->qsel, RTW_TX_DESC_W1_QSEL) |
-+ tx_desc->w1 = le32_encode_bits(pkt_info->mac_id, RTW_TX_DESC_W1_MACID) |
-+ le32_encode_bits(pkt_info->qsel, RTW_TX_DESC_W1_QSEL) |
- le32_encode_bits(pkt_info->rate_id, RTW_TX_DESC_W1_RATE_ID) |
- le32_encode_bits(pkt_info->sec_type, RTW_TX_DESC_W1_SEC_TYPE) |
- le32_encode_bits(pkt_info->pkt_offset, RTW_TX_DESC_W1_PKT_OFFSET) |
-@@ -401,14 +402,18 @@ void rtw_tx_pkt_info_update(struct rtw_d
- const struct rtw_chip_info *chip = rtwdev->chip;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
-+ struct ieee80211_vif *vif = info->control.vif;
- struct rtw_sta_info *si;
-- struct ieee80211_vif *vif = NULL;
-+ struct rtw_vif *rtwvif;
- __le16 fc = hdr->frame_control;
- bool bmc;
-
- if (sta) {
- si = (struct rtw_sta_info *)sta->drv_priv;
-- vif = si->vif;
-+ pkt_info->mac_id = si->mac_id;
-+ } else if (vif) {
-+ rtwvif = (struct rtw_vif *)vif->drv_priv;
-+ pkt_info->mac_id = rtwvif->mac_id;
- }
-
- if (ieee80211_is_mgmt(fc) || ieee80211_is_nullfunc(fc))
---- a/drivers/net/wireless/realtek/rtw88/tx.h
-+++ b/drivers/net/wireless/realtek/rtw88/tx.h
-@@ -27,6 +27,7 @@ struct rtw_tx_desc {
- #define RTW_TX_DESC_W0_BMC BIT(24)
- #define RTW_TX_DESC_W0_LS BIT(26)
- #define RTW_TX_DESC_W0_DISQSELSEQ BIT(31)
-+#define RTW_TX_DESC_W1_MACID GENMASK(7, 0)
- #define RTW_TX_DESC_W1_QSEL GENMASK(12, 8)
- #define RTW_TX_DESC_W1_RATE_ID GENMASK(20, 16)
- #define RTW_TX_DESC_W1_SEC_TYPE GENMASK(23, 22)
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -570,8 +570,8 @@ static void rtw_usb_rx_handler(struct wo
+@@ -571,8 +571,8 @@ static void rtw_usb_rx_handler(struct wo
do {
rx_desc = skb->data;
+++ /dev/null
-From 4aefde403da7af30757915e0462d88398c9388c5 Mon Sep 17 00:00:00 2001
-From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Date: Tue, 8 Oct 2024 21:44:02 +0300
-Subject: [PATCH] wifi: rtw88: Fix the RX aggregation in USB 3 mode
-
-RTL8822CU, RTL8822BU, and RTL8821CU don't need BIT_EN_PRE_CALC.
-In fact, RTL8822BU in USB 3 mode doesn't pass all the frames to the
-driver, resulting in much lower download speed than normal:
-
-$ iperf3 -c 192.168.0.1 -R
-Connecting to host 192.168.0.1, port 5201
-Reverse mode, remote host 192.168.0.1 is sending
-[ 5] local 192.168.0.50 port 43062 connected to 192.168.0.1 port 5201
-[ ID] Interval Transfer Bitrate
-[ 5] 0.00-1.00 sec 26.9 MBytes 225 Mbits/sec
-[ 5] 1.00-2.00 sec 7.50 MBytes 62.9 Mbits/sec
-[ 5] 2.00-3.00 sec 8.50 MBytes 71.3 Mbits/sec
-[ 5] 3.00-4.00 sec 8.38 MBytes 70.3 Mbits/sec
-[ 5] 4.00-5.00 sec 7.75 MBytes 65.0 Mbits/sec
-[ 5] 5.00-6.00 sec 8.00 MBytes 67.1 Mbits/sec
-[ 5] 6.00-7.00 sec 8.00 MBytes 67.1 Mbits/sec
-[ 5] 7.00-8.00 sec 7.75 MBytes 65.0 Mbits/sec
-[ 5] 8.00-9.00 sec 7.88 MBytes 66.1 Mbits/sec
-[ 5] 9.00-10.00 sec 7.88 MBytes 66.1 Mbits/sec
-- - - - - - - - - - - - - - - - - - - - - - - - -
-[ ID] Interval Transfer Bitrate Retr
-[ 5] 0.00-10.02 sec 102 MBytes 85.1 Mbits/sec 224 sender
-[ 5] 0.00-10.00 sec 98.6 MBytes 82.7 Mbits/sec receiver
-
-Don't set BIT_EN_PRE_CALC. Then the speed is much better:
-
-% iperf3 -c 192.168.0.1 -R
-Connecting to host 192.168.0.1, port 5201
-Reverse mode, remote host 192.168.0.1 is sending
-[ 5] local 192.168.0.50 port 39000 connected to 192.168.0.1 port 5201
-[ ID] Interval Transfer Bitrate
-[ 5] 0.00-1.00 sec 52.8 MBytes 442 Mbits/sec
-[ 5] 1.00-2.00 sec 71.9 MBytes 603 Mbits/sec
-[ 5] 2.00-3.00 sec 74.8 MBytes 627 Mbits/sec
-[ 5] 3.00-4.00 sec 75.9 MBytes 636 Mbits/sec
-[ 5] 4.00-5.00 sec 76.0 MBytes 638 Mbits/sec
-[ 5] 5.00-6.00 sec 74.1 MBytes 622 Mbits/sec
-[ 5] 6.00-7.00 sec 74.0 MBytes 621 Mbits/sec
-[ 5] 7.00-8.00 sec 76.0 MBytes 638 Mbits/sec
-[ 5] 8.00-9.00 sec 74.4 MBytes 624 Mbits/sec
-[ 5] 9.00-10.00 sec 63.9 MBytes 536 Mbits/sec
-- - - - - - - - - - - - - - - - - - - - - - - - -
-[ ID] Interval Transfer Bitrate Retr
-[ 5] 0.00-10.00 sec 717 MBytes 601 Mbits/sec 24 sender
-[ 5] 0.00-10.00 sec 714 MBytes 599 Mbits/sec receiver
-
-Fixes: 002a5db9a52a ("wifi: rtw88: Enable USB RX aggregation for 8822c/8822b/8821c")
-Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
-Acked-by: Ping-Ke Shih <pkshih@realtek.com>
-Signed-off-by: Kalle Valo <kvalo@kernel.org>
-Link: https://patch.msgid.link/afb94a82-3d18-459e-97fc-1a217608cdf0@gmail.com
----
- drivers/net/wireless/realtek/rtw88/usb.c | 1 -
- 1 file changed, 1 deletion(-)
-
---- a/drivers/net/wireless/realtek/rtw88/usb.c
-+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -771,7 +771,6 @@ static void rtw_usb_dynamic_rx_agg_v1(st
- u8 size, timeout;
- u16 val16;
-
-- rtw_write32_set(rtwdev, REG_RXDMA_AGG_PG_TH, BIT_EN_PRE_CALC);
- rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_AGG_EN);
- rtw_write8_clr(rtwdev, REG_RXDMA_AGG_PG_TH + 3, BIT(7));
-
void rtw_tx_rsvd_page_pkt_info_update(struct rtw_dev *rtwdev,
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -457,7 +457,7 @@ static int rtw_usb_write_data(struct rtw
+@@ -458,7 +458,7 @@ static int rtw_usb_write_data(struct rtw
skb_put_data(skb, buf, size);
skb_push(skb, chip->tx_pkt_desc_sz);
memset(skb->data, 0, chip->tx_pkt_desc_sz);
rtw_tx_fill_txdesc_checksum(rtwdev, pkt_info, skb->data);
ret = rtw_usb_write_port(rtwdev, qsel, skb,
-@@ -524,7 +524,7 @@ static int rtw_usb_tx_write(struct rtw_d
+@@ -525,7 +525,7 @@ static int rtw_usb_tx_write(struct rtw_d
pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
ep = qsel_to_ep(rtwusb, pkt_info->qsel);
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -477,6 +477,7 @@ static int rtw_usb_write_data_rsvd_page(
+@@ -478,6 +478,7 @@ static int rtw_usb_write_data_rsvd_page(
pkt_info.tx_pkt_size = size;
pkt_info.qsel = TX_DESC_QSEL_BEACON;
pkt_info.offset = chip->tx_pkt_desc_sz;
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -929,6 +929,32 @@ static void rtw_usb_intf_deinit(struct r
+@@ -930,6 +930,32 @@ static void rtw_usb_intf_deinit(struct r
usb_set_intfdata(intf, NULL);
}
static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev)
{
enum usb_device_speed cur_speed;
-@@ -982,7 +1008,8 @@ static int rtw_usb_switch_mode(struct rt
+@@ -983,7 +1009,8 @@ static int rtw_usb_switch_mode(struct rt
{
u8 id = rtwdev->chip->id;
return 0;
if (!rtwdev->efuse.usb_mode_switch) {
-@@ -997,7 +1024,10 @@ static int rtw_usb_switch_mode(struct rt
+@@ -998,7 +1025,10 @@ static int rtw_usb_switch_mode(struct rt
return 0;
}
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
-@@ -788,6 +788,32 @@ static void rtw_usb_dynamic_rx_agg_v1(st
+@@ -789,6 +789,32 @@ static void rtw_usb_dynamic_rx_agg_v1(st
rtw_write16(rtwdev, REG_RXDMA_AGG_PG_TH, val16);
}
static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable)
{
switch (rtwdev->chip->id) {
-@@ -796,6 +822,10 @@ static void rtw_usb_dynamic_rx_agg(struc
+@@ -797,6 +823,10 @@ static void rtw_usb_dynamic_rx_agg(struc
case RTW_CHIP_TYPE_8821C:
rtw_usb_dynamic_rx_agg_v1(rtwdev, enable);
break;
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
-@@ -1581,24 +1581,6 @@ int ieee80211_register_hw(struct ieee802
+@@ -1583,24 +1583,6 @@ int ieee80211_register_hw(struct ieee802
ieee80211_check_wbrf_support(local);
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
-@@ -654,21 +654,6 @@ static int wiphy_verify_combinations(str
+@@ -678,21 +678,6 @@ int wiphy_verify_iface_combinations(stru
c->limits[j].max > 1))
return -EINVAL;
{
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
-@@ -467,6 +467,8 @@ void cfg80211_set_dfs_state(struct wiphy
+@@ -475,6 +475,8 @@ void cfg80211_set_dfs_state(struct wiphy
enum nl80211_dfs_state dfs_state);
void cfg80211_dfs_channels_update_work(struct work_struct *work);
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
-@@ -1037,6 +1037,8 @@ void cfg80211_dfs_channels_update_work(s
+@@ -1031,6 +1031,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;
} 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
+@@ -1038,11 +1040,10 @@ void cfg80211_dfs_channels_update_work(s
time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS;
radar_event = NL80211_RADAR_PRE_CAC_EXPIRED;
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_
+@@ -954,6 +954,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
spin_lock_init(&local->rx_path_lock);
spin_lock_init(&local->queue_stop_reason_lock);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 2 Oct 2024 11:45:35 +0200
-Subject: [PATCH] wifi: mac80211: do not pass a stopped vif to the driver in
- .get_txpower
-
-Avoid potentially crashing in the driver because of uninitialized private data
-
-Fixes: 5b3dc42b1b0d ("mac80211: add support for driver tx power reporting")
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3134,7 +3134,8 @@ static int ieee80211_get_tx_power(struct
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
-
-- if (local->ops->get_txpower)
-+ if (local->ops->get_txpower &&
-+ (sdata->flags & IEEE80211_SDATA_IN_DRIVER))
- return drv_get_txpower(local, sdata, dbm);
-
- if (local->emulate_chanctx)
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 17 Jul 2024 15:43:52 +0200
+Subject: [PATCH] wifi: cfg80211: add option for vif allowed radios
+
+This allows users to prevent a vif from affecting radios other than the
+configured ones. This can be useful in cases where e.g. an AP is running
+on one radio, and triggering a scan on another radio should not disturb it.
+
+Changing the allowed radios list for a vif is supported, but only while
+it is down.
+
+While it is possible to achieve the same by always explicitly specifying
+a frequency list for scan requests and ensuring that the wrong channel/band
+is never accidentally set on an unrelated interface, this change makes
+multi-radio wiphy setups a lot easier to deal with for CLI users.
+
+By itself, this patch only enforces the radio mask for scanning requests
+and remain-on-channel. Follow-up changes build on this to limit configured
+frequencies.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -6271,6 +6271,7 @@ enum ieee80211_ap_reg_power {
+ * entered.
+ * @links.cac_time_ms: CAC time in ms
+ * @valid_links: bitmap describing what elements of @links are valid
++ * @radio_mask: Bitmask of radios that this interface is allowed to operate on.
+ */
+ struct wireless_dev {
+ struct wiphy *wiphy;
+@@ -6383,6 +6384,8 @@ struct wireless_dev {
+ unsigned int cac_time_ms;
+ } links[IEEE80211_MLD_MAX_NUM_LINKS];
+ u16 valid_links;
++
++ u32 radio_mask;
+ };
+
+ static inline const u8 *wdev_address(struct wireless_dev *wdev)
+@@ -6569,6 +6572,17 @@ bool cfg80211_radio_chandef_valid(const
+ const struct cfg80211_chan_def *chandef);
+
+ /**
++ * cfg80211_wdev_channel_allowed - Check if the wdev may use the channel
++ *
++ * @wdev: the wireless device
++ * @chan: channel to check
++ *
++ * Return: whether or not the wdev may use the channel
++ */
++bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
++ struct ieee80211_channel *chan);
++
++/**
+ * ieee80211_get_response_rate - get basic rate for a given rate
+ *
+ * @sband: the band to look for rates in
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -2868,6 +2868,9 @@ enum nl80211_commands {
+ * nested item, it contains attributes defined in
+ * &enum nl80211_if_combination_attrs.
+ *
++ * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32).
++ * A value of 0 means all radios.
++ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+@@ -3416,6 +3419,8 @@ enum nl80211_attrs {
+ NL80211_ATTR_WIPHY_RADIOS,
+ NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
+
++ NL80211_ATTR_VIF_RADIO_MASK,
++
+ /* add attributes here, update the policy in nl80211.c */
+
+ __NL80211_ATTR_AFTER_LAST,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -829,6 +829,7 @@ static const struct nla_policy nl80211_p
+ [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
+ [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
++ [NL80211_ATTR_VIF_RADIO_MASK] = { .type = NLA_U32 },
+ };
+
+ /* policy for the key attributes */
+@@ -3996,7 +3997,8 @@ static int nl80211_send_iface(struct sk_
+ nla_put_u32(msg, NL80211_ATTR_GENERATION,
+ rdev->devlist_generation ^
+ (cfg80211_rdev_list_generation << 2)) ||
+- nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr))
++ nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr) ||
++ nla_put_u32(msg, NL80211_ATTR_VIF_RADIO_MASK, wdev->radio_mask))
+ goto nla_put_failure;
+
+ if (rdev->ops->get_channel && !wdev->valid_links) {
+@@ -4312,6 +4314,29 @@ static int nl80211_valid_4addr(struct cf
+ return -EOPNOTSUPP;
+ }
+
++static int nl80211_parse_vif_radio_mask(struct genl_info *info,
++ u32 *radio_mask)
++{
++ struct cfg80211_registered_device *rdev = info->user_ptr[0];
++ struct nlattr *attr = info->attrs[NL80211_ATTR_VIF_RADIO_MASK];
++ u32 mask, allowed;
++
++ if (!attr) {
++ *radio_mask = 0;
++ return 0;
++ }
++
++ allowed = BIT(rdev->wiphy.n_radio) - 1;
++ mask = nla_get_u32(attr);
++ if (mask & ~allowed)
++ return -EINVAL;
++ if (!mask)
++ mask = allowed;
++ *radio_mask = mask;
++
++ return 1;
++}
++
+ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
+ {
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+@@ -4319,6 +4344,8 @@ static int nl80211_set_interface(struct
+ int err;
+ enum nl80211_iftype otype, ntype;
+ struct net_device *dev = info->user_ptr[1];
++ struct wireless_dev *wdev = dev->ieee80211_ptr;
++ u32 radio_mask = 0;
+ bool change = false;
+
+ memset(¶ms, 0, sizeof(params));
+@@ -4332,8 +4359,6 @@ static int nl80211_set_interface(struct
+ }
+
+ if (info->attrs[NL80211_ATTR_MESH_ID]) {
+- struct wireless_dev *wdev = dev->ieee80211_ptr;
+-
+ if (ntype != NL80211_IFTYPE_MESH_POINT)
+ return -EINVAL;
+ if (otype != NL80211_IFTYPE_MESH_POINT)
+@@ -4364,6 +4389,12 @@ static int nl80211_set_interface(struct
+ if (err > 0)
+ change = true;
+
++ err = nl80211_parse_vif_radio_mask(info, &radio_mask);
++ if (err < 0)
++ return err;
++ if (err && netif_running(dev))
++ return -EBUSY;
++
+ if (change)
+ err = cfg80211_change_iface(rdev, dev, ntype, ¶ms);
+ else
+@@ -4372,11 +4403,11 @@ static int nl80211_set_interface(struct
+ if (!err && params.use_4addr != -1)
+ dev->ieee80211_ptr->use_4addr = params.use_4addr;
+
+- if (change && !err) {
+- struct wireless_dev *wdev = dev->ieee80211_ptr;
++ if (radio_mask)
++ wdev->radio_mask = radio_mask;
+
++ if (change && !err)
+ nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
+- }
+
+ return err;
+ }
+@@ -4387,6 +4418,7 @@ static int _nl80211_new_interface(struct
+ struct vif_params params;
+ struct wireless_dev *wdev;
+ struct sk_buff *msg;
++ u32 radio_mask;
+ int err;
+ enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
+
+@@ -4424,6 +4456,10 @@ static int _nl80211_new_interface(struct
+ if (err < 0)
+ return err;
+
++ err = nl80211_parse_vif_radio_mask(info, &radio_mask);
++ if (err < 0)
++ return err;
++
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+@@ -4465,6 +4501,9 @@ static int _nl80211_new_interface(struct
+ break;
+ }
+
++ if (radio_mask)
++ wdev->radio_mask = radio_mask;
++
+ if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
+ rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
+ nlmsg_free(msg);
+@@ -9180,6 +9219,9 @@ static bool cfg80211_off_channel_oper_al
+
+ lockdep_assert_wiphy(wdev->wiphy);
+
++ if (!cfg80211_wdev_channel_allowed(wdev, chan))
++ return false;
++
+ if (!cfg80211_beaconing_iface_active(wdev))
+ return true;
+
+@@ -9392,7 +9434,8 @@ static int nl80211_trigger_scan(struct s
+ }
+
+ /* ignore disabled channels */
+- if (chan->flags & IEEE80211_CHAN_DISABLED)
++ if (chan->flags & IEEE80211_CHAN_DISABLED ||
++ !cfg80211_wdev_channel_allowed(wdev, chan))
+ continue;
+
+ request->channels[i] = chan;
+@@ -9412,7 +9455,8 @@ static int nl80211_trigger_scan(struct s
+
+ chan = &wiphy->bands[band]->channels[j];
+
+- if (chan->flags & IEEE80211_CHAN_DISABLED)
++ if (chan->flags & IEEE80211_CHAN_DISABLED ||
++ !cfg80211_wdev_channel_allowed(wdev, chan))
+ continue;
+
+ request->channels[i] = chan;
+--- a/net/wireless/scan.c
++++ b/net/wireless/scan.c
+@@ -956,7 +956,8 @@ static int cfg80211_scan_6ghz(struct cfg
+ struct ieee80211_channel *chan =
+ ieee80211_get_channel(&rdev->wiphy, ap->center_freq);
+
+- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
++ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED ||
++ !cfg80211_wdev_channel_allowed(rdev_req->wdev, chan))
+ continue;
+
+ for (i = 0; i < rdev_req->n_channels; i++) {
+@@ -3519,9 +3520,12 @@ int cfg80211_wext_siwscan(struct net_dev
+ continue;
+
+ for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
++ struct ieee80211_channel *chan;
++
+ /* ignore disabled channels */
+- if (wiphy->bands[band]->channels[j].flags &
+- IEEE80211_CHAN_DISABLED)
++ chan = &wiphy->bands[band]->channels[j];
++ if (chan->flags & IEEE80211_CHAN_DISABLED ||
++ !cfg80211_wdev_channel_allowed(creq->wdev, chan))
+ continue;
+
+ /* If we have a wireless request structure and the
+--- a/net/wireless/util.c
++++ b/net/wireless/util.c
+@@ -2923,3 +2923,32 @@ bool cfg80211_radio_chandef_valid(const
+ return true;
+ }
+ EXPORT_SYMBOL(cfg80211_radio_chandef_valid);
++
++bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
++ struct ieee80211_channel *chan)
++{
++ struct wiphy *wiphy = wdev->wiphy;
++ const struct wiphy_radio *radio;
++ struct cfg80211_chan_def chandef;
++ u32 radio_mask;
++ int i;
++
++ radio_mask = wdev->radio_mask;
++ if (!wiphy->n_radio || radio_mask == BIT(wiphy->n_radio) - 1)
++ return true;
++
++ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
++ for (i = 0; i < wiphy->n_radio; i++) {
++ if (!(radio_mask & BIT(i)))
++ continue;
++
++ radio = &wiphy->radio[i];
++ if (!cfg80211_radio_chandef_valid(radio, &chandef))
++ continue;
++
++ return true;
++ }
++
++ return false;
++}
++EXPORT_SYMBOL(cfg80211_wdev_channel_allowed);
+--- a/net/wireless/core.c
++++ b/net/wireless/core.c
+@@ -1424,6 +1424,8 @@ void cfg80211_init_wdev(struct wireless_
+ /* allow mac80211 to determine the timeout */
+ wdev->ps_timeout = -1;
+
++ wdev->radio_mask = BIT(wdev->wiphy->n_radio) - 1;
++
+ if ((wdev->iftype == NL80211_IFTYPE_STATION ||
+ wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
+ wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
+++ /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: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
-Date: Tue, 17 Sep 2024 19:32:39 +0530
-Subject: [PATCH] wifi: cfg80211: check radio iface combination for multi radio
- per wiphy
-
-Currently, wiphy_verify_combinations() fails for the multi-radio per wiphy
-due to the condition check on new global interface combination that DFS
-only works on one channel. In a multi-radio scenario, new global interface
-combination encompasses the capabilities of all radio combinations, so it
-supports more than one channel with DFS. For multi-radio per wiphy,
-interface combination verification needs to be performed for radio specific
-interface combinations. This is necessary as the new global interface
-combination combines the capabilities of all radio combinations.
-
-Fixes: a01b1e9f9955 ("wifi: mac80211: add support for DFS with multiple radios")
-Signed-off-by: Karthikeyan Periyasamy <quic_periyasa@quicinc.com>
----
-
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -599,16 +599,20 @@ use_default_name:
- }
- EXPORT_SYMBOL(wiphy_new_nm);
-
--static int wiphy_verify_combinations(struct wiphy *wiphy)
-+static
-+int wiphy_verify_iface_combinations(struct wiphy *wiphy,
-+ const struct ieee80211_iface_combination *iface_comb,
-+ int n_iface_comb,
-+ bool combined_radio)
- {
- const struct ieee80211_iface_combination *c;
- int i, j;
-
-- for (i = 0; i < wiphy->n_iface_combinations; i++) {
-+ for (i = 0; i < n_iface_comb; i++) {
- u32 cnt = 0;
- u16 all_iftypes = 0;
-
-- c = &wiphy->iface_combinations[i];
-+ c = &iface_comb[i];
-
- /*
- * Combinations with just one interface aren't real,
-@@ -621,9 +625,13 @@ static int wiphy_verify_combinations(str
- if (WARN_ON(!c->num_different_channels))
- return -EINVAL;
-
-- /* DFS only works on one channel. */
-- if (WARN_ON(c->radar_detect_widths &&
-- (c->num_different_channels > 1)))
-+ /* DFS only works on one channel. Avoid this check
-+ * for multi-radio global combination, since it hold
-+ * the capabilities of all radio combinations.
-+ */
-+ if (!combined_radio &&
-+ WARN_ON(c->radar_detect_widths &&
-+ c->num_different_channels > 1))
- return -EINVAL;
-
- if (WARN_ON(!c->n_limits))
-@@ -644,13 +652,21 @@ static int wiphy_verify_combinations(str
- if (WARN_ON(wiphy->software_iftypes & types))
- return -EINVAL;
-
-- /* Only a single P2P_DEVICE can be allowed */
-- if (WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
-+ /* Only a single P2P_DEVICE can be allowed, avoid this
-+ * check for multi-radio global combination, since it
-+ * hold the capabilities of all radio combinations.
-+ */
-+ if (!combined_radio &&
-+ WARN_ON(types & BIT(NL80211_IFTYPE_P2P_DEVICE) &&
- c->limits[j].max > 1))
- return -EINVAL;
-
-- /* Only a single NAN can be allowed */
-- if (WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
-+ /* Only a single NAN can be allowed, avoid this
-+ * check for multi-radio global combination, since it
-+ * hold the capabilities of all radio combinations.
-+ */
-+ if (!combined_radio &&
-+ WARN_ON(types & BIT(NL80211_IFTYPE_NAN) &&
- c->limits[j].max > 1))
- return -EINVAL;
-
-@@ -674,6 +690,34 @@ static int wiphy_verify_combinations(str
- return 0;
- }
-
-+static int wiphy_verify_combinations(struct wiphy *wiphy)
-+{
-+ int i, ret;
-+ bool combined_radio = false;
-+
-+ if (wiphy->n_radio) {
-+ for (i = 0; i < wiphy->n_radio; i++) {
-+ const struct wiphy_radio *radio = &wiphy->radio[i];
-+
-+ ret = wiphy_verify_iface_combinations(wiphy,
-+ radio->iface_combinations,
-+ radio->n_iface_combinations,
-+ false);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ combined_radio = true;
-+ }
-+
-+ ret = wiphy_verify_iface_combinations(wiphy,
-+ wiphy->iface_combinations,
-+ wiphy->n_iface_combinations,
-+ combined_radio);
-+
-+ return ret;
-+}
-+
- int wiphy_register(struct wiphy *wiphy)
- {
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 26 Sep 2024 14:06:11 +0200
+Subject: [PATCH] wifi: mac80211: use vif radio mask to limit ibss scan
+ frequencies
+
+Reject frequencies not supported by any radio that the vif is allowed to use.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -1176,14 +1176,14 @@ int ieee80211_request_ibss_scan(struct i
+ unsigned int n_channels)
+ {
+ struct ieee80211_local *local = sdata->local;
+- int ret = -EBUSY, i, n_ch = 0;
++ int i, n_ch = 0;
+ enum nl80211_band band;
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+ /* busy scanning */
+ if (local->scan_req)
+- goto unlock;
++ return -EBUSY;
+
+ /* fill internal scan request */
+ if (!channels) {
+@@ -1200,7 +1200,9 @@ int ieee80211_request_ibss_scan(struct i
+ &local->hw.wiphy->bands[band]->channels[i];
+
+ if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
+- IEEE80211_CHAN_DISABLED))
++ IEEE80211_CHAN_DISABLED) ||
++ !cfg80211_wdev_channel_allowed(&sdata->wdev,
++ tmp_ch))
+ continue;
+
+ local->int_scan_req->channels[n_ch] = tmp_ch;
+@@ -1209,21 +1211,23 @@ int ieee80211_request_ibss_scan(struct i
+ }
+
+ if (WARN_ON_ONCE(n_ch == 0))
+- goto unlock;
++ return -EINVAL;
+
+ local->int_scan_req->n_channels = n_ch;
+ } else {
+ for (i = 0; i < n_channels; i++) {
+ if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
+- IEEE80211_CHAN_DISABLED))
++ IEEE80211_CHAN_DISABLED) ||
++ !cfg80211_wdev_channel_allowed(&sdata->wdev,
++ channels[i]))
+ continue;
+
+ local->int_scan_req->channels[n_ch] = channels[i];
+ n_ch++;
+ }
+
+- if (WARN_ON_ONCE(n_ch == 0))
+- goto unlock;
++ if (n_ch == 0)
++ return -EINVAL;
+
+ local->int_scan_req->n_channels = n_ch;
+ }
+@@ -1233,9 +1237,7 @@ int ieee80211_request_ibss_scan(struct i
+ memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
+ local->int_scan_req->ssids[0].ssid_len = ssid_len;
+
+- ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
+- unlock:
+- return ret;
++ return __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
+ }
+
+ void ieee80211_scan_cancel(struct ieee80211_local *local)
+++ /dev/null
-From: Issam Hamdi <ih@simonwunderlich.de>
-Date: Fri, 16 Aug 2024 16:24:18 +0200
-Subject: [PATCH] wifi: cfg80211: Set correct chandef when starting CAC
-
-When starting CAC in a mode other than AP mode, it return a
-"WARNING: CPU: 0 PID: 63 at cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211]"
-caused by the chandef.chan being null at the end of CAC.
-
-Solution: Ensure the channel definition is set for the different modes
-when starting CAC to avoid getting a NULL 'chan' at the end of CAC.
-
- Call Trace:
- ? show_regs.part.0+0x14/0x16
- ? __warn+0x67/0xc0
- ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211]
- ? report_bug+0xa7/0x130
- ? exc_overflow+0x30/0x30
- ? handle_bug+0x27/0x50
- ? exc_invalid_op+0x18/0x60
- ? handle_exception+0xf6/0xf6
- ? exc_overflow+0x30/0x30
- ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211]
- ? exc_overflow+0x30/0x30
- ? cfg80211_chandef_dfs_usable+0x20/0xaf [cfg80211]
- ? regulatory_propagate_dfs_state.cold+0x1b/0x4c [cfg80211]
- ? cfg80211_propagate_cac_done_wk+0x1a/0x30 [cfg80211]
- ? process_one_work+0x165/0x280
- ? worker_thread+0x120/0x3f0
- ? kthread+0xc2/0xf0
- ? process_one_work+0x280/0x280
- ? kthread_complete_and_exit+0x20/0x20
- ? ret_from_fork+0x19/0x24
-
-Reported-by: Kretschmer Mathias <mathias.kretschmer@fit.fraunhofer.de>
-Signed-off-by: Issam Hamdi <ih@simonwunderlich.de>
-Link: https://patch.msgid.link/20240816142418.3381951-1-ih@simonwunderlich.de
-[shorten subject, remove OCB, reorder cases to match previous list]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10144,7 +10144,20 @@ static int nl80211_start_radar_detection
-
- err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
- if (!err) {
-- wdev->links[0].ap.chandef = chandef;
-+ switch (wdev->iftype) {
-+ case NL80211_IFTYPE_AP:
-+ case NL80211_IFTYPE_P2P_GO:
-+ wdev->links[0].ap.chandef = chandef;
-+ break;
-+ case NL80211_IFTYPE_ADHOC:
-+ wdev->u.ibss.chandef = chandef;
-+ break;
-+ case NL80211_IFTYPE_MESH_POINT:
-+ wdev->u.mesh.chandef = chandef;
-+ break;
-+ default:
-+ break;
-+ }
- wdev->cac_started = true;
- wdev->cac_start_time = jiffies;
- wdev->cac_time_ms = cac_time_ms;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 26 Sep 2024 14:07:50 +0200
+Subject: [PATCH] wifi: mac80211: use vif radio mask to limit creating chanctx
+
+Reject frequencies not supported by any radio that the vif is allowed to use.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -1169,7 +1169,7 @@ ieee80211_replace_chanctx(struct ieee802
+ static bool
+ ieee80211_find_available_radio(struct ieee80211_local *local,
+ const struct ieee80211_chan_req *chanreq,
+- int *radio_idx)
++ u32 radio_mask, int *radio_idx)
+ {
+ struct wiphy *wiphy = local->hw.wiphy;
+ const struct wiphy_radio *radio;
+@@ -1180,6 +1180,9 @@ ieee80211_find_available_radio(struct ie
+ return true;
+
+ for (i = 0; i < wiphy->n_radio; i++) {
++ if (!(radio_mask & BIT(i)))
++ continue;
++
+ radio = &wiphy->radio[i];
+ if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
+ continue;
+@@ -1213,7 +1216,9 @@ int ieee80211_link_reserve_chanctx(struc
+ new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
+ if (!new_ctx) {
+ if (ieee80211_can_create_new_chanctx(local, -1) &&
+- ieee80211_find_available_radio(local, chanreq, &radio_idx))
++ ieee80211_find_available_radio(local, chanreq,
++ sdata->wdev.radio_mask,
++ &radio_idx))
+ new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
+ false, radio_idx);
+ else
+@@ -1883,7 +1888,9 @@ int _ieee80211_link_use_channel(struct i
+ /* Note: context is now reserved */
+ if (ctx)
+ reserved = true;
+- else if (!ieee80211_find_available_radio(local, chanreq, &radio_idx))
++ else if (!ieee80211_find_available_radio(local, chanreq,
++ sdata->wdev.radio_mask,
++ &radio_idx))
+ ctx = ERR_PTR(-EBUSY);
+ else
+ ctx = ieee80211_new_chanctx(local, chanreq, mode,
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:19 +0530
-Subject: [PATCH] Revert "wifi: mac80211: move radar detect work to sdata"
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-This reverts commit ce9e660ef32e ("wifi: mac80211: move radar detect work to sdata").
-
-To enable radar detection with MLO, it’s essential to handle it on a
-per-link basis. This is because when using MLO, multiple links may already
-be active and beaconing. In this scenario, another link should be able to
-initiate a radar detection. Also, if underlying links are associated with
-different hardware devices but grouped together for MLO, they could
-potentially start radar detection simultaneously. Therefore, it makes
-sense to manage radar detection settings separately for each link by moving
-them back to a per-link data structure.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-2-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1658,7 +1658,7 @@ static int ieee80211_stop_ap(struct wiph
-
- if (sdata->wdev.cac_started) {
- chandef = link_conf->chanreq.oper;
-- wiphy_delayed_work_cancel(wiphy, &sdata->dfs_cac_timer_work);
-+ wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_ABORTED,
- GFP_KERNEL);
-@@ -3482,7 +3482,7 @@ static int ieee80211_start_radar_detecti
- if (err)
- goto out_unlock;
-
-- wiphy_delayed_work_queue(wiphy, &sdata->dfs_cac_timer_work,
-+ wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
- msecs_to_jiffies(cac_time_ms));
-
- out_unlock:
-@@ -3499,7 +3499,7 @@ static void ieee80211_end_cac(struct wip
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- wiphy_delayed_work_cancel(wiphy,
-- &sdata->dfs_cac_timer_work);
-+ &sdata->deflink.dfs_cac_timer_work);
-
- if (sdata->wdev.cac_started) {
- ieee80211_link_release_channel(&sdata->deflink);
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1069,6 +1069,7 @@ struct ieee80211_link_data {
- int ap_power_level; /* in dBm */
-
- bool radar_required;
-+ struct wiphy_delayed_work dfs_cac_timer_work;
-
- union {
- struct ieee80211_link_data_managed mgd;
-@@ -1167,8 +1168,6 @@ struct ieee80211_sub_if_data {
- struct ieee80211_link_data deflink;
- struct ieee80211_link_data __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
-
-- struct wiphy_delayed_work dfs_cac_timer_work;
--
- /* for ieee80211_set_active_links_async() */
- struct wiphy_work activate_links_work;
- u16 desired_active_links;
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -551,7 +551,7 @@ static void ieee80211_do_stop(struct iee
- wiphy_work_cancel(local->hw.wiphy,
- &sdata->deflink.color_change_finalize_work);
- wiphy_delayed_work_cancel(local->hw.wiphy,
-- &sdata->dfs_cac_timer_work);
-+ &sdata->deflink.dfs_cac_timer_work);
-
- if (sdata->wdev.cac_started) {
- chandef = sdata->vif.bss_conf.chanreq.oper;
-@@ -1744,8 +1744,6 @@ static void ieee80211_setup_sdata(struct
- wiphy_work_init(&sdata->work, ieee80211_iface_work);
- wiphy_work_init(&sdata->activate_links_work,
- ieee80211_activate_links_work);
-- wiphy_delayed_work_init(&sdata->dfs_cac_timer_work,
-- ieee80211_dfs_cac_timer_work);
-
- switch (type) {
- case NL80211_IFTYPE_P2P_GO:
---- a/net/mac80211/link.c
-+++ b/net/mac80211/link.c
-@@ -45,6 +45,8 @@ void ieee80211_link_init(struct ieee8021
- ieee80211_color_collision_detection_work);
- INIT_LIST_HEAD(&link->assigned_chanctx_list);
- INIT_LIST_HEAD(&link->reserved_chanctx_list);
-+ wiphy_delayed_work_init(&link->dfs_cac_timer_work,
-+ ieee80211_dfs_cac_timer_work);
-
- if (!deflink) {
- switch (sdata->vif.type) {
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -3031,15 +3031,16 @@ void ieee80211_dynamic_ps_timer(struct t
-
- void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
- {
-- struct ieee80211_sub_if_data *sdata =
-- container_of(work, struct ieee80211_sub_if_data,
-+ struct ieee80211_link_data *link =
-+ container_of(work, struct ieee80211_link_data,
- dfs_cac_timer_work.work);
-- struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chanreq.oper;
-+ struct cfg80211_chan_def chandef = link->conf->chanreq.oper;
-+ struct ieee80211_sub_if_data *sdata = link->sdata;
-
- lockdep_assert_wiphy(sdata->local->hw.wiphy);
-
- if (sdata->wdev.cac_started) {
-- ieee80211_link_release_channel(&sdata->deflink);
-+ ieee80211_link_release_channel(link);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_FINISHED,
- GFP_KERNEL);
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3460,7 +3460,7 @@ void ieee80211_dfs_cac_cancel(struct iee
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- wiphy_delayed_work_cancel(local->hw.wiphy,
-- &sdata->dfs_cac_timer_work);
-+ &sdata->deflink.dfs_cac_timer_work);
-
- if (sdata->wdev.cac_started) {
- chandef = sdata->vif.bss_conf.chanreq.oper;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 17 Jul 2024 22:49:16 +0200
+Subject: [PATCH] wifi: mac80211: remove status->ampdu_delimiter_crc
+
+This was never used by any driver, so remove it to free up some space.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1448,8 +1448,6 @@ ieee80211_tx_info_clear_status(struct ie
+ * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
+ * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected
+ * on this subframe
+- * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
+- * is stored in the @ampdu_delimiter_crc field)
+ * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was
+ * done by the hardware
+ * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
+@@ -1521,7 +1519,7 @@ enum mac80211_rx_flags {
+ RX_FLAG_AMPDU_LAST_KNOWN = BIT(12),
+ RX_FLAG_AMPDU_IS_LAST = BIT(13),
+ RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14),
+- RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15),
++ /* one free bit at 15 */
+ RX_FLAG_MACTIME = BIT(16) | BIT(17),
+ RX_FLAG_MACTIME_PLCP_START = 1 << 16,
+ RX_FLAG_MACTIME_START = 2 << 16,
+@@ -1618,7 +1616,6 @@ enum mac80211_rx_encoding {
+ * @rx_flags: internal RX flags for mac80211
+ * @ampdu_reference: A-MPDU reference number, must be a different value for
+ * each A-MPDU but the same for each subframe within one A-MPDU
+- * @ampdu_delimiter_crc: A-MPDU delimiter CRC
+ * @zero_length_psdu_type: radiotap type of the 0-length PSDU
+ * @link_valid: if the link which is identified by @link_id is valid. This flag
+ * is set only when connection is MLO.
+@@ -1656,7 +1653,6 @@ struct ieee80211_rx_status {
+ s8 signal;
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
+- u8 ampdu_delimiter_crc;
+ u8 zero_length_psdu_type;
+ u8 link_valid:1, link_id:4;
+ };
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -508,18 +508,13 @@ ieee80211_add_rx_radiotap_header(struct
+ flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST;
+ if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR)
+ flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR;
+- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
+- flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN;
+ if (status->flag & RX_FLAG_AMPDU_EOF_BIT_KNOWN)
+ flags |= IEEE80211_RADIOTAP_AMPDU_EOF_KNOWN;
+ if (status->flag & RX_FLAG_AMPDU_EOF_BIT)
+ flags |= IEEE80211_RADIOTAP_AMPDU_EOF;
+ put_unaligned_le16(flags, pos);
+ pos += 2;
+- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
+- *pos++ = status->ampdu_delimiter_crc;
+- else
+- *pos++ = 0;
++ *pos++ = 0;
+ *pos++ = 0;
+ }
+
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 26 Sep 2024 19:52:30 +0200
+Subject: [PATCH] wifi: cfg80211: pass net_device to .set_monitor_channel
+
+Preparation for allowing multiple monitor interfaces with different channels
+on a multi-radio wiphy.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
++++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
+@@ -1493,6 +1493,7 @@ out:
+ }
+
+ static int wil_cfg80211_set_channel(struct wiphy *wiphy,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+--- a/drivers/net/wireless/marvell/libertas/cfg.c
++++ b/drivers/net/wireless/marvell/libertas/cfg.c
+@@ -486,6 +486,7 @@ static int lbs_add_wps_enrollee_tlv(u8 *
+ */
+
+ static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct lbs_private *priv = wiphy_priv(wiphy);
+--- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c
++++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
+@@ -231,6 +231,7 @@ struct wilc_vif *wilc_get_wl_to_vif(stru
+ }
+
+ static int set_channel(struct wiphy *wiphy,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct wilc *wl = wiphy_priv(wiphy);
+@@ -1424,7 +1425,7 @@ static int start_ap(struct wiphy *wiphy,
+ struct wilc_vif *vif = netdev_priv(dev);
+ int ret;
+
+- ret = set_channel(wiphy, &settings->chandef);
++ ret = set_channel(wiphy, dev, &settings->chandef);
+ if (ret != 0)
+ netdev_err(dev, "Error in setting channel\n");
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -4700,6 +4700,7 @@ struct cfg80211_ops {
+ struct ieee80211_channel *chan);
+
+ int (*set_monitor_channel)(struct wiphy *wiphy,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef);
+
+ int (*scan)(struct wiphy *wiphy,
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -879,6 +879,7 @@ static int ieee80211_get_station(struct
+ }
+
+ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+--- a/net/wireless/chan.c
++++ b/net/wireless/chan.c
+@@ -1673,6 +1673,7 @@ bool cfg80211_reg_check_beaconing(struct
+ EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
+
+ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ if (!rdev->ops->set_monitor_channel)
+@@ -1680,7 +1681,7 @@ int cfg80211_set_monitor_channel(struct
+ if (!cfg80211_has_monitors_only(rdev))
+ return -EBUSY;
+
+- return rdev_set_monitor_channel(rdev, chandef);
++ return rdev_set_monitor_channel(rdev, dev, chandef);
+ }
+
+ bool cfg80211_any_usable_channels(struct wiphy *wiphy,
+--- a/net/wireless/core.h
++++ b/net/wireless/core.h
+@@ -518,6 +518,7 @@ static inline unsigned int elapsed_jiffi
+ }
+
+ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef);
+
+ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -3562,7 +3562,7 @@ static int __nl80211_set_channel(struct
+ case NL80211_IFTYPE_MESH_POINT:
+ return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
+ case NL80211_IFTYPE_MONITOR:
+- return cfg80211_set_monitor_channel(rdev, &chandef);
++ return cfg80211_set_monitor_channel(rdev, dev, &chandef);
+ default:
+ break;
+ }
+--- a/net/wireless/rdev-ops.h
++++ b/net/wireless/rdev-ops.h
+@@ -445,11 +445,12 @@ rdev_libertas_set_mesh_channel(struct cf
+
+ static inline int
+ rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
++ struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
+ {
+ int ret;
+- trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
+- ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
++ trace_rdev_set_monitor_channel(&rdev->wiphy, dev, chandef);
++ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, dev, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+ }
+--- a/net/wireless/trace.h
++++ b/net/wireless/trace.h
+@@ -1318,19 +1318,21 @@ TRACE_EVENT(rdev_libertas_set_mesh_chann
+ );
+
+ TRACE_EVENT(rdev_set_monitor_channel,
+- TP_PROTO(struct wiphy *wiphy,
++ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+- TP_ARGS(wiphy, chandef),
++ TP_ARGS(wiphy, netdev, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
++ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
++ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
++ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
++ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+ );
+
+ TRACE_EVENT(rdev_auth,
+--- a/net/wireless/wext-compat.c
++++ b/net/wireless/wext-compat.c
+@@ -830,7 +830,7 @@ static int cfg80211_wext_siwfreq(struct
+ ret = -EINVAL;
+ break;
+ }
+- ret = cfg80211_set_monitor_channel(rdev, &chandef);
++ ret = cfg80211_set_monitor_channel(rdev, dev, &chandef);
+ break;
+ case NL80211_IFTYPE_MESH_POINT:
+ freq = cfg80211_wext_freq(wextfreq);
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:20 +0530
-Subject: [PATCH] wifi: mac80211: remove label usage in
- ieee80211_start_radar_detection()
-
-After locks rework [1], ieee80211_start_radar_detection() function is no
-longer acquiring any lock as such explicitly. Hence, it is not unlocking
-anything as well. However, label "out_unlock" is still used which creates
-confusion. Also, now there is no need of goto label as such.
-
-Get rid of the goto logic and use direct return statements.
-
-[1]: https://lore.kernel.org/all/20230828135928.b1c6efffe9ad.I4aec875e25abc9ef0b5ad1e70b5747fd483fbd3c@changeid/
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-3-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3468,10 +3468,8 @@ static int ieee80211_start_radar_detecti
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
-- if (!list_empty(&local->roc_list) || local->scanning) {
-- err = -EBUSY;
-- goto out_unlock;
-- }
-+ if (!list_empty(&local->roc_list) || local->scanning)
-+ return -EBUSY;
-
- /* whatever, but channel contexts should not complain about that one */
- sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
-@@ -3480,13 +3478,12 @@ static int ieee80211_start_radar_detecti
- err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
- IEEE80211_CHANCTX_SHARED);
- if (err)
-- goto out_unlock;
-+ return err;
-
- wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
- msecs_to_jiffies(cac_time_ms));
-
-- out_unlock:
-- return err;
-+ return 0;
- }
-
- static void ieee80211_end_cac(struct wiphy *wiphy,
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 30 Sep 2024 15:09:45 +0200
+Subject: [PATCH] wifi: mac80211: add flag to opt out of virtual monitor
+ support
+
+This is useful for multi-radio devices that are capable of monitoring on
+multiple channels simultanenously. When this flag is set, each monitor
+interface is passed to the driver individually and can have a configured
+channel.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -2679,6 +2679,11 @@ struct ieee80211_txq {
+ * a virtual monitor interface when monitor interfaces are the only
+ * active interfaces.
+ *
++ * @IEEE80211_HW_NO_VIRTUAL_MONITOR: The driver would like to be informed
++ * of any monitor interface, as well as their configured channel.
++ * This is useful for supporting multiple monitor interfaces on different
++ * channels.
++ *
+ * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
+ * be created. It is expected user-space will create vifs as
+ * desired (and thus have them named as desired).
+@@ -2838,6 +2843,7 @@ enum ieee80211_hw_flags {
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS,
+ IEEE80211_HW_MFP_CAPABLE,
+ IEEE80211_HW_WANT_MONITOR_VIF,
++ IEEE80211_HW_NO_VIRTUAL_MONITOR,
+ IEEE80211_HW_NO_AUTO_VIF,
+ IEEE80211_HW_SW_CRYPTO_CONTROL,
+ IEEE80211_HW_SUPPORT_FAST_XMIT,
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -105,8 +105,11 @@ static int ieee80211_set_mon_options(str
+ }
+
+ /* also validate MU-MIMO change */
+- monitor_sdata = wiphy_dereference(local->hw.wiphy,
+- local->monitor_sdata);
++ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ monitor_sdata = sdata;
++ else
++ monitor_sdata = wiphy_dereference(local->hw.wiphy,
++ local->monitor_sdata);
+
+ if (!monitor_sdata &&
+ (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
+@@ -114,7 +117,9 @@ static int ieee80211_set_mon_options(str
+
+ /* apply all changes now - no failures allowed */
+
+- if (monitor_sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
++ if (monitor_sdata &&
++ (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) ||
++ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)))
+ ieee80211_set_mu_mimo_follow(monitor_sdata, params);
+
+ if (params->flags) {
+@@ -889,22 +894,25 @@ static int ieee80211_set_monitor_channel
+
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+- if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
+- &chanreq.oper))
+- return 0;
++ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
++ if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
++ if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
++ &chanreq.oper))
++ return 0;
+
+- sdata = wiphy_dereference(local->hw.wiphy,
+- local->monitor_sdata);
+- if (!sdata)
+- goto done;
++ sdata = wiphy_dereference(wiphy, local->monitor_sdata);
++ if (!sdata)
++ goto done;
++ }
+
+- if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
++ if (rcu_access_pointer(sdata->deflink.conf->chanctx_conf) &&
++ cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
+ &chanreq.oper))
+ return 0;
+
+ ieee80211_link_release_channel(&sdata->deflink);
+ ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
+- IEEE80211_CHANCTX_EXCLUSIVE);
++ IEEE80211_CHANCTX_SHARED);
+ if (ret)
+ return ret;
+ done:
+@@ -3050,7 +3058,8 @@ static int ieee80211_set_tx_power(struct
+ if (wdev) {
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
+ return -EOPNOTSUPP;
+
+@@ -3098,7 +3107,8 @@ static int ieee80211_set_tx_power(struct
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ has_monitor = true;
+ continue;
+ }
+@@ -3108,7 +3118,8 @@ static int ieee80211_set_tx_power(struct
+ sdata->vif.bss_conf.txpower_type = txp_type;
+ }
+ list_for_each_entry(sdata, &local->interfaces, list) {
+- if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ continue;
+ ieee80211_recalc_txpower(sdata, update_txp_type);
+ }
+@@ -4303,7 +4314,8 @@ static int ieee80211_cfg_get_channel(str
+ if (chanctx_conf) {
+ *chandef = link->conf->chanreq.oper;
+ ret = 0;
+- } else if (local->open_count > 0 &&
++ } else if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
++ local->open_count > 0 &&
+ local->open_count == local->monitors &&
+ sdata->vif.type == NL80211_IFTYPE_MONITOR) {
+ *chandef = local->monitor_chanreq.oper;
+--- a/net/mac80211/chan.c
++++ b/net/mac80211/chan.c
+@@ -339,6 +339,10 @@ ieee80211_get_chanctx_max_required_bw(st
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_NAN:
+ continue;
++ case NL80211_IFTYPE_MONITOR:
++ WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
++ NO_VIRTUAL_MONITOR));
++ fallthrough;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
+@@ -347,7 +351,6 @@ ieee80211_get_chanctx_max_required_bw(st
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+- case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ WARN_ON_ONCE(1);
+@@ -956,6 +959,10 @@ void ieee80211_recalc_smps_chanctx(struc
+ if (!link->sdata->u.mgd.associated)
+ continue;
+ break;
++ case NL80211_IFTYPE_MONITOR:
++ if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ continue;
++ break;
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+@@ -968,6 +975,11 @@ void ieee80211_recalc_smps_chanctx(struc
+ if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
+ continue;
+
++ if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
++ rx_chains_dynamic = rx_chains_static = local->rx_chains;
++ break;
++ }
++
+ switch (link->smps_mode) {
+ default:
+ WARN_ONCE(1, "Invalid SMPS mode %d\n",
+--- a/net/mac80211/debugfs.c
++++ b/net/mac80211/debugfs.c
+@@ -465,6 +465,7 @@ static const char *hw_flag_names[] = {
+ FLAG(SUPPORTS_DYNAMIC_PS),
+ FLAG(MFP_CAPABLE),
+ FLAG(WANT_MONITOR_VIF),
++ FLAG(NO_VIRTUAL_MONITOR),
+ FLAG(NO_AUTO_VIF),
+ FLAG(SW_CRYPTO_CONTROL),
+ FLAG(SUPPORT_FAST_XMIT),
+--- a/net/mac80211/driver-ops.c
++++ b/net/mac80211/driver-ops.c
+@@ -65,6 +65,7 @@ int drv_add_interface(struct ieee80211_l
+ if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
+ (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
+ !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
+ !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
+ return -EINVAL;
+
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -279,8 +279,13 @@ static int _ieee80211_change_mac(struct
+ ret = eth_mac_addr(sdata->dev, sa);
+
+ if (ret == 0) {
+- memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
+- ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
++ if (check_dup) {
++ memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
++ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
++ } else {
++ memset(sdata->vif.addr, 0, ETH_ALEN);
++ memset(sdata->vif.bss_conf.addr, 0, ETH_ALEN);
++ }
+ }
+
+ /* Regardless of eth_mac_addr() return we still want to add the
+@@ -699,9 +704,11 @@ static void ieee80211_do_stop(struct iee
+ ieee80211_recalc_idle(local);
+ ieee80211_recalc_offload(local);
+
+- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
++ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ break;
+
++ ieee80211_link_release_channel(&sdata->deflink);
+ fallthrough;
+ default:
+ if (!going_down)
+@@ -1131,7 +1138,8 @@ int ieee80211_add_virtual_monitor(struct
+ ASSERT_RTNL();
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+- if (local->monitor_sdata)
++ if (local->monitor_sdata ||
++ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ return 0;
+
+ sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
+@@ -1193,6 +1201,9 @@ void ieee80211_del_virtual_monitor(struc
+ {
+ struct ieee80211_sub_if_data *sdata;
+
++ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ return;
++
+ ASSERT_RTNL();
+ lockdep_assert_wiphy(local->hw.wiphy);
+
+@@ -1328,7 +1339,8 @@ int ieee80211_do_open(struct wireless_de
+ break;
+ }
+
+- if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
++ if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
++ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ res = drv_add_interface(local, sdata);
+ if (res)
+ goto err_stop;
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -840,6 +840,9 @@ ieee80211_rx_monitor(struct ieee80211_lo
+ bool last_monitor = list_is_last(&sdata->u.mntr.list,
+ &local->mon_list);
+
++ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
++
+ if (!monskb)
+ monskb = ieee80211_make_monitor_skb(local, &origskb,
+ rate, rtap_space,
+--- a/net/mac80211/tx.c
++++ b/net/mac80211/tx.c
+@@ -1763,7 +1763,8 @@ static bool __ieee80211_tx(struct ieee80
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+- if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
++ if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
++ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ vif = &sdata->vif;
+ break;
+ }
+@@ -3952,7 +3953,8 @@ begin:
+
+ switch (tx.sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+- if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
++ if ((tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
++ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
+ vif = &tx.sdata->vif;
+ break;
+ }
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -739,7 +739,8 @@ static void __iterate_interfaces(struct
+ lockdep_is_held(&local->hw.wiphy->mtx)) {
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
++ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ continue;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+@@ -1856,8 +1857,10 @@ int ieee80211_reconfig(struct ieee80211_
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+- sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ ieee80211_sdata_running(sdata)) {
+ res = drv_add_interface(local, sdata);
+ if (WARN_ON(res))
+@@ -1870,11 +1873,14 @@ int ieee80211_reconfig(struct ieee80211_
+ */
+ if (res) {
+ list_for_each_entry_continue_reverse(sdata, &local->interfaces,
+- list)
++ list) {
++ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
++ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
++ continue;
+ if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
+- sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ ieee80211_sdata_running(sdata))
+ drv_remove_interface(local, sdata);
++ }
+ ieee80211_handle_reconfig_failure(local);
+ return res;
+ }
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:21 +0530
-Subject: [PATCH] wifi: trace: unlink rdev_end_cac trace event from
- wiphy_netdev_evt class
-
-rdev_end_cac trace event is linked with wiphy_netdev_evt event class.
-There is no option to pass link ID currently to wiphy_netdev_evt class.
-A subsequent change would pass link ID to rdev_end_cac event and hence
-it can no longer derive the event class from wiphy_netdev_evt.
-
-Therefore, unlink rdev_end_cac event from wiphy_netdev_evt and define it's
-own independent trace event. Link ID would be passed in subsequent change.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-4-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -805,9 +805,18 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flus
- TP_ARGS(wiphy, netdev)
- );
-
--DEFINE_EVENT(wiphy_netdev_evt, rdev_end_cac,
-- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
-- TP_ARGS(wiphy, netdev)
-+TRACE_EVENT(rdev_end_cac,
-+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
-+ TP_ARGS(wiphy, netdev),
-+ TP_STRUCT__entry(
-+ WIPHY_ENTRY
-+ NETDEV_ENTRY
-+ ),
-+ TP_fast_assign(
-+ WIPHY_ASSIGN;
-+ NETDEV_ASSIGN;
-+ ),
-+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
- );
-
- DECLARE_EVENT_CLASS(station_add_change,
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 30 Sep 2024 17:04:09 +0200
+Subject: [PATCH] wifi: cfg80211: add monitor SKIP_TX flag
+
+This can be used to indicate that the user is not interested in receiving
+locally sent packets on the monitor interface.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -2272,6 +2272,7 @@ static inline int cfg80211_get_station(s
+ * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering
+ * @MONITOR_FLAG_COOK_FRAMES: report frames after processing
+ * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address
++ * @MONITOR_FLAG_SKIP_TX: do not pass locally transmitted frames
+ */
+ enum monitor_flags {
+ MONITOR_FLAG_CHANGED = BIT(__NL80211_MNTR_FLAG_INVALID),
+@@ -2281,6 +2282,7 @@ enum monitor_flags {
+ MONITOR_FLAG_OTHER_BSS = BIT(NL80211_MNTR_FLAG_OTHER_BSS),
+ MONITOR_FLAG_COOK_FRAMES = BIT(NL80211_MNTR_FLAG_COOK_FRAMES),
+ MONITOR_FLAG_ACTIVE = BIT(NL80211_MNTR_FLAG_ACTIVE),
++ MONITOR_FLAG_SKIP_TX = BIT(NL80211_MNTR_FLAG_SKIP_TX),
+ };
+
+ /**
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -4703,6 +4703,7 @@ enum nl80211_survey_info {
+ * overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ * and ACK incoming unicast packets.
++ * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets
+ *
+ * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
+ * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
+@@ -4715,6 +4716,7 @@ enum nl80211_mntr_flags {
+ NL80211_MNTR_FLAG_OTHER_BSS,
+ NL80211_MNTR_FLAG_COOK_FRAMES,
+ NL80211_MNTR_FLAG_ACTIVE,
++ NL80211_MNTR_FLAG_SKIP_TX,
+
+ /* keep last */
+ __NL80211_MNTR_FLAG_AFTER_LAST,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -4201,6 +4201,7 @@ static const struct nla_policy mntr_flag
+ [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
+ [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
++ [NL80211_MNTR_FLAG_SKIP_TX] = { .type = NLA_FLAG },
+ };
+
+ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:22 +0530
-Subject: [PATCH] wifi: cfg80211: move DFS related members to links[] in
- wireless_dev
-
-A few members related to DFS handling are currently under per wireless
-device data structure. However, in order to support DFS with MLO, there is
-a need to have them on a per-link manner.
-
-Hence, as a preliminary step, move members cac_started, cac_start_time
-and cac_time_ms to be on a per-link basis.
-
-Since currently, link ID is not known at all places, use default value of
-0 for now.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-5-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/marvell/mwifiex/11h.c
-+++ b/drivers/net/wireless/marvell/mwifiex/11h.c
-@@ -117,7 +117,7 @@ void mwifiex_dfs_cac_work_queue(struct w
- dfs_cac_work);
-
- chandef = priv->dfs_chandef;
-- if (priv->wdev.cac_started) {
-+ if (priv->wdev.links[0].cac_started) {
- mwifiex_dbg(priv->adapter, MSG,
- "CAC timer finished; No radar detected\n");
- cfg80211_cac_event(priv->netdev, &chandef,
-@@ -174,7 +174,7 @@ int mwifiex_stop_radar_detection(struct
- */
- void mwifiex_abort_cac(struct mwifiex_private *priv)
- {
-- if (priv->wdev.cac_started) {
-+ if (priv->wdev.links[0].cac_started) {
- if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef))
- mwifiex_dbg(priv->adapter, ERROR,
- "failed to stop CAC in FW\n");
---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
-+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
-@@ -1880,7 +1880,7 @@ mwifiex_cfg80211_del_station(struct wiph
- struct mwifiex_sta_node *sta_node;
- u8 deauth_mac[ETH_ALEN];
-
-- if (!priv->bss_started && priv->wdev.cac_started) {
-+ if (!priv->bss_started && priv->wdev.links[0].cac_started) {
- mwifiex_dbg(priv->adapter, INFO, "%s: abort CAC!\n", __func__);
- mwifiex_abort_cac(priv);
- }
-@@ -3978,7 +3978,7 @@ mwifiex_cfg80211_channel_switch(struct w
- return -EBUSY;
- }
-
-- if (priv->wdev.cac_started)
-+ if (priv->wdev.links[0].cac_started)
- return -EBUSY;
-
- if (cfg80211_chandef_identical(¶ms->chandef,
---- a/drivers/net/wireless/quantenna/qtnfmac/event.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
-@@ -520,21 +520,21 @@ static int qtnf_event_handle_radar(struc
- cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL);
- break;
- case QLINK_RADAR_CAC_FINISHED:
-- if (!vif->wdev.cac_started)
-+ if (!vif->wdev.links[0].cac_started)
- break;
-
- cfg80211_cac_event(vif->netdev, &chandef,
- NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
- break;
- case QLINK_RADAR_CAC_ABORTED:
-- if (!vif->wdev.cac_started)
-+ if (!vif->wdev.links[0].cac_started)
- break;
-
- cfg80211_cac_event(vif->netdev, &chandef,
- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
- break;
- case QLINK_RADAR_CAC_STARTED:
-- if (vif->wdev.cac_started)
-+ if (vif->wdev.links[0].cac_started)
- break;
-
- if (!wiphy_ext_feature_isset(wiphy,
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6198,9 +6198,6 @@ enum ieee80211_ap_reg_power {
- * @address: The address for this device, valid only if @netdev is %NULL
- * @is_running: true if this is a non-netdev device that has been started, e.g.
- * the P2P Device.
-- * @cac_started: true if DFS channel availability check has been started
-- * @cac_start_time: timestamp (jiffies) when the dfs state was entered.
-- * @cac_time_ms: CAC time in ms
- * @ps: powersave mode is enabled
- * @ps_timeout: dynamic powersave timeout
- * @ap_unexpected_nlportid: (private) netlink port ID of application
-@@ -6224,6 +6221,11 @@ enum ieee80211_ap_reg_power {
- * unprotected beacon report
- * @links: array of %IEEE80211_MLD_MAX_NUM_LINKS elements containing @addr
- * @ap and @client for each link
-+ * @links[].cac_started: true if DFS channel availability check has been
-+ * started
-+ * @links[].cac_start_time: timestamp (jiffies) when the dfs state was
-+ * entered.
-+ * @links[].cac_time_ms: CAC time in ms
- * @valid_links: bitmap describing what elements of @links are valid
- */
- struct wireless_dev {
-@@ -6265,11 +6267,6 @@ struct wireless_dev {
- u32 owner_nlportid;
- bool nl_owner_dead;
-
-- /* FIXME: need to rework radar detection for MLO */
-- bool cac_started;
-- unsigned long cac_start_time;
-- unsigned int cac_time_ms;
--
- #ifdef CPTCFG_CFG80211_WEXT
- /* wext data */
- struct {
-@@ -6336,6 +6333,10 @@ struct wireless_dev {
- struct cfg80211_internal_bss *current_bss;
- } client;
- };
-+
-+ bool cac_started;
-+ unsigned long cac_start_time;
-+ unsigned int cac_time_ms;
- } links[IEEE80211_MLD_MAX_NUM_LINKS];
- u16 valid_links;
- };
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1656,7 +1656,7 @@ static int ieee80211_stop_ap(struct wiph
- ieee80211_link_info_change_notify(sdata, link,
- BSS_CHANGED_BEACON_ENABLED);
-
-- if (sdata->wdev.cac_started) {
-+ if (sdata->wdev.links[0].cac_started) {
- chandef = link_conf->chanreq.oper;
- wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
- cfg80211_cac_event(sdata->dev, &chandef,
-@@ -3498,9 +3498,9 @@ static void ieee80211_end_cac(struct wip
- wiphy_delayed_work_cancel(wiphy,
- &sdata->deflink.dfs_cac_timer_work);
-
-- if (sdata->wdev.cac_started) {
-+ if (sdata->wdev.links[0].cac_started) {
- ieee80211_link_release_channel(&sdata->deflink);
-- sdata->wdev.cac_started = false;
-+ sdata->wdev.links[0].cac_started = false;
- }
- }
- }
-@@ -3955,7 +3955,7 @@ __ieee80211_channel_switch(struct wiphy
- if (!list_empty(&local->roc_list) || local->scanning)
- return -EBUSY;
-
-- if (sdata->wdev.cac_started)
-+ if (sdata->wdev.links[0].cac_started)
- return -EBUSY;
-
- if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -553,7 +553,7 @@ static void ieee80211_do_stop(struct iee
- wiphy_delayed_work_cancel(local->hw.wiphy,
- &sdata->deflink.dfs_cac_timer_work);
-
-- if (sdata->wdev.cac_started) {
-+ if (sdata->wdev.links[0].cac_started) {
- chandef = sdata->vif.bss_conf.chanreq.oper;
- WARN_ON(local->suspended);
- ieee80211_link_release_channel(&sdata->deflink);
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -3039,7 +3039,7 @@ void ieee80211_dfs_cac_timer_work(struct
-
- lockdep_assert_wiphy(sdata->local->hw.wiphy);
-
-- if (sdata->wdev.cac_started) {
-+ if (sdata->wdev.links[0].cac_started) {
- ieee80211_link_release_channel(link);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_FINISHED,
---- a/net/mac80211/scan.c
-+++ b/net/mac80211/scan.c
-@@ -585,7 +585,7 @@ static bool __ieee80211_can_leave_ch(str
- return false;
-
- list_for_each_entry(sdata_iter, &local->interfaces, list) {
-- if (sdata_iter->wdev.cac_started)
-+ if (sdata_iter->wdev.links[0].cac_started)
- return false;
- }
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3462,7 +3462,7 @@ void ieee80211_dfs_cac_cancel(struct iee
- wiphy_delayed_work_cancel(local->hw.wiphy,
- &sdata->deflink.dfs_cac_timer_work);
-
-- if (sdata->wdev.cac_started) {
-+ if (sdata->wdev.links[0].cac_started) {
- chandef = sdata->vif.bss_conf.chanreq.oper;
- ieee80211_link_release_channel(&sdata->deflink);
- cfg80211_cac_event(sdata->dev,
---- a/net/wireless/ibss.c
-+++ b/net/wireless/ibss.c
-@@ -94,7 +94,7 @@ int __cfg80211_join_ibss(struct cfg80211
-
- lockdep_assert_held(&rdev->wiphy.mtx);
-
-- if (wdev->cac_started)
-+ if (wdev->links[0].cac_started)
- return -EBUSY;
-
- if (wdev->u.ibss.ssid_len)
---- a/net/wireless/mesh.c
-+++ b/net/wireless/mesh.c
-@@ -127,7 +127,7 @@ int __cfg80211_join_mesh(struct cfg80211
- if (!rdev->ops->join_mesh)
- return -EOPNOTSUPP;
-
-- if (wdev->cac_started)
-+ if (wdev->links[0].cac_started)
- return -EBUSY;
-
- if (!setup->chandef.chan) {
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1124,13 +1124,14 @@ void cfg80211_cac_event(struct net_devic
-
- trace_cfg80211_cac_event(netdev, event);
-
-- if (WARN_ON(!wdev->cac_started && event != NL80211_RADAR_CAC_STARTED))
-+ if (WARN_ON(!wdev->links[0].cac_started &&
-+ event != NL80211_RADAR_CAC_STARTED))
- return;
-
- switch (event) {
- case NL80211_RADAR_CAC_FINISHED:
-- timeout = wdev->cac_start_time +
-- msecs_to_jiffies(wdev->cac_time_ms);
-+ timeout = wdev->links[0].cac_start_time +
-+ msecs_to_jiffies(wdev->links[0].cac_time_ms);
- WARN_ON(!time_after_eq(jiffies, timeout));
- cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
- memcpy(&rdev->cac_done_chandef, chandef,
-@@ -1139,10 +1140,10 @@ void cfg80211_cac_event(struct net_devic
- cfg80211_sched_dfs_chan_update(rdev);
- fallthrough;
- case NL80211_RADAR_CAC_ABORTED:
-- wdev->cac_started = false;
-+ wdev->links[0].cac_started = false;
- break;
- case NL80211_RADAR_CAC_STARTED:
-- wdev->cac_started = true;
-+ wdev->links[0].cac_started = true;
- break;
- default:
- WARN_ON(1);
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -6066,7 +6066,7 @@ static int nl80211_start_ap(struct sk_bu
- if (!rdev->ops->start_ap)
- return -EOPNOTSUPP;
-
-- if (wdev->cac_started)
-+ if (wdev->links[0].cac_started)
- return -EBUSY;
-
- if (wdev->links[link_id].ap.beacon_interval)
-@@ -10122,7 +10122,7 @@ static int nl80211_start_radar_detection
- goto unlock;
- }
-
-- if (cfg80211_beaconing_iface_active(wdev) || wdev->cac_started) {
-+ if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
- err = -EBUSY;
- goto unlock;
- }
-@@ -10158,9 +10158,9 @@ static int nl80211_start_radar_detection
- default:
- break;
- }
-- wdev->cac_started = true;
-- wdev->cac_start_time = jiffies;
-- wdev->cac_time_ms = cac_time_ms;
-+ wdev->links[0].cac_started = true;
-+ wdev->links[0].cac_start_time = jiffies;
-+ wdev->links[0].cac_time_ms = cac_time_ms;
- }
- unlock:
- wiphy_unlock(wiphy);
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -4241,7 +4241,7 @@ static void cfg80211_check_and_end_cac(s
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- struct cfg80211_chan_def *chandef;
-
-- if (!wdev->cac_started)
-+ if (!wdev->links[0].cac_started)
- continue;
-
- /* FIXME: radar detection is tied to link 0 for now */
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:23 +0530
-Subject: [PATCH] wifi: cfg80211: handle DFS per link
-
-Currently, during starting a radar detection, no link id information is
-parsed and passed down. In order to support starting radar detection
-during Multi Link Operation, it is required to pass link id as well.
-
-Add changes to first parse and then pass link id in the start radar
-detection path.
-
-Additionally, update notification APIs to allow drivers/mac80211 to
-pass the link ID.
-
-However, everything is handled at link 0 only until all API's are ready to
-handle it per link.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-6-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/marvell/mwifiex/11h.c
-+++ b/drivers/net/wireless/marvell/mwifiex/11h.c
-@@ -122,7 +122,7 @@ void mwifiex_dfs_cac_work_queue(struct w
- "CAC timer finished; No radar detected\n");
- cfg80211_cac_event(priv->netdev, &chandef,
- NL80211_RADAR_CAC_FINISHED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
- }
-
-@@ -182,7 +182,8 @@ void mwifiex_abort_cac(struct mwifiex_pr
- "Aborting delayed work for CAC.\n");
- cancel_delayed_work_sync(&priv->dfs_cac_work);
- cfg80211_cac_event(priv->netdev, &priv->dfs_chandef,
-- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
-+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL,
-+ 0);
- }
- }
-
-@@ -221,7 +222,7 @@ int mwifiex_11h_handle_chanrpt_ready(str
- cfg80211_cac_event(priv->netdev,
- &priv->dfs_chandef,
- NL80211_RADAR_DETECTED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
- break;
- default:
---- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
-+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
-@@ -4145,7 +4145,7 @@ static int
- mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms)
-+ u32 cac_time_ms, int link_id)
- {
- struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
- struct mwifiex_radar_params radar_params;
---- a/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/cfg80211.c
-@@ -837,7 +837,7 @@ static int qtnf_channel_switch(struct wi
- static int qtnf_start_radar_detection(struct wiphy *wiphy,
- struct net_device *ndev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms)
-+ u32 cac_time_ms, int link_id)
- {
- struct qtnf_vif *vif = qtnf_netdev_get_priv(ndev);
- int ret;
---- a/drivers/net/wireless/quantenna/qtnfmac/event.c
-+++ b/drivers/net/wireless/quantenna/qtnfmac/event.c
-@@ -524,14 +524,14 @@ static int qtnf_event_handle_radar(struc
- break;
-
- cfg80211_cac_event(vif->netdev, &chandef,
-- NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
-+ NL80211_RADAR_CAC_FINISHED, GFP_KERNEL, 0);
- break;
- case QLINK_RADAR_CAC_ABORTED:
- if (!vif->wdev.links[0].cac_started)
- break;
-
- cfg80211_cac_event(vif->netdev, &chandef,
-- NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
-+ NL80211_RADAR_CAC_ABORTED, GFP_KERNEL, 0);
- break;
- case QLINK_RADAR_CAC_STARTED:
- if (vif->wdev.links[0].cac_started)
-@@ -542,7 +542,7 @@ static int qtnf_event_handle_radar(struc
- break;
-
- cfg80211_cac_event(vif->netdev, &chandef,
-- NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
-+ NL80211_RADAR_CAC_STARTED, GFP_KERNEL, 0);
- break;
- default:
- pr_warn("%s: unhandled radar event %u\n",
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4841,9 +4841,9 @@ struct cfg80211_ops {
- int (*start_radar_detection)(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms);
-+ u32 cac_time_ms, int link_id);
- void (*end_cac)(struct wiphy *wiphy,
-- struct net_device *dev);
-+ struct net_device *dev, unsigned int link_id);
- int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_update_ft_ies_params *ftie);
- int (*crit_proto_start)(struct wiphy *wiphy,
-@@ -8745,6 +8745,7 @@ void cfg80211_sta_opmode_change_notify(s
- * @chandef: chandef for the current channel
- * @event: type of event
- * @gfp: context flags
-+ * @link_id: valid link_id for MLO operation or 0 otherwise.
- *
- * This function is called when a Channel availability check (CAC) is finished
- * or aborted. This must be called to notify the completion of a CAC process,
-@@ -8752,7 +8753,8 @@ void cfg80211_sta_opmode_change_notify(s
- */
- void cfg80211_cac_event(struct net_device *netdev,
- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event, gfp_t gfp);
-+ enum nl80211_radar_event event, gfp_t gfp,
-+ unsigned int link_id);
-
- /**
- * cfg80211_background_cac_abort - Channel Availability Check offchan abort event
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1661,7 +1661,7 @@ static int ieee80211_stop_ap(struct wiph
- wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_ABORTED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
-
- drv_stop_ap(sdata->local, sdata, link_conf);
-@@ -3459,7 +3459,7 @@ static int ieee80211_set_bitrate_mask(st
- static int ieee80211_start_radar_detection(struct wiphy *wiphy,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms)
-+ u32 cac_time_ms, int link_id)
- {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_chan_req chanreq = { .oper = *chandef };
-@@ -3487,7 +3487,7 @@ static int ieee80211_start_radar_detecti
- }
-
- static void ieee80211_end_cac(struct wiphy *wiphy,
-- struct net_device *dev)
-+ struct net_device *dev, unsigned int link_id)
- {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -559,7 +559,7 @@ static void ieee80211_do_stop(struct iee
- ieee80211_link_release_channel(&sdata->deflink);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_ABORTED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
-
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -3043,7 +3043,7 @@ void ieee80211_dfs_cac_timer_work(struct
- ieee80211_link_release_channel(link);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_FINISHED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
- }
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3468,7 +3468,7 @@ void ieee80211_dfs_cac_cancel(struct iee
- cfg80211_cac_event(sdata->dev,
- &chandef,
- NL80211_RADAR_CAC_ABORTED,
-- GFP_KERNEL);
-+ GFP_KERNEL, 0);
- }
- }
- }
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1111,18 +1111,19 @@ EXPORT_SYMBOL(__cfg80211_radar_event);
-
- void cfg80211_cac_event(struct net_device *netdev,
- const struct cfg80211_chan_def *chandef,
-- enum nl80211_radar_event event, gfp_t gfp)
-+ enum nl80211_radar_event event, gfp_t gfp,
-+ unsigned int link_id)
- {
- struct wireless_dev *wdev = netdev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- unsigned long timeout;
-
-- /* not yet supported */
-- if (wdev->valid_links)
-+ if (WARN_ON(wdev->valid_links &&
-+ !(wdev->valid_links & BIT(link_id))))
- return;
-
-- trace_cfg80211_cac_event(netdev, event);
-+ trace_cfg80211_cac_event(netdev, event, link_id);
-
- if (WARN_ON(!wdev->links[0].cac_started &&
- event != NL80211_RADAR_CAC_STARTED))
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -10122,7 +10122,20 @@ static int nl80211_start_radar_detection
- goto unlock;
- }
-
-- if (cfg80211_beaconing_iface_active(wdev) || wdev->links[0].cac_started) {
-+ if (cfg80211_beaconing_iface_active(wdev)) {
-+ /* During MLO other link(s) can beacon, only the current link
-+ * can not already beacon
-+ */
-+ if (wdev->valid_links &&
-+ !wdev->links[0].ap.beacon_interval) {
-+ /* nothing */
-+ } else {
-+ err = -EBUSY;
-+ goto unlock;
-+ }
-+ }
-+
-+ if (wdev->links[0].cac_started) {
- err = -EBUSY;
- goto unlock;
- }
-@@ -10142,7 +10155,8 @@ static int nl80211_start_radar_detection
- if (WARN_ON(!cac_time_ms))
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
-- err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms);
-+ err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
-+ 0);
- if (!err) {
- switch (wdev->iftype) {
- case NL80211_IFTYPE_AP:
-@@ -16512,10 +16526,10 @@ nl80211_set_ttlm(struct sk_buff *skb, st
- SELECTOR(__sel, NETDEV_UP_NOTMX, \
- NL80211_FLAG_NEED_NETDEV_UP | \
- NL80211_FLAG_NO_WIPHY_MTX) \
-- SELECTOR(__sel, NETDEV_UP_NOTMX_NOMLO, \
-+ SELECTOR(__sel, NETDEV_UP_NOTMX_MLO, \
- NL80211_FLAG_NEED_NETDEV_UP | \
- NL80211_FLAG_NO_WIPHY_MTX | \
-- NL80211_FLAG_MLO_UNSUPPORTED) \
-+ NL80211_FLAG_MLO_VALID_LINK_ID) \
- SELECTOR(__sel, NETDEV_UP_CLEAR, \
- NL80211_FLAG_NEED_NETDEV_UP | \
- NL80211_FLAG_CLEAR_SKB) \
-@@ -17410,7 +17424,7 @@ static const struct genl_small_ops nl802
- .flags = GENL_UNS_ADMIN_PERM,
- .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP |
- NL80211_FLAG_NO_WIPHY_MTX |
-- NL80211_FLAG_MLO_UNSUPPORTED),
-+ NL80211_FLAG_MLO_VALID_LINK_ID),
- },
- {
- .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -1200,26 +1200,27 @@ static inline int
- rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
- struct net_device *dev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms)
-+ u32 cac_time_ms, int link_id)
- {
- int ret = -EOPNOTSUPP;
-
- trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
-- cac_time_ms);
-+ cac_time_ms, link_id);
- if (rdev->ops->start_radar_detection)
- ret = rdev->ops->start_radar_detection(&rdev->wiphy, dev,
-- chandef, cac_time_ms);
-+ chandef, cac_time_ms,
-+ link_id);
- trace_rdev_return_int(&rdev->wiphy, ret);
- return ret;
- }
-
- static inline void
- rdev_end_cac(struct cfg80211_registered_device *rdev,
-- struct net_device *dev)
-+ struct net_device *dev, unsigned int link_id)
- {
-- trace_rdev_end_cac(&rdev->wiphy, dev);
-+ trace_rdev_end_cac(&rdev->wiphy, dev, link_id);
- if (rdev->ops->end_cac)
-- rdev->ops->end_cac(&rdev->wiphy, dev);
-+ rdev->ops->end_cac(&rdev->wiphy, dev, link_id);
- trace_rdev_return_void(&rdev->wiphy);
- }
-
---- a/net/wireless/reg.c
-+++ b/net/wireless/reg.c
-@@ -4229,6 +4229,8 @@ EXPORT_SYMBOL(regulatory_pre_cac_allowed
- static void cfg80211_check_and_end_cac(struct cfg80211_registered_device *rdev)
- {
- struct wireless_dev *wdev;
-+ unsigned int link_id;
-+
- /* If we finished CAC or received radar, we should end any
- * CAC running on the same channels.
- * the check !cfg80211_chandef_dfs_usable contain 2 options:
-@@ -4241,16 +4243,17 @@ static void cfg80211_check_and_end_cac(s
- list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
- struct cfg80211_chan_def *chandef;
-
-- if (!wdev->links[0].cac_started)
-- continue;
-+ for_each_valid_link(wdev, link_id) {
-+ if (!wdev->links[link_id].cac_started)
-+ continue;
-
-- /* FIXME: radar detection is tied to link 0 for now */
-- chandef = wdev_chandef(wdev, 0);
-- if (!chandef)
-- continue;
-+ chandef = wdev_chandef(wdev, link_id);
-+ if (!chandef)
-+ continue;
-
-- if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
-- rdev_end_cac(rdev, wdev->netdev);
-+ if (!cfg80211_chandef_dfs_usable(&rdev->wiphy, chandef))
-+ rdev_end_cac(rdev, wdev->netdev, link_id);
-+ }
- }
- }
-
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -806,17 +806,21 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_flus
- );
-
- TRACE_EVENT(rdev_end_cac,
-- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
-- TP_ARGS(wiphy, netdev),
-+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
-+ unsigned int link_id),
-+ TP_ARGS(wiphy, netdev, link_id),
- TP_STRUCT__entry(
- WIPHY_ENTRY
- NETDEV_ENTRY
-+ __field(unsigned int, link_id)
- ),
- TP_fast_assign(
- WIPHY_ASSIGN;
- NETDEV_ASSIGN;
-+ __entry->link_id = link_id;
- ),
-- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
-+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", link_id: %d",
-+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->link_id)
- );
-
- DECLARE_EVENT_CLASS(station_add_change,
-@@ -2661,24 +2665,26 @@ TRACE_EVENT(rdev_external_auth,
- TRACE_EVENT(rdev_start_radar_detection,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
- struct cfg80211_chan_def *chandef,
-- u32 cac_time_ms),
-- TP_ARGS(wiphy, netdev, chandef, cac_time_ms),
-+ u32 cac_time_ms, int link_id),
-+ TP_ARGS(wiphy, netdev, chandef, cac_time_ms, link_id),
- TP_STRUCT__entry(
- WIPHY_ENTRY
- NETDEV_ENTRY
- CHAN_DEF_ENTRY
- __field(u32, cac_time_ms)
-+ __field(int, link_id)
- ),
- TP_fast_assign(
- WIPHY_ASSIGN;
- NETDEV_ASSIGN;
- CHAN_DEF_ASSIGN(chandef);
- __entry->cac_time_ms = cac_time_ms;
-+ __entry->link_id = link_id;
- ),
- TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
-- ", cac_time_ms=%u",
-+ ", cac_time_ms=%u, link_id=%d",
- WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
-- __entry->cac_time_ms)
-+ __entry->cac_time_ms, __entry->link_id)
- );
-
- TRACE_EVENT(rdev_set_mcast_rate,
-@@ -3492,18 +3498,21 @@ TRACE_EVENT(cfg80211_radar_event,
- );
-
- TRACE_EVENT(cfg80211_cac_event,
-- TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt),
-- TP_ARGS(netdev, evt),
-+ TP_PROTO(struct net_device *netdev, enum nl80211_radar_event evt,
-+ unsigned int link_id),
-+ TP_ARGS(netdev, evt, link_id),
- TP_STRUCT__entry(
- NETDEV_ENTRY
- __field(enum nl80211_radar_event, evt)
-+ __field(unsigned int, link_id)
- ),
- TP_fast_assign(
- NETDEV_ASSIGN;
- __entry->evt = evt;
-+ __entry->link_id = link_id;
- ),
-- TP_printk(NETDEV_PR_FMT ", event: %d",
-- NETDEV_PR_ARG, __entry->evt)
-+ TP_printk(NETDEV_PR_FMT ", event: %d, link_id=%u",
-+ NETDEV_PR_ARG, __entry->evt, __entry->link_id)
- );
-
- DECLARE_EVENT_CLASS(cfg80211_rx_evt,
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 30 Sep 2024 17:05:18 +0200
+Subject: [PATCH] wifi: mac80211: add support for the monitor SKIP_TX flag
+
+Do not pass locally sent packets to monitor interfaces with this flag set.
+Skip processing tx packets on the status call entirely if no monitor
+interfaces without this flag are present.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/ieee80211_i.h
++++ b/net/mac80211/ieee80211_i.h
+@@ -1373,7 +1373,7 @@ struct ieee80211_local {
+ spinlock_t queue_stop_reason_lock;
+
+ int open_count;
+- int monitors, cooked_mntrs;
++ int monitors, cooked_mntrs, tx_mntrs;
+ /* number of interfaces with corresponding FIF_ flags */
+ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
+ fif_probe_req;
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -1094,6 +1094,8 @@ void ieee80211_adjust_monitor_flags(stru
+ ADJUST(CONTROL, control);
+ ADJUST(CONTROL, pspoll);
+ ADJUST(OTHER_BSS, other_bss);
++ if (!(flags & MONITOR_FLAG_SKIP_TX))
++ local->tx_mntrs += offset;
+
+ #undef ADJUST
+ }
+--- a/net/mac80211/status.c
++++ b/net/mac80211/status.c
+@@ -927,6 +927,9 @@ void ieee80211_tx_monitor(struct ieee802
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
++ if (sdata->u.mntr.flags & MONITOR_FLAG_SKIP_TX)
++ continue;
++
+ if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) &&
+ !send_to_cooked)
+ continue;
+@@ -1099,7 +1102,7 @@ static void __ieee80211_tx_status(struct
+ * This is a bit racy but we can avoid a lot of work
+ * with this test...
+ */
+- if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
++ if (!local->tx_mntrs && (!send_to_cooked || !local->cooked_mntrs)) {
+ if (status->free_list)
+ list_add_tail(&skb->list, status->free_list);
+ else
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:24 +0530
-Subject: [PATCH] wifi: mac80211: handle DFS per link
-
-In order to support DFS with MLO, handle the link ID now passed from
-cfg80211, adjust the code to do everything per link and call the
-notifications to cfg80211 correctly.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-7-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -3464,6 +3464,7 @@ static int ieee80211_start_radar_detecti
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_chan_req chanreq = { .oper = *chandef };
- struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_link_data *link_data;
- int err;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-@@ -3471,16 +3472,20 @@ static int ieee80211_start_radar_detecti
- if (!list_empty(&local->roc_list) || local->scanning)
- return -EBUSY;
-
-+ link_data = sdata_dereference(sdata->link[link_id], sdata);
-+ if (!link_data)
-+ return -ENOLINK;
-+
- /* whatever, but channel contexts should not complain about that one */
-- sdata->deflink.smps_mode = IEEE80211_SMPS_OFF;
-- sdata->deflink.needed_rx_chains = local->rx_chains;
-+ link_data->smps_mode = IEEE80211_SMPS_OFF;
-+ link_data->needed_rx_chains = local->rx_chains;
-
-- err = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
-+ err = ieee80211_link_use_channel(link_data, &chanreq,
- IEEE80211_CHANCTX_SHARED);
- if (err)
- return err;
-
-- wiphy_delayed_work_queue(wiphy, &sdata->deflink.dfs_cac_timer_work,
-+ wiphy_delayed_work_queue(wiphy, &link_data->dfs_cac_timer_work,
- msecs_to_jiffies(cac_time_ms));
-
- return 0;
-@@ -3491,16 +3496,21 @@ static void ieee80211_end_cac(struct wip
- {
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
-+ struct ieee80211_link_data *link_data;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
- list_for_each_entry(sdata, &local->interfaces, list) {
-+ link_data = sdata_dereference(sdata->link[link_id], sdata);
-+ if (!link_data)
-+ continue;
-+
- wiphy_delayed_work_cancel(wiphy,
-- &sdata->deflink.dfs_cac_timer_work);
-+ &link_data->dfs_cac_timer_work);
-
-- if (sdata->wdev.links[0].cac_started) {
-- ieee80211_link_release_channel(&sdata->deflink);
-- sdata->wdev.links[0].cac_started = false;
-+ if (sdata->wdev.links[link_id].cac_started) {
-+ ieee80211_link_release_channel(link_data);
-+ sdata->wdev.links[link_id].cac_started = false;
- }
- }
- }
---- a/net/mac80211/link.c
-+++ b/net/mac80211/link.c
-@@ -77,6 +77,16 @@ void ieee80211_link_stop(struct ieee8021
- &link->color_change_finalize_work);
- wiphy_work_cancel(link->sdata->local->hw.wiphy,
- &link->csa.finalize_work);
-+
-+ if (link->sdata->wdev.links[link->link_id].cac_started) {
-+ wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
-+ &link->dfs_cac_timer_work);
-+ cfg80211_cac_event(link->sdata->dev,
-+ &link->conf->chanreq.oper,
-+ NL80211_RADAR_CAC_ABORTED,
-+ GFP_KERNEL, link->link_id);
-+ }
-+
- ieee80211_link_release_channel(link);
- }
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3455,20 +3455,30 @@ void ieee80211_dfs_cac_cancel(struct iee
- {
- struct ieee80211_sub_if_data *sdata;
- struct cfg80211_chan_def chandef;
-+ struct ieee80211_link_data *link;
-+ unsigned int link_id;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
- list_for_each_entry(sdata, &local->interfaces, list) {
-- wiphy_delayed_work_cancel(local->hw.wiphy,
-- &sdata->deflink.dfs_cac_timer_work);
-+ for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS;
-+ link_id++) {
-+ link = sdata_dereference(sdata->link[link_id],
-+ sdata);
-+ if (!link)
-+ continue;
-
-- if (sdata->wdev.links[0].cac_started) {
-- chandef = sdata->vif.bss_conf.chanreq.oper;
-- ieee80211_link_release_channel(&sdata->deflink);
-- cfg80211_cac_event(sdata->dev,
-- &chandef,
-+ wiphy_delayed_work_cancel(local->hw.wiphy,
-+ &link->dfs_cac_timer_work);
-+
-+ if (!sdata->wdev.links[link_id].cac_started)
-+ continue;
-+
-+ chandef = link->conf->chanreq.oper;
-+ ieee80211_link_release_channel(link);
-+ cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_ABORTED,
-- GFP_KERNEL, 0);
-+ GFP_KERNEL, link_id);
- }
- }
- }
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 2 Oct 2024 12:31:22 +0200
+Subject: [PATCH] wifi: mac80211: refactor ieee80211_rx_monitor
+
+Rework the monitor mode interface iteration to get rid of the last_monitor
+condition. Preparation for further filtering received monitor packets.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -762,8 +762,8 @@ ieee80211_rx_monitor(struct ieee80211_lo
+ struct ieee80211_rate *rate)
+ {
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
+- struct ieee80211_sub_if_data *sdata;
+- struct sk_buff *monskb = NULL;
++ struct ieee80211_sub_if_data *sdata, *prev_sdata = NULL;
++ struct sk_buff *skb, *monskb = NULL;
+ int present_fcs_len = 0;
+ unsigned int rtap_space = 0;
+ struct ieee80211_sub_if_data *monitor_sdata =
+@@ -837,8 +837,10 @@ ieee80211_rx_monitor(struct ieee80211_lo
+ ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
+
+ list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
+- bool last_monitor = list_is_last(&sdata->u.mntr.list,
+- &local->mon_list);
++ if (!prev_sdata) {
++ prev_sdata = sdata;
++ continue;
++ }
+
+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
+@@ -846,34 +848,34 @@ ieee80211_rx_monitor(struct ieee80211_lo
+ if (!monskb)
+ monskb = ieee80211_make_monitor_skb(local, &origskb,
+ rate, rtap_space,
+- only_monitor &&
+- last_monitor);
+-
+- if (monskb) {
+- struct sk_buff *skb;
++ false);
++ if (!monskb)
++ continue;
+
+- if (last_monitor) {
+- skb = monskb;
+- monskb = NULL;
+- } else {
+- skb = skb_clone(monskb, GFP_ATOMIC);
+- }
++ skb = skb_clone(monskb, GFP_ATOMIC);
++ if (!skb)
++ continue;
++
++ skb->dev = prev_sdata->dev;
++ dev_sw_netstats_rx_add(skb->dev, skb->len);
++ netif_receive_skb(skb);
++ prev_sdata = sdata;
++ }
+
+- if (skb) {
+- skb->dev = sdata->dev;
+- dev_sw_netstats_rx_add(skb->dev, skb->len);
+- netif_receive_skb(skb);
+- }
++ if (prev_sdata) {
++ if (monskb)
++ skb = monskb;
++ else
++ skb = ieee80211_make_monitor_skb(local, &origskb,
++ rate, rtap_space,
++ only_monitor);
++ if (skb) {
++ skb->dev = prev_sdata->dev;
++ dev_sw_netstats_rx_add(skb->dev, skb->len);
++ netif_receive_skb(skb);
+ }
+-
+- if (last_monitor)
+- break;
+ }
+
+- /* this happens if last_monitor was erroneously false */
+- dev_kfree_skb(monskb);
+-
+- /* ditto */
+ if (!origskb)
+ return NULL;
+
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:25 +0530
-Subject: [PATCH] wifi: cfg80211/mac80211: use proper link ID for DFS
-
-Now that all APIs have support to handle DFS per link, use proper link ID
-instead of 0.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-8-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1656,12 +1656,12 @@ static int ieee80211_stop_ap(struct wiph
- ieee80211_link_info_change_notify(sdata, link,
- BSS_CHANGED_BEACON_ENABLED);
-
-- if (sdata->wdev.links[0].cac_started) {
-+ if (sdata->wdev.links[link_id].cac_started) {
- chandef = link_conf->chanreq.oper;
- wiphy_delayed_work_cancel(wiphy, &link->dfs_cac_timer_work);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_ABORTED,
-- GFP_KERNEL, 0);
-+ GFP_KERNEL, link_id);
- }
-
- drv_stop_ap(sdata->local, sdata, link_conf);
-@@ -3965,7 +3965,7 @@ __ieee80211_channel_switch(struct wiphy
- if (!list_empty(&local->roc_list) || local->scanning)
- return -EBUSY;
-
-- if (sdata->wdev.links[0].cac_started)
-+ if (sdata->wdev.links[link_id].cac_started)
- return -EBUSY;
-
- if (WARN_ON(link_id >= IEEE80211_MLD_MAX_NUM_LINKS))
---- a/net/mac80211/mlme.c
-+++ b/net/mac80211/mlme.c
-@@ -3039,11 +3039,11 @@ void ieee80211_dfs_cac_timer_work(struct
-
- lockdep_assert_wiphy(sdata->local->hw.wiphy);
-
-- if (sdata->wdev.links[0].cac_started) {
-+ if (sdata->wdev.links[link->link_id].cac_started) {
- ieee80211_link_release_channel(link);
- cfg80211_cac_event(sdata->dev, &chandef,
- NL80211_RADAR_CAC_FINISHED,
-- GFP_KERNEL, 0);
-+ GFP_KERNEL, link->link_id);
- }
- }
-
---- a/net/mac80211/scan.c
-+++ b/net/mac80211/scan.c
-@@ -575,6 +575,7 @@ static bool __ieee80211_can_leave_ch(str
- {
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_sub_if_data *sdata_iter;
-+ unsigned int link_id;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
-@@ -585,8 +586,9 @@ static bool __ieee80211_can_leave_ch(str
- return false;
-
- list_for_each_entry(sdata_iter, &local->interfaces, list) {
-- if (sdata_iter->wdev.links[0].cac_started)
-- return false;
-+ for_each_valid_link(&sdata_iter->wdev, link_id)
-+ if (sdata_iter->wdev.links[link_id].cac_started)
-+ return false;
- }
-
- return true;
---- a/net/wireless/mlme.c
-+++ b/net/wireless/mlme.c
-@@ -1125,14 +1125,14 @@ void cfg80211_cac_event(struct net_devic
-
- trace_cfg80211_cac_event(netdev, event, link_id);
-
-- if (WARN_ON(!wdev->links[0].cac_started &&
-+ if (WARN_ON(!wdev->links[link_id].cac_started &&
- event != NL80211_RADAR_CAC_STARTED))
- return;
-
- switch (event) {
- case NL80211_RADAR_CAC_FINISHED:
-- timeout = wdev->links[0].cac_start_time +
-- msecs_to_jiffies(wdev->links[0].cac_time_ms);
-+ timeout = wdev->links[link_id].cac_start_time +
-+ msecs_to_jiffies(wdev->links[link_id].cac_time_ms);
- WARN_ON(!time_after_eq(jiffies, timeout));
- cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
- memcpy(&rdev->cac_done_chandef, chandef,
-@@ -1141,10 +1141,10 @@ void cfg80211_cac_event(struct net_devic
- cfg80211_sched_dfs_chan_update(rdev);
- fallthrough;
- case NL80211_RADAR_CAC_ABORTED:
-- wdev->links[0].cac_started = false;
-+ wdev->links[link_id].cac_started = false;
- break;
- case NL80211_RADAR_CAC_STARTED:
-- wdev->links[0].cac_started = true;
-+ wdev->links[link_id].cac_started = true;
- break;
- default:
- WARN_ON(1);
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -6066,7 +6066,7 @@ static int nl80211_start_ap(struct sk_bu
- if (!rdev->ops->start_ap)
- return -EOPNOTSUPP;
-
-- if (wdev->links[0].cac_started)
-+ if (wdev->links[link_id].cac_started)
- return -EBUSY;
-
- if (wdev->links[link_id].ap.beacon_interval)
-@@ -10073,6 +10073,7 @@ static int nl80211_start_radar_detection
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct net_device *dev = info->user_ptr[1];
- struct wireless_dev *wdev = dev->ieee80211_ptr;
-+ int link_id = nl80211_link_id(info->attrs);
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_chan_def chandef;
- enum nl80211_dfs_regions dfs_region;
-@@ -10127,7 +10128,7 @@ static int nl80211_start_radar_detection
- * can not already beacon
- */
- if (wdev->valid_links &&
-- !wdev->links[0].ap.beacon_interval) {
-+ !wdev->links[link_id].ap.beacon_interval) {
- /* nothing */
- } else {
- err = -EBUSY;
-@@ -10135,7 +10136,7 @@ static int nl80211_start_radar_detection
- }
- }
-
-- if (wdev->links[0].cac_started) {
-+ if (wdev->links[link_id].cac_started) {
- err = -EBUSY;
- goto unlock;
- }
-@@ -10156,7 +10157,7 @@ static int nl80211_start_radar_detection
- cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
-
- err = rdev_start_radar_detection(rdev, dev, &chandef, cac_time_ms,
-- 0);
-+ link_id);
- if (!err) {
- switch (wdev->iftype) {
- case NL80211_IFTYPE_AP:
-@@ -10172,9 +10173,9 @@ static int nl80211_start_radar_detection
- default:
- break;
- }
-- wdev->links[0].cac_started = true;
-- wdev->links[0].cac_start_time = jiffies;
-- wdev->links[0].cac_time_ms = cac_time_ms;
-+ wdev->links[link_id].cac_started = true;
-+ wdev->links[link_id].cac_start_time = jiffies;
-+ wdev->links[link_id].cac_time_ms = cac_time_ms;
- }
- unlock:
- wiphy_unlock(wiphy);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 2 Oct 2024 12:35:13 +0200
+Subject: [PATCH] wifi: mac80211: filter on monitor interfaces based on
+ configured channel
+
+When a monitor interface has an assigned channel (only happens with the
+NO_VIRTUAL_MONITOR feature), only pass packets received on that channel.
+This is useful for monitoring on multiple channels at the same time using
+multiple monitor interfaces.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/mac80211/rx.c
++++ b/net/mac80211/rx.c
+@@ -837,6 +837,13 @@ ieee80211_rx_monitor(struct ieee80211_lo
+ ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
+
+ list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
++ struct cfg80211_chan_def *chandef;
++
++ chandef = &sdata->vif.bss_conf.chanreq.oper;
++ if (chandef->chan &&
++ chandef->chan->center_freq != status->freq)
++ continue;
++
+ if (!prev_sdata) {
+ prev_sdata = sdata;
+ continue;
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Wed, 7 Aug 2024 13:31:07 +0200
+Subject: [PATCH] wifi: cfg80211: report per wiphy radio antenna mask
+
+With multi-radio devices, each radio typically gets a fixed set of antennas.
+In order to be able to disable specific antennas for some radios, user space
+needs to know which antenna mask bits are assigned to which radio.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -5443,6 +5443,8 @@ struct wiphy_radio_freq_range {
+ * @iface_combinations: Valid interface combinations array, should not
+ * list single interface types.
+ * @n_iface_combinations: number of entries in @iface_combinations array.
++ *
++ * @antenna_mask: bitmask of antennas connected to this radio.
+ */
+ struct wiphy_radio {
+ const struct wiphy_radio_freq_range *freq_range;
+@@ -5450,6 +5452,8 @@ struct wiphy_radio {
+
+ const struct ieee80211_iface_combination *iface_combinations;
+ int n_iface_combinations;
++
++ u32 antenna_mask;
+ };
+
+ #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
+--- a/include/uapi/linux/nl80211.h
++++ b/include/uapi/linux/nl80211.h
+@@ -8038,6 +8038,8 @@ enum nl80211_ap_settings_flags {
+ * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
+ * combination for this radio. Attribute may be present multiple times
+ * and contains attributes defined in &enum nl80211_if_combination_attrs.
++ * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas
++ * connected to this radio.
+ *
+ * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
+ * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
+@@ -8048,6 +8050,7 @@ enum nl80211_wiphy_radio_attrs {
+ NL80211_WIPHY_RADIO_ATTR_INDEX,
+ NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
+ NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
++ NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
+
+ /* keep last */
+ __NL80211_WIPHY_RADIO_ATTR_LAST,
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -2431,6 +2431,11 @@ static int nl80211_put_radio(struct wiph
+ if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
+ goto nla_put_failure;
+
++ if (r->antenna_mask &&
++ nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
++ r->antenna_mask))
++ goto nla_put_failure;
++
+ for (i = 0; i < r->n_freq_range; i++) {
+ const struct wiphy_radio_freq_range *range = &r->freq_range[i];
+
+++ /dev/null
-From: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Date: Fri, 6 Sep 2024 12:14:26 +0530
-Subject: [PATCH] wifi: mac80211: handle ieee80211_radar_detected() for MLO
-
-Currently DFS works under assumption there could be only one channel
-context in the hardware. Hence, drivers just calls the function
-ieee80211_radar_detected() passing the hardware structure. However, with
-MLO, this obviously will not work since number of channel contexts will be
-more than one and hence drivers would need to pass the channel information
-as well on which the radar is detected.
-
-Also, when radar is detected in one of the links, other link's CAC should
-not be cancelled.
-
-Hence, in order to support DFS with MLO, do the following changes -
- * Add channel context conf pointer as an argument to the function
- ieee80211_radar_detected(). During MLO, drivers would have to pass on
- which channel context conf radar is detected. Otherwise, drivers could
- just pass NULL.
- * ieee80211_radar_detected() will iterate over all channel contexts
- present and
- * if channel context conf is passed, only mark that as radar
- detected
- * if NULL is passed, then mark all channel contexts as radar
- detected
- * Then as usual, schedule the radar detected work.
- * In the worker, go over all the contexts again and for all such context
- which is marked with radar detected, cancel the ongoing CAC by calling
- ieee80211_dfs_cac_cancel() and then notify cfg80211 via
- cfg80211_radar_event().
- * To cancel the CAC, pass the channel context as well where radar is
- detected to ieee80211_dfs_cac_cancel(). This ensures that CAC is
- canceled only on the links using the provided context, leaving other
- links unaffected.
-
-This would also help in scenarios where there is split phy 5 GHz radio,
-which is capable of DFS channels in both lower and upper band. In this
-case, simultaneous radars can be detected.
-
-Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
-Link: https://patch.msgid.link/20240906064426.2101315-9-quic_adisi@quicinc.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/drivers/net/wireless/ath/ath10k/debug.c
-+++ b/drivers/net/wireless/ath/ath10k/debug.c
-@@ -3,7 +3,7 @@
- * Copyright (c) 2005-2011 Atheros Communications Inc.
- * Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
-- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
-+ * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
-
- #include <linux/module.h>
-@@ -1774,7 +1774,7 @@ static ssize_t ath10k_write_simulate_rad
- if (!arvif->is_started)
- return -EINVAL;
-
-- ieee80211_radar_detected(ar->hw);
-+ ieee80211_radar_detected(ar->hw, NULL);
-
- return count;
- }
---- a/drivers/net/wireless/ath/ath10k/mac.c
-+++ b/drivers/net/wireless/ath/ath10k/mac.c
-@@ -1437,7 +1437,7 @@ static void ath10k_recalc_radar_detectio
- * by indicating that radar was detected.
- */
- ath10k_warn(ar, "failed to start CAC: %d\n", ret);
-- ieee80211_radar_detected(ar->hw);
-+ ieee80211_radar_detected(ar->hw, NULL);
- }
- }
-
---- a/drivers/net/wireless/ath/ath10k/wmi.c
-+++ b/drivers/net/wireless/ath/ath10k/wmi.c
-@@ -3990,7 +3990,7 @@ static void ath10k_radar_detected(struct
- if (ar->dfs_block_radar_events)
- ath10k_info(ar, "DFS Radar detected, but ignored as requested\n");
- else
-- ieee80211_radar_detected(ar->hw);
-+ ieee80211_radar_detected(ar->hw, NULL);
- }
-
- static void ath10k_radar_confirmation_work(struct work_struct *work)
---- a/drivers/net/wireless/ath/ath11k/wmi.c
-+++ b/drivers/net/wireless/ath/ath11k/wmi.c
-@@ -8358,7 +8358,7 @@ ath11k_wmi_pdev_dfs_radar_detected_event
- if (ar->dfs_block_radar_events)
- ath11k_info(ab, "DFS Radar detected, but ignored as requested\n");
- else
-- ieee80211_radar_detected(ar->hw);
-+ ieee80211_radar_detected(ar->hw, NULL);
-
- exit:
- rcu_read_unlock();
---- a/drivers/net/wireless/ath/ath12k/wmi.c
-+++ b/drivers/net/wireless/ath/ath12k/wmi.c
-@@ -6789,7 +6789,7 @@ ath12k_wmi_pdev_dfs_radar_detected_event
- if (ar->dfs_block_radar_events)
- ath12k_info(ab, "DFS Radar detected, but ignored as requested\n");
- else
-- ieee80211_radar_detected(ath12k_ar_to_hw(ar));
-+ ieee80211_radar_detected(ath12k_ar_to_hw(ar), NULL);
-
- exit:
- rcu_read_unlock();
---- a/drivers/net/wireless/ath/ath9k/dfs.c
-+++ b/drivers/net/wireless/ath/ath9k/dfs.c
-@@ -280,7 +280,7 @@ ath9k_dfs_process_radar_pulse(struct ath
- if (!pd->add_pulse(pd, pe, NULL))
- return;
- DFS_STAT_INC(sc, radar_detected);
-- ieee80211_radar_detected(sc->hw);
-+ ieee80211_radar_detected(sc->hw, NULL);
- }
-
- /*
---- a/drivers/net/wireless/ath/ath9k/dfs_debug.c
-+++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c
-@@ -116,7 +116,7 @@ static ssize_t write_file_simulate_radar
- {
- struct ath_softc *sc = file->private_data;
-
-- ieee80211_radar_detected(sc->hw);
-+ ieee80211_radar_detected(sc->hw, NULL);
-
- return count;
- }
---- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c
-@@ -394,7 +394,7 @@ mt7615_mcu_rx_radar_detected(struct mt76
- if (mt76_phy_dfs_state(mphy) < MT_DFS_STATE_CAC)
- return;
-
-- ieee80211_radar_detected(mphy->hw);
-+ ieee80211_radar_detected(mphy->hw, NULL);
- dev->hw_pattern++;
- }
-
---- a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c
-@@ -630,7 +630,7 @@ static void mt76x02_dfs_tasklet(struct t
- radar_detected = mt76x02_dfs_check_detection(dev);
- if (radar_detected) {
- /* sw detector rx radar pattern */
-- ieee80211_radar_detected(dev->mt76.hw);
-+ ieee80211_radar_detected(dev->mt76.hw, NULL);
- mt76x02_dfs_detector_reset(dev);
-
- return;
-@@ -658,7 +658,7 @@ static void mt76x02_dfs_tasklet(struct t
-
- /* hw detector rx radar pattern */
- dfs_pd->stats[i].hw_pattern++;
-- ieee80211_radar_detected(dev->mt76.hw);
-+ ieee80211_radar_detected(dev->mt76.hw, NULL);
- mt76x02_dfs_detector_reset(dev);
-
- return;
---- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c
-@@ -293,7 +293,7 @@ mt7915_mcu_rx_radar_detected(struct mt79
- &dev->rdd2_chandef,
- GFP_ATOMIC);
- else
-- ieee80211_radar_detected(mphy->hw);
-+ ieee80211_radar_detected(mphy->hw, NULL);
- dev->hw_pattern++;
- }
-
---- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
-+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
-@@ -371,7 +371,7 @@ mt7996_mcu_rx_radar_detected(struct mt79
- &dev->rdd2_chandef,
- GFP_ATOMIC);
- else
-- ieee80211_radar_detected(mphy->hw);
-+ ieee80211_radar_detected(mphy->hw, NULL);
- dev->hw_pattern++;
- }
-
---- a/drivers/net/wireless/ti/wl18xx/event.c
-+++ b/drivers/net/wireless/ti/wl18xx/event.c
-@@ -142,7 +142,7 @@ int wl18xx_process_mailbox_events(struct
- wl18xx_radar_type_decode(mbox->radar_type));
-
- if (!wl->radar_debug_mode)
-- ieee80211_radar_detected(wl->hw);
-+ ieee80211_radar_detected(wl->hw, NULL);
- }
-
- if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) {
---- a/drivers/net/wireless/virtual/mac80211_hwsim.c
-+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
-@@ -1146,7 +1146,7 @@ static int hwsim_write_simulate_radar(vo
- {
- struct mac80211_hwsim_data *data = dat;
-
-- ieee80211_radar_detected(data->hw);
-+ ieee80211_radar_detected(data->hw, NULL);
-
- return 0;
- }
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -6717,8 +6717,11 @@ void ieee80211_cqm_beacon_loss_notify(st
- * ieee80211_radar_detected - inform that a radar was detected
- *
- * @hw: pointer as obtained from ieee80211_alloc_hw()
-+ * @chanctx_conf: Channel context on which radar is detected. Mandatory to
-+ * pass a valid pointer during MLO. For non-MLO %NULL can be passed
- */
--void ieee80211_radar_detected(struct ieee80211_hw *hw);
-+void ieee80211_radar_detected(struct ieee80211_hw *hw,
-+ struct ieee80211_chanctx_conf *chanctx_conf);
-
- /**
- * ieee80211_chswitch_done - Complete channel switch process
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -681,6 +681,7 @@ ieee80211_alloc_chanctx(struct ieee80211
- ctx->mode = mode;
- ctx->conf.radar_enabled = false;
- ctx->conf.radio_idx = radio_idx;
-+ ctx->radar_detected = false;
- _ieee80211_recalc_chanctx_min_def(local, ctx, NULL, false);
-
- return ctx;
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -895,6 +895,8 @@ struct ieee80211_chanctx {
- struct ieee80211_chan_req req;
-
- struct ieee80211_chanctx_conf conf;
-+
-+ bool radar_detected;
- };
-
- struct mac80211_qos_map {
-@@ -2632,7 +2634,8 @@ void ieee80211_recalc_chanctx_min_def(st
- bool ieee80211_is_radar_required(struct ieee80211_local *local);
-
- void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work);
--void ieee80211_dfs_cac_cancel(struct ieee80211_local *local);
-+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
-+ struct ieee80211_chanctx *chanctx);
- void ieee80211_dfs_radar_detected_work(struct wiphy *wiphy,
- struct wiphy_work *work);
- int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
---- a/net/mac80211/pm.c
-+++ b/net/mac80211/pm.c
-@@ -32,7 +32,7 @@ int __ieee80211_suspend(struct ieee80211
-
- ieee80211_scan_cancel(local);
-
-- ieee80211_dfs_cac_cancel(local);
-+ ieee80211_dfs_cac_cancel(local, NULL);
-
- ieee80211_roc_purge(local, NULL);
-
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -3451,11 +3451,16 @@ u64 ieee80211_calculate_rx_timestamp(str
- return ts;
- }
-
--void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
-+/* Cancel CAC for the interfaces under the specified @local. If @ctx is
-+ * also provided, only the interfaces using that ctx will be canceled.
-+ */
-+void ieee80211_dfs_cac_cancel(struct ieee80211_local *local,
-+ struct ieee80211_chanctx *ctx)
- {
- struct ieee80211_sub_if_data *sdata;
- struct cfg80211_chan_def chandef;
- struct ieee80211_link_data *link;
-+ struct ieee80211_chanctx_conf *chanctx_conf;
- unsigned int link_id;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-@@ -3468,6 +3473,11 @@ void ieee80211_dfs_cac_cancel(struct iee
- if (!link)
- continue;
-
-+ chanctx_conf = sdata_dereference(link->conf->chanctx_conf,
-+ sdata);
-+ if (ctx && &ctx->conf != chanctx_conf)
-+ continue;
-+
- wiphy_delayed_work_cancel(local->hw.wiphy,
- &link->dfs_cac_timer_work);
-
-@@ -3488,9 +3498,8 @@ void ieee80211_dfs_radar_detected_work(s
- {
- struct ieee80211_local *local =
- container_of(work, struct ieee80211_local, radar_detected_work);
-- struct cfg80211_chan_def chandef = local->hw.conf.chandef;
-+ struct cfg80211_chan_def chandef;
- struct ieee80211_chanctx *ctx;
-- int num_chanctx = 0;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
-@@ -3498,25 +3507,46 @@ void ieee80211_dfs_radar_detected_work(s
- if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
- continue;
-
-- num_chanctx++;
-+ if (!ctx->radar_detected)
-+ continue;
-+
-+ ctx->radar_detected = false;
-+
- chandef = ctx->conf.def;
-+
-+ ieee80211_dfs_cac_cancel(local, ctx);
-+ cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
- }
-+}
-
-- ieee80211_dfs_cac_cancel(local);
-+static void
-+ieee80211_radar_mark_chan_ctx_iterator(struct ieee80211_hw *hw,
-+ struct ieee80211_chanctx_conf *chanctx_conf,
-+ void *data)
-+{
-+ struct ieee80211_chanctx *ctx =
-+ container_of(chanctx_conf, struct ieee80211_chanctx,
-+ conf);
-
-- if (num_chanctx > 1)
-- /* XXX: multi-channel is not supported yet */
-- WARN_ON(1);
-- else
-- cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
-+ if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
-+ return;
-+
-+ if (data && data != chanctx_conf)
-+ return;
-+
-+ ctx->radar_detected = true;
- }
-
--void ieee80211_radar_detected(struct ieee80211_hw *hw)
-+void ieee80211_radar_detected(struct ieee80211_hw *hw,
-+ struct ieee80211_chanctx_conf *chanctx_conf)
- {
- struct ieee80211_local *local = hw_to_local(hw);
-
- trace_api_radar_detected(local);
-
-+ ieee80211_iter_chan_contexts_atomic(hw, ieee80211_radar_mark_chan_ctx_iterator,
-+ chanctx_conf);
-+
- wiphy_work_queue(hw->wiphy, &local->radar_detected_work);
- }
- EXPORT_SYMBOL(ieee80211_radar_detected);
--- /dev/null
+From: Felix Fietkau <nbd@nbd.name>
+Date: Fri, 15 Nov 2024 12:28:43 +0100
+Subject: [PATCH] wifi: mac80211: fix vif addr when switching from monitor
+ to station
+
+Since adding support for opting out of virtual monitor support, a zero vif
+addr was used to indicate passive vs active monitor to the driver.
+This would break the vif->addr when changing the netdev mac address before
+switching the interface from monitor to sta mode.
+Fix the regression by adding a separate flag to indicate whether vif->addr
+is valid.
+
+Reported-by: syzbot+9ea265d998de25ac6a46@syzkaller.appspotmail.com
+Fixes: 9d40f7e32774 ("wifi: mac80211: add flag to opt out of virtual monitor support")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -1972,6 +1972,8 @@ enum ieee80211_neg_ttlm_res {
+ * @neg_ttlm: negotiated TID to link mapping info.
+ * see &struct ieee80211_neg_ttlm.
+ * @addr: address of this interface
++ * @addr_valid: indicates if the address is actively used. Set to false for
++ * passive monitor interfaces, true in all other cases.
+ * @p2p: indicates whether this AP or STA interface is a p2p
+ * interface, i.e. a GO or p2p-sta respectively
+ * @netdev_features: tx netdev features supported by the hardware for this
+@@ -2011,6 +2013,7 @@ struct ieee80211_vif {
+ u16 valid_links, active_links, dormant_links, suspended_links;
+ struct ieee80211_neg_ttlm neg_ttlm;
+ u8 addr[ETH_ALEN] __aligned(2);
++ bool addr_valid;
+ bool p2p;
+
+ u8 cab_queue;
+--- a/net/mac80211/iface.c
++++ b/net/mac80211/iface.c
+@@ -279,13 +279,8 @@ static int _ieee80211_change_mac(struct
+ ret = eth_mac_addr(sdata->dev, sa);
+
+ if (ret == 0) {
+- if (check_dup) {
+- memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
+- ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
+- } else {
+- memset(sdata->vif.addr, 0, ETH_ALEN);
+- memset(sdata->vif.bss_conf.addr, 0, ETH_ALEN);
+- }
++ memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
++ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
+ }
+
+ /* Regardless of eth_mac_addr() return we still want to add the
+@@ -1324,6 +1319,8 @@ int ieee80211_do_open(struct wireless_de
+ }
+ }
+
++ sdata->vif.addr_valid = sdata->vif.type != NL80211_IFTYPE_MONITOR ||
++ (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE);
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP_VLAN:
+ /* no need to tell driver, but set carrier and chanctx */
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Sun, 6 Oct 2024 17:34:08 +0200
-Subject: [PATCH] wifi: mac80211: skip non-uploaded keys in ieee80211_iter_keys
-
-Sync iterator conditions with ieee80211_iter_keys_rcu.
-
-Fixes: 830af02f24fb ("mac80211: allow driver to iterate keys")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/key.c
-+++ b/net/mac80211/key.c
-@@ -987,6 +987,26 @@ void ieee80211_reenable_keys(struct ieee
- }
- }
-
-+static void
-+ieee80211_key_iter(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif,
-+ struct ieee80211_key *key,
-+ void (*iter)(struct ieee80211_hw *hw,
-+ struct ieee80211_vif *vif,
-+ struct ieee80211_sta *sta,
-+ struct ieee80211_key_conf *key,
-+ void *data),
-+ void *iter_data)
-+{
-+ /* skip keys of station in removal process */
-+ if (key->sta && key->sta->removed)
-+ return;
-+ if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-+ return;
-+ iter(hw, vif, key->sta ? &key->sta->sta : NULL,
-+ &key->conf, iter_data);
-+}
-+
- void ieee80211_iter_keys(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- void (*iter)(struct ieee80211_hw *hw,
-@@ -1005,16 +1025,13 @@ void ieee80211_iter_keys(struct ieee8021
- if (vif) {
- sdata = vif_to_sdata(vif);
- list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
-- iter(hw, &sdata->vif,
-- key->sta ? &key->sta->sta : NULL,
-- &key->conf, iter_data);
-+ ieee80211_key_iter(hw, vif, key, iter, iter_data);
- } else {
- list_for_each_entry(sdata, &local->interfaces, list)
- list_for_each_entry_safe(key, tmp,
- &sdata->key_list, list)
-- iter(hw, &sdata->vif,
-- key->sta ? &key->sta->sta : NULL,
-- &key->conf, iter_data);
-+ ieee80211_key_iter(hw, &sdata->vif, key,
-+ iter, iter_data);
- }
- }
- EXPORT_SYMBOL(ieee80211_iter_keys);
-@@ -1031,17 +1048,8 @@ _ieee80211_iter_keys_rcu(struct ieee8021
- {
- struct ieee80211_key *key;
-
-- list_for_each_entry_rcu(key, &sdata->key_list, list) {
-- /* skip keys of station in removal process */
-- if (key->sta && key->sta->removed)
-- continue;
-- if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
-- continue;
--
-- iter(hw, &sdata->vif,
-- key->sta ? &key->sta->sta : NULL,
-- &key->conf, iter_data);
-- }
-+ list_for_each_entry_rcu(key, &sdata->key_list, list)
-+ ieee80211_key_iter(hw, &sdata->vif, key, iter, iter_data);
- }
-
- void ieee80211_iter_keys_rcu(struct ieee80211_hw *hw,
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 17 Jul 2024 15:43:52 +0200
-Subject: [PATCH] wifi: cfg80211: add option for vif allowed radios
-
-This allows users to prevent a vif from affecting radios other than the
-configured ones. This can be useful in cases where e.g. an AP is running
-on one radio, and triggering a scan on another radio should not disturb it.
-
-Changing the allowed radios list for a vif is supported, but only while
-it is down.
-
-While it is possible to achieve the same by always explicitly specifying
-a frequency list for scan requests and ensuring that the wrong channel/band
-is never accidentally set on an unrelated interface, this change makes
-multi-radio wiphy setups a lot easier to deal with for CLI users.
-
-By itself, this patch only enforces the radio mask for scanning requests
-and remain-on-channel. Follow-up changes build on this to limit configured
-frequencies.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6227,6 +6227,7 @@ enum ieee80211_ap_reg_power {
- * entered.
- * @links[].cac_time_ms: CAC time in ms
- * @valid_links: bitmap describing what elements of @links are valid
-+ * @radio_mask: Bitmask of radios that this interface is allowed to operate on.
- */
- struct wireless_dev {
- struct wiphy *wiphy;
-@@ -6339,6 +6340,8 @@ struct wireless_dev {
- unsigned int cac_time_ms;
- } links[IEEE80211_MLD_MAX_NUM_LINKS];
- u16 valid_links;
-+
-+ u32 radio_mask;
- };
-
- static inline const u8 *wdev_address(struct wireless_dev *wdev)
-@@ -6525,6 +6528,17 @@ bool cfg80211_radio_chandef_valid(const
- const struct cfg80211_chan_def *chandef);
-
- /**
-+ * cfg80211_wdev_channel_allowed - Check if the wdev may use the channel
-+ *
-+ * @wdev: the wireless device
-+ * @chan: channel to check
-+ *
-+ * Return: whether or not the wdev may use the channel
-+ */
-+bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
-+ struct ieee80211_channel *chan);
-+
-+/**
- * ieee80211_get_response_rate - get basic rate for a given rate
- *
- * @sband: the band to look for rates in
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -2868,6 +2868,9 @@ enum nl80211_commands {
- * nested item, it contains attributes defined in
- * &enum nl80211_if_combination_attrs.
- *
-+ * @NL80211_ATTR_VIF_RADIO_MASK: Bitmask of allowed radios (u32).
-+ * A value of 0 means all radios.
-+ *
- * @NUM_NL80211_ATTR: total number of nl80211_attrs available
- * @NL80211_ATTR_MAX: highest attribute number currently defined
- * @__NL80211_ATTR_AFTER_LAST: internal use
-@@ -3416,6 +3419,8 @@ enum nl80211_attrs {
- NL80211_ATTR_WIPHY_RADIOS,
- NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS,
-
-+ NL80211_ATTR_VIF_RADIO_MASK,
-+
- /* add attributes here, update the policy in nl80211.c */
-
- __NL80211_ATTR_AFTER_LAST,
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -829,6 +829,7 @@ static const struct nla_policy nl80211_p
- [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
- [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
- [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
-+ [NL80211_ATTR_VIF_RADIO_MASK] = { .type = NLA_U32 },
- };
-
- /* policy for the key attributes */
-@@ -3996,7 +3997,8 @@ static int nl80211_send_iface(struct sk_
- nla_put_u32(msg, NL80211_ATTR_GENERATION,
- rdev->devlist_generation ^
- (cfg80211_rdev_list_generation << 2)) ||
-- nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr))
-+ nla_put_u8(msg, NL80211_ATTR_4ADDR, wdev->use_4addr) ||
-+ nla_put_u32(msg, NL80211_ATTR_VIF_RADIO_MASK, wdev->radio_mask))
- goto nla_put_failure;
-
- if (rdev->ops->get_channel && !wdev->valid_links) {
-@@ -4312,6 +4314,29 @@ static int nl80211_valid_4addr(struct cf
- return -EOPNOTSUPP;
- }
-
-+static int nl80211_parse_vif_radio_mask(struct genl_info *info,
-+ u32 *radio_mask)
-+{
-+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
-+ struct nlattr *attr = info->attrs[NL80211_ATTR_VIF_RADIO_MASK];
-+ u32 mask, allowed;
-+
-+ if (!attr) {
-+ *radio_mask = 0;
-+ return 0;
-+ }
-+
-+ allowed = BIT(rdev->wiphy.n_radio) - 1;
-+ mask = nla_get_u32(attr);
-+ if (mask & ~allowed)
-+ return -EINVAL;
-+ if (!mask)
-+ mask = allowed;
-+ *radio_mask = mask;
-+
-+ return 1;
-+}
-+
- static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
- {
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
-@@ -4319,6 +4344,8 @@ static int nl80211_set_interface(struct
- int err;
- enum nl80211_iftype otype, ntype;
- struct net_device *dev = info->user_ptr[1];
-+ struct wireless_dev *wdev = dev->ieee80211_ptr;
-+ u32 radio_mask = 0;
- bool change = false;
-
- memset(¶ms, 0, sizeof(params));
-@@ -4332,8 +4359,6 @@ static int nl80211_set_interface(struct
- }
-
- if (info->attrs[NL80211_ATTR_MESH_ID]) {
-- struct wireless_dev *wdev = dev->ieee80211_ptr;
--
- if (ntype != NL80211_IFTYPE_MESH_POINT)
- return -EINVAL;
- if (otype != NL80211_IFTYPE_MESH_POINT)
-@@ -4364,6 +4389,12 @@ static int nl80211_set_interface(struct
- if (err > 0)
- change = true;
-
-+ err = nl80211_parse_vif_radio_mask(info, &radio_mask);
-+ if (err < 0)
-+ return err;
-+ if (err && netif_running(dev))
-+ return -EBUSY;
-+
- if (change)
- err = cfg80211_change_iface(rdev, dev, ntype, ¶ms);
- else
-@@ -4372,11 +4403,11 @@ static int nl80211_set_interface(struct
- if (!err && params.use_4addr != -1)
- dev->ieee80211_ptr->use_4addr = params.use_4addr;
-
-- if (change && !err) {
-- struct wireless_dev *wdev = dev->ieee80211_ptr;
-+ if (radio_mask)
-+ wdev->radio_mask = radio_mask;
-
-+ if (change && !err)
- nl80211_notify_iface(rdev, wdev, NL80211_CMD_SET_INTERFACE);
-- }
-
- return err;
- }
-@@ -4387,6 +4418,7 @@ static int _nl80211_new_interface(struct
- struct vif_params params;
- struct wireless_dev *wdev;
- struct sk_buff *msg;
-+ u32 radio_mask;
- int err;
- enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
-
-@@ -4424,6 +4456,10 @@ static int _nl80211_new_interface(struct
- if (err < 0)
- return err;
-
-+ err = nl80211_parse_vif_radio_mask(info, &radio_mask);
-+ if (err < 0)
-+ return err;
-+
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
- if (!msg)
- return -ENOMEM;
-@@ -4465,6 +4501,9 @@ static int _nl80211_new_interface(struct
- break;
- }
-
-+ if (radio_mask)
-+ wdev->radio_mask = radio_mask;
-+
- if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
- rdev, wdev, NL80211_CMD_NEW_INTERFACE) < 0) {
- nlmsg_free(msg);
-@@ -9180,6 +9219,9 @@ static bool cfg80211_off_channel_oper_al
-
- lockdep_assert_wiphy(wdev->wiphy);
-
-+ if (!cfg80211_wdev_channel_allowed(wdev, chan))
-+ return false;
-+
- if (!cfg80211_beaconing_iface_active(wdev))
- return true;
-
-@@ -9392,7 +9434,8 @@ static int nl80211_trigger_scan(struct s
- }
-
- /* ignore disabled channels */
-- if (chan->flags & IEEE80211_CHAN_DISABLED)
-+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
-+ !cfg80211_wdev_channel_allowed(wdev, chan))
- continue;
-
- request->channels[i] = chan;
-@@ -9412,7 +9455,8 @@ static int nl80211_trigger_scan(struct s
-
- chan = &wiphy->bands[band]->channels[j];
-
-- if (chan->flags & IEEE80211_CHAN_DISABLED)
-+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
-+ !cfg80211_wdev_channel_allowed(wdev, chan))
- continue;
-
- request->channels[i] = chan;
---- a/net/wireless/scan.c
-+++ b/net/wireless/scan.c
-@@ -956,7 +956,8 @@ static int cfg80211_scan_6ghz(struct cfg
- struct ieee80211_channel *chan =
- ieee80211_get_channel(&rdev->wiphy, ap->center_freq);
-
-- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
-+ if (!chan || chan->flags & IEEE80211_CHAN_DISABLED ||
-+ !cfg80211_wdev_channel_allowed(rdev_req->wdev, chan))
- continue;
-
- for (i = 0; i < rdev_req->n_channels; i++) {
-@@ -3490,9 +3491,12 @@ int cfg80211_wext_siwscan(struct net_dev
- continue;
-
- for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
-+ struct ieee80211_channel *chan;
-+
- /* ignore disabled channels */
-- if (wiphy->bands[band]->channels[j].flags &
-- IEEE80211_CHAN_DISABLED)
-+ chan = &wiphy->bands[band]->channels[j];
-+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
-+ !cfg80211_wdev_channel_allowed(creq->wdev, chan))
- continue;
-
- /* If we have a wireless request structure and the
---- a/net/wireless/util.c
-+++ b/net/wireless/util.c
-@@ -2923,3 +2923,32 @@ bool cfg80211_radio_chandef_valid(const
- return true;
- }
- EXPORT_SYMBOL(cfg80211_radio_chandef_valid);
-+
-+bool cfg80211_wdev_channel_allowed(struct wireless_dev *wdev,
-+ struct ieee80211_channel *chan)
-+{
-+ struct wiphy *wiphy = wdev->wiphy;
-+ const struct wiphy_radio *radio;
-+ struct cfg80211_chan_def chandef;
-+ u32 radio_mask;
-+ int i;
-+
-+ radio_mask = wdev->radio_mask;
-+ if (!wiphy->n_radio || radio_mask == BIT(wiphy->n_radio) - 1)
-+ return true;
-+
-+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
-+ for (i = 0; i < wiphy->n_radio; i++) {
-+ if (!(radio_mask & BIT(i)))
-+ continue;
-+
-+ radio = &wiphy->radio[i];
-+ if (!cfg80211_radio_chandef_valid(radio, &chandef))
-+ continue;
-+
-+ return true;
-+ }
-+
-+ return false;
-+}
-+EXPORT_SYMBOL(cfg80211_wdev_channel_allowed);
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -1415,6 +1415,8 @@ void cfg80211_init_wdev(struct wireless_
- /* allow mac80211 to determine the timeout */
- wdev->ps_timeout = -1;
-
-+ wdev->radio_mask = BIT(wdev->wiphy->n_radio) - 1;
-+
- if ((wdev->iftype == NL80211_IFTYPE_STATION ||
- wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
- wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 26 Sep 2024 14:06:11 +0200
-Subject: [PATCH] wifi: mac80211: use vif radio mask to limit ibss scan
- frequencies
-
-Reject frequencies not supported by any radio that the vif is allowed to use.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/scan.c
-+++ b/net/mac80211/scan.c
-@@ -1178,14 +1178,14 @@ int ieee80211_request_ibss_scan(struct i
- unsigned int n_channels)
- {
- struct ieee80211_local *local = sdata->local;
-- int ret = -EBUSY, i, n_ch = 0;
-+ int i, n_ch = 0;
- enum nl80211_band band;
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
- /* busy scanning */
- if (local->scan_req)
-- goto unlock;
-+ return -EBUSY;
-
- /* fill internal scan request */
- if (!channels) {
-@@ -1202,7 +1202,9 @@ int ieee80211_request_ibss_scan(struct i
- &local->hw.wiphy->bands[band]->channels[i];
-
- if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
-- IEEE80211_CHAN_DISABLED))
-+ IEEE80211_CHAN_DISABLED) ||
-+ !cfg80211_wdev_channel_allowed(&sdata->wdev,
-+ tmp_ch))
- continue;
-
- local->int_scan_req->channels[n_ch] = tmp_ch;
-@@ -1211,21 +1213,23 @@ int ieee80211_request_ibss_scan(struct i
- }
-
- if (WARN_ON_ONCE(n_ch == 0))
-- goto unlock;
-+ return -EINVAL;
-
- local->int_scan_req->n_channels = n_ch;
- } else {
- for (i = 0; i < n_channels; i++) {
- if (channels[i]->flags & (IEEE80211_CHAN_NO_IR |
-- IEEE80211_CHAN_DISABLED))
-+ IEEE80211_CHAN_DISABLED) ||
-+ !cfg80211_wdev_channel_allowed(&sdata->wdev,
-+ channels[i]))
- continue;
-
- local->int_scan_req->channels[n_ch] = channels[i];
- n_ch++;
- }
-
-- if (WARN_ON_ONCE(n_ch == 0))
-- goto unlock;
-+ if (n_ch == 0)
-+ return -EINVAL;
-
- local->int_scan_req->n_channels = n_ch;
- }
-@@ -1235,9 +1239,7 @@ int ieee80211_request_ibss_scan(struct i
- memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
- local->int_scan_req->ssids[0].ssid_len = ssid_len;
-
-- ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
-- unlock:
-- return ret;
-+ return __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
- }
-
- void ieee80211_scan_cancel(struct ieee80211_local *local)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 26 Sep 2024 14:07:50 +0200
-Subject: [PATCH] wifi: mac80211: use vif radio mask to limit creating chanctx
-
-Reject frequencies not supported by any radio that the vif is allowed to use.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -1167,7 +1167,7 @@ ieee80211_replace_chanctx(struct ieee802
- static bool
- ieee80211_find_available_radio(struct ieee80211_local *local,
- const struct ieee80211_chan_req *chanreq,
-- int *radio_idx)
-+ u32 radio_mask, int *radio_idx)
- {
- struct wiphy *wiphy = local->hw.wiphy;
- const struct wiphy_radio *radio;
-@@ -1178,6 +1178,9 @@ ieee80211_find_available_radio(struct ie
- return true;
-
- for (i = 0; i < wiphy->n_radio; i++) {
-+ if (!(radio_mask & BIT(i)))
-+ continue;
-+
- radio = &wiphy->radio[i];
- if (!cfg80211_radio_chandef_valid(radio, &chanreq->oper))
- continue;
-@@ -1211,7 +1214,9 @@ int ieee80211_link_reserve_chanctx(struc
- new_ctx = ieee80211_find_reservation_chanctx(local, chanreq, mode);
- if (!new_ctx) {
- if (ieee80211_can_create_new_chanctx(local, -1) &&
-- ieee80211_find_available_radio(local, chanreq, &radio_idx))
-+ ieee80211_find_available_radio(local, chanreq,
-+ sdata->wdev.radio_mask,
-+ &radio_idx))
- new_ctx = ieee80211_new_chanctx(local, chanreq, mode,
- false, radio_idx);
- else
-@@ -1881,7 +1886,9 @@ int _ieee80211_link_use_channel(struct i
- /* Note: context is now reserved */
- if (ctx)
- reserved = true;
-- else if (!ieee80211_find_available_radio(local, chanreq, &radio_idx))
-+ else if (!ieee80211_find_available_radio(local, chanreq,
-+ sdata->wdev.radio_mask,
-+ &radio_idx))
- ctx = ERR_PTR(-EBUSY);
- else
- ctx = ieee80211_new_chanctx(local, chanreq, mode,
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 17 Jul 2024 22:49:16 +0200
-Subject: [PATCH] wifi: mac80211: remove status->ampdu_delimiter_crc
-
-This was never used by any driver, so remove it to free up some space.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1448,8 +1448,6 @@ ieee80211_tx_info_clear_status(struct ie
- * @RX_FLAG_AMPDU_IS_LAST: this subframe is the last subframe of the A-MPDU
- * @RX_FLAG_AMPDU_DELIM_CRC_ERROR: A delimiter CRC error has been detected
- * on this subframe
-- * @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
-- * is stored in the @ampdu_delimiter_crc field)
- * @RX_FLAG_MIC_STRIPPED: The mic was stripped of this packet. Decryption was
- * done by the hardware
- * @RX_FLAG_ONLY_MONITOR: Report frame only to monitor interfaces without
-@@ -1521,7 +1519,7 @@ enum mac80211_rx_flags {
- RX_FLAG_AMPDU_LAST_KNOWN = BIT(12),
- RX_FLAG_AMPDU_IS_LAST = BIT(13),
- RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(14),
-- RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(15),
-+ /* one free bit at 15 */
- RX_FLAG_MACTIME = BIT(16) | BIT(17),
- RX_FLAG_MACTIME_PLCP_START = 1 << 16,
- RX_FLAG_MACTIME_START = 2 << 16,
-@@ -1618,7 +1616,6 @@ enum mac80211_rx_encoding {
- * @rx_flags: internal RX flags for mac80211
- * @ampdu_reference: A-MPDU reference number, must be a different value for
- * each A-MPDU but the same for each subframe within one A-MPDU
-- * @ampdu_delimiter_crc: A-MPDU delimiter CRC
- * @zero_length_psdu_type: radiotap type of the 0-length PSDU
- * @link_valid: if the link which is identified by @link_id is valid. This flag
- * is set only when connection is MLO.
-@@ -1656,7 +1653,6 @@ struct ieee80211_rx_status {
- s8 signal;
- u8 chains;
- s8 chain_signal[IEEE80211_MAX_CHAINS];
-- u8 ampdu_delimiter_crc;
- u8 zero_length_psdu_type;
- u8 link_valid:1, link_id:4;
- };
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -508,18 +508,13 @@ ieee80211_add_rx_radiotap_header(struct
- flags |= IEEE80211_RADIOTAP_AMPDU_IS_LAST;
- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_ERROR)
- flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR;
-- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
-- flags |= IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN;
- if (status->flag & RX_FLAG_AMPDU_EOF_BIT_KNOWN)
- flags |= IEEE80211_RADIOTAP_AMPDU_EOF_KNOWN;
- if (status->flag & RX_FLAG_AMPDU_EOF_BIT)
- flags |= IEEE80211_RADIOTAP_AMPDU_EOF;
- put_unaligned_le16(flags, pos);
- pos += 2;
-- if (status->flag & RX_FLAG_AMPDU_DELIM_CRC_KNOWN)
-- *pos++ = status->ampdu_delimiter_crc;
-- else
-- *pos++ = 0;
-+ *pos++ = 0;
- *pos++ = 0;
- }
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Thu, 26 Sep 2024 19:52:30 +0200
-Subject: [PATCH] wifi: cfg80211: pass net_device to .set_monitor_channel
-
-Preparation for allowing multiple monitor interfaces with different channels
-on a multi-radio wiphy.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/ath/wil6210/cfg80211.c
-+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
-@@ -1493,6 +1493,7 @@ out:
- }
-
- static int wil_cfg80211_set_channel(struct wiphy *wiphy,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- struct wil6210_priv *wil = wiphy_to_wil(wiphy);
---- a/drivers/net/wireless/marvell/libertas/cfg.c
-+++ b/drivers/net/wireless/marvell/libertas/cfg.c
-@@ -486,6 +486,7 @@ static int lbs_add_wps_enrollee_tlv(u8 *
- */
-
- static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- struct lbs_private *priv = wiphy_priv(wiphy);
---- a/drivers/net/wireless/microchip/wilc1000/cfg80211.c
-+++ b/drivers/net/wireless/microchip/wilc1000/cfg80211.c
-@@ -231,6 +231,7 @@ struct wilc_vif *wilc_get_wl_to_vif(stru
- }
-
- static int set_channel(struct wiphy *wiphy,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- struct wilc *wl = wiphy_priv(wiphy);
-@@ -1424,7 +1425,7 @@ static int start_ap(struct wiphy *wiphy,
- struct wilc_vif *vif = netdev_priv(dev);
- int ret;
-
-- ret = set_channel(wiphy, &settings->chandef);
-+ ret = set_channel(wiphy, dev, &settings->chandef);
- if (ret != 0)
- netdev_err(dev, "Error in setting channel\n");
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -4700,6 +4700,7 @@ struct cfg80211_ops {
- struct ieee80211_channel *chan);
-
- int (*set_monitor_channel)(struct wiphy *wiphy,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef);
-
- int (*scan)(struct wiphy *wiphy,
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -879,6 +879,7 @@ static int ieee80211_get_station(struct
- }
-
- static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- struct ieee80211_local *local = wiphy_priv(wiphy);
---- a/net/wireless/chan.c
-+++ b/net/wireless/chan.c
-@@ -1673,6 +1673,7 @@ bool cfg80211_reg_check_beaconing(struct
- EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
-
- int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- if (!rdev->ops->set_monitor_channel)
-@@ -1680,7 +1681,7 @@ int cfg80211_set_monitor_channel(struct
- if (!cfg80211_has_monitors_only(rdev))
- return -EBUSY;
-
-- return rdev_set_monitor_channel(rdev, chandef);
-+ return rdev_set_monitor_channel(rdev, dev, chandef);
- }
-
- bool cfg80211_any_usable_channels(struct wiphy *wiphy,
---- a/net/wireless/core.h
-+++ b/net/wireless/core.h
-@@ -510,6 +510,7 @@ static inline unsigned int elapsed_jiffi
- }
-
- int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef);
-
- int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -3562,7 +3562,7 @@ static int __nl80211_set_channel(struct
- case NL80211_IFTYPE_MESH_POINT:
- return cfg80211_set_mesh_channel(rdev, wdev, &chandef);
- case NL80211_IFTYPE_MONITOR:
-- return cfg80211_set_monitor_channel(rdev, &chandef);
-+ return cfg80211_set_monitor_channel(rdev, dev, &chandef);
- default:
- break;
- }
---- a/net/wireless/rdev-ops.h
-+++ b/net/wireless/rdev-ops.h
-@@ -445,11 +445,12 @@ rdev_libertas_set_mesh_channel(struct cf
-
- static inline int
- rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
-+ struct net_device *dev,
- struct cfg80211_chan_def *chandef)
- {
- int ret;
-- trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
-- ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
-+ trace_rdev_set_monitor_channel(&rdev->wiphy, dev, chandef);
-+ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, dev, chandef);
- trace_rdev_return_int(&rdev->wiphy, ret);
- return ret;
- }
---- a/net/wireless/trace.h
-+++ b/net/wireless/trace.h
-@@ -1318,19 +1318,21 @@ TRACE_EVENT(rdev_libertas_set_mesh_chann
- );
-
- TRACE_EVENT(rdev_set_monitor_channel,
-- TP_PROTO(struct wiphy *wiphy,
-+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
- struct cfg80211_chan_def *chandef),
-- TP_ARGS(wiphy, chandef),
-+ TP_ARGS(wiphy, netdev, chandef),
- TP_STRUCT__entry(
- WIPHY_ENTRY
-+ NETDEV_ENTRY
- CHAN_DEF_ENTRY
- ),
- TP_fast_assign(
- WIPHY_ASSIGN;
-+ NETDEV_ASSIGN;
- CHAN_DEF_ASSIGN(chandef);
- ),
-- TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
-- WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
-+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
-+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
- );
-
- TRACE_EVENT(rdev_auth,
---- a/net/wireless/wext-compat.c
-+++ b/net/wireless/wext-compat.c
-@@ -830,7 +830,7 @@ static int cfg80211_wext_siwfreq(struct
- ret = -EINVAL;
- break;
- }
-- ret = cfg80211_set_monitor_channel(rdev, &chandef);
-+ ret = cfg80211_set_monitor_channel(rdev, dev, &chandef);
- break;
- case NL80211_IFTYPE_MESH_POINT:
- freq = cfg80211_wext_freq(wextfreq);
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 30 Sep 2024 15:09:45 +0200
-Subject: [PATCH] wifi: mac80211: add flag to opt out of virtual monitor
- support
-
-This is useful for multi-radio devices that are capable of monitoring on
-multiple channels simultanenously. When this flag is set, each monitor
-interface is passed to the driver individually and can have a configured
-channel.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -2679,6 +2679,11 @@ struct ieee80211_txq {
- * a virtual monitor interface when monitor interfaces are the only
- * active interfaces.
- *
-+ * @IEEE80211_HW_NO_VIRTUAL_MONITOR: The driver would like to be informed
-+ * of any monitor interface, as well as their configured channel.
-+ * This is useful for supporting multiple monitor interfaces on different
-+ * channels.
-+ *
- * @IEEE80211_HW_NO_AUTO_VIF: The driver would like for no wlanX to
- * be created. It is expected user-space will create vifs as
- * desired (and thus have them named as desired).
-@@ -2838,6 +2843,7 @@ enum ieee80211_hw_flags {
- IEEE80211_HW_SUPPORTS_DYNAMIC_PS,
- IEEE80211_HW_MFP_CAPABLE,
- IEEE80211_HW_WANT_MONITOR_VIF,
-+ IEEE80211_HW_NO_VIRTUAL_MONITOR,
- IEEE80211_HW_NO_AUTO_VIF,
- IEEE80211_HW_SW_CRYPTO_CONTROL,
- IEEE80211_HW_SUPPORT_FAST_XMIT,
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -105,8 +105,11 @@ static int ieee80211_set_mon_options(str
- }
-
- /* also validate MU-MIMO change */
-- monitor_sdata = wiphy_dereference(local->hw.wiphy,
-- local->monitor_sdata);
-+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ monitor_sdata = sdata;
-+ else
-+ monitor_sdata = wiphy_dereference(local->hw.wiphy,
-+ local->monitor_sdata);
-
- if (!monitor_sdata &&
- (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
-@@ -114,7 +117,9 @@ static int ieee80211_set_mon_options(str
-
- /* apply all changes now - no failures allowed */
-
-- if (monitor_sdata && ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
-+ if (monitor_sdata &&
-+ (ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) ||
-+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)))
- ieee80211_set_mu_mimo_follow(monitor_sdata, params);
-
- if (params->flags) {
-@@ -889,22 +894,25 @@ static int ieee80211_set_monitor_channel
-
- lockdep_assert_wiphy(local->hw.wiphy);
-
-- if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
-- &chanreq.oper))
-- return 0;
-+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-+ if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
-+ if (cfg80211_chandef_identical(&local->monitor_chanreq.oper,
-+ &chanreq.oper))
-+ return 0;
-
-- sdata = wiphy_dereference(local->hw.wiphy,
-- local->monitor_sdata);
-- if (!sdata)
-- goto done;
-+ sdata = wiphy_dereference(wiphy, local->monitor_sdata);
-+ if (!sdata)
-+ goto done;
-+ }
-
-- if (cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
-+ if (rcu_access_pointer(sdata->deflink.conf->chanctx_conf) &&
-+ cfg80211_chandef_identical(&sdata->vif.bss_conf.chanreq.oper,
- &chanreq.oper))
- return 0;
-
- ieee80211_link_release_channel(&sdata->deflink);
- ret = ieee80211_link_use_channel(&sdata->deflink, &chanreq,
-- IEEE80211_CHANCTX_EXCLUSIVE);
-+ IEEE80211_CHANCTX_SHARED);
- if (ret)
- return ret;
- done:
-@@ -3049,7 +3057,8 @@ static int ieee80211_set_tx_power(struct
- if (wdev) {
- sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
-
-- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
- return -EOPNOTSUPP;
-
-@@ -3097,7 +3106,8 @@ static int ieee80211_set_tx_power(struct
- }
-
- list_for_each_entry(sdata, &local->interfaces, list) {
-- if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- has_monitor = true;
- continue;
- }
-@@ -3107,7 +3117,8 @@ static int ieee80211_set_tx_power(struct
- sdata->vif.bss_conf.txpower_type = txp_type;
- }
- list_for_each_entry(sdata, &local->interfaces, list) {
-- if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- continue;
- ieee80211_recalc_txpower(sdata, update_txp_type);
- }
-@@ -4299,7 +4310,8 @@ static int ieee80211_cfg_get_channel(str
- if (chanctx_conf) {
- *chandef = link->conf->chanreq.oper;
- ret = 0;
-- } else if (local->open_count > 0 &&
-+ } else if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
-+ local->open_count > 0 &&
- local->open_count == local->monitors &&
- sdata->vif.type == NL80211_IFTYPE_MONITOR) {
- *chandef = local->monitor_chanreq.oper;
---- a/net/mac80211/chan.c
-+++ b/net/mac80211/chan.c
-@@ -337,6 +337,10 @@ ieee80211_get_chanctx_max_required_bw(st
- case NL80211_IFTYPE_P2P_DEVICE:
- case NL80211_IFTYPE_NAN:
- continue;
-+ case NL80211_IFTYPE_MONITOR:
-+ WARN_ON_ONCE(!ieee80211_hw_check(&local->hw,
-+ NO_VIRTUAL_MONITOR));
-+ fallthrough;
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
- case NL80211_IFTYPE_OCB:
-@@ -345,7 +349,6 @@ ieee80211_get_chanctx_max_required_bw(st
- case NL80211_IFTYPE_WDS:
- case NL80211_IFTYPE_UNSPECIFIED:
- case NUM_NL80211_IFTYPES:
-- case NL80211_IFTYPE_MONITOR:
- case NL80211_IFTYPE_P2P_CLIENT:
- case NL80211_IFTYPE_P2P_GO:
- WARN_ON_ONCE(1);
-@@ -954,6 +957,10 @@ void ieee80211_recalc_smps_chanctx(struc
- if (!link->sdata->u.mgd.associated)
- continue;
- break;
-+ case NL80211_IFTYPE_MONITOR:
-+ if (!ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ continue;
-+ break;
- case NL80211_IFTYPE_AP:
- case NL80211_IFTYPE_ADHOC:
- case NL80211_IFTYPE_MESH_POINT:
-@@ -966,6 +973,11 @@ void ieee80211_recalc_smps_chanctx(struc
- if (rcu_access_pointer(link->conf->chanctx_conf) != &chanctx->conf)
- continue;
-
-+ if (link->sdata->vif.type == NL80211_IFTYPE_MONITOR) {
-+ rx_chains_dynamic = rx_chains_static = local->rx_chains;
-+ break;
-+ }
-+
- switch (link->smps_mode) {
- default:
- WARN_ONCE(1, "Invalid SMPS mode %d\n",
---- a/net/mac80211/debugfs.c
-+++ b/net/mac80211/debugfs.c
-@@ -465,6 +465,7 @@ static const char *hw_flag_names[] = {
- FLAG(SUPPORTS_DYNAMIC_PS),
- FLAG(MFP_CAPABLE),
- FLAG(WANT_MONITOR_VIF),
-+ FLAG(NO_VIRTUAL_MONITOR),
- FLAG(NO_AUTO_VIF),
- FLAG(SW_CRYPTO_CONTROL),
- FLAG(SUPPORT_FAST_XMIT),
---- a/net/mac80211/driver-ops.c
-+++ b/net/mac80211/driver-ops.c
-@@ -65,6 +65,7 @@ int drv_add_interface(struct ieee80211_l
- if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
- (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
- !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR) &&
- !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
- return -EINVAL;
-
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -279,8 +279,13 @@ static int _ieee80211_change_mac(struct
- ret = eth_mac_addr(sdata->dev, sa);
-
- if (ret == 0) {
-- memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
-- ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
-+ if (check_dup) {
-+ memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
-+ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
-+ } else {
-+ memset(sdata->vif.addr, 0, ETH_ALEN);
-+ memset(sdata->vif.bss_conf.addr, 0, ETH_ALEN);
-+ }
- }
-
- /* Regardless of eth_mac_addr() return we still want to add the
-@@ -699,9 +704,11 @@ static void ieee80211_do_stop(struct iee
- ieee80211_recalc_idle(local);
- ieee80211_recalc_offload(local);
-
-- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
-+ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- break;
-
-+ ieee80211_link_release_channel(&sdata->deflink);
- fallthrough;
- default:
- if (!going_down)
-@@ -1131,7 +1138,8 @@ int ieee80211_add_virtual_monitor(struct
- ASSERT_RTNL();
- lockdep_assert_wiphy(local->hw.wiphy);
-
-- if (local->monitor_sdata)
-+ if (local->monitor_sdata ||
-+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- return 0;
-
- sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
-@@ -1193,6 +1201,9 @@ void ieee80211_del_virtual_monitor(struc
- {
- struct ieee80211_sub_if_data *sdata;
-
-+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ return;
-+
- ASSERT_RTNL();
- lockdep_assert_wiphy(local->hw.wiphy);
-
-@@ -1328,7 +1339,8 @@ int ieee80211_do_open(struct wireless_de
- break;
- }
-
-- if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
-+ if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
-+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- res = drv_add_interface(local, sdata);
- if (res)
- goto err_stop;
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -840,6 +840,9 @@ ieee80211_rx_monitor(struct ieee80211_lo
- bool last_monitor = list_is_last(&sdata->u.mntr.list,
- &local->mon_list);
-
-+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
-+
- if (!monskb)
- monskb = ieee80211_make_monitor_skb(local, &origskb,
- rate, rtap_space,
---- a/net/mac80211/tx.c
-+++ b/net/mac80211/tx.c
-@@ -1763,7 +1763,8 @@ static bool __ieee80211_tx(struct ieee80
-
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
-- if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
-+ if ((sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
-+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- vif = &sdata->vif;
- break;
- }
-@@ -3952,7 +3953,8 @@ begin:
-
- switch (tx.sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
-- if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
-+ if ((tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) ||
-+ ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR)) {
- vif = &tx.sdata->vif;
- break;
- }
---- a/net/mac80211/util.c
-+++ b/net/mac80211/util.c
-@@ -754,7 +754,8 @@ static void __iterate_interfaces(struct
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
-- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
-+ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- continue;
- break;
- case NL80211_IFTYPE_AP_VLAN:
-@@ -1857,8 +1858,10 @@ int ieee80211_reconfig(struct ieee80211_
- }
-
- list_for_each_entry(sdata, &local->interfaces, list) {
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ continue;
- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
-- sdata->vif.type != NL80211_IFTYPE_MONITOR &&
- ieee80211_sdata_running(sdata)) {
- res = drv_add_interface(local, sdata);
- if (WARN_ON(res))
-@@ -1871,11 +1874,14 @@ int ieee80211_reconfig(struct ieee80211_
- */
- if (res) {
- list_for_each_entry_continue_reverse(sdata, &local->interfaces,
-- list)
-+ list) {
-+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
-+ !ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
-+ continue;
- if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
-- sdata->vif.type != NL80211_IFTYPE_MONITOR &&
- ieee80211_sdata_running(sdata))
- drv_remove_interface(local, sdata);
-+ }
- ieee80211_handle_reconfig_failure(local);
- return res;
- }
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 30 Sep 2024 17:04:09 +0200
-Subject: [PATCH] wifi: cfg80211: add monitor SKIP_TX flag
-
-This can be used to indicate that the user is not interested in receiving
-locally sent packets on the monitor interface.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -2272,6 +2272,7 @@ static inline int cfg80211_get_station(s
- * @MONITOR_FLAG_OTHER_BSS: disable BSSID filtering
- * @MONITOR_FLAG_COOK_FRAMES: report frames after processing
- * @MONITOR_FLAG_ACTIVE: active monitor, ACKs frames on its MAC address
-+ * @MONITOR_FLAG_SKIP_TX: do not pass locally transmitted frames
- */
- enum monitor_flags {
- MONITOR_FLAG_CHANGED = BIT(__NL80211_MNTR_FLAG_INVALID),
-@@ -2281,6 +2282,7 @@ enum monitor_flags {
- MONITOR_FLAG_OTHER_BSS = BIT(NL80211_MNTR_FLAG_OTHER_BSS),
- MONITOR_FLAG_COOK_FRAMES = BIT(NL80211_MNTR_FLAG_COOK_FRAMES),
- MONITOR_FLAG_ACTIVE = BIT(NL80211_MNTR_FLAG_ACTIVE),
-+ MONITOR_FLAG_SKIP_TX = BIT(NL80211_MNTR_FLAG_SKIP_TX),
- };
-
- /**
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -4703,6 +4703,7 @@ enum nl80211_survey_info {
- * overrides all other flags.
- * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
- * and ACK incoming unicast packets.
-+ * @NL80211_MNTR_FLAG_SKIP_TX: do not pass local tx packets
- *
- * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
- * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
-@@ -4715,6 +4716,7 @@ enum nl80211_mntr_flags {
- NL80211_MNTR_FLAG_OTHER_BSS,
- NL80211_MNTR_FLAG_COOK_FRAMES,
- NL80211_MNTR_FLAG_ACTIVE,
-+ NL80211_MNTR_FLAG_SKIP_TX,
-
- /* keep last */
- __NL80211_MNTR_FLAG_AFTER_LAST,
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -4201,6 +4201,7 @@ static const struct nla_policy mntr_flag
- [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
- [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
- [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG },
-+ [NL80211_MNTR_FLAG_SKIP_TX] = { .type = NLA_FLAG },
- };
-
- static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Mon, 30 Sep 2024 17:05:18 +0200
-Subject: [PATCH] wifi: mac80211: add support for the monitor SKIP_TX flag
-
-Do not pass locally sent packets to monitor interfaces with this flag set.
-Skip processing tx packets on the status call entirely if no monitor
-interfaces without this flag are present.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1374,7 +1374,7 @@ struct ieee80211_local {
- spinlock_t queue_stop_reason_lock;
-
- int open_count;
-- int monitors, cooked_mntrs;
-+ int monitors, cooked_mntrs, tx_mntrs;
- /* number of interfaces with corresponding FIF_ flags */
- int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll,
- fif_probe_req;
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -1094,6 +1094,8 @@ void ieee80211_adjust_monitor_flags(stru
- ADJUST(CONTROL, control);
- ADJUST(CONTROL, pspoll);
- ADJUST(OTHER_BSS, other_bss);
-+ if (!(flags & MONITOR_FLAG_SKIP_TX))
-+ local->tx_mntrs += offset;
-
- #undef ADJUST
- }
---- a/net/mac80211/status.c
-+++ b/net/mac80211/status.c
-@@ -927,6 +927,9 @@ void ieee80211_tx_monitor(struct ieee802
- if (!ieee80211_sdata_running(sdata))
- continue;
-
-+ if (sdata->u.mntr.flags & MONITOR_FLAG_SKIP_TX)
-+ continue;
-+
- if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) &&
- !send_to_cooked)
- continue;
-@@ -1099,7 +1102,7 @@ static void __ieee80211_tx_status(struct
- * This is a bit racy but we can avoid a lot of work
- * with this test...
- */
-- if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) {
-+ if (!local->tx_mntrs && (!send_to_cooked || !local->cooked_mntrs)) {
- if (status->free_list)
- list_add_tail(&skb->list, status->free_list);
- else
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 2 Oct 2024 12:31:22 +0200
-Subject: [PATCH] wifi: mac80211: refactor ieee80211_rx_monitor
-
-Rework the monitor mode interface iteration to get rid of the last_monitor
-condition. Preparation for further filtering received monitor packets.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -762,8 +762,8 @@ ieee80211_rx_monitor(struct ieee80211_lo
- struct ieee80211_rate *rate)
- {
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
-- struct ieee80211_sub_if_data *sdata;
-- struct sk_buff *monskb = NULL;
-+ struct ieee80211_sub_if_data *sdata, *prev_sdata = NULL;
-+ struct sk_buff *skb, *monskb = NULL;
- int present_fcs_len = 0;
- unsigned int rtap_space = 0;
- struct ieee80211_sub_if_data *monitor_sdata =
-@@ -837,8 +837,10 @@ ieee80211_rx_monitor(struct ieee80211_lo
- ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
-
- list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
-- bool last_monitor = list_is_last(&sdata->u.mntr.list,
-- &local->mon_list);
-+ if (!prev_sdata) {
-+ prev_sdata = sdata;
-+ continue;
-+ }
-
- if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
- ieee80211_handle_mu_mimo_mon(sdata, origskb, rtap_space);
-@@ -846,34 +848,34 @@ ieee80211_rx_monitor(struct ieee80211_lo
- if (!monskb)
- monskb = ieee80211_make_monitor_skb(local, &origskb,
- rate, rtap_space,
-- only_monitor &&
-- last_monitor);
--
-- if (monskb) {
-- struct sk_buff *skb;
-+ false);
-+ if (!monskb)
-+ continue;
-
-- if (last_monitor) {
-- skb = monskb;
-- monskb = NULL;
-- } else {
-- skb = skb_clone(monskb, GFP_ATOMIC);
-- }
-+ skb = skb_clone(monskb, GFP_ATOMIC);
-+ if (!skb)
-+ continue;
-+
-+ skb->dev = prev_sdata->dev;
-+ dev_sw_netstats_rx_add(skb->dev, skb->len);
-+ netif_receive_skb(skb);
-+ prev_sdata = sdata;
-+ }
-
-- if (skb) {
-- skb->dev = sdata->dev;
-- dev_sw_netstats_rx_add(skb->dev, skb->len);
-- netif_receive_skb(skb);
-- }
-+ if (prev_sdata) {
-+ if (monskb)
-+ skb = monskb;
-+ else
-+ skb = ieee80211_make_monitor_skb(local, &origskb,
-+ rate, rtap_space,
-+ only_monitor);
-+ if (skb) {
-+ skb->dev = prev_sdata->dev;
-+ dev_sw_netstats_rx_add(skb->dev, skb->len);
-+ netif_receive_skb(skb);
- }
--
-- if (last_monitor)
-- break;
- }
-
-- /* this happens if last_monitor was erroneously false */
-- dev_kfree_skb(monskb);
--
-- /* ditto */
- if (!origskb)
- return NULL;
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 2 Oct 2024 12:35:13 +0200
-Subject: [PATCH] wifi: mac80211: filter on monitor interfaces based on
- configured channel
-
-When a monitor interface has an assigned channel (only happens with the
-NO_VIRTUAL_MONITOR feature), only pass packets received on that channel.
-This is useful for monitoring on multiple channels at the same time using
-multiple monitor interfaces.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/net/mac80211/rx.c
-+++ b/net/mac80211/rx.c
-@@ -837,6 +837,13 @@ ieee80211_rx_monitor(struct ieee80211_lo
- ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
-
- list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
-+ struct cfg80211_chan_def *chandef;
-+
-+ chandef = &sdata->vif.bss_conf.chanreq.oper;
-+ if (chandef->chan &&
-+ chandef->chan->center_freq != status->freq)
-+ continue;
-+
- if (!prev_sdata) {
- prev_sdata = sdata;
- continue;
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 7 Aug 2024 13:31:07 +0200
-Subject: [PATCH] wifi: cfg80211: report per wiphy radio antenna mask
-
-With multi-radio devices, each radio typically gets a fixed set of antennas.
-In order to be able to disable specific antennas for some radios, user space
-needs to know which antenna mask bits are assigned to which radio.
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -5443,6 +5443,8 @@ struct wiphy_radio_freq_range {
- * @iface_combinations: Valid interface combinations array, should not
- * list single interface types.
- * @n_iface_combinations: number of entries in @iface_combinations array.
-+ *
-+ * @antenna_mask: bitmask of antennas connected to this radio.
- */
- struct wiphy_radio {
- const struct wiphy_radio_freq_range *freq_range;
-@@ -5450,6 +5452,8 @@ struct wiphy_radio {
-
- const struct ieee80211_iface_combination *iface_combinations;
- int n_iface_combinations;
-+
-+ u32 antenna_mask;
- };
-
- #define CFG80211_HW_TIMESTAMP_ALL_PEERS 0xffff
---- a/include/uapi/linux/nl80211.h
-+++ b/include/uapi/linux/nl80211.h
-@@ -8038,6 +8038,8 @@ enum nl80211_ap_settings_flags {
- * @NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: Supported interface
- * combination for this radio. Attribute may be present multiple times
- * and contains attributes defined in &enum nl80211_if_combination_attrs.
-+ * @NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK: bitmask (u32) of antennas
-+ * connected to this radio.
- *
- * @__NL80211_WIPHY_RADIO_ATTR_LAST: Internal
- * @NL80211_WIPHY_RADIO_ATTR_MAX: Highest attribute
-@@ -8048,6 +8050,7 @@ enum nl80211_wiphy_radio_attrs {
- NL80211_WIPHY_RADIO_ATTR_INDEX,
- NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE,
- NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION,
-+ NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
-
- /* keep last */
- __NL80211_WIPHY_RADIO_ATTR_LAST,
---- a/net/wireless/nl80211.c
-+++ b/net/wireless/nl80211.c
-@@ -2431,6 +2431,11 @@ static int nl80211_put_radio(struct wiph
- if (nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_INDEX, idx))
- goto nla_put_failure;
-
-+ if (r->antenna_mask &&
-+ nla_put_u32(msg, NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK,
-+ r->antenna_mask))
-+ goto nla_put_failure;
-+
- for (i = 0; i < r->n_freq_range; i++) {
- const struct wiphy_radio_freq_range *range = &r->freq_range[i];
-
+++ /dev/null
-From: Felix Fietkau <nbd@nbd.name>
-Date: Fri, 15 Nov 2024 12:28:43 +0100
-Subject: [PATCH] wifi: mac80211: fix vif addr when switching from monitor
- to station
-
-Since adding support for opting out of virtual monitor support, a zero vif
-addr was used to indicate passive vs active monitor to the driver.
-This would break the vif->addr when changing the netdev mac address before
-switching the interface from monitor to sta mode.
-Fix the regression by adding a separate flag to indicate whether vif->addr
-is valid.
-
-Reported-by: syzbot+9ea265d998de25ac6a46@syzkaller.appspotmail.com
-Fixes: 9d40f7e32774 ("wifi: mac80211: add flag to opt out of virtual monitor support")
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/include/net/mac80211.h
-+++ b/include/net/mac80211.h
-@@ -1972,6 +1972,8 @@ enum ieee80211_neg_ttlm_res {
- * @neg_ttlm: negotiated TID to link mapping info.
- * see &struct ieee80211_neg_ttlm.
- * @addr: address of this interface
-+ * @addr_valid: indicates if the address is actively used. Set to false for
-+ * passive monitor interfaces, true in all other cases.
- * @p2p: indicates whether this AP or STA interface is a p2p
- * interface, i.e. a GO or p2p-sta respectively
- * @netdev_features: tx netdev features supported by the hardware for this
-@@ -2011,6 +2013,7 @@ struct ieee80211_vif {
- u16 valid_links, active_links, dormant_links, suspended_links;
- struct ieee80211_neg_ttlm neg_ttlm;
- u8 addr[ETH_ALEN] __aligned(2);
-+ bool addr_valid;
- bool p2p;
-
- u8 cab_queue;
---- a/net/mac80211/iface.c
-+++ b/net/mac80211/iface.c
-@@ -279,13 +279,8 @@ static int _ieee80211_change_mac(struct
- ret = eth_mac_addr(sdata->dev, sa);
-
- if (ret == 0) {
-- if (check_dup) {
-- memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
-- ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
-- } else {
-- memset(sdata->vif.addr, 0, ETH_ALEN);
-- memset(sdata->vif.bss_conf.addr, 0, ETH_ALEN);
-- }
-+ memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN);
-+ ether_addr_copy(sdata->vif.bss_conf.addr, sdata->vif.addr);
- }
-
- /* Regardless of eth_mac_addr() return we still want to add the
-@@ -1324,6 +1319,8 @@ int ieee80211_do_open(struct wireless_de
- }
- }
-
-+ sdata->vif.addr_valid = sdata->vif.type != NL80211_IFTYPE_MONITOR ||
-+ (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE);
- switch (sdata->vif.type) {
- case NL80211_IFTYPE_AP_VLAN:
- /* no need to tell driver, but set carrier and chanctx */
+++ /dev/null
-From: Benjamin Lin <benjamin-jw.lin@mediatek.com>
-Date: Mon, 18 Nov 2024 16:07:22 +0800
-Subject: [PATCH] wifi: mac80211: fix incorrect timing to initialize
- station NSS capability
-
-Station's spatial streaming capability should be initialized before
-handling VHT OMN, because the handling requires the capability information.
-
-Fixes: a8bca3e9371d ("wifi: mac80211: track capability/opmode NSS separately")
-Signed-off-by: Benjamin Lin <benjamin-jw.lin@mediatek.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -1914,6 +1914,8 @@ static int sta_link_apply_parameters(str
- params->eht_capa_len,
- link_sta);
-
-+ ieee80211_sta_init_nss(link_sta);
-+
- if (params->opmode_notif_used) {
- /* returned value is only needed for rc update, but the
- * rc isn't initialized here yet, so ignore it
-@@ -1923,8 +1925,6 @@ static int sta_link_apply_parameters(str
- sband->band);
- }
-
-- ieee80211_sta_init_nss(link_sta);
--
- return 0;
- }
-
+++ /dev/null
-From: "Gustavo A. R. Silva" <gustavoars@kernel.org>
-Date: Fri, 25 Oct 2024 16:05:50 -0600
-Subject: [PATCH] wifi: mac80211: ieee80211_i: Fix memory corruption bug in
- struct ieee80211_chanctx
-
-Move the `struct ieee80211_chanctx_conf conf` to the end of
-`struct ieee80211_chanctx` and fix a memory corruption bug
-triggered e.g. in `hwsim_set_chanctx_magic()`: `radar_detected`
-is being overwritten when `cp->magic = HWSIM_CHANCTX_MAGIC;`
-See the function call sequence below:
-
-drv_add_chanctx(... struct ieee80211_chanctx *ctx) ->
- local->ops->add_chanctx(&local->hw, &ctx->conf) ->
- mac80211_hwsim_add_chanctx(... struct ieee80211_chanctx_conf *ctx) ->
- hwsim_set_chanctx_magic(ctx)
-
-This also happens in a number of other drivers.
-
-Also, add a code comment to try to prevent people from introducing
-new members after `struct ieee80211_chanctx_conf conf`. Notice that
-`struct ieee80211_chanctx_conf` is a flexible structure --a structure
-that contains a flexible-array member, so it should always be at
-the end of any other containing structures.
-
-This change also fixes 50 of the following warnings:
-
-net/mac80211/ieee80211_i.h:895:39: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
-
--Wflex-array-member-not-at-end was introduced in GCC-14, and we are
-getting ready to enable it, globally.
-
-Fixes: bca8bc0399ac ("wifi: mac80211: handle ieee80211_radar_detected() for MLO")
-Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
-Link: https://patch.msgid.link/ZxwWPrncTeSi1UTq@kspp
-[also refer to other drivers in commit message]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -894,9 +894,10 @@ struct ieee80211_chanctx {
- /* temporary data for search algorithm etc. */
- struct ieee80211_chan_req req;
-
-- struct ieee80211_chanctx_conf conf;
--
- bool radar_detected;
-+
-+ /* MUST be last - ends in a flexible-array member. */
-+ struct ieee80211_chanctx_conf conf;
- };
-
- struct mac80211_qos_map {
+++ /dev/null
-From: Ben Greear <greearb@candelatech.com>
-Date: Thu, 10 Oct 2024 13:39:54 -0700
-Subject: [PATCH] mac80211: fix user-power when emulating chanctx
-
-ieee80211_calc_hw_conf_chan was ignoring the configured
-user_txpower. If it is set, use it to potentially decrease
-txpower as requested.
-
-Signed-off-by: Ben Greear <greearb@candelatech.com>
-Link: https://patch.msgid.link/20241010203954.1219686-1-greearb@candelatech.com
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/main.c
-+++ b/net/mac80211/main.c
-@@ -167,6 +167,8 @@ static u32 ieee80211_calc_hw_conf_chan(s
- }
-
- power = ieee80211_chandef_max_power(&chandef);
-+ if (local->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
-+ power = min(local->user_power_level, power);
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+++ /dev/null
-From: Remi Pommarel <repk@triplefau.lt>
-Date: Tue, 24 Sep 2024 21:28:04 +0200
-Subject: [PATCH] wifi: cfg80211: Add wiphy_delayed_work_pending()
-
-Add wiphy_delayed_work_pending() to check if any delayed work timer is
-pending, that can be used to be sure that wiphy_delayed_work_queue()
-won't postpone an already pending delayed work.
-
-Signed-off-by: Remi Pommarel <repk@triplefau.lt>
-Link: https://patch.msgid.link/20240924192805.13859-2-repk@triplefau.lt
-[fix return value kernel-doc]
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/include/net/cfg80211.h
-+++ b/include/net/cfg80211.h
-@@ -6141,6 +6141,50 @@ void wiphy_delayed_work_flush(struct wip
- struct wiphy_delayed_work *dwork);
-
- /**
-+ * wiphy_delayed_work_pending - Find out whether a wiphy delayable
-+ * work item is currently pending.
-+ *
-+ * @wiphy: the wiphy, for debug purposes
-+ * @dwork: the delayed work in question
-+ *
-+ * Return: true if timer is pending, false otherwise
-+ *
-+ * How wiphy_delayed_work_queue() works is by setting a timer which
-+ * when it expires calls wiphy_work_queue() to queue the wiphy work.
-+ * Because wiphy_delayed_work_queue() uses mod_timer(), if it is
-+ * called twice and the second call happens before the first call
-+ * deadline, the work will rescheduled for the second deadline and
-+ * won't run before that.
-+ *
-+ * wiphy_delayed_work_pending() can be used to detect if calling
-+ * wiphy_work_delayed_work_queue() would start a new work schedule
-+ * or delayed a previous one. As seen below it cannot be used to
-+ * detect precisely if the work has finished to execute nor if it
-+ * is currently executing.
-+ *
-+ * CPU0 CPU1
-+ * wiphy_delayed_work_queue(wk)
-+ * mod_timer(wk->timer)
-+ * wiphy_delayed_work_pending(wk) -> true
-+ *
-+ * [...]
-+ * expire_timers(wk->timer)
-+ * detach_timer(wk->timer)
-+ * wiphy_delayed_work_pending(wk) -> false
-+ * wk->timer->function() |
-+ * wiphy_work_queue(wk) | delayed work pending
-+ * list_add_tail() | returns false but
-+ * queue_work(cfg80211_wiphy_work) | wk->func() has not
-+ * | been run yet
-+ * [...] |
-+ * cfg80211_wiphy_work() |
-+ * wk->func() V
-+ *
-+ */
-+bool wiphy_delayed_work_pending(struct wiphy *wiphy,
-+ struct wiphy_delayed_work *dwork);
-+
-+/**
- * enum ieee80211_ap_reg_power - regulatory power for an Access Point
- *
- * @IEEE80211_REG_UNSET_AP: Access Point has no regulatory power mode
---- a/net/wireless/core.c
-+++ b/net/wireless/core.c
-@@ -1735,6 +1735,13 @@ void wiphy_delayed_work_flush(struct wip
- }
- EXPORT_SYMBOL_GPL(wiphy_delayed_work_flush);
-
-+bool wiphy_delayed_work_pending(struct wiphy *wiphy,
-+ struct wiphy_delayed_work *dwork)
-+{
-+ return timer_pending(&dwork->timer);
-+}
-+EXPORT_SYMBOL_GPL(wiphy_delayed_work_pending);
-+
- static int __init cfg80211_init(void)
- {
- int err;
+++ /dev/null
-From: Remi Pommarel <repk@triplefau.lt>
-Date: Tue, 24 Sep 2024 21:28:05 +0200
-Subject: [PATCH] wifi: mac80211: Convert color collision detection to wiphy
- work
-
-Call to ieee80211_color_collision_detection_work() needs wiphy lock to
-be held (see lockdep assert in cfg80211_bss_color_notify()). Not locking
-wiphy causes the following lockdep error:
-
- WARNING: CPU: 2 PID: 42 at net/wireless/nl80211.c:19505 cfg80211_bss_color_notify+0x1a4/0x25c
- Modules linked in:
- CPU: 2 PID: 42 Comm: kworker/u8:3 Tainted: G W 6.4.0-02327-g36c6cb260481 #1048
- Hardware name:
- Workqueue: phy1 ieee80211_color_collision_detection_work
- pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
- pc : cfg80211_bss_color_notify+0x1a4/0x25c
- lr : cfg80211_bss_color_notify+0x1a0/0x25c
- sp : ffff000002947d00
- x29: ffff000002947d00 x28: ffff800008e1a000 x27: ffff000002bd4705
- x26: ffff00000d034000 x25: ffff80000903cf40 x24: 0000000000000000
- x23: ffff00000cb70720 x22: 0000000000800000 x21: ffff800008dfb008
- x20: 000000000000008d x19: ffff00000d035fa8 x18: 0000000000000010
- x17: 0000000000000001 x16: 000003564b1ce96a x15: 000d69696d057970
- x14: 000000000000003b x13: 0000000000000001 x12: 0000000000040000
- x11: 0000000000000001 x10: ffff80000978f9c0 x9 : ffff0000028d3174
- x8 : ffff800008e30000 x7 : 0000000000000000 x6 : 0000000000000028
- x5 : 000000000002f498 x4 : ffff00000d034a80 x3 : 0000000000800000
- x2 : ffff800016143000 x1 : 0000000000000000 x0 : 0000000000000000
- Call trace:
- cfg80211_bss_color_notify+0x1a4/0x25c
- ieee80211_color_collision_detection_work+0x20/0x118
- process_one_work+0x294/0x554
- worker_thread+0x70/0x440
- kthread+0xf4/0xf8
- ret_from_fork+0x10/0x20
- irq event stamp: 77372
- hardirqs last enabled at (77371): [<ffff800008a346fc>] _raw_spin_unlock_irq+0x2c/0x4c
- hardirqs last disabled at (77372): [<ffff800008a28754>] el1_dbg+0x20/0x48
- softirqs last enabled at (77350): [<ffff8000089e120c>] batadv_send_outstanding_bcast_packet+0xb8/0x120
- softirqs last disabled at (77348): [<ffff8000089e11d4>] batadv_send_outstanding_bcast_packet+0x80/0x120
-
-The wiphy lock cannot be taken directly from color collision detection
-delayed work (ieee80211_color_collision_detection_work()) because this
-work is cancel_delayed_work_sync() under this wiphy lock causing a
-potential deadlock( see [0] for details).
-
-To fix that ieee80211_color_collision_detection_work() could be
-converted to a wiphy work and cancel_delayed_work_sync() can be simply
-replaced by wiphy_delayed_work_cancel() serving the same purpose under
-wiphy lock.
-
-This could potentially fix [1].
-
-[0]: https://lore.kernel.org/linux-wireless/D4A40Q44OAY2.W3SIF6UEPBUN@freebox.fr/
-[1]: https://lore.kernel.org/lkml/000000000000612f290618eee3e5@google.com/
-
-Reported-by: Nicolas Escande <nescande@freebox.fr>
-Signed-off-by: Remi Pommarel <repk@triplefau.lt>
-Link: https://patch.msgid.link/20240924192805.13859-3-repk@triplefau.lt
-Signed-off-by: Johannes Berg <johannes.berg@intel.com>
----
-
---- a/net/mac80211/cfg.c
-+++ b/net/mac80211/cfg.c
-@@ -4836,12 +4836,12 @@ void ieee80211_color_change_finalize_wor
- ieee80211_color_change_finalize(link);
- }
-
--void ieee80211_color_collision_detection_work(struct work_struct *work)
-+void ieee80211_color_collision_detection_work(struct wiphy *wiphy,
-+ struct wiphy_work *work)
- {
-- struct delayed_work *delayed_work = to_delayed_work(work);
- struct ieee80211_link_data *link =
-- container_of(delayed_work, struct ieee80211_link_data,
-- color_collision_detect_work);
-+ container_of(work, struct ieee80211_link_data,
-+ color_collision_detect_work.work);
- struct ieee80211_sub_if_data *sdata = link->sdata;
-
- cfg80211_obss_color_collision_notify(sdata->dev, link->color_bitmap,
-@@ -4894,7 +4894,8 @@ ieee80211_obss_color_collision_notify(st
- return;
- }
-
-- if (delayed_work_pending(&link->color_collision_detect_work)) {
-+ if (wiphy_delayed_work_pending(sdata->local->hw.wiphy,
-+ &link->color_collision_detect_work)) {
- rcu_read_unlock();
- return;
- }
-@@ -4903,9 +4904,9 @@ ieee80211_obss_color_collision_notify(st
- /* queue the color collision detection event every 500 ms in order to
- * avoid sending too much netlink messages to userspace.
- */
-- ieee80211_queue_delayed_work(&sdata->local->hw,
-- &link->color_collision_detect_work,
-- msecs_to_jiffies(500));
-+ wiphy_delayed_work_queue(sdata->local->hw.wiphy,
-+ &link->color_collision_detect_work,
-+ msecs_to_jiffies(500));
-
- rcu_read_unlock();
- }
---- a/net/mac80211/ieee80211_i.h
-+++ b/net/mac80211/ieee80211_i.h
-@@ -1056,7 +1056,7 @@ struct ieee80211_link_data {
- } csa;
-
- struct wiphy_work color_change_finalize_work;
-- struct delayed_work color_collision_detect_work;
-+ struct wiphy_delayed_work color_collision_detect_work;
- u64 color_bitmap;
-
- /* context reservation -- protected with wiphy mutex */
-@@ -2010,7 +2010,8 @@ int ieee80211_channel_switch(struct wiph
- /* color change handling */
- void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
- struct wiphy_work *work);
--void ieee80211_color_collision_detection_work(struct work_struct *work);
-+void ieee80211_color_collision_detection_work(struct wiphy *wiphy,
-+ struct wiphy_work *work);
-
- /* interface handling */
- #define MAC80211_SUPPORTED_FEATURES_TX (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | \
---- a/net/mac80211/link.c
-+++ b/net/mac80211/link.c
-@@ -41,8 +41,8 @@ void ieee80211_link_init(struct ieee8021
- ieee80211_csa_finalize_work);
- wiphy_work_init(&link->color_change_finalize_work,
- ieee80211_color_change_finalize_work);
-- INIT_DELAYED_WORK(&link->color_collision_detect_work,
-- ieee80211_color_collision_detection_work);
-+ wiphy_delayed_work_init(&link->color_collision_detect_work,
-+ ieee80211_color_collision_detection_work);
- INIT_LIST_HEAD(&link->assigned_chanctx_list);
- INIT_LIST_HEAD(&link->reserved_chanctx_list);
- wiphy_delayed_work_init(&link->dfs_cac_timer_work,
-@@ -72,7 +72,8 @@ void ieee80211_link_stop(struct ieee8021
- if (link->sdata->vif.type == NL80211_IFTYPE_STATION)
- ieee80211_mgd_stop_link(link);
-
-- cancel_delayed_work_sync(&link->color_collision_detect_work);
-+ wiphy_delayed_work_cancel(link->sdata->local->hw.wiphy,
-+ &link->color_collision_detect_work);
- wiphy_work_cancel(link->sdata->local->hw.wiphy,
- &link->color_change_finalize_work);
- wiphy_work_cancel(link->sdata->local->hw.wiphy,