blob: 39e8817533a5474a0d4279bf77d285ea6eef2b38 [file] [log] [blame]
From 16ec7de7b7f4d09a4b84ea2d85fb287a579626b2 Mon Sep 17 00:00:00 2001
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Date: Tue, 6 Feb 2024 17:46:10 +0800
Subject: [PATCH 72/89] mtk: mac80211: add DFS CAC countdown in CSA flow
Add DFS channel CAC countdown mechanism in CSA flow
Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
---
include/net/cfg80211.h | 38 ++++++++++++++++-
net/mac80211/cfg.c | 85 ++++++++++++++++++++++++++++++++++++--
net/mac80211/ieee80211_i.h | 1 +
net/mac80211/mlme.c | 6 ++-
net/mac80211/util.c | 7 +++-
net/wireless/chan.c | 74 +++++++++++++++++++++++++++++++++
net/wireless/nl80211.c | 5 ++-
net/wireless/rdev-ops.h | 18 ++++++++
net/wireless/reg.c | 8 +++-
9 files changed, 232 insertions(+), 10 deletions(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5de1d1c..bedf711 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -4481,6 +4481,8 @@ struct mgmt_frame_regs {
*
* @start_radar_detection: Start radar detection in the driver.
*
+ * @start_radar_detection_post_csa: Start radar detection during post CSA.
+ *
* @end_cac: End running CAC, probably because a related CAC
* was finished on another phy.
*
@@ -4853,9 +4855,13 @@ struct cfg80211_ops {
int (*start_radar_detection)(struct wiphy *wiphy,
struct net_device *dev,
- unsigned int link_id,
struct cfg80211_chan_def *chandef,
u32 cac_time_ms, int link_id);
+ int (*start_radar_detection_post_csa)(struct wiphy *wiphy,
+ struct net_device *dev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms);
void (*end_cac)(struct wiphy *wiphy,
struct net_device *dev, unsigned int link_id);
int (*update_ft_ies)(struct wiphy *wiphy, struct net_device *dev,
@@ -9018,6 +9024,36 @@ cfg80211_reg_can_beacon_relax(struct wiphy *wiphy,
}
/**
+ * cfg80211_reg_can_beacon_dfs_relax - check if beaconing is allowed with DFS & IR-relaxation
+ * @wiphy: the wiphy
+ * @chandef: the channel definition
+ * @iftype: interface type
+ *
+ * Return: %true if there is no secondary channel or the secondary channel(s)
+ * can be used for beaconing. This version bypasses radar channel check, allowing
+ * channel switch to a USABLE DFS channel and performing CAC after the channel switch.
+ * It also checks if IR-relaxation conditions apply, to allow beaconing under more
+ * permissive conditions.
+ *
+ * Requires the wiphy mutex to be held.
+ */
+bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype);
+
+/**
+ * cfg80211_start_radar_detection_post_csa - start radar detection after CSA
+ * @wiphy: the wiphy
+ * @wdev: the wireless device
+ * @link_id: the link ID for MLO, must be 0 for non-MLO
+ * @chandef: the channel definition to start radar detection on
+ */
+int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef);
+
+/*
* cfg80211_ch_switch_notify - update wdev channel and notify userspace
* @dev: the device which switched channels
* @chandef: the new channel definition
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index c52cde0..36c898c 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1576,7 +1576,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-static void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
+void ieee80211_free_next_beacon(struct ieee80211_link_data *link)
{
if (!link->u.ap.next_beacon)
return;
@@ -3725,6 +3725,72 @@ static int ieee80211_set_after_csa_beacon(struct ieee80211_link_data *link_data,
return 0;
}
+static int ieee80211_start_radar_detection_post_csa(struct wiphy *wiphy,
+ struct net_device *dev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_link_data *link;
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ link = sdata_dereference(sdata->link[link_id], sdata);
+ if (!link)
+ return -ENOLINK;
+
+ /* whatever, but channel contexts should not complain about that one */
+ link->smps_mode = IEEE80211_SMPS_OFF;
+ link->needed_rx_chains = local->rx_chains;
+
+ if (hweight16(sdata->vif.valid_links) <= 1)
+ sta_info_flush(sdata, -1);
+
+ wiphy_delayed_work_queue(wiphy, &link->dfs_cac_timer_work,
+ msecs_to_jiffies(cac_time_ms));
+
+ return 1;
+}
+
+static void ieee80211_csa_send_deauth(struct ieee80211_link_data *link_data)
+{
+ struct ieee80211_sub_if_data *sdata = link_data->sdata;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_bss_conf *link_conf = link_data->conf;
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
+ u8 broadcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ bool send_deauth;
+
+ send_deauth = !cfg80211_chandef_identical(&link_conf->chanreq.oper,
+ &link_data->csa.chanreq.oper) &&
+ !cfg80211_reg_can_beacon_relax(local->hw.wiphy,
+ &link_data->csa.chanreq.oper,
+ sdata->wdev.iftype) &&
+ hweight16(sdata->vif.valid_links) <= 1;
+ /* broadcast deauth frame if CAC is required for non MLD or single link MLD AP */
+ if (!send_deauth)
+ return;
+
+ if (sdata->csa_blocked_queues) {
+ ieee80211_wake_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA);
+ ieee80211_send_deauth_disassoc(sdata, broadcast,
+ link_conf->bssid,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DEAUTH_LEAVING,
+ send_deauth, frame_buf);
+ ieee80211_stop_vif_queues(local, sdata, IEEE80211_QUEUE_STOP_REASON_CSA);
+ return;
+ }
+
+ ieee80211_send_deauth_disassoc(sdata, broadcast,
+ link_conf->bssid, IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DEAUTH_LEAVING,
+ send_deauth, frame_buf);
+}
+
static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
{
struct ieee80211_sub_if_data *sdata = link_data->sdata;
@@ -3735,6 +3801,8 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
lockdep_assert_wiphy(local->hw.wiphy);
+ ieee80211_csa_send_deauth(link_data);
+
/*
* using reservation isn't immediate as it may be deferred until later
* with multi-vif. once reservation is complete it will re-schedule the
@@ -3758,6 +3826,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_link_data *link_data)
&link_data->csa.chanreq.oper))
return -EINVAL;
+ err = cfg80211_start_radar_detection_post_csa(local->hw.wiphy, &sdata->wdev,
+ link_data->link_id,
+ &link_conf->chanreq.oper);
+ if (err)
+ return err > 0 ? 0 : err;
+
link_conf->csa_active = false;
err = ieee80211_set_after_csa_beacon(link_data, &changed);
@@ -5140,8 +5214,12 @@ ieee80211_skip_cac(struct wireless_dev *wdev, unsigned int link_id)
wiphy_delayed_work_cancel(sdata->local->hw.wiphy,
&link->dfs_cac_timer_work);
- if (wdev->cac_links & BIT(link_id)) {
- ieee80211_link_release_channel(link);
+ if (wdev->links[link_id].cac_started) {
+ if (link->conf->csa_active)
+ wiphy_work_queue(sdata->local->hw.wiphy,
+ &link->csa.finalize_work);
+ else
+ ieee80211_link_release_channel(link);
cac_time_ms = wdev->links[link_id].cac_time_ms;
wdev->links[link_id].cac_start_time = jiffies -
msecs_to_jiffies(cac_time_ms + 1);
@@ -5233,6 +5311,7 @@ const struct cfg80211_ops mac80211_config_ops = {
#endif
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
+ .start_radar_detection_post_csa = ieee80211_start_radar_detection_post_csa,
.end_cac = ieee80211_end_cac,
.channel_switch = ieee80211_channel_switch,
.set_qos_map = ieee80211_set_qos_map,
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index a5c0d6c..2cb80c3 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2015,6 +2015,7 @@ int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy,
void ieee80211_csa_finalize_work(struct wiphy *wiphy, struct wiphy_work *work);
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_csa_settings *params);
+void ieee80211_free_next_beacon(struct ieee80211_link_data *link);
/* color change handling */
void ieee80211_color_change_finalize_work(struct wiphy *wiphy,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 1b19583..b684fed 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3051,7 +3051,11 @@ void ieee80211_dfs_cac_timer_work(struct wiphy *wiphy, struct wiphy_work *work)
lockdep_assert_wiphy(sdata->local->hw.wiphy);
if (sdata->wdev.links[link->link_id].cac_started) {
- ieee80211_link_release_channel(link);
+ if (link->conf->csa_active)
+ wiphy_work_queue(sdata->local->hw.wiphy,
+ &link->csa.finalize_work);
+ else
+ ieee80211_link_release_channel(link);
cfg80211_cac_event(sdata->dev, &chandef,
NL80211_RADAR_CAC_FINISHED,
GFP_KERNEL, link->link_id);
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 2d2b871..df4fa1a 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -3494,7 +3494,12 @@ void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
continue;
chandef = link_conf->chanreq.oper;
- ieee80211_link_release_channel(link_data);
+ if (link_conf->csa_active) {
+ link_conf->csa_active = false;
+ ieee80211_free_next_beacon(link_data);
+ } else {
+ ieee80211_link_release_channel(link_data);
+ }
cfg80211_cac_event(sdata->dev,
&chandef,
NL80211_RADAR_CAC_ABORTED,
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 38e8432..03747bb 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -1680,6 +1680,80 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_reg_check_beaconing);
+bool cfg80211_reg_can_beacon_dfs_relax(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef,
+ enum nl80211_iftype iftype)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_RADAR;
+
+ lockdep_assert_held(&rdev->wiphy.mtx);
+
+ /* Bypass available and usable dfs channel */
+ if (cfg80211_chandef_dfs_required(wiphy, chandef, iftype) > 0 &&
+ (cfg80211_chandef_dfs_usable(wiphy, chandef) ||
+ cfg80211_chandef_dfs_available(wiphy, chandef)))
+ prohibited_flags = IEEE80211_CHAN_DISABLED;
+
+ /*
+ * Under certain conditions suggested by some regulatory bodies a
+ * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag
+ * only if such relaxations are not enabled and the conditions are not
+ * met.
+ */
+ if (!cfg80211_ir_permissive_chan(wiphy, iftype, chandef->chan))
+ prohibited_flags |= IEEE80211_CHAN_NO_IR;
+
+ return cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
+}
+EXPORT_SYMBOL(cfg80211_reg_can_beacon_dfs_relax);
+
+int cfg80211_start_radar_detection_post_csa(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef)
+{
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ u32 cac_time_ms;
+ enum nl80211_dfs_regions dfs_region;
+ int ret = 0;
+
+ if (cfg80211_chandef_dfs_available(wiphy, chandef))
+ goto out;
+
+ /* Update DFS channel state especially when original channel include DFS channel */
+ cfg80211_sched_dfs_chan_update(rdev);
+
+ dfs_region = reg_get_dfs_region(wiphy);
+ if (dfs_region == NL80211_DFS_UNSET)
+ goto out;
+
+ cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy, chandef);
+ if (WARN_ON(!cac_time_ms))
+ cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+
+ pr_info("%s: region = %u, center freq1 = %u, center freq2 = %u, cac time ms = %u\n",
+ __func__, dfs_region, chandef->center_freq1, chandef->center_freq2, cac_time_ms);
+
+ ret = rdev_start_radar_detection_post_csa(rdev, wdev->netdev, link_id,
+ chandef, cac_time_ms);
+ if (ret > 0) {
+ wdev->links[link_id].ap.chandef = *chandef;
+ wdev->links[link_id].cac_start_time = jiffies;
+ wdev->links[link_id].cac_time_ms = cac_time_ms;
+ if (rdev->background_cac_started &&
+ cfg80211_is_sub_chan(chandef, rdev->background_radar_chandef.chan, false))
+ cfg80211_stop_background_radar_detection(rdev->background_radar_wdev);
+ cfg80211_cac_event(wdev->netdev, chandef,
+ NL80211_RADAR_CAC_STARTED, GFP_KERNEL, link_id);
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL(cfg80211_start_radar_detection_post_csa);
+
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
struct cfg80211_chan_def *chandef)
{
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ba1190d..756a29a 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -10462,8 +10462,9 @@ skip_beacons:
cfg80211_set_dfs_state(&rdev->wiphy, &params.chandef, NL80211_DFS_AVAILABLE);
}
- if (!cfg80211_reg_can_beacon_relax(&rdev->wiphy, &params.chandef,
- wdev->iftype)) {
+ /* handle DFS CAC after CSA is sent */
+ if (!cfg80211_reg_can_beacon_dfs_relax(&rdev->wiphy, &params.chandef,
+ wdev->iftype)) {
err = -EINVAL;
goto free;
}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index e4a77a8..5b3c94f 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1214,6 +1214,24 @@ rdev_start_radar_detection(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int
+rdev_start_radar_detection_post_csa(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ unsigned int link_id,
+ struct cfg80211_chan_def *chandef,
+ u32 cac_time_ms)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_rdev_start_radar_detection(&rdev->wiphy, dev, chandef,
+ cac_time_ms, link_id);
+ if (rdev->ops->start_radar_detection_post_csa)
+ ret = rdev->ops->start_radar_detection_post_csa(&rdev->wiphy, dev, link_id,
+ chandef, cac_time_ms);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline void
rdev_end_cac(struct cfg80211_registered_device *rdev,
struct net_device *dev, unsigned int link_id)
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2a13721..651f286 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -2441,8 +2441,12 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
- ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef,
- iftype);
+ if (wdev->links[link].cac_started)
+ ret = cfg80211_reg_can_beacon_dfs_relax(wiphy, &chandef,
+ iftype);
+ else
+ ret = cfg80211_reg_can_beacon_relax(wiphy, &chandef,
+ iftype);
if (!ret)
return ret;
break;
--
2.18.0