[][MAC80211][core][zero wait dfs]

[Description]
Add zero wait DFS for MAC80211

[Release-log]

Change-Id: I67da7ae8b7d623c832dd4f8b8c935944910c2847
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5342188
diff --git a/openwrt_patches-21.02/402-master-mac80211-zero-wait-dfs.patch b/openwrt_patches-21.02/402-master-mac80211-zero-wait-dfs.patch
new file mode 100644
index 0000000..cbf29b2
--- /dev/null
+++ b/openwrt_patches-21.02/402-master-mac80211-zero-wait-dfs.patch
@@ -0,0 +1,715 @@
+diff --git a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
+index 27eecf3..ff3169c 100644
+--- a/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
++++ b/package/kernel/mac80211/files/lib/netifd/wireless/mac80211.sh
+@@ -61,7 +61,8 @@ drv_mac80211_init_device_config() {
+ 		rx_stbc \
+ 		tx_stbc \
+ 		he_bss_color \
+-		he_spr_non_srg_obss_pd_max_offset
++		he_spr_non_srg_obss_pd_max_offset \
++		radar_offchan
+ 	config_add_boolean \
+ 		ldpc \
+ 		greenfield \
+@@ -137,12 +138,9 @@ mac80211_hostapd_setup_base() {
+ 	[ -n "$acs_exclude_dfs" ] && [ "$acs_exclude_dfs" -gt 0 ] &&
+ 		append base_cfg "acs_exclude_dfs=1" "$N"
+ 
+-	json_get_vars noscan ht_coex
++	json_get_vars noscan ht_coex radar_offchan:0
+ 	json_get_values ht_capab_list ht_capab tx_burst
+-	json_get_values channel_list channels
+-
+-	[ "$auto_channel" = 0 ] && [ -z "$channel_list" ] && \
+-		channel_list="$channel"
++	json_get_values channels
+ 
+ 	set_default noscan 0
+ 
+@@ -462,10 +460,11 @@ mac80211_hostapd_setup_base() {
+ 		append base_cfg "he_mu_edca_ac_vo_timer=255" "$N"
+ 	fi
+ 
++	append base_cfg "radar_offchan=$radar_offchan" "$N"
++
+ 	hostapd_prepare_device_config "$hostapd_conf_file" nl80211
+ 	cat >> "$hostapd_conf_file" <<EOF
+ ${channel:+channel=$channel}
+-${channel_list:+chanlist=$channel_list}
+ ${hostapd_noscan:+noscan=1}
+ ${tx_burst:+tx_queue_data2_burst=$tx_burst}
+ $base_cfg
+diff --git a/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch b/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch
+new file mode 100644
+index 0000000..ba3ac4b
+--- /dev/null
++++ b/package/kernel/mac80211/patches/subsys/900-mac80211-zero-wait-dfs.patch
+@@ -0,0 +1,667 @@
++diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
++index 080d220..6278545 100644
++--- a/include/net/cfg80211.h
+++++ b/include/net/cfg80211.h
++@@ -4024,6 +4024,15 @@ struct mgmt_frame_regs {
++  * @set_sar_specs: Update the SAR (TX power) settings.
++  *
++  * @color_change: Initiate a color change.
+++ *
+++ * @set_radar_offchan: Configure dedicated offchannel chain available for
+++ *	radar/CAC detection on some hw. This chain can't be used to transmit
+++ *	or receive frames and it is bounded to a running wdev.
+++ *	Offchannel radar/CAC detection allows to avoid the CAC downtime
+++ *	switching to a different channel during CAC detection on the selected
+++ *	radar channel.
+++ *	The caller is expected to set chandef pointer to NULL in order to
+++ *	disable offchannel CAC/radar detection.
++  */
++ struct cfg80211_ops {
++ 	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
++@@ -4355,6 +4364,8 @@ struct cfg80211_ops {
++ 	int	(*color_change)(struct wiphy *wiphy,
++ 				struct net_device *dev,
++ 				struct cfg80211_color_change_settings *params);
+++	int	(*set_radar_offchan)(struct wiphy *wiphy,
+++				     struct cfg80211_chan_def *chandef);
++ };
++ 
++ /*
++@@ -7529,15 +7540,33 @@ void cfg80211_cqm_txe_notify(struct net_device *dev, const u8 *peer,
++ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp);
++ 
++ /**
++- * cfg80211_radar_event - radar detection event
+++ * __cfg80211_radar_event - radar detection event
++  * @wiphy: the wiphy
++  * @chandef: chandef for the current channel
+++ * @offchan: the radar has been detected on the offchannel chain
++  * @gfp: context flags
++  *
++  * This function is called when a radar is detected on the current chanenl.
++  */
++-void cfg80211_radar_event(struct wiphy *wiphy,
++-			  struct cfg80211_chan_def *chandef, gfp_t gfp);
+++void __cfg80211_radar_event(struct wiphy *wiphy,
+++			    struct cfg80211_chan_def *chandef,
+++			    bool offchan, gfp_t gfp);
+++
+++static inline void
+++cfg80211_radar_event(struct wiphy *wiphy,
+++		     struct cfg80211_chan_def *chandef,
+++		     gfp_t gfp)
+++{
+++	__cfg80211_radar_event(wiphy, chandef, false, gfp);
+++}
+++
+++static inline void
+++cfg80211_offchan_radar_event(struct wiphy *wiphy,
+++			     struct cfg80211_chan_def *chandef,
+++			     gfp_t gfp)
+++{
+++	__cfg80211_radar_event(wiphy, chandef, true, gfp);
+++}
++ 
++ /**
++  * cfg80211_sta_opmode_change_notify - STA's ht/vht operation mode change event
++@@ -7568,6 +7597,14 @@ void cfg80211_cac_event(struct net_device *netdev,
++ 			const struct cfg80211_chan_def *chandef,
++ 			enum nl80211_radar_event event, gfp_t gfp);
++ 
+++/**
+++ * cfg80211_offchan_cac_abort - Channel Availability Check offchan abort event
+++ * @wiphy: the wiphy
+++ *
+++ * This function is called by the driver when a Channel Availability Check
+++ * (CAC) is aborted by a offchannel dedicated chain.
+++ */
+++void cfg80211_offchan_cac_abort(struct wiphy *wiphy);
++ 
++ /**
++  * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
++diff --git a/include/net/mac80211.h b/include/net/mac80211.h
++index afab7ec..acf637d 100644
++--- a/include/net/mac80211.h
+++++ b/include/net/mac80211.h
++@@ -3939,6 +3939,14 @@ struct ieee80211_prep_tx_info {
++  *	twt structure.
++  * @twt_teardown_request: Update the hw with TWT teardown request received
++  *	from the peer.
+++ * @set_radar_offchan: Configure dedicated offchannel chain available for
+++ *	radar/CAC detection on some hw. This chain can't be used to transmit
+++ *	or receive frames and it is bounded to a running wdev.
+++ *	Offchannel radar/CAC detection allows to avoid the CAC downtime
+++ *	switching to a different channel during CAC detection on the selected
+++ *	radar channel.
+++ *	The caller is expected to set chandef pointer to NULL in order to
+++ *	disable offchannel CAC/radar detection.
++  */
++ struct ieee80211_ops {
++ 	void (*tx)(struct ieee80211_hw *hw,
++@@ -4267,6 +4275,8 @@ struct ieee80211_ops {
++ 			      struct ieee80211_twt_setup *twt);
++ 	void (*twt_teardown_request)(struct ieee80211_hw *hw,
++ 				     struct ieee80211_sta *sta, u8 flowid);
+++	int (*set_radar_offchan)(struct ieee80211_hw *hw,
+++				 struct cfg80211_chan_def *chandef);
++ };
++ 
++ /**
++diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
++index 5f4a094..7634ba0 100644
++--- a/include/uapi/linux/nl80211.h
+++++ b/include/uapi/linux/nl80211.h
++@@ -2596,6 +2596,13 @@ enum nl80211_commands {
++  * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
++  *	transmit power to stay within regulatory limits. u32, dBi.
++  *
+++ * @NL80211_ATTR_RADAR_OFFCHAN: Configure dedicated offchannel chain available for
+++ *	radar/CAC detection on some hw. This chain can't be used to transmit
+++ *	or receive frames and it is bounded to a running wdev.
+++ *	Offchannel radar/CAC detection allows to avoid the CAC downtime
+++ *	switching on a different channel during CAC detection on the selected
+++ *	radar channel.
+++ *
++  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++  * @NL80211_ATTR_MAX: highest attribute number currently defined
++  * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3101,6 +3108,8 @@ enum nl80211_attrs {
++ 
++ 	NL80211_ATTR_WIPHY_ANTENNA_GAIN,
++ 
+++	NL80211_ATTR_RADAR_OFFCHAN,
+++
++ 	/* add attributes here, update the policy in nl80211.c */
++ 
++ 	__NL80211_ATTR_AFTER_LAST,
++@@ -6000,6 +6009,9 @@ enum nl80211_feature_flags {
++  * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
++  *	detection and change announcemnts.
++  *
+++ * @NL80211_EXT_FEATURE_RADAR_OFFCHAN: Device supports offchannel radar/CAC
+++ *	detection.
+++ *
++  * @NUM_NL80211_EXT_FEATURES: number of extended features.
++  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
++  */
++@@ -6065,6 +6077,7 @@ enum nl80211_ext_feature_index {
++ 	NL80211_EXT_FEATURE_SECURE_RTT,
++ 	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
++ 	NL80211_EXT_FEATURE_BSS_COLOR,
+++	NL80211_EXT_FEATURE_RADAR_OFFCHAN,
++ 
++ 	/* add new features before the definition below */
++ 	NUM_NL80211_EXT_FEATURES,
++diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
++index 8b7c12a..887af1e 100644
++--- a/net/mac80211/cfg.c
+++++ b/net/mac80211/cfg.c
++@@ -4353,6 +4353,18 @@ out:
++ 	return err;
++ }
++ 
+++static int
+++ieee80211_set_radar_offchan(struct wiphy *wiphy,
+++			    struct cfg80211_chan_def *chandef)
+++{
+++	struct ieee80211_local *local = wiphy_priv(wiphy);
+++
+++	if (!local->ops->set_radar_offchan)
+++		return -EOPNOTSUPP;
+++
+++	return local->ops->set_radar_offchan(&local->hw, chandef);
+++}
+++
++ const struct cfg80211_ops mac80211_config_ops = {
++ 	.add_virtual_intf = ieee80211_add_iface,
++ 	.del_virtual_intf = ieee80211_del_iface,
++@@ -4458,4 +4470,5 @@ const struct cfg80211_ops mac80211_config_ops = {
++ 	.reset_tid_config = ieee80211_reset_tid_config,
++ 	.set_sar_specs = ieee80211_set_sar_specs,
++ 	.color_change = ieee80211_color_change,
+++	.set_radar_offchan = ieee80211_set_radar_offchan,
++ };
++diff --git a/net/wireless/chan.c b/net/wireless/chan.c
++index 1382a5f..75b4151 100644
++--- a/net/wireless/chan.c
+++++ b/net/wireless/chan.c
++@@ -712,6 +712,20 @@ static bool cfg80211_is_wiphy_oper_chan(struct wiphy *wiphy,
++ 	return false;
++ }
++ 
+++static bool
+++cfg80211_offchan_chain_is_active(struct cfg80211_registered_device *rdev,
+++				 struct ieee80211_channel *channel)
+++{
+++
+++	if (!rdev->offchan_radar_wdev)
+++		return false;
+++
+++	if (!cfg80211_chandef_valid(&rdev->offchan_radar_chandef))
+++		return false;
+++
+++	return cfg80211_is_sub_chan(&rdev->offchan_radar_chandef, channel);
+++}
+++
++ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
++ 				  struct ieee80211_channel *chan)
++ {
++@@ -728,6 +742,9 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
++ 
++ 		if (cfg80211_is_wiphy_oper_chan(&rdev->wiphy, chan))
++ 			return true;
+++
+++		if (cfg80211_offchan_chain_is_active(rdev, chan))
+++			return true;
++ 	}
++ 
++ 	return false;
++diff --git a/net/wireless/core.c b/net/wireless/core.c
++index b181dad..378fb4e 100644
++--- a/net/wireless/core.c
+++++ b/net/wireless/core.c
++@@ -551,6 +551,9 @@ use_default_name:
++ 	INIT_WORK(&rdev->rfkill_block, cfg80211_rfkill_block_work);
++ 	INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
++ 	INIT_WORK(&rdev->event_work, cfg80211_event_work);
+++	INIT_WORK(&rdev->offchan_cac_abort_wk, cfg80211_offchan_cac_abort_wk);
+++	INIT_DELAYED_WORK(&rdev->offchan_cac_done_wk,
+++			  cfg80211_offchan_cac_done_wk);
++ 
++ 	init_waitqueue_head(&rdev->dev_wait);
++ 
++@@ -1045,11 +1048,13 @@ void wiphy_unregister(struct wiphy *wiphy)
++ 	cancel_work_sync(&rdev->conn_work);
++ 	flush_work(&rdev->event_work);
++ 	cancel_delayed_work_sync(&rdev->dfs_update_channels_wk);
+++	cancel_delayed_work_sync(&rdev->offchan_cac_done_wk);
++ 	flush_work(&rdev->destroy_work);
++ 	flush_work(&rdev->sched_scan_stop_wk);
++ 	flush_work(&rdev->propagate_radar_detect_wk);
++ 	flush_work(&rdev->propagate_cac_done_wk);
++ 	flush_work(&rdev->mgmt_registrations_update_wk);
+++	flush_work(&rdev->offchan_cac_abort_wk);
++ 
++ #ifdef CONFIG_PM
++ 	if (rdev->wiphy.wowlan_config && rdev->ops->set_wakeup)
++@@ -1188,6 +1193,8 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
++ 
++ 	cfg80211_pmsr_wdev_down(wdev);
++ 
+++	cfg80211_stop_offchan_radar_detection(wdev);
+++
++ 	switch (wdev->iftype) {
++ 	case NL80211_IFTYPE_ADHOC:
++ 		__cfg80211_leave_ibss(rdev, dev, true);
++diff --git a/net/wireless/core.h b/net/wireless/core.h
++index 19fcdd7..7fe31aa 100644
++--- a/net/wireless/core.h
+++++ b/net/wireless/core.h
++@@ -84,6 +84,11 @@ struct cfg80211_registered_device {
++ 
++ 	struct delayed_work dfs_update_channels_wk;
++ 
+++	struct wireless_dev *offchan_radar_wdev;
+++	struct cfg80211_chan_def offchan_radar_chandef;
+++	struct delayed_work offchan_cac_done_wk;
+++	struct work_struct offchan_cac_abort_wk;
+++
++ 	/* netlink port which started critical protocol (0 means not started) */
++ 	u32 crit_proto_nlportid;
++ 
++@@ -489,6 +494,17 @@ cfg80211_chandef_dfs_cac_time(struct wiphy *wiphy,
++ 
++ void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev);
++ 
+++int
+++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
+++				       struct wireless_dev *wdev,
+++				       struct cfg80211_chan_def *chandef);
+++
+++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev);
+++
+++void cfg80211_offchan_cac_done_wk(struct work_struct *work);
+++
+++void cfg80211_offchan_cac_abort_wk(struct work_struct *work);
+++
++ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
++ 				  struct ieee80211_channel *chan);
++ 
++diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
++index a8a17b9..8f230d3 100644
++--- a/net/wireless/mlme.c
+++++ b/net/wireless/mlme.c
++@@ -903,13 +903,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work)
++ }
++ 
++ 
++-void cfg80211_radar_event(struct wiphy *wiphy,
++-			  struct cfg80211_chan_def *chandef,
++-			  gfp_t gfp)
+++void __cfg80211_radar_event(struct wiphy *wiphy,
+++			    struct cfg80211_chan_def *chandef,
+++			    bool offchan, gfp_t gfp)
++ {
++ 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
++ 
++-	trace_cfg80211_radar_event(wiphy, chandef);
+++	trace_cfg80211_radar_event(wiphy, chandef, offchan);
++ 
++ 	/* only set the chandef supplied channel to unavailable, in
++ 	 * case the radar is detected on only one of multiple channels
++@@ -917,6 +917,9 @@ void cfg80211_radar_event(struct wiphy *wiphy,
++ 	 */
++ 	cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_UNAVAILABLE);
++ 
+++	if (offchan)
+++		queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
+++
++ 	cfg80211_sched_dfs_chan_update(rdev);
++ 
++ 	nl80211_radar_notify(rdev, chandef, NL80211_RADAR_DETECTED, NULL, gfp);
++@@ -924,7 +927,7 @@ void cfg80211_radar_event(struct wiphy *wiphy,
++ 	memcpy(&rdev->radar_chandef, chandef, sizeof(struct cfg80211_chan_def));
++ 	queue_work(cfg80211_wq, &rdev->propagate_radar_detect_wk);
++ }
++-EXPORT_SYMBOL(cfg80211_radar_event);
+++EXPORT_SYMBOL(__cfg80211_radar_event);
++ 
++ void cfg80211_cac_event(struct net_device *netdev,
++ 			const struct cfg80211_chan_def *chandef,
++@@ -968,3 +971,142 @@ void cfg80211_cac_event(struct net_device *netdev,
++ 	nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
++ }
++ EXPORT_SYMBOL(cfg80211_cac_event);
+++
+++static void
+++__cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
+++			     struct wireless_dev *wdev,
+++			     const struct cfg80211_chan_def *chandef,
+++			     enum nl80211_radar_event event)
+++{
+++	struct wiphy *wiphy = &rdev->wiphy;
+++	struct net_device *netdev;
+++
+++	lockdep_assert_wiphy(&rdev->wiphy);
+++
+++	if (!cfg80211_chandef_valid(chandef))
+++		return;
+++
+++	if (!rdev->offchan_radar_wdev)
+++		return;
+++
+++	switch (event) {
+++	case NL80211_RADAR_CAC_FINISHED:
+++		cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
+++		memcpy(&rdev->cac_done_chandef, chandef, sizeof(*chandef));
+++		queue_work(cfg80211_wq, &rdev->propagate_cac_done_wk);
+++		cfg80211_sched_dfs_chan_update(rdev);
+++		wdev = rdev->offchan_radar_wdev;
+++		break;
+++	case NL80211_RADAR_CAC_ABORTED:
+++		if (!cancel_delayed_work(&rdev->offchan_cac_done_wk))
+++			return;
+++		wdev = rdev->offchan_radar_wdev;
+++		break;
+++	case NL80211_RADAR_CAC_STARTED:
+++		break;
+++	default:
+++		return;
+++	}
+++
+++	netdev = wdev ? wdev->netdev : NULL;
+++	nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL);
+++}
+++
+++static void
+++cfg80211_offchan_cac_event(struct cfg80211_registered_device *rdev,
+++			   const struct cfg80211_chan_def *chandef,
+++			   enum nl80211_radar_event event)
+++{
+++	wiphy_lock(&rdev->wiphy);
+++	__cfg80211_offchan_cac_event(rdev, rdev->offchan_radar_wdev,
+++				     chandef, event);
+++	wiphy_unlock(&rdev->wiphy);
+++}
+++
+++void cfg80211_offchan_cac_done_wk(struct work_struct *work)
+++{
+++	struct delayed_work *delayed_work = to_delayed_work(work);
+++	struct cfg80211_registered_device *rdev;
+++
+++	rdev = container_of(delayed_work, struct cfg80211_registered_device,
+++			    offchan_cac_done_wk);
+++	cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
+++				   NL80211_RADAR_CAC_FINISHED);
+++}
+++
+++void cfg80211_offchan_cac_abort_wk(struct work_struct *work)
+++{
+++	struct cfg80211_registered_device *rdev;
+++
+++	rdev = container_of(work, struct cfg80211_registered_device,
+++			    offchan_cac_abort_wk);
+++	cfg80211_offchan_cac_event(rdev, &rdev->offchan_radar_chandef,
+++				   NL80211_RADAR_CAC_ABORTED);
+++}
+++
+++void cfg80211_offchan_cac_abort(struct wiphy *wiphy)
+++{
+++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+++
+++	queue_work(cfg80211_wq, &rdev->offchan_cac_abort_wk);
+++}
+++EXPORT_SYMBOL(cfg80211_offchan_cac_abort);
+++
+++int
+++cfg80211_start_offchan_radar_detection(struct cfg80211_registered_device *rdev,
+++				       struct wireless_dev *wdev,
+++				       struct cfg80211_chan_def *chandef)
+++{
+++	unsigned int cac_time_ms;
+++	int err;
+++
+++	lockdep_assert_wiphy(&rdev->wiphy);
+++
+++	if (!wiphy_ext_feature_isset(&rdev->wiphy,
+++				     NL80211_EXT_FEATURE_RADAR_OFFCHAN))
+++		return -EOPNOTSUPP;
+++
+++	/* Offchannel chain already locked by another wdev */
+++	if (rdev->offchan_radar_wdev && rdev->offchan_radar_wdev != wdev)
+++		return -EBUSY;
+++
+++	/* CAC already in progress on the offchannel chain */
+++	if (rdev->offchan_radar_wdev == wdev &&
+++	    delayed_work_pending(&rdev->offchan_cac_done_wk))
+++		return -EBUSY;
+++
+++	err = rdev_set_radar_offchan(rdev, chandef);
+++	if (err)
+++		return err;
+++
+++	cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, chandef);
+++	if (!cac_time_ms)
+++		cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+++
+++	rdev->offchan_radar_chandef = *chandef;
+++	rdev->offchan_radar_wdev = wdev; /* Get offchain ownership */
+++
+++	__cfg80211_offchan_cac_event(rdev, wdev, chandef,
+++				     NL80211_RADAR_CAC_STARTED);
+++	queue_delayed_work(cfg80211_wq, &rdev->offchan_cac_done_wk,
+++			   msecs_to_jiffies(cac_time_ms));
+++
+++	return 0;
+++}
+++
+++void cfg80211_stop_offchan_radar_detection(struct wireless_dev *wdev)
+++{
+++	struct wiphy *wiphy = wdev->wiphy;
+++	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+++
+++	lockdep_assert_wiphy(wiphy);
+++
+++	if (wdev != rdev->offchan_radar_wdev)
+++		return;
+++
+++	rdev_set_radar_offchan(rdev, NULL);
+++	rdev->offchan_radar_wdev = NULL; /* Release offchain ownership */
+++
+++	__cfg80211_offchan_cac_event(rdev, wdev, &rdev->offchan_radar_chandef,
+++				     NL80211_RADAR_CAC_ABORTED);
+++}
++diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
++index 4a3de61..0cf9f68 100644
++--- a/net/wireless/nl80211.c
+++++ b/net/wireless/nl80211.c
++@@ -781,6 +781,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
++ 	[NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 },
++ 	[NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy),
++ 	[NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
+++	[NL80211_ATTR_RADAR_OFFCHAN] = { .type = NLA_FLAG },
++ };
++ 
++ /* policy for the key attributes */
++@@ -9106,38 +9107,60 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
++ 	struct cfg80211_chan_def chandef;
++ 	enum nl80211_dfs_regions dfs_region;
++ 	unsigned int cac_time_ms;
++-	int err;
+++	int err = -EINVAL;
+++
+++	flush_delayed_work(&rdev->dfs_update_channels_wk);
+++
+++	wiphy_lock(wiphy);
++ 
++ 	dfs_region = reg_get_dfs_region(wiphy);
++ 	if (dfs_region == NL80211_DFS_UNSET)
++-		return -EINVAL;
+++		goto unlock;
++ 
++ 	err = nl80211_parse_chandef(rdev, info, &chandef);
++ 	if (err)
++-		return err;
++-
++-	if (netif_carrier_ok(dev))
++-		return -EBUSY;
++-
++-	if (wdev->cac_started)
++-		return -EBUSY;
+++		goto unlock;
++ 
++ 	err = cfg80211_chandef_dfs_required(wiphy, &chandef, wdev->iftype);
++ 	if (err < 0)
++-		return err;
+++		goto unlock;
++ 
++-	if (err == 0)
++-		return -EINVAL;
+++	if (err == 0) {
+++		err = -EINVAL;
+++		goto unlock;
+++	}
++ 
++-	if (!cfg80211_chandef_dfs_usable(wiphy, &chandef))
++-		return -EINVAL;
+++	if (!cfg80211_chandef_dfs_usable(wiphy, &chandef)) {
+++		err = -EINVAL;
+++		goto unlock;
+++	}
+++
+++	if (nla_get_flag(info->attrs[NL80211_ATTR_RADAR_OFFCHAN])) {
+++		err = cfg80211_start_offchan_radar_detection(rdev, wdev,
+++							     &chandef);
+++		goto unlock;
+++	}
+++
+++	if (netif_carrier_ok(dev)) {
+++		err = -EBUSY;
+++		goto unlock;
+++	}
+++
+++	if (wdev->cac_started) {
+++		err = -EBUSY;
+++		goto unlock;
+++	}
++ 
++ 	/* CAC start is offloaded to HW and can't be started manually */
++-	if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD))
++-		return -EOPNOTSUPP;
+++	if (wiphy_ext_feature_isset(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD)) {
+++		err = -EOPNOTSUPP;
+++		goto unlock;
+++	}
++ 
++-	if (!rdev->ops->start_radar_detection)
++-		return -EOPNOTSUPP;
+++	if (!rdev->ops->start_radar_detection) {
+++		err = -EOPNOTSUPP;
+++		goto unlock;
+++	}
++ 
++ 	cac_time_ms = cfg80211_chandef_dfs_cac_time(&rdev->wiphy, &chandef);
++ 	if (WARN_ON(!cac_time_ms))
++@@ -9150,6 +9173,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
++ 		wdev->cac_start_time = jiffies;
++ 		wdev->cac_time_ms = cac_time_ms;
++ 	}
+++unlock:
+++	wiphy_unlock(wiphy);
+++
++ 	return err;
++ }
++ 
++@@ -15762,7 +15788,8 @@ static const struct genl_small_ops nl80211_small_ops[] = {
++ 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
++ 		.doit = nl80211_start_radar_detection,
++ 		.flags = GENL_UNS_ADMIN_PERM,
++-		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP,
+++		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+++				  NL80211_FLAG_NO_WIPHY_MTX,
++ 	},
++ 	{
++ 		.cmd = NL80211_CMD_GET_PROTOCOL_FEATURES,
++diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
++index 36422cf..c566f47 100644
++--- a/net/wireless/rdev-ops.h
+++++ b/net/wireless/rdev-ops.h
++@@ -1381,4 +1381,21 @@ static inline int rdev_color_change(struct cfg80211_registered_device *rdev,
++ 	return ret;
++ }
++ 
+++static inline int
+++rdev_set_radar_offchan(struct cfg80211_registered_device *rdev,
+++		       struct cfg80211_chan_def *chandef)
+++{
+++	struct wiphy *wiphy = &rdev->wiphy;
+++	int ret;
+++
+++	if (!rdev->ops->set_radar_offchan)
+++		return -EOPNOTSUPP;
+++
+++	trace_rdev_set_radar_offchan(wiphy, chandef);
+++	ret = rdev->ops->set_radar_offchan(wiphy, chandef);
+++	trace_rdev_return_int(wiphy, ret);
+++
+++	return ret;
+++}
+++
++ #endif /* __CFG80211_RDEV_OPS */
++diff --git a/net/wireless/trace.h b/net/wireless/trace.h
++index 973ce68..cd60ada 100644
++--- a/net/wireless/trace.h
+++++ b/net/wireless/trace.h
++@@ -3022,18 +3022,21 @@ TRACE_EVENT(cfg80211_ch_switch_started_notify,
++ );
++ 
++ TRACE_EVENT(cfg80211_radar_event,
++-	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
++-	TP_ARGS(wiphy, chandef),
+++	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef,
+++		 bool offchan),
+++	TP_ARGS(wiphy, chandef, offchan),
++ 	TP_STRUCT__entry(
++ 		WIPHY_ENTRY
++ 		CHAN_DEF_ENTRY
+++		__field(bool, offchan)
++ 	),
++ 	TP_fast_assign(
++ 		WIPHY_ASSIGN;
++ 		CHAN_DEF_ASSIGN(chandef);
+++		__entry->offchan = offchan;
++ 	),
++-	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
++-		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+++	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", offchan %d",
+++		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->offchan)
++ );
++ 
++ TRACE_EVENT(cfg80211_cac_event,
++@@ -3643,6 +3646,25 @@ TRACE_EVENT(cfg80211_bss_color_notify,
++ 		  __entry->color_bitmap)
++ );
++ 
+++TRACE_EVENT(rdev_set_radar_offchan,
+++	TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+++
+++	TP_ARGS(wiphy, chandef),
+++
+++	TP_STRUCT__entry(
+++		WIPHY_ENTRY
+++		CHAN_DEF_ENTRY
+++	),
+++
+++	TP_fast_assign(
+++		WIPHY_ASSIGN;
+++		CHAN_DEF_ASSIGN(chandef)
+++	),
+++
+++	TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+++		  WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+++);
+++
++ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
++ 
++ #undef TRACE_INCLUDE_PATH
++