[][MAC80211][hostapd][zero wait dfs]

[Description]
Add zero wait DFS for hostapd
The following patches are for openwrt 2102:
 404-2102-hostapd-sync-include-uapi-linux-nl80211.patch
 405-2102-hostapd-zero-wait-dfs.patch
The following patches are for openwrt master:
 406-master-hostapd-sync-include-uapi-linux-nl80211.patch
 407-master-hostapd-zero-wait-dfs.patch

[Release-log]

Change-Id: I6df36dc7178c236e0e79f6b934e9600fad3c5bcd
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5342191
diff --git a/openwrt_patches-21.02/404-2102-hostapd-sync-include-uapi-linux-nl80211.patch b/openwrt_patches-21.02/404-2102-hostapd-sync-include-uapi-linux-nl80211.patch
new file mode 100644
index 0000000..f3dd2cd
--- /dev/null
+++ b/openwrt_patches-21.02/404-2102-hostapd-sync-include-uapi-linux-nl80211.patch
@@ -0,0 +1,169 @@
+diff --git a/package/network/services/hostapd/patches/903-2102-sync-include-uapi-linux-nl80211.patch b/package/network/services/hostapd/patches/903-2102-sync-include-uapi-linux-nl80211.patch
+new file mode 100644
+index 0000000..51a0d7f
+--- /dev/null
++++ b/package/network/services/hostapd/patches/903-2102-sync-include-uapi-linux-nl80211.patch
+@@ -0,0 +1,163 @@
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index 9679d56..ab84efc 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2483,6 +2483,56 @@ enum nl80211_commands {
++  *
++  * @NL80211_ATTR_RECEIVE_MULTICAST: multicast flag for the
++  *	%NL80211_CMD_REGISTER_FRAME command, see the description there.
+++ * @NL80211_ATTR_WIPHY_FREQ_OFFSET: offset of the associated
+++ *	%NL80211_ATTR_WIPHY_FREQ in positive KHz. Only valid when supplied with
+++ *	an %NL80211_ATTR_WIPHY_FREQ_OFFSET.
+++ * @NL80211_ATTR_CENTER_FREQ1_OFFSET: Center frequency offset in KHz for the
+++ *	first channel segment specified in %NL80211_ATTR_CENTER_FREQ1.
+++ * @NL80211_ATTR_SCAN_FREQ_KHZ: nested attribute with KHz frequencies
+++ *
+++ * @NL80211_ATTR_HE_6GHZ_CAPABILITY: HE 6 GHz Band Capability element (from
+++ *	association request when used with NL80211_CMD_NEW_STATION).
+++ *
+++ * @NL80211_ATTR_FILS_DISCOVERY: Optional parameter to configure FILS
+++ *	discovery. It is a nested attribute, see
+++ *	&enum nl80211_fils_discovery_attributes.
+++ *
+++ * @NL80211_ATTR_UNSOL_BCAST_PROBE_RESP: Optional parameter to configure
+++ *	unsolicited broadcast probe response. It is a nested attribute, see
+++ *	&enum nl80211_unsol_bcast_probe_resp_attributes.
+++ *
+++ * @NL80211_ATTR_S1G_CAPABILITY: S1G Capability information element (from
+++ *	association request when used with NL80211_CMD_NEW_STATION)
+++ * @NL80211_ATTR_S1G_CAPABILITY_MASK: S1G Capability Information element
+++ *	override mask. Used with NL80211_ATTR_S1G_CAPABILITY in
+++ *	NL80211_CMD_ASSOCIATE or NL80211_CMD_CONNECT.
+++ *
+++ * @NL80211_ATTR_SAE_PWE: Indicates the mechanism(s) allowed for SAE PWE
+++ *	derivation in WPA3-Personal networks which are using SAE authentication.
+++ *	This is a u8 attribute that encapsulates one of the values from
+++ *	&enum nl80211_sae_pwe_mechanism.
+++ *
+++ * @NL80211_ATTR_SAR_SPEC: SAR power limitation specification when
+++ *	used with %NL80211_CMD_SET_SAR_SPECS. The message contains fields
+++ *	of %nl80211_sar_attrs which specifies the sar type and related
+++ *	sar specs. Sar specs contains array of %nl80211_sar_specs_attrs.
+++ *
+++ * @NL80211_ATTR_RECONNECT_REQUESTED: flag attribute, used with deauth and
+++ *	disassoc events to indicate that an immediate reconnect to the AP
+++ *	is desired.
+++ *
+++ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+++ *	%NL80211_CMD_OBSS_COLOR_COLLISION event.
+++ *
+++ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+++ *	until the color switch event.
+++ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+++ *	switching to
+++ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+++ *	information for the time while performing a color switch.
+++ *
+++ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+++ *	transmit power to stay within regulatory limits. u32, dBi.
++  *
++  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++  * @NL80211_ATTR_MAX: highest attribute number currently defined
++@@ -2960,6 +3010,34 @@ enum nl80211_attrs {
++ 	NL80211_ATTR_PMK_REAUTH_THRESHOLD,
++ 
++ 	NL80211_ATTR_RECEIVE_MULTICAST,
+++	NL80211_ATTR_WIPHY_FREQ_OFFSET,
+++	NL80211_ATTR_CENTER_FREQ1_OFFSET,
+++	NL80211_ATTR_SCAN_FREQ_KHZ,
+++
+++	NL80211_ATTR_HE_6GHZ_CAPABILITY,
+++
+++	NL80211_ATTR_FILS_DISCOVERY,
+++
+++	NL80211_ATTR_UNSOL_BCAST_PROBE_RESP,
+++
+++	NL80211_ATTR_S1G_CAPABILITY,
+++	NL80211_ATTR_S1G_CAPABILITY_MASK,
+++
+++	NL80211_ATTR_SAE_PWE,
+++
+++	NL80211_ATTR_RECONNECT_REQUESTED,
+++
+++	NL80211_ATTR_SAR_SPEC,
+++
+++	NL80211_ATTR_DISABLE_HE,
+++
+++	NL80211_ATTR_OBSS_COLOR_BITMAP,
+++
+++	NL80211_ATTR_COLOR_CHANGE_COUNT,
+++	NL80211_ATTR_COLOR_CHANGE_COLOR,
+++	NL80211_ATTR_COLOR_CHANGE_ELEMS,
+++
+++	NL80211_ATTR_WIPHY_ANTENNA_GAIN,
++ 
++ 	/* add attributes here, update the policy in nl80211.c */
++ 
++@@ -5705,6 +5783,46 @@ enum nl80211_feature_flags {
++  * @NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS: management frame registrations
++  *	are possible for multicast frames and those will be reported properly.
++  *
+++ * @NL80211_EXT_FEATURE_SCAN_FREQ_KHZ: This driver supports receiving and
+++ *	reporting scan request with %NL80211_ATTR_SCAN_FREQ_KHZ. In order to
+++ *	report %NL80211_ATTR_SCAN_FREQ_KHZ, %NL80211_SCAN_FLAG_FREQ_KHZ must be
+++ *	included in the scan request.
+++ *
+++ * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS: The driver
+++ *	can report tx status for control port over nl80211 tx operations.
+++ *
+++ * @NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION: Driver supports Operating
+++ *	Channel Validation (OCV) when using driver's SME for RSNA handshakes.
+++ *
+++ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK: Device wants to do 4-way
+++ *	handshake with PSK in AP mode (PSK is passed as part of the start AP
+++ *	command).
+++ *
+++ * @NL80211_EXT_FEATURE_SAE_OFFLOAD_AP: Device wants to do SAE authentication
+++ *	in AP mode (SAE password is passed as part of the start AP command).
+++ *
+++ * @NL80211_EXT_FEATURE_FILS_DISCOVERY: Driver/device supports FILS discovery
+++ *	frames transmission
+++ *
+++ * @NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP: Driver/device supports
+++ *	unsolicited broadcast probe response transmission
+++ *
+++ * @NL80211_EXT_FEATURE_BEACON_RATE_HE: Driver supports beacon rate
+++ *	configuration (AP/mesh) with HE rates.
+++ *
+++ * @NL80211_EXT_FEATURE_SECURE_LTF: Device supports secure LTF measurement
+++ *      exchange protocol.
+++ *
+++ * @NL80211_EXT_FEATURE_SECURE_RTT: Device supports secure RTT measurement
+++ *      exchange protocol.
+++ *
+++ * @NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE: Device supports management
+++ *      frame protection for all management frames exchanged during the
+++ *      negotiation and range measurement procedure.
+++ *
+++ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+++ *	detection and change announcemnts.
+++ *
++  * @NUM_NL80211_EXT_FEATURES: number of extended features.
++  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
++  */
++@@ -5758,6 +5876,18 @@ enum nl80211_ext_feature_index {
++ 	NL80211_EXT_FEATURE_DEL_IBSS_STA,
++ 	NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS,
++ 	NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT,
+++	NL80211_EXT_FEATURE_SCAN_FREQ_KHZ,
+++	NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_TX_STATUS,
+++	NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION,
+++	NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK,
+++	NL80211_EXT_FEATURE_SAE_OFFLOAD_AP,
+++	NL80211_EXT_FEATURE_FILS_DISCOVERY,
+++	NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP,
+++	NL80211_EXT_FEATURE_BEACON_RATE_HE,
+++	NL80211_EXT_FEATURE_SECURE_LTF,
+++	NL80211_EXT_FEATURE_SECURE_RTT,
+++	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+++	NL80211_EXT_FEATURE_BSS_COLOR,
++ 
++ 	/* add new features before the definition below */
++ 	NUM_NL80211_EXT_FEATURES,
++
diff --git a/openwrt_patches-21.02/405-2102-hostapd-zero-wait-dfs.patch b/openwrt_patches-21.02/405-2102-hostapd-zero-wait-dfs.patch
new file mode 100644
index 0000000..3b01e26
--- /dev/null
+++ b/openwrt_patches-21.02/405-2102-hostapd-zero-wait-dfs.patch
@@ -0,0 +1,854 @@
+diff --git a/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch b/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch
+new file mode 100644
+index 0000000..417c311
+--- /dev/null
++++ b/package/network/services/hostapd/patches/904-2102-zero-wait_dfs.patch
+@@ -0,0 +1,848 @@
++diff --git a/hostapd/config_file.c b/hostapd/config_file.c
++index 6b6101f..704afbb 100644
++--- a/hostapd/config_file.c
+++++ b/hostapd/config_file.c
++@@ -2524,6 +2524,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
++ 		conf->ieee80211d = atoi(pos);
++ 	} else if (os_strcmp(buf, "ieee80211h") == 0) {
++ 		conf->ieee80211h = atoi(pos);
+++	} else if (os_strcmp(buf, "radar_offchan") == 0) {
+++		conf->radar_offchan = atoi(pos);
++ 	} else if (os_strcmp(buf, "ieee8021x") == 0) {
++ 		bss->ieee802_1x = atoi(pos);
++ 	} else if (os_strcmp(buf, "eapol_version") == 0) {
++diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
++index c71f2ac..58c7034 100644
++--- a/hostapd/hostapd.conf
+++++ b/hostapd/hostapd.conf
++@@ -143,6 +143,13 @@ ssid=test
++ # ieee80211d=1 and local_pwr_constraint configured.
++ #spectrum_mgmt_required=1
++ 
+++# Enable radar/CAC detection through a dedicated offchannel chain available on
+++# some hw. The chain can't be used to transmits or receives frames.
+++# This feature allows to avoid CAC downtime switching on a different channel
+++# during CAC detection on the selected radar channel.
+++# (default: 0 = disabled, 1 = enabled)
+++#radar_offchan=0
+++
++ # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
++ # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
++ # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
++diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
++index 920dba1..44d3b85 100644
++--- a/src/ap/ap_config.h
+++++ b/src/ap/ap_config.h
++@@ -965,6 +965,7 @@ struct hostapd_config {
++ 	int ieee80211d;
++ 
++ 	int ieee80211h; /* DFS */
+++	int radar_offchan;
++ 
++ 	/*
++ 	 * Local power constraint is an octet encoded as an unsigned integer in
++diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
++index 85728d4..ed16d1f 100644
++--- a/src/ap/ap_drv_ops.c
+++++ b/src/ap/ap_drv_ops.c
++@@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 			  int channel, int ht_enabled, int vht_enabled,
++ 			  int he_enabled,
++ 			  int sec_channel_offset, int oper_chwidth,
++-			  int center_segment0, int center_segment1)
+++			  int center_segment0, int center_segment1,
+++			  int radar_offchan)
++ {
++ 	struct hostapd_data *hapd = iface->bss[0];
++ 	struct hostapd_freq_params data;
++@@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 		wpa_printf(MSG_ERROR, "Can't set freq params");
++ 		return -1;
++ 	}
+++	data.radar_offchan = radar_offchan;
++ 
++ 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
++ 	if (!res) {
++-		iface->cac_started = 1;
+++		if (radar_offchan)
+++			iface->radar_offchan.cac_started = 1;
+++		else
+++			iface->cac_started = 1;
++ 		os_get_reltime(&iface->dfs_cac_start);
++ 	}
++ 
++diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
++index 5738c1c..12c87b0 100644
++--- a/src/ap/ap_drv_ops.h
+++++ b/src/ap/ap_drv_ops.h
++@@ -130,7 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 			  int channel, int ht_enabled, int vht_enabled,
++ 			  int he_enabled,
++ 			  int sec_channel_offset, int oper_chwidth,
++-			  int center_segment0, int center_segment1);
+++			  int center_segment0, int center_segment1,
+++			  int radar_offchan);
++ int hostapd_drv_do_acs(struct hostapd_data *hapd);
++ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
++ 			     u16 reason_code, const u8 *ie, size_t ielen);
++diff --git a/src/ap/dfs.c b/src/ap/dfs.c
++index 54a61b0..217c0fc 100644
++--- a/src/ap/dfs.c
+++++ b/src/ap/dfs.c
++@@ -51,16 +51,31 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
++ 	return n_chans;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_channel_available(struct hostapd_channel_data *chan,
++-				 int skip_radar)
+++				 int flags)
++ {
+++	if (flags == 2) {
+++		/* Select only radar channel where CAC has not been
+++		 * performed yet
+++		 */
+++		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+++		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+++		     HOSTAPD_CHAN_DFS_USABLE)
+++			return 1;
+++		return 0;
+++	}
++ 	/*
++ 	 * When radar detection happens, CSA is performed. However, there's no
++ 	 * time for CAC, so radar channels must be skipped when finding a new
++ 	 * channel for CSA, unless they are available for immediate use.
++ 	 */
++-	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+++	if (flags && (chan->flag & HOSTAPD_CHAN_RADAR) &&
++ 	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
++ 	     HOSTAPD_CHAN_DFS_AVAILABLE))
++ 		return 0;
++@@ -136,10 +151,15 @@ dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
++ 	return NULL;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ 				    int first_chan_idx, int num_chans,
++-				    int skip_radar)
+++				    int flags)
++ {
++ 	struct hostapd_channel_data *first_chan, *chan;
++ 	int i;
++@@ -178,7 +198,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ 			return 0;
++ 		}
++ 
++-		if (!dfs_channel_available(chan, skip_radar)) {
+++		if (!dfs_channel_available(chan, flags)) {
++ 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
++ 				   first_chan->freq + i * 20);
++ 			return 0;
++@@ -205,10 +225,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
++  *  - hapd->secondary_channel
++  *  - hapd->vht/he_oper_centr_freq_seg0_idx
++  *  - hapd->vht/he_oper_centr_freq_seg1_idx
+++ *
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
++  */
++ static int dfs_find_channel(struct hostapd_iface *iface,
++ 			    struct hostapd_channel_data **ret_chan,
++-			    int idx, int skip_radar)
+++			    int idx, int flags)
++ {
++ 	struct hostapd_hw_modes *mode;
++ 	struct hostapd_channel_data *chan;
++@@ -233,7 +258,7 @@ static int dfs_find_channel(struct hostapd_iface *iface,
++ 		}
++ 
++ 		/* Skip incompatible chandefs */
++-		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
+++		if (!dfs_chan_range_available(mode, i, n_chans, flags)) {
++ 			wpa_printf(MSG_DEBUG,
++ 				   "DFS: range not available for %d (%d)",
++ 				   chan->freq, chan->chan);
++@@ -467,13 +492,18 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
++ 	return res;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static struct hostapd_channel_data *
++ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		      int *secondary_channel,
++ 		      u8 *oper_centr_freq_seg0_idx,
++ 		      u8 *oper_centr_freq_seg1_idx,
++-		      int skip_radar)
+++		      int flags)
++ {
++ 	struct hostapd_hw_modes *mode;
++ 	struct hostapd_channel_data *chan = NULL;
++@@ -502,7 +532,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		return NULL;
++ 
++ 	/* Get the count first */
++-	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, flags);
++ 	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
++ 		   num_available_chandefs);
++ 	if (num_available_chandefs == 0)
++@@ -523,7 +553,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		return NULL;
++ 
++ 	chan_idx = _rand % num_available_chandefs;
++-	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+++	dfs_find_channel(iface, &chan, chan_idx, flags);
++ 	if (!chan) {
++ 		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
++ 		return NULL;
++@@ -552,7 +582,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		for (i = 0; i < num_available_chandefs - 1; i++) {
++ 			/* start from chan_idx + 1, end when chan_idx - 1 */
++ 			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
++-			dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
+++			dfs_find_channel(iface, &chan2, chan_idx2, flags);
++ 			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
++ 				/* two channels are not adjacent */
++ 				sec_chan_idx_80p80 = chan2->chan;
++@@ -582,6 +612,27 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 	return chan;
++ }
++ 
+++static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+++{
+++	struct hostapd_channel_data *channel;
+++	u8 cf1 = 0, cf2 = 0;
+++	int sec = 0;
+++
+++	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+++					skip_radar);
+++	if (!channel) {
+++		wpa_printf(MSG_ERROR, "could not get valid channel");
+++		return -1;
+++	}
+++
+++	iface->freq = channel->freq;
+++	iface->conf->channel = channel->chan;
+++	iface->conf->secondary_channel = sec;
+++	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+++	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+++
+++	return 0;
+++}
++ 
++ static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
++ {
++@@ -761,6 +812,11 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++ 	return cac_time_ms;
++ }
++ 
+++static int hostapd_is_radar_offchan_enabled(struct hostapd_iface *iface)
+++{
+++	return (iface->drv_flags2 & WPA_DRIVER_RADAR_OFFCHAN) &&
+++	       iface->conf->radar_offchan;
+++}
++ 
++ /*
++  * Main DFS handler
++@@ -770,9 +826,8 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++  */
++ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ {
++-	struct hostapd_channel_data *channel;
++ 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
++-	int skip_radar = 0;
+++	int skip_radar = 0, radar_offchan;
++ 
++ 	if (is_6ghz_freq(iface->freq))
++ 		return 1;
++@@ -825,28 +880,18 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ 		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
++ 			   res, res ? "yes": "no");
++ 		if (res) {
++-			int sec = 0;
++-			u8 cf1 = 0, cf2 = 0;
++-
++-			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
++-							skip_radar);
++-			if (!channel) {
++-				wpa_printf(MSG_ERROR, "could not get valid channel");
+++			if (dfs_set_valid_channel(iface, skip_radar) < 0) {
++ 				hostapd_set_state(iface, HAPD_IFACE_DFS);
++ 				return 0;
++ 			}
++-
++-			iface->freq = channel->freq;
++-			iface->conf->channel = channel->chan;
++-			iface->conf->secondary_channel = sec;
++-			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
++-			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
++ 		}
++ 	} while (res);
++ 
++ 	/* Finally start CAC */
++ 	hostapd_set_state(iface, HAPD_IFACE_DFS);
++-	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+++	radar_offchan = hostapd_is_radar_offchan_enabled(iface);
+++	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz offchan %d",
+++		   iface->freq, radar_offchan);
++ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
++ 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
++ 		iface->freq,
++@@ -863,13 +908,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ 		iface->conf->secondary_channel,
++ 		hostapd_get_oper_chwidth(iface->conf),
++ 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
++-		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+++		radar_offchan);
++ 
++ 	if (res) {
++ 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
++ 		return -1;
++ 	}
++ 
+++	if (radar_offchan) {
+++		/* Cache offchannel radar parameters */
+++		iface->radar_offchan.channel = iface->conf->channel;
+++		iface->radar_offchan.secondary_channel =
+++			iface->conf->secondary_channel;
+++		iface->radar_offchan.freq = iface->freq;
+++		iface->radar_offchan.centr_freq_seg0_idx =
+++			hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+++		iface->radar_offchan.centr_freq_seg1_idx =
+++			hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+++
+++		/*
+++		 * Let's select a random channel for the moment
+++		 * and perform CAC on dedicated radar chain
+++		 */
+++		res = dfs_set_valid_channel(iface, 1);
+++		if (res < 0)
+++			return res;
+++
+++		iface->radar_offchan.temp_ch = 1;
+++		return 1;
+++	}
+++
++ 	return 0;
++ }
++ 
++@@ -890,6 +959,157 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
++ 	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
++ }
++ 
+++static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+++					      int channel, int freq,
+++					      int secondary_channel,
+++					      u8 oper_centr_freq_seg0_idx,
+++					      u8 oper_centr_freq_seg1_idx)
+++{
+++	struct hostapd_hw_modes *cmode = iface->current_mode;
+++	int ieee80211_mode = IEEE80211_MODE_AP, err, i;
+++	struct csa_settings csa_settings;
+++	u8 new_vht_oper_chwidth;
+++
+++	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+++	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+++		"freq=%d chan=%d sec_chan=%d", freq, channel,
+++		secondary_channel);
+++
+++	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+++	hostapd_set_oper_chwidth(iface->conf,
+++				 hostapd_get_oper_chwidth(iface->conf));
+++
+++	/* Setup CSA request */
+++	os_memset(&csa_settings, 0, sizeof(csa_settings));
+++	csa_settings.cs_count = 5;
+++	csa_settings.block_tx = 1;
+++#ifdef CONFIG_MESH
+++	if (iface->mconf)
+++		ieee80211_mode = IEEE80211_MODE_MESH;
+++#endif /* CONFIG_MESH */
+++	err = hostapd_set_freq_params(&csa_settings.freq_params,
+++				      iface->conf->hw_mode,
+++				      freq, channel,
+++				      iface->conf->enable_edmg,
+++				      iface->conf->edmg_channel,
+++				      iface->conf->ieee80211n,
+++				      iface->conf->ieee80211ac,
+++				      iface->conf->ieee80211ax,
+++				      secondary_channel,
+++				      new_vht_oper_chwidth,
+++				      oper_centr_freq_seg0_idx,
+++				      oper_centr_freq_seg1_idx,
+++				      cmode->vht_capab,
+++				      &cmode->he_capab[ieee80211_mode]);
+++
+++	if (err) {
+++		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+++		hostapd_disable_iface(iface);
+++		return err;
+++	}
+++
+++	for (i = 0; i < iface->num_bss; i++) {
+++		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+++		if (err)
+++			break;
+++	}
+++
+++	if (err) {
+++		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+++			   err);
+++		iface->freq = freq;
+++		iface->conf->channel = channel;
+++		iface->conf->secondary_channel = secondary_channel;
+++		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+++		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++						     oper_centr_freq_seg0_idx);
+++		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++						     oper_centr_freq_seg1_idx);
+++
+++		hostapd_disable_iface(iface);
+++		hostapd_enable_iface(iface);
+++
+++		return 0;
+++	}
+++
+++	/* Channel configuration will be updated once CSA completes and
+++	 * ch_switch_notify event is received */
+++	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+++
+++	return 0;
+++}
+++
+++static struct hostapd_channel_data *
+++dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+++			u8 *oper_centr_freq_seg0_idx,
+++			u8 *oper_centr_freq_seg1_idx, int *skip_radar);
+++
+++static void
+++hostpad_dfs_update_offchannel_chain(struct hostapd_iface *iface)
+++{
+++	struct hostapd_channel_data *channel;
+++	int sec = 0, flags = 2;
+++	u8 cf1 = 0, cf2 = 0;
+++
+++	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 2);
+++	if (!channel || channel->chan == iface->conf->channel)
+++		channel = dfs_downgrade_bandwidth(iface, &sec, &cf1, &cf2,
+++						  &flags);
+++	if (!channel ||
+++	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+++				  channel->freq, channel->chan,
+++				  iface->conf->ieee80211n,
+++				  iface->conf->ieee80211ac,
+++				  iface->conf->ieee80211ax,
+++				  sec, hostapd_get_oper_chwidth(iface->conf),
+++				  cf1, cf2, 1)) {
+++		/*
+++		 * Toggle interface state to enter DFS state
+++		 * until NOP is finished.
+++		 */
+++		wpa_printf(MSG_ERROR, "DFS failed start CAC offchannel");
+++		return;
+++	}
+++
+++	wpa_printf(MSG_DEBUG, "%s: setting offchannel chain to chan %d (%d MHz)",
+++		   __func__, channel->chan, channel->freq);
+++
+++	iface->radar_offchan.channel = channel->chan;
+++	iface->radar_offchan.freq = channel->freq;
+++	iface->radar_offchan.secondary_channel = sec;
+++	iface->radar_offchan.centr_freq_seg0_idx = cf1;
+++	iface->radar_offchan.centr_freq_seg1_idx = cf2;
+++}
+++
+++/* FIXME: check if all channel bandwith */
+++static int
+++hostapd_dfs_is_offchan_event(struct hostapd_iface *iface, int freq)
+++{
+++	if (iface->radar_offchan.freq != freq)
+++		return 0;
+++
+++	return 1;
+++}
+++
+++static int
+++hostapd_dfs_start_channel_switch_offchan(struct hostapd_iface *iface)
+++{
+++	iface->conf->channel = iface->radar_offchan.channel;
+++	iface->freq = iface->radar_offchan.freq;
+++	iface->conf->secondary_channel =
+++		iface->radar_offchan.secondary_channel;
+++	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++			iface->radar_offchan.centr_freq_seg0_idx);
+++	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++			iface->radar_offchan.centr_freq_seg1_idx);
+++
+++	hostpad_dfs_update_offchannel_chain(iface);
+++
+++	return hostapd_dfs_request_channel_switch(iface, iface->conf->channel,
+++						  iface->freq, iface->conf->secondary_channel,
+++						  hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+++						  hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++}
++ 
++ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 			     int ht_enabled, int chan_offset, int chan_width,
++@@ -911,6 +1131,23 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
++ 				      chan_width, cf1, cf2,
++ 				      HOSTAPD_CHAN_DFS_AVAILABLE);
+++
+++			/*
+++			 * radar event from offchannel chain for selected
+++			 * channel. Perfrom CSA, move main chain to selected
+++			 * channel and configure offchannel chain to a new DFS
+++			 * channel
+++			 */
+++			if (hostapd_is_radar_offchan_enabled(iface) &&
+++			    hostapd_dfs_is_offchan_event(iface, freq)) {
+++				iface->radar_offchan.cac_started = 0;
+++				if (iface->radar_offchan.temp_ch) {
+++					iface->radar_offchan.temp_ch = 0;
+++					return hostapd_dfs_start_channel_switch_offchan(iface);
+++				}
+++				return 0;
+++			}
+++
++ 			/*
++ 			 * Just mark the channel available when CAC completion
++ 			 * event is received in enabled state. CAC result could
++@@ -927,6 +1164,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 				iface->cac_started = 0;
++ 			}
++ 		}
+++	} else if (hostapd_is_radar_offchan_enabled(iface) &&
+++		   hostapd_dfs_is_offchan_event(iface, freq)) {
+++		iface->radar_offchan.cac_started = 0;
+++		hostpad_dfs_update_offchannel_chain(iface);
++ 	}
++ 
++ 	return 0;
++@@ -1035,6 +1276,44 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
++ 	return err;
++ }
++ 
+++static int
+++hostapd_dfs_offchan_start_channel_switch(struct hostapd_iface *iface, int freq)
+++{
+++	if (!hostapd_is_radar_offchan_enabled(iface))
+++		return -1; /* Offchannel chain not supported */
+++
+++	wpa_printf(MSG_DEBUG,
+++		   "%s called (offchannel CAC active: %s, CSA active: %s)",
+++		   __func__, iface->radar_offchan.cac_started ? "yes" : "no",
+++		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+++
+++	/* Check if CSA in progress */
+++	if (hostapd_csa_in_progress(iface))
+++		return 0;
+++
+++	/*
+++	 * If offchannel radar detation is supported and radar channel
+++	 * monitored by offchain is available switch to it without waiting
+++	 * for the CAC otherwise let's keep a random channel.
+++	 * If radar pattern is reported on offchannel chain, just switch to
+++	 * monitor another radar channel.
+++	 */
+++	if (hostapd_dfs_is_offchan_event(iface, freq)) {
+++		hostpad_dfs_update_offchannel_chain(iface);
+++		return 0;
+++	}
+++
+++	/* Offchannel not availanle yet. Perform CAC on main chain */
+++	if (iface->radar_offchan.cac_started) {
+++		/* We want to switch to monitored channel as soon as
+++		 * CAC is completed.
+++		 */
+++		iface->radar_offchan.temp_ch = 1;
+++		return -1;
+++	}
+++
+++	return hostapd_dfs_start_channel_switch_offchan(iface);
+++}
++ 
++ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ {
++@@ -1042,13 +1321,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ 	int secondary_channel;
++ 	u8 oper_centr_freq_seg0_idx;
++ 	u8 oper_centr_freq_seg1_idx;
++-	u8 new_vht_oper_chwidth;
++ 	int skip_radar = 1;
++-	struct csa_settings csa_settings;
++-	unsigned int i;
++-	int err = 1;
++-	struct hostapd_hw_modes *cmode = iface->current_mode;
++-	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++ 
++ 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
++ 		   __func__, iface->cac_started ? "yes" : "no",
++@@ -1110,69 +1383,11 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ 		}
++ 	}
++ 
++-	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
++-		   channel->chan);
++-	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
++-		"freq=%d chan=%d sec_chan=%d", channel->freq,
++-		channel->chan, secondary_channel);
++-
++-	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++-	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
++-
++-	/* Setup CSA request */
++-	os_memset(&csa_settings, 0, sizeof(csa_settings));
++-	csa_settings.cs_count = 5;
++-	csa_settings.block_tx = 1;
++-	err = hostapd_set_freq_params(&csa_settings.freq_params,
++-				      iface->conf->hw_mode,
++-				      channel->freq,
++-				      channel->chan,
++-				      iface->conf->enable_edmg,
++-				      iface->conf->edmg_channel,
++-				      iface->conf->ieee80211n,
++-				      iface->conf->ieee80211ac,
++-				      iface->conf->ieee80211ax,
++-				      secondary_channel,
++-				      new_vht_oper_chwidth,
++-				      oper_centr_freq_seg0_idx,
++-				      oper_centr_freq_seg1_idx,
++-				      cmode->vht_capab,
++-				      &cmode->he_capab[iface->conf->hw_mode]);
++-
++-	if (err) {
++-		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
++-		hostapd_disable_iface(iface);
++-		return err;
++-	}
++-
++-	for (i = 0; i < iface->num_bss; i++) {
++-		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
++-		if (err)
++-			break;
++-	}
++-
++-	if (err) {
++-		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
++-			   err);
++-		iface->freq = channel->freq;
++-		iface->conf->channel = channel->chan;
++-		iface->conf->secondary_channel = secondary_channel;
++-		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++-		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
++-						     oper_centr_freq_seg0_idx);
++-		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
++-						     oper_centr_freq_seg1_idx);
++-
++-		hostapd_disable_iface(iface);
++-		hostapd_enable_iface(iface);
++-		return 0;
++-	}
++-
++-	/* Channel configuration will be updated once CSA completes and
++-	 * ch_switch_notify event is received */
++-
++-	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
++-	return 0;
+++	return hostapd_dfs_request_channel_switch(iface, channel->chan,
+++						  channel->freq,
+++						  secondary_channel,
+++						  oper_centr_freq_seg0_idx,
+++						  oper_centr_freq_seg1_idx);
++ }
++ 
++ 
++@@ -1199,15 +1414,19 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
++ 	if (!res)
++ 		return 0;
++ 
++-	/* Skip if reported radar event not overlapped our channels */
++-	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
++-	if (!res)
++-		return 0;
+++	if (!hostapd_dfs_is_offchan_event(iface, freq)) {
+++		/* Skip if reported radar event not overlapped our channels */
+++		res = dfs_are_channels_overlapped(iface, freq, chan_width,
+++						  cf1, cf2);
+++		if (!res)
+++			return 0;
+++	}
++ 
++-	/* radar detected while operating, switch the channel. */
++-	res = hostapd_dfs_start_channel_switch(iface);
+++	if (hostapd_dfs_offchan_start_channel_switch(iface, freq))
+++		/* radar detected while operating, switch the channel. */
+++		return hostapd_dfs_start_channel_switch(iface);
++ 
++-	return res;
+++	return 0;
++ }
++ 
++ 
++@@ -1275,7 +1494,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
++ 		"seg1=%d cac_time=%ds",
++ 		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++ 		iface->dfs_cac_ms / 1000);
++-	iface->cac_started = 1;
+++
+++	if (hostapd_dfs_is_offchan_event(iface, freq))
+++		iface->radar_offchan.cac_started = 1;
+++	else
+++		iface->cac_started = 1;
++ 	os_get_reltime(&iface->dfs_cac_start);
++ 	return 0;
++ }
++diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
++index 54b3674..e9b5500 100644
++--- a/src/ap/hostapd.h
+++++ b/src/ap/hostapd.h
++@@ -517,6 +517,21 @@ struct hostapd_iface {
++ 	int *basic_rates;
++ 	int freq;
++ 
+++	/* Offchanel chain configuration */
+++	struct {
+++		int channel;
+++		int secondary_channel;
+++		int freq;
+++		int centr_freq_seg0_idx;
+++		int centr_freq_seg1_idx;
+++		/* Main chain is on temporary channel during
+++		 * CAC detection on radar offchain
+++		 */
+++		unsigned int temp_ch:1;
+++		/* CAC started on radar offchain */
+++		unsigned int cac_started:1;
+++	} radar_offchan;
+++
++ 	u16 hw_flags;
++ 
++ 	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
++diff --git a/src/drivers/driver.h b/src/drivers/driver.h
++index 05ec9c2..0521c20 100644
++--- a/src/drivers/driver.h
+++++ b/src/drivers/driver.h
++@@ -776,6 +776,11 @@ struct hostapd_freq_params {
++ 	 * for IEEE 802.11ay EDMG configuration.
++ 	 */
++ 	struct ieee80211_edmg_config edmg;
+++
+++	/**
+++	 * radar_offchan - Whether radar/CAC offchannel is requested
+++	 */
+++	int radar_offchan;
++ };
++ 
++ /**
++@@ -1938,6 +1943,8 @@ struct wpa_driver_capa {
++ 
++ /** Driver supports a separate control port RX for EAPOL frames */
++ #define WPA_DRIVER_FLAGS2_CONTROL_PORT_RX	0x0000000000000001ULL
+++/** Driver supports offchannel radar/CAC detection */
+++#define WPA_DRIVER_RADAR_OFFCHAN		0x0000000000000200ULL
++ 	u64 flags2;
++ 
++ #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
++diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
++index 72ae074..09e0bc9 100644
++--- a/src/drivers/driver_nl80211.c
+++++ b/src/drivers/driver_nl80211.c
++@@ -4576,6 +4576,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ 	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
++ 	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
++ 	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+++	wpa_printf(MSG_DEBUG, "  * radar_offchan=%d", freq->radar_offchan);
++ 
++ 	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
++ 	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++@@ -4653,6 +4654,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ 				NL80211_CHAN_NO_HT))
++ 			return -ENOBUFS;
++ 	}
+++	if (freq->radar_offchan)
+++		nla_put_flag(msg, NL80211_ATTR_RADAR_OFFCHAN);
+++
++ 	return 0;
++ }
++ 
++diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
++index 3e8dcef..1dcdbe6 100644
++--- a/src/drivers/driver_nl80211_capa.c
+++++ b/src/drivers/driver_nl80211_capa.c
++@@ -639,6 +639,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
++ 	if (ext_feature_isset(ext_features, len,
++ 			      NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS))
++ 		info->drv->multicast_registrations = 1;
+++
+++	if (ext_feature_isset(ext_features, len,
+++			      NL80211_EXT_FEATURE_RADAR_OFFCHAN))
+++		capa->flags2 |= WPA_DRIVER_RADAR_OFFCHAN;
++ }
++ 
++ 
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index ab84efc..31a227c 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2534,6 +2534,10 @@ 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 chain available for radar
+++ *	detection on some hw. The chain can't be used to transmits or receives
+++ *	frames. The driver is supposed to implement CAC management in sw or fw.
+++ *
++  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++  * @NL80211_ATTR_MAX: highest attribute number currently defined
++  * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3039,6 +3043,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,
++@@ -5823,6 +5829,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.
++  */
++@@ -5888,6 +5897,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/openwrt_patches-21.02/406-master-hostapd-sync-include-uapi-linux-nl80211.patch b/openwrt_patches-21.02/406-master-hostapd-sync-include-uapi-linux-nl80211.patch
new file mode 100644
index 0000000..b804a97
--- /dev/null
+++ b/openwrt_patches-21.02/406-master-hostapd-sync-include-uapi-linux-nl80211.patch
@@ -0,0 +1,63 @@
+diff --git a/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch b/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch
+new file mode 100644
+index 0000000..fe47b57
+--- /dev/null
++++ b/package/network/services/hostapd/patches/903-master-sync-include-uapi-linux-nl80211.patch
+@@ -0,0 +1,57 @@
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index f962c06..f7be755 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2560,6 +2560,19 @@ enum nl80211_commands {
++  *	disassoc events to indicate that an immediate reconnect to the AP
++  *	is desired.
++  *
+++ * @NL80211_ATTR_OBSS_COLOR_BITMAP: bitmap of the u64 BSS colors for the
+++ *	%NL80211_CMD_OBSS_COLOR_COLLISION event.
+++ *
+++ * @NL80211_ATTR_COLOR_CHANGE_COUNT: u8 attribute specifying the number of TBTT's
+++ *	until the color switch event.
+++ * @NL80211_ATTR_COLOR_CHANGE_COLOR: u8 attribute specifying the color that we are
+++ *	switching to
+++ * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE
+++ *	information for the time while performing a color switch.
+++ *
+++ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
+++ *	transmit power to stay within regulatory limits. u32, dBi.
+++ *
++  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++  * @NL80211_ATTR_MAX: highest attribute number currently defined
++  * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3057,6 +3070,14 @@ enum nl80211_attrs {
++ 
++ 	NL80211_ATTR_DISABLE_HE,
++ 
+++	NL80211_ATTR_OBSS_COLOR_BITMAP,
+++
+++	NL80211_ATTR_COLOR_CHANGE_COUNT,
+++	NL80211_ATTR_COLOR_CHANGE_COLOR,
+++	NL80211_ATTR_COLOR_CHANGE_ELEMS,
+++
+++	NL80211_ATTR_WIPHY_ANTENNA_GAIN,
+++
++ 	/* add attributes here, update the policy in nl80211.c */
++ 
++ 	__NL80211_ATTR_AFTER_LAST,
++@@ -5950,6 +5971,9 @@ enum nl80211_feature_flags {
++  *      frame protection for all management frames exchanged during the
++  *      negotiation and range measurement procedure.
++  *
+++ * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision
+++ *	detection and change announcemnts.
+++ *
++  * @NUM_NL80211_EXT_FEATURES: number of extended features.
++  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
++  */
++@@ -6014,6 +6038,7 @@ enum nl80211_ext_feature_index {
++ 	NL80211_EXT_FEATURE_SECURE_LTF,
++ 	NL80211_EXT_FEATURE_SECURE_RTT,
++ 	NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE,
+++	NL80211_EXT_FEATURE_BSS_COLOR,
++ 
++ 	/* add new features before the definition below */
++ 	NUM_NL80211_EXT_FEATURES,
diff --git a/openwrt_patches-21.02/407-master-hostapd-zero-wait-dfs.patch b/openwrt_patches-21.02/407-master-hostapd-zero-wait-dfs.patch
new file mode 100644
index 0000000..005022a
--- /dev/null
+++ b/openwrt_patches-21.02/407-master-hostapd-zero-wait-dfs.patch
@@ -0,0 +1,857 @@
+diff --git a/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch b/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch
+new file mode 100644
+index 0000000..cb11aee
+--- /dev/null
++++ b/package/network/services/hostapd/patches/904-master-zero-wait_dfs.patch
+@@ -0,0 +1,851 @@
++diff --git a/hostapd/config_file.c b/hostapd/config_file.c
++index 1e1b685..8f6281a 100644
++--- a/hostapd/config_file.c
+++++ b/hostapd/config_file.c
++@@ -2476,6 +2476,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
++ 		conf->ieee80211d = atoi(pos);
++ 	} else if (os_strcmp(buf, "ieee80211h") == 0) {
++ 		conf->ieee80211h = atoi(pos);
+++	} else if (os_strcmp(buf, "radar_offchan") == 0) {
+++		conf->radar_offchan = atoi(pos);
++ 	} else if (os_strcmp(buf, "ieee8021x") == 0) {
++ 		bss->ieee802_1x = atoi(pos);
++ 	} else if (os_strcmp(buf, "eapol_version") == 0) {
++diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
++index a89ce9b..0c951a9 100644
++--- a/hostapd/hostapd.conf
+++++ b/hostapd/hostapd.conf
++@@ -143,6 +143,13 @@ ssid=test
++ # ieee80211d=1 and local_pwr_constraint configured.
++ #spectrum_mgmt_required=1
++ 
+++# Enable radar/CAC detection through a dedicated offchannel chain available on
+++# some hw. The chain can't be used to transmits or receives frames.
+++# This feature allows to avoid CAC downtime switching on a different channel
+++# during CAC detection on the selected radar channel.
+++# (default: 0 = disabled, 1 = enabled)
+++#radar_offchan=0
+++
++ # Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
++ # g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
++ # with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
++diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
++index 28b7efe..ffc3c2c 100644
++--- a/src/ap/ap_config.h
+++++ b/src/ap/ap_config.h
++@@ -993,6 +993,7 @@ struct hostapd_config {
++ 	int ieee80211d;
++ 
++ 	int ieee80211h; /* DFS */
+++	int radar_offchan;
++ 
++ 	/*
++ 	 * Local power constraint is an octet encoded as an unsigned integer in
++diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
++index bc49079..c97ee39 100644
++--- a/src/ap/ap_drv_ops.c
+++++ b/src/ap/ap_drv_ops.c
++@@ -810,7 +810,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 			  int channel, int ht_enabled, int vht_enabled,
++ 			  int he_enabled,
++ 			  int sec_channel_offset, int oper_chwidth,
++-			  int center_segment0, int center_segment1)
+++			  int center_segment0, int center_segment1,
+++			  int radar_offchan)
++ {
++ 	struct hostapd_data *hapd = iface->bss[0];
++ 	struct hostapd_freq_params data;
++@@ -836,10 +837,14 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 		wpa_printf(MSG_ERROR, "Can't set freq params");
++ 		return -1;
++ 	}
+++	data.radar_offchan = radar_offchan;
++ 
++ 	res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
++ 	if (!res) {
++-		iface->cac_started = 1;
+++		if (radar_offchan)
+++			iface->radar_offchan.cac_started = 1;
+++		else
+++			iface->cac_started = 1;
++ 		os_get_reltime(&iface->dfs_cac_start);
++ 	}
++ 
++diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
++index 61c8f64..92842a1 100644
++--- a/src/ap/ap_drv_ops.h
+++++ b/src/ap/ap_drv_ops.h
++@@ -130,7 +130,8 @@ int hostapd_start_dfs_cac(struct hostapd_iface *iface,
++ 			  int channel, int ht_enabled, int vht_enabled,
++ 			  int he_enabled,
++ 			  int sec_channel_offset, int oper_chwidth,
++-			  int center_segment0, int center_segment1);
+++			  int center_segment0, int center_segment1,
+++			  int radar_offchan);
++ int hostapd_drv_do_acs(struct hostapd_data *hapd);
++ int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
++ 			     u16 reason_code, const u8 *ie, size_t ielen);
++diff --git a/src/ap/dfs.c b/src/ap/dfs.c
++index eccda1a..3b1276f 100644
++--- a/src/ap/dfs.c
+++++ b/src/ap/dfs.c
++@@ -51,16 +51,31 @@ static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
++ 	return n_chans;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_channel_available(struct hostapd_channel_data *chan,
++-				 int skip_radar)
+++				 int flags)
++ {
+++	if (flags == 2) {
+++		/* Select only radar channel where CAC has not been
+++		 * performed yet
+++		 */
+++		if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+++		    (chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+++		     HOSTAPD_CHAN_DFS_USABLE)
+++			return 1;
+++		return 0;
+++	}
++ 	/*
++ 	 * When radar detection happens, CSA is performed. However, there's no
++ 	 * time for CAC, so radar channels must be skipped when finding a new
++ 	 * channel for CSA, unless they are available for immediate use.
++ 	 */
++-	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+++	if (flags && (chan->flag & HOSTAPD_CHAN_RADAR) &&
++ 	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
++ 	     HOSTAPD_CHAN_DFS_AVAILABLE))
++ 		return 0;
++@@ -136,10 +151,15 @@ dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
++ 	return NULL;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ 				    int first_chan_idx, int num_chans,
++-				    int skip_radar)
+++				    int flags)
++ {
++ 	struct hostapd_channel_data *first_chan, *chan;
++ 	int i;
++@@ -178,7 +198,7 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
++ 			return 0;
++ 		}
++ 
++-		if (!dfs_channel_available(chan, skip_radar)) {
+++		if (!dfs_channel_available(chan, flags)) {
++ 			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
++ 				   first_chan->freq + i * 20);
++ 			return 0;
++@@ -205,10 +225,15 @@ static int is_in_chanlist(struct hostapd_iface *iface,
++  *  - hapd->secondary_channel
++  *  - hapd->vht/he_oper_centr_freq_seg0_idx
++  *  - hapd->vht/he_oper_centr_freq_seg1_idx
+++ *
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
++  */
++ static int dfs_find_channel(struct hostapd_iface *iface,
++ 			    struct hostapd_channel_data **ret_chan,
++-			    int idx, int skip_radar)
+++			    int idx, int flags)
++ {
++ 	struct hostapd_hw_modes *mode;
++ 	struct hostapd_channel_data *chan;
++@@ -233,7 +258,7 @@ static int dfs_find_channel(struct hostapd_iface *iface,
++ 		}
++ 
++ 		/* Skip incompatible chandefs */
++-		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
+++		if (!dfs_chan_range_available(mode, i, n_chans, flags)) {
++ 			wpa_printf(MSG_DEBUG,
++ 				   "DFS: range not available for %d (%d)",
++ 				   chan->freq, chan->chan);
++@@ -467,13 +492,18 @@ static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
++ 	return res;
++ }
++ 
++-
+++/*
+++ * flags:
+++ * - 0: any channel
+++ * - 1: non-radar channel or radar available one
+++ * - 2: radar-only channel not yet available
+++ */
++ static struct hostapd_channel_data *
++ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		      int *secondary_channel,
++ 		      u8 *oper_centr_freq_seg0_idx,
++ 		      u8 *oper_centr_freq_seg1_idx,
++-		      int skip_radar)
+++		      int flags)
++ {
++ 	struct hostapd_hw_modes *mode;
++ 	struct hostapd_channel_data *chan = NULL;
++@@ -502,7 +532,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		return NULL;
++ 
++ 	/* Get the count first */
++-	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+++	num_available_chandefs = dfs_find_channel(iface, NULL, 0, flags);
++ 	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
++ 		   num_available_chandefs);
++ 	if (num_available_chandefs == 0)
++@@ -523,7 +553,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		return NULL;
++ 
++ 	chan_idx = _rand % num_available_chandefs;
++-	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+++	dfs_find_channel(iface, &chan, chan_idx, flags);
++ 	if (!chan) {
++ 		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
++ 		return NULL;
++@@ -552,7 +582,7 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 		for (i = 0; i < num_available_chandefs - 1; i++) {
++ 			/* start from chan_idx + 1, end when chan_idx - 1 */
++ 			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
++-			dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
+++			dfs_find_channel(iface, &chan2, chan_idx2, flags);
++ 			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
++ 				/* two channels are not adjacent */
++ 				sec_chan_idx_80p80 = chan2->chan;
++@@ -582,6 +612,27 @@ dfs_get_valid_channel(struct hostapd_iface *iface,
++ 	return chan;
++ }
++ 
+++static int dfs_set_valid_channel(struct hostapd_iface *iface, int skip_radar)
+++{
+++	struct hostapd_channel_data *channel;
+++	u8 cf1 = 0, cf2 = 0;
+++	int sec = 0;
+++
+++	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+++					skip_radar);
+++	if (!channel) {
+++		wpa_printf(MSG_ERROR, "could not get valid channel");
+++		return -1;
+++	}
+++
+++	iface->freq = channel->freq;
+++	iface->conf->channel = channel->chan;
+++	iface->conf->secondary_channel = sec;
+++	hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
+++	hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
+++
+++	return 0;
+++}
++ 
++ static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
++ {
++@@ -761,6 +812,11 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++ 	return cac_time_ms;
++ }
++ 
+++static int hostapd_is_radar_offchan_enabled(struct hostapd_iface *iface)
+++{
+++	return (iface->drv_flags2 & WPA_DRIVER_RADAR_OFFCHAN) &&
+++	       iface->conf->radar_offchan;
+++}
++ 
++ /*
++  * Main DFS handler
++@@ -770,9 +826,8 @@ static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
++  */
++ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ {
++-	struct hostapd_channel_data *channel;
++ 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
++-	int skip_radar = 0;
+++	int skip_radar = 0, radar_offchan;
++ 
++ 	if (is_6ghz_freq(iface->freq))
++ 		return 1;
++@@ -825,28 +880,18 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ 		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
++ 			   res, res ? "yes": "no");
++ 		if (res) {
++-			int sec = 0;
++-			u8 cf1 = 0, cf2 = 0;
++-
++-			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
++-							skip_radar);
++-			if (!channel) {
++-				wpa_printf(MSG_ERROR, "could not get valid channel");
+++			if (dfs_set_valid_channel(iface, skip_radar) < 0) {
++ 				hostapd_set_state(iface, HAPD_IFACE_DFS);
++ 				return 0;
++ 			}
++-
++-			iface->freq = channel->freq;
++-			iface->conf->channel = channel->chan;
++-			iface->conf->secondary_channel = sec;
++-			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
++-			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
++ 		}
++ 	} while (res);
++ 
++ 	/* Finally start CAC */
++ 	hostapd_set_state(iface, HAPD_IFACE_DFS);
++-	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+++	radar_offchan = hostapd_is_radar_offchan_enabled(iface);
+++	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz offchan %d",
+++		   iface->freq, radar_offchan);
++ 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
++ 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
++ 		iface->freq,
++@@ -863,13 +908,37 @@ int hostapd_handle_dfs(struct hostapd_iface *iface)
++ 		iface->conf->secondary_channel,
++ 		hostapd_get_oper_chwidth(iface->conf),
++ 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
++-		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
+++		radar_offchan);
++ 
++ 	if (res) {
++ 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
++ 		return -1;
++ 	}
++ 
+++	if (radar_offchan) {
+++		/* Cache offchannel radar parameters */
+++		iface->radar_offchan.channel = iface->conf->channel;
+++		iface->radar_offchan.secondary_channel =
+++			iface->conf->secondary_channel;
+++		iface->radar_offchan.freq = iface->freq;
+++		iface->radar_offchan.centr_freq_seg0_idx =
+++			hostapd_get_oper_centr_freq_seg0_idx(iface->conf);
+++		iface->radar_offchan.centr_freq_seg1_idx =
+++			hostapd_get_oper_centr_freq_seg1_idx(iface->conf);
+++
+++		/*
+++		 * Let's select a random channel for the moment
+++		 * and perform CAC on dedicated radar chain
+++		 */
+++		res = dfs_set_valid_channel(iface, 1);
+++		if (res < 0)
+++			return res;
+++
+++		iface->radar_offchan.temp_ch = 1;
+++		return 1;
+++	}
+++
++ 	return 0;
++ }
++ 
++@@ -890,6 +959,157 @@ int hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
++ 	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
++ }
++ 
+++static int hostapd_dfs_request_channel_switch(struct hostapd_iface *iface,
+++					      int channel, int freq,
+++					      int secondary_channel,
+++					      u8 oper_centr_freq_seg0_idx,
+++					      u8 oper_centr_freq_seg1_idx)
+++{
+++	struct hostapd_hw_modes *cmode = iface->current_mode;
+++	int ieee80211_mode = IEEE80211_MODE_AP, err, i;
+++	struct csa_settings csa_settings;
+++	u8 new_vht_oper_chwidth;
+++
+++	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d", channel);
+++	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+++		"freq=%d chan=%d sec_chan=%d", freq, channel,
+++		secondary_channel);
+++
+++	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
+++	hostapd_set_oper_chwidth(iface->conf,
+++				 hostapd_get_oper_chwidth(iface->conf));
+++
+++	/* Setup CSA request */
+++	os_memset(&csa_settings, 0, sizeof(csa_settings));
+++	csa_settings.cs_count = 5;
+++	csa_settings.block_tx = 1;
+++#ifdef CONFIG_MESH
+++	if (iface->mconf)
+++		ieee80211_mode = IEEE80211_MODE_MESH;
+++#endif /* CONFIG_MESH */
+++	err = hostapd_set_freq_params(&csa_settings.freq_params,
+++				      iface->conf->hw_mode,
+++				      freq, channel,
+++				      iface->conf->enable_edmg,
+++				      iface->conf->edmg_channel,
+++				      iface->conf->ieee80211n,
+++				      iface->conf->ieee80211ac,
+++				      iface->conf->ieee80211ax,
+++				      secondary_channel,
+++				      new_vht_oper_chwidth,
+++				      oper_centr_freq_seg0_idx,
+++				      oper_centr_freq_seg1_idx,
+++				      cmode->vht_capab,
+++				      &cmode->he_capab[ieee80211_mode]);
+++
+++	if (err) {
+++		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+++		hostapd_disable_iface(iface);
+++		return err;
+++	}
+++
+++	for (i = 0; i < iface->num_bss; i++) {
+++		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+++		if (err)
+++			break;
+++	}
+++
+++	if (err) {
+++		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+++			   err);
+++		iface->freq = freq;
+++		iface->conf->channel = channel;
+++		iface->conf->secondary_channel = secondary_channel;
+++		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
+++		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++						     oper_centr_freq_seg0_idx);
+++		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++						     oper_centr_freq_seg1_idx);
+++
+++		hostapd_disable_iface(iface);
+++		hostapd_enable_iface(iface);
+++
+++		return 0;
+++	}
+++
+++	/* Channel configuration will be updated once CSA completes and
+++	 * ch_switch_notify event is received */
+++	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+++
+++	return 0;
+++}
+++
+++static struct hostapd_channel_data *
+++dfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
+++			u8 *oper_centr_freq_seg0_idx,
+++			u8 *oper_centr_freq_seg1_idx, int *skip_radar);
+++
+++static void
+++hostpad_dfs_update_offchannel_chain(struct hostapd_iface *iface)
+++{
+++	struct hostapd_channel_data *channel;
+++	int sec = 0, flags = 2;
+++	u8 cf1 = 0, cf2 = 0;
+++
+++	channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2, 2);
+++	if (!channel || channel->chan == iface->conf->channel)
+++		channel = dfs_downgrade_bandwidth(iface, &sec, &cf1, &cf2,
+++						  &flags);
+++	if (!channel ||
+++	    hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+++				  channel->freq, channel->chan,
+++				  iface->conf->ieee80211n,
+++				  iface->conf->ieee80211ac,
+++				  iface->conf->ieee80211ax,
+++				  sec, hostapd_get_oper_chwidth(iface->conf),
+++				  cf1, cf2, 1)) {
+++		/*
+++		 * Toggle interface state to enter DFS state
+++		 * until NOP is finished.
+++		 */
+++		wpa_printf(MSG_ERROR, "DFS failed start CAC offchannel");
+++		return;
+++	}
+++
+++	wpa_printf(MSG_DEBUG, "%s: setting offchannel chain to chan %d (%d MHz)",
+++		   __func__, channel->chan, channel->freq);
+++
+++	iface->radar_offchan.channel = channel->chan;
+++	iface->radar_offchan.freq = channel->freq;
+++	iface->radar_offchan.secondary_channel = sec;
+++	iface->radar_offchan.centr_freq_seg0_idx = cf1;
+++	iface->radar_offchan.centr_freq_seg1_idx = cf2;
+++}
+++
+++/* FIXME: check if all channel bandwith */
+++static int
+++hostapd_dfs_is_offchan_event(struct hostapd_iface *iface, int freq)
+++{
+++	if (iface->radar_offchan.freq != freq)
+++		return 0;
+++
+++	return 1;
+++}
+++
+++static int
+++hostapd_dfs_start_channel_switch_offchan(struct hostapd_iface *iface)
+++{
+++	iface->conf->channel = iface->radar_offchan.channel;
+++	iface->freq = iface->radar_offchan.freq;
+++	iface->conf->secondary_channel =
+++		iface->radar_offchan.secondary_channel;
+++	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
+++			iface->radar_offchan.centr_freq_seg0_idx);
+++	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
+++			iface->radar_offchan.centr_freq_seg1_idx);
+++
+++	hostpad_dfs_update_offchannel_chain(iface);
+++
+++	return hostapd_dfs_request_channel_switch(iface, iface->conf->channel,
+++						  iface->freq, iface->conf->secondary_channel,
+++						  hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
+++						  hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
+++}
++ 
++ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 			     int ht_enabled, int chan_offset, int chan_width,
++@@ -911,6 +1131,23 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
++ 				      chan_width, cf1, cf2,
++ 				      HOSTAPD_CHAN_DFS_AVAILABLE);
+++
+++			/*
+++			 * radar event from offchannel chain for selected
+++			 * channel. Perfrom CSA, move main chain to selected
+++			 * channel and configure offchannel chain to a new DFS
+++			 * channel
+++			 */
+++			if (hostapd_is_radar_offchan_enabled(iface) &&
+++			    hostapd_dfs_is_offchan_event(iface, freq)) {
+++				iface->radar_offchan.cac_started = 0;
+++				if (iface->radar_offchan.temp_ch) {
+++					iface->radar_offchan.temp_ch = 0;
+++					return hostapd_dfs_start_channel_switch_offchan(iface);
+++				}
+++				return 0;
+++			}
+++
++ 			/*
++ 			 * Just mark the channel available when CAC completion
++ 			 * event is received in enabled state. CAC result could
++@@ -927,6 +1164,10 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
++ 				iface->cac_started = 0;
++ 			}
++ 		}
+++	} else if (hostapd_is_radar_offchan_enabled(iface) &&
+++		   hostapd_dfs_is_offchan_event(iface, freq)) {
+++		iface->radar_offchan.cac_started = 0;
+++		hostpad_dfs_update_offchannel_chain(iface);
++ 	}
++ 
++ 	return 0;
++@@ -1036,6 +1277,44 @@ static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
++ 	return err;
++ }
++ 
+++static int
+++hostapd_dfs_offchan_start_channel_switch(struct hostapd_iface *iface, int freq)
+++{
+++	if (!hostapd_is_radar_offchan_enabled(iface))
+++		return -1; /* Offchannel chain not supported */
+++
+++	wpa_printf(MSG_DEBUG,
+++		   "%s called (offchannel CAC active: %s, CSA active: %s)",
+++		   __func__, iface->radar_offchan.cac_started ? "yes" : "no",
+++		   hostapd_csa_in_progress(iface) ? "yes" : "no");
+++
+++	/* Check if CSA in progress */
+++	if (hostapd_csa_in_progress(iface))
+++		return 0;
+++
+++	/*
+++	 * If offchannel radar detation is supported and radar channel
+++	 * monitored by offchain is available switch to it without waiting
+++	 * for the CAC otherwise let's keep a random channel.
+++	 * If radar pattern is reported on offchannel chain, just switch to
+++	 * monitor another radar channel.
+++	 */
+++	if (hostapd_dfs_is_offchan_event(iface, freq)) {
+++		hostpad_dfs_update_offchannel_chain(iface);
+++		return 0;
+++	}
+++
+++	/* Offchannel not availanle yet. Perform CAC on main chain */
+++	if (iface->radar_offchan.cac_started) {
+++		/* We want to switch to monitored channel as soon as
+++		 * CAC is completed.
+++		 */
+++		iface->radar_offchan.temp_ch = 1;
+++		return -1;
+++	}
+++
+++	return hostapd_dfs_start_channel_switch_offchan(iface);
+++}
++ 
++ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ {
++@@ -1043,13 +1322,7 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ 	int secondary_channel;
++ 	u8 oper_centr_freq_seg0_idx;
++ 	u8 oper_centr_freq_seg1_idx;
++-	u8 new_vht_oper_chwidth;
++ 	int skip_radar = 1;
++-	struct csa_settings csa_settings;
++-	unsigned int i;
++-	int err = 1;
++-	struct hostapd_hw_modes *cmode = iface->current_mode;
++-	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++ 	int ieee80211_mode = IEEE80211_MODE_AP;
++ 
++ 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
++@@ -1113,73 +1386,16 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
++ 		}
++ 	}
++ 
++-	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
++-		   channel->chan);
++-	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
++-		"freq=%d chan=%d sec_chan=%d", channel->freq,
++-		channel->chan, secondary_channel);
++-
++-	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
++-	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
++-
++-	/* Setup CSA request */
++-	os_memset(&csa_settings, 0, sizeof(csa_settings));
++-	csa_settings.cs_count = 5;
++-	csa_settings.block_tx = 1;
++ #ifdef CONFIG_MESH
++ 	if (iface->mconf)
++ 		ieee80211_mode = IEEE80211_MODE_MESH;
++ #endif /* CONFIG_MESH */
++-	err = hostapd_set_freq_params(&csa_settings.freq_params,
++-				      iface->conf->hw_mode,
++-				      channel->freq,
++-				      channel->chan,
++-				      iface->conf->enable_edmg,
++-				      iface->conf->edmg_channel,
++-				      iface->conf->ieee80211n,
++-				      iface->conf->ieee80211ac,
++-				      iface->conf->ieee80211ax,
++-				      secondary_channel,
++-				      new_vht_oper_chwidth,
++-				      oper_centr_freq_seg0_idx,
++-				      oper_centr_freq_seg1_idx,
++-				      cmode->vht_capab,
++-				      &cmode->he_capab[ieee80211_mode]);
++-
++-	if (err) {
++-		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
++-		hostapd_disable_iface(iface);
++-		return err;
++-	}
++ 
++-	for (i = 0; i < iface->num_bss; i++) {
++-		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
++-		if (err)
++-			break;
++-	}
++-
++-	if (err) {
++-		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
++-			   err);
++-		iface->freq = channel->freq;
++-		iface->conf->channel = channel->chan;
++-		iface->conf->secondary_channel = secondary_channel;
++-		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
++-		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
++-						     oper_centr_freq_seg0_idx);
++-		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
++-						     oper_centr_freq_seg1_idx);
++-
++-		hostapd_disable_iface(iface);
++-		hostapd_enable_iface(iface);
++-		return 0;
++-	}
++-
++-	/* Channel configuration will be updated once CSA completes and
++-	 * ch_switch_notify event is received */
++-
++-	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
++-	return 0;
+++	return hostapd_dfs_request_channel_switch(iface, channel->chan,
+++						  channel->freq,
+++						  secondary_channel,
+++						  oper_centr_freq_seg0_idx,
+++						  oper_centr_freq_seg1_idx);
++ }
++ 
++ 
++@@ -1208,15 +1424,19 @@ int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
++ 	if (!res)
++ 		return 0;
++ 
++-	/* Skip if reported radar event not overlapped our channels */
++-	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
++-	if (!res)
++-		return 0;
+++	if (!hostapd_dfs_is_offchan_event(iface, freq)) {
+++		/* Skip if reported radar event not overlapped our channels */
+++		res = dfs_are_channels_overlapped(iface, freq, chan_width,
+++						  cf1, cf2);
+++		if (!res)
+++			return 0;
+++	}
++ 
++-	/* radar detected while operating, switch the channel. */
++-	res = hostapd_dfs_start_channel_switch(iface);
+++	if (hostapd_dfs_offchan_start_channel_switch(iface, freq))
+++		/* radar detected while operating, switch the channel. */
+++		return hostapd_dfs_start_channel_switch(iface);
++ 
++-	return res;
+++	return 0;
++ }
++ 
++ 
++@@ -1284,7 +1504,11 @@ int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
++ 		"seg1=%d cac_time=%ds",
++ 		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
++ 		iface->dfs_cac_ms / 1000);
++-	iface->cac_started = 1;
+++
+++	if (hostapd_dfs_is_offchan_event(iface, freq))
+++		iface->radar_offchan.cac_started = 1;
+++	else
+++		iface->cac_started = 1;
++ 	os_get_reltime(&iface->dfs_cac_start);
++ 	return 0;
++ }
++diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
++index 27b985d..1c6c94e 100644
++--- a/src/ap/hostapd.h
+++++ b/src/ap/hostapd.h
++@@ -521,6 +521,21 @@ struct hostapd_iface {
++ 	int *basic_rates;
++ 	int freq;
++ 
+++	/* Offchanel chain configuration */
+++	struct {
+++		int channel;
+++		int secondary_channel;
+++		int freq;
+++		int centr_freq_seg0_idx;
+++		int centr_freq_seg1_idx;
+++		/* Main chain is on temporary channel during
+++		 * CAC detection on radar offchain
+++		 */
+++		unsigned int temp_ch:1;
+++		/* CAC started on radar offchain */
+++		unsigned int cac_started:1;
+++	} radar_offchan;
+++
++ 	u16 hw_flags;
++ 
++ 	/* Number of associated Non-ERP stations (i.e., stations using 802.11b
++diff --git a/src/drivers/driver.h b/src/drivers/driver.h
++index 6d9194f..7ed47c0 100644
++--- a/src/drivers/driver.h
+++++ b/src/drivers/driver.h
++@@ -777,6 +777,11 @@ struct hostapd_freq_params {
++ 	 * for IEEE 802.11ay EDMG configuration.
++ 	 */
++ 	struct ieee80211_edmg_config edmg;
+++
+++	/**
+++	 * radar_offchan - Whether radar/CAC offchannel is requested
+++	 */
+++	int radar_offchan;
++ };
++ 
++ /**
++@@ -2026,6 +2031,8 @@ struct wpa_driver_capa {
++ #define WPA_DRIVER_FLAGS2_OCV			0x0000000000000080ULL
++ /** Driver expects user space implementation of SME in AP mode */
++ #define WPA_DRIVER_FLAGS2_AP_SME		0x0000000000000100ULL
+++/** Driver supports offchannel radar/CAC detection */
+++#define WPA_DRIVER_RADAR_OFFCHAN		0x0000000000000200ULL
++ 	u64 flags2;
++ 
++ #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
++diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
++index 4db8cce..62c3cd8 100644
++--- a/src/drivers/driver_nl80211.c
+++++ b/src/drivers/driver_nl80211.c
++@@ -4885,6 +4885,7 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ 	wpa_printf(MSG_DEBUG, "  * he_enabled=%d", freq->he_enabled);
++ 	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
++ 	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+++	wpa_printf(MSG_DEBUG, "  * radar_offchan=%d", freq->radar_offchan);
++ 
++ 	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
++ 	is_24ghz = hw_mode == HOSTAPD_MODE_IEEE80211G ||
++@@ -4962,6 +4963,9 @@ static int nl80211_put_freq_params(struct nl_msg *msg,
++ 				NL80211_CHAN_NO_HT))
++ 			return -ENOBUFS;
++ 	}
+++	if (freq->radar_offchan)
+++		nla_put_flag(msg, NL80211_ATTR_RADAR_OFFCHAN);
+++
++ 	return 0;
++ }
++ 
++diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
++index cd596e3..e370ef3 100644
++--- a/src/drivers/driver_nl80211_capa.c
+++++ b/src/drivers/driver_nl80211_capa.c
++@@ -665,6 +665,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
++ 	if (ext_feature_isset(ext_features, len,
++ 			      NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION))
++ 		capa->flags2 |= WPA_DRIVER_FLAGS2_OCV;
+++
+++	if (ext_feature_isset(ext_features, len,
+++			      NL80211_EXT_FEATURE_RADAR_OFFCHAN))
+++		capa->flags2 |= WPA_DRIVER_RADAR_OFFCHAN;
++ }
++ 
++ 
++diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
++index f7be755..736b483 100644
++--- a/src/drivers/nl80211_copy.h
+++++ b/src/drivers/nl80211_copy.h
++@@ -2573,6 +2573,10 @@ 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 chain available for radar
+++ *	detection on some hw. The chain can't be used to transmits or receives
+++ *	frames. The driver is supposed to implement CAC management in sw or fw.
+++ *
++  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
++  * @NL80211_ATTR_MAX: highest attribute number currently defined
++  * @__NL80211_ATTR_AFTER_LAST: internal use
++@@ -3078,6 +3082,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,
++@@ -5974,6 +5980,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.
++  */
++@@ -6039,6 +6048,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,