]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: cfg80211: set and report chandef CAC ongoing
authorJanusz Dziedzic <janusz.dziedzic@gmail.com>
Fri, 6 Feb 2026 17:15:49 +0000 (18:15 +0100)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 2 Mar 2026 08:10:28 +0000 (09:10 +0100)
Allow to track and check CAC state from user mode by
simple check phy channels eg. using iw phy1 channels
command.
This is done for regular CAC and background CAC.
It is important for background CAC while we can start
it from any app (eg. iw or hostapd).

Signed-off-by: Janusz Dziedzic <janusz.dziedzic@gmail.com>
Link: https://patch.msgid.link/20260206171830.553879-3-janusz.dziedzic@gmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/chan.c
net/wireless/core.h
net/wireless/mlme.c
net/wireless/nl80211.c

index fc01de19c7981a906a5303899e7000d8193c60a8..e00045c150e78adbbcea8f7ef5bd6bb3e347ec52 100644 (file)
@@ -190,6 +190,8 @@ enum ieee80211_channel_flags {
  *     on this channel.
  * @dfs_state_entered: timestamp (jiffies) when the dfs state was entered.
  * @dfs_cac_ms: DFS CAC time in milliseconds, this is valid for DFS channels.
+ * @cac_start_time: timestamp (CLOCK_BOOTTIME, nanoseconds) when CAC was
+ *     started on this channel. Zero when CAC is not in progress.
  * @psd: power spectral density (in dBm)
  */
 struct ieee80211_channel {
@@ -207,6 +209,7 @@ struct ieee80211_channel {
        enum nl80211_dfs_state dfs_state;
        unsigned long dfs_state_entered;
        unsigned int dfs_cac_ms;
+       u64 cac_start_time;
        s8 psd;
 };
 
index b63f718509060dd4b7b59e2d6cc2aad280acaa22..c75aa039f09657a15489719e36a83e8aa8127545 100644 (file)
@@ -4480,6 +4480,10 @@ enum nl80211_wmm_rule {
  *     as a non-primary subchannel. Only applicable to S1G channels.
  * @NL80211_FREQUENCY_ATTR_NO_UHR: UHR operation is not allowed on this channel
  *     in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_CAC_START_TIME: Channel Availability Check (CAC)
+ *     start time (CLOCK_BOOTTIME, nanoseconds). Only present when CAC is
+ *     currently in progress on this channel.
+ * @NL80211_FREQUENCY_ATTR_PAD: attribute used for padding for 64-bit alignment
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
@@ -4530,6 +4534,8 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_NO_16MHZ,
        NL80211_FREQUENCY_ATTR_S1G_NO_PRIMARY,
        NL80211_FREQUENCY_ATTR_NO_UHR,
+       NL80211_FREQUENCY_ATTR_CAC_START_TIME,
+       NL80211_FREQUENCY_ATTR_PAD,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
index 68221b1ab45e108a5ef86008a88f26581c294923..dfe319565280fa637c19317fd404fbe90695b83d 100644 (file)
@@ -642,6 +642,33 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
        }
 }
 
+void cfg80211_set_cac_state(struct wiphy *wiphy,
+                           const struct cfg80211_chan_def *chandef,
+                           bool cac_ongoing)
+{
+       struct ieee80211_channel *c;
+       int width;
+       u64 cac_time;
+
+       if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+               return;
+
+       width = cfg80211_chandef_get_width(chandef);
+       if (width < 0)
+               return;
+
+       /* Get the same timestamp for all subchannels */
+       cac_time = cac_ongoing ? ktime_get_boottime_ns() : 0;
+
+       for_each_subchan(chandef, freq, cf) {
+               c = ieee80211_get_channel_khz(wiphy, freq);
+               if (!c)
+                       continue;
+
+               c->cac_start_time = cac_time;
+       }
+}
+
 static bool
 cfg80211_dfs_permissive_check_wdev(struct cfg80211_registered_device *rdev,
                                   enum nl80211_iftype iftype,
index 6ac57b7b26153203630c05d6611dd69edbb0779c..6cace846d7a31f7ae786c7ad7e99b98f3a65c270 100644 (file)
@@ -481,6 +481,10 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
                            const struct cfg80211_chan_def *chandef,
                            enum nl80211_dfs_state dfs_state);
 
+void cfg80211_set_cac_state(struct wiphy *wiphy,
+                           const struct cfg80211_chan_def *chandef,
+                           bool cac_ongoing);
+
 void cfg80211_dfs_channels_update_work(struct work_struct *work);
 
 void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
index 212178d04efaf15959d248547abde9a784b6375e..283ea4c7c61eb0fd37c49f7a9a3949116695fbbf 100644 (file)
@@ -1162,9 +1162,11 @@ void cfg80211_cac_event(struct net_device *netdev,
                fallthrough;
        case NL80211_RADAR_CAC_ABORTED:
                wdev->links[link_id].cac_started = false;
+               cfg80211_set_cac_state(wiphy, chandef, false);
                break;
        case NL80211_RADAR_CAC_STARTED:
                wdev->links[link_id].cac_started = true;
+               cfg80211_set_cac_state(wiphy, chandef, true);
                break;
        default:
                WARN_ON(1);
@@ -1192,15 +1194,18 @@ __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev,
        switch (event) {
        case NL80211_RADAR_CAC_FINISHED:
                cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+               cfg80211_set_cac_state(wiphy, chandef, false);
                memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
                queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
                cfg80211_sched_dfs_chan_update(rdev);
                break;
        case NL80211_RADAR_CAC_ABORTED:
+               cfg80211_set_cac_state(wiphy, chandef, false);
                if (!cancel_delayed_work(&rdev->background_cac_done_wk))
                        return;
                break;
        case NL80211_RADAR_CAC_STARTED:
+               cfg80211_set_cac_state(wiphy, chandef, true);
                break;
        default:
                return;
@@ -1307,6 +1312,8 @@ void cfg80211_stop_radar_detection(struct wireless_dev *wdev)
 
                chandef = *wdev_chandef(wdev, link_id);
                rdev_end_cac(rdev, wdev->netdev, link_id);
+               wdev->links[link_id].cac_started = false;
+               cfg80211_set_cac_state(wiphy, &chandef, false);
                nl80211_radar_notify(rdev, &chandef, NL80211_RADAR_CAC_ABORTED,
                                     wdev->netdev, GFP_KERNEL);
        }
index b94231c8441c4829784ed727a4a2897bbc62b001..7e288d3ce5aee58b9d9aaac29d785813f77720cf 100644 (file)
@@ -1333,6 +1333,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
                if ((chan->flags & IEEE80211_CHAN_NO_UHR) &&
                    nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_UHR))
                        goto nla_put_failure;
+               if (chan->cac_start_time &&
+                   nla_put_u64_64bit(msg,
+                                     NL80211_FREQUENCY_ATTR_CAC_START_TIME,
+                                     chan->cac_start_time,
+                                     NL80211_FREQUENCY_ATTR_PAD))
+                       goto nla_put_failure;
        }
 
        if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
@@ -11353,6 +11359,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        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;
+       cfg80211_set_cac_state(wiphy, &chandef, true);
 
        return 0;
 }