mac80211: backport upstream DFS fixes
authorFelix Fietkau <nbd@nbd.name>
Sat, 5 Oct 2024 07:35:49 +0000 (09:35 +0200)
committerFelix Fietkau <nbd@nbd.name>
Sat, 5 Oct 2024 07:45:41 +0000 (09:45 +0200)
Mostly MLO related

Signed-off-by: Felix Fietkau <nbd@nbd.name>
12 files changed:
package/kernel/ath10k-ct/patches/010-api_update.patch
package/kernel/mac80211/patches/subsys/332-wifi-cfg80211-Set-correct-chandef-when-starting-CAC.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/333-Revert-wifi-mac80211-move-radar-detect-work-to-sdata.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/334-wifi-mac80211-remove-label-usage-in-ieee80211_start_.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/335-wifi-trace-unlink-rdev_end_cac-trace-event-from-wiph.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/336-wifi-cfg80211-move-DFS-related-members-to-links-in-w.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/337-wifi-cfg80211-handle-DFS-per-link.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/338-wifi-mac80211-handle-DFS-per-link.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/339-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch [new file with mode: 0644]
package/kernel/mac80211/patches/subsys/340-wifi-mac80211-handle-ieee80211_radar_detected-for-ML.patch [new file with mode: 0644]
package/kernel/mt76/patches/100-api_update.patch
package/kernel/mwlwifi/patches/005-mac80211_update.patch

index c0d2574748b717a39d568fd46eaa00671cef1184..f70d8eb4609a090338d65b50ebe0078ea8f3ebf5 100644 (file)
@@ -1,5 +1,14 @@
 --- a/ath10k-6.9/mac.c
 +++ b/ath10k-6.9/mac.c
+@@ -1675,7 +1675,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);
+       }
+ }
 @@ -6238,7 +6238,7 @@ err:
        return ret;
  }
@@ -9,3 +18,25 @@
  {
        struct ath10k *ar = hw->priv;
        u32 opt;
+--- a/ath10k-6.9/debug.c
++++ b/ath10k-6.9/debug.c
+@@ -3319,7 +3319,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/ath10k-6.9/wmi.c
++++ b/ath10k-6.9/wmi.c
+@@ -4402,7 +4402,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)
diff --git a/package/kernel/mac80211/patches/subsys/332-wifi-cfg80211-Set-correct-chandef-when-starting-CAC.patch b/package/kernel/mac80211/patches/subsys/332-wifi-cfg80211-Set-correct-chandef-when-starting-CAC.patch
new file mode 100644 (file)
index 0000000..72a3510
--- /dev/null
@@ -0,0 +1,64 @@
+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;
diff --git a/package/kernel/mac80211/patches/subsys/333-Revert-wifi-mac80211-move-radar-detect-work-to-sdata.patch b/package/kernel/mac80211/patches/subsys/333-Revert-wifi-mac80211-move-radar-detect-work-to-sdata.patch
new file mode 100644 (file)
index 0000000..d12df8f
--- /dev/null
@@ -0,0 +1,136 @@
+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;
diff --git a/package/kernel/mac80211/patches/subsys/334-wifi-mac80211-remove-label-usage-in-ieee80211_start_.patch b/package/kernel/mac80211/patches/subsys/334-wifi-mac80211-remove-label-usage-in-ieee80211_start_.patch
new file mode 100644 (file)
index 0000000..329b645
--- /dev/null
@@ -0,0 +1,50 @@
+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,
diff --git a/package/kernel/mac80211/patches/subsys/335-wifi-trace-unlink-rdev_end_cac-trace-event-from-wiph.patch b/package/kernel/mac80211/patches/subsys/335-wifi-trace-unlink-rdev_end_cac-trace-event-from-wiph.patch
new file mode 100644 (file)
index 0000000..98bc0c2
--- /dev/null
@@ -0,0 +1,42 @@
+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,
diff --git a/package/kernel/mac80211/patches/subsys/336-wifi-cfg80211-move-DFS-related-members-to-links-in-w.patch b/package/kernel/mac80211/patches/subsys/336-wifi-cfg80211-move-DFS-related-members-to-links-in-w.patch
new file mode 100644 (file)
index 0000000..95dac04
--- /dev/null
@@ -0,0 +1,309 @@
+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(&params->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 */
diff --git a/package/kernel/mac80211/patches/subsys/337-wifi-cfg80211-handle-DFS-per-link.patch b/package/kernel/mac80211/patches/subsys/337-wifi-cfg80211-handle-DFS-per-link.patch
new file mode 100644 (file)
index 0000000..a4e0333
--- /dev/null
@@ -0,0 +1,435 @@
+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,
diff --git a/package/kernel/mac80211/patches/subsys/338-wifi-mac80211-handle-DFS-per-link.patch b/package/kernel/mac80211/patches/subsys/338-wifi-mac80211-handle-DFS-per-link.patch
new file mode 100644 (file)
index 0000000..388c057
--- /dev/null
@@ -0,0 +1,134 @@
+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);
+               }
+       }
+ }
diff --git a/package/kernel/mac80211/patches/subsys/339-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch b/package/kernel/mac80211/patches/subsys/339-wifi-cfg80211-mac80211-use-proper-link-ID-for-DFS.patch
new file mode 100644 (file)
index 0000000..53079b6
--- /dev/null
@@ -0,0 +1,168 @@
+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);
diff --git a/package/kernel/mac80211/patches/subsys/340-wifi-mac80211-handle-ieee80211_radar_detected-for-ML.patch b/package/kernel/mac80211/patches/subsys/340-wifi-mac80211-handle-ieee80211_radar_detected-for-ML.patch
new file mode 100644 (file)
index 0000000..3f64864
--- /dev/null
@@ -0,0 +1,360 @@
+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);
index 8b528f05f72222c7dee71a5be9adffb5eae0b284..4cd19f65b2429614c26740d75112bddff586b68e 100644 (file)
@@ -9,3 +9,56 @@
                head = &wcid->tx_offchannel;
        else
                head = &wcid->tx_pending;
+--- a/mt7615/mcu.c
++++ b/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/mt76x02_dfs.c
++++ b/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/mt7915/mcu.c
++++ b/mt7915/mcu.c
+@@ -297,7 +297,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/mt7996/mcu.c
++++ b/mt7996/mcu.c
+@@ -383,7 +383,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++;
+ }
index 54dc4db7838a7bb7adcb29271b1671886e127605..2ceee4732d5c3434dd43151e1dbee9b614aa0963 100644 (file)
                        sta->tdls,
                        sta->tdls_initiator,
                        sta->wme,
+@@ -1158,7 +1158,7 @@ static ssize_t mwl_debugfs_dfs_radar_wri
+       struct mwl_priv *priv = (struct mwl_priv *)file->private_data;
+       wiphy_info(priv->hw->wiphy, "simulate radar detected\n");
+-      ieee80211_radar_detected(priv->hw);
++      ieee80211_radar_detected(priv->hw, NULL);
+       return count;
+ }
 --- a/hif/fwcmd.c
 +++ b/hif/fwcmd.c
 @@ -633,11 +633,15 @@ einval:
  
        switch (format) {
        case TX_RATE_FORMAT_LEGACY:
+--- a/hif/pcie/pcie.c
++++ b/hif/pcie/pcie.c
+@@ -546,7 +546,7 @@ static irqreturn_t pcie_isr_8864(struct
+               if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
+                       wiphy_info(hw->wiphy, "radar detected by firmware\n");
+-                      ieee80211_radar_detected(hw);
++                      ieee80211_radar_detected(hw, NULL);
+               }
+               if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH) ieee80211_queue_work(hw, &priv->chnl_switch_handle);
+@@ -593,7 +593,7 @@ static irqreturn_t pcie_isr_8997(struct
+               if (int_status & MACREG_A2HRIC_BIT_RADAR_DETECT) {
+                       wiphy_info(hw->wiphy, "radar detected by firmware\n");
+-                      ieee80211_radar_detected(hw);
++                      ieee80211_radar_detected(hw, NULL);
+               }
+               if (int_status & MACREG_A2HRIC_BIT_CHAN_SWITCH)
+@@ -1071,7 +1071,7 @@ static irqreturn_t pcie_isr_ndp(struct i
+               if (int_status & MACREG_A2HRIC_NEWDP_DFS) {
+                       wiphy_info(hw->wiphy, "radar detected by firmware\n");
+-                      ieee80211_radar_detected(hw);
++                      ieee80211_radar_detected(hw, NULL);
+               }
+               if (int_status & MACREG_A2HRIC_NEWDP_CHANNEL_SWITCH)