wifi: cfg80211: set and report chandef CAC ongoing

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>
This commit is contained in:
Janusz Dziedzic 2026-02-06 18:15:49 +01:00 committed by Johannes Berg
parent 92fecd2744
commit d69cb039ab
6 changed files with 54 additions and 0 deletions

View 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;
};

View 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,

View 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,

View 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);

View 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);
}

View 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;
}