diff mbox series

[v2,13/14] P2P: Add support for Assited DFS for P2P2 GO in 5GHz

Message ID 1721364006-21658-14-git-send-email-quic_shivbara@quicinc.com
State Deferred
Headers show
Series Add support for P2P2 | expand

Commit Message

Shivani Baranwal July 19, 2024, 4:40 a.m. UTC
Signed-off-by: Shivani Baranwal <quic_shivbara@quicinc.com>
---
 src/ap/hostapd.c                  |  15 ++++-
 src/ap/hostapd.h                  |   3 +
 src/ap/hw_features.c              |   3 +-
 src/common/ieee802_11_common.c    |  14 ++---
 src/common/ieee802_11_defs.h      |   9 +++
 src/p2p/p2p.c                     | 116 +++++++++++++++++++++++++++++++++++++-
 src/p2p/p2p.h                     |  22 ++++++++
 src/p2p/p2p_build.c               |  44 +++++++++++++++
 src/p2p/p2p_go_neg.c              |  68 ++++++++++++++++++++++
 src/p2p/p2p_group.c               |  11 ++++
 src/p2p/p2p_i.h                   |  15 ++++-
 src/p2p/p2p_parse.c               |   9 +++
 src/p2p/p2p_utils.c               |  50 ++++++++++++++++
 wpa_supplicant/ap.c               |   1 +
 wpa_supplicant/config.c           |   2 +
 wpa_supplicant/config.h           |   2 +
 wpa_supplicant/ctrl_iface.c       |   5 ++
 wpa_supplicant/events.c           |   8 +++
 wpa_supplicant/p2p_supplicant.c   |  52 +++++++++++++++--
 wpa_supplicant/p2p_supplicant.h   |   7 +++
 wpa_supplicant/wpa_supplicant_i.h |   1 +
 21 files changed, 440 insertions(+), 17 deletions(-)
diff mbox series

Patch

diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 84a943e..81c13bf 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -2493,8 +2493,17 @@  static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 			   iface->conf->channel, iface->freq);
 
 #ifdef NEED_AP_MLME
-		/* Handle DFS only if it is not offloaded to the driver */
-		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+		if (iface->assisted_dfs_go) {
+			if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+				wpa_printf(MSG_DEBUG,
+					   "Fail: Offload not supported for assisted DFS P2P GO");
+				goto fail;
+			}
+			// FIXME: Check with shivani if this is correct or not
+			wpa_printf(MSG_DEBUG,
+				   "Request for Assisted DFS P2P GO");
+		} else if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+			/* Handle DFS only if it is not offloaded to the driver */
 			/* Check DFS */
 			res = hostapd_handle_dfs(iface);
 			if (res <= 0) {
@@ -2643,7 +2652,7 @@  static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 	}
 
 	if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
-	    !res_dfs_offload) {
+	    !res_dfs_offload && !iface->assisted_dfs_go) {
 		/*
 		 * If freq is DFS, and DFS is offloaded to the driver, then wait
 		 * for CAC to complete.
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dcf395c..66239dc 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -711,6 +711,9 @@  struct hostapd_iface {
 	bool is_no_ir;
 
 	bool is_ch_switch_dfs; /* Channel switch from ACS to DFS */
+
+	/* P2P GO in assisted DFS mode */
+	bool assisted_dfs_go;
 };
 
 /* hostapd.c */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index c455660..23b193b 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -142,7 +142,8 @@  int hostapd_get_hw_features(struct hostapd_iface *iface)
 			} else if (((feature->channels[j].flag &
 				     HOSTAPD_CHAN_RADAR) &&
 				    !(iface->drv_flags &
-				      WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+				      WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+				    !iface->assisted_dfs_go) ||
 				   (feature->channels[j].flag &
 				    HOSTAPD_CHAN_NO_IR)) {
 				feature->channels[j].flag |=
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 71a812c..fb8bebc 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -2049,7 +2049,7 @@  int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
  */
 int is_dfs_global_op_class(u8 op_class)
 {
-    return (op_class >= 118) && (op_class <= 123);
+    return (op_class >= 118) && (op_class <= 130);
 }
 
 
@@ -2405,12 +2405,12 @@  const struct oper_class_map global_op_class[] = {
 	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 144, 4, BW20, NO_P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 140, 8, BW40PLUS, NO_P2P_SUPP },
-	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 144, 8, BW40MINUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 144, 4, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 140, 8, BW40PLUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 144, 8, BW40MINUS, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 177, 4, BW20, P2P_SUPP },
 	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 173, 8, BW40PLUS, P2P_SUPP },
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 7e3233d..90b3fdc 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1746,6 +1746,7 @@  enum p2p_attr_id {
 	P2P_ATTR_FEATURE_CAPABILITY = 27,
 	P2P_ATTR_PERSISTENT_GROUP = 28,
 	P2P_ATTR_CAPABILITY_EXTENSION = 29,
+	P2P_ATTR_WLAN_AP_INFORMATION = 30,
 	P2P_ATTR_DEVICE_IDENTITY_KEY = 31,
 	P2P_ATTR_DEVICE_IDENTITY_RESOLUTION = 32,
 	P2P_ATTR_PAIRING_AND_BOOTSTRAPPING = 33,
@@ -3098,6 +3099,14 @@  struct ieee80211_s1g_beacon_compat {
 	le32 tsf_completion;
 } STRUCT_PACKED;
 
+struct ieee80211_dfs_ap_info_list {
+	u8 flag;
+	u8 bssid[ETH_ALEN];
+	u8 country[3];
+	u8 op_class;
+	u8 op_chan;
+};
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index a609dca..946cccd 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1427,7 +1427,15 @@  static int p2p_prepare_channel_pref(struct p2p_data *p2p,
 
 	p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
 		force_freq, pref_freq, go);
-	if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
+
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, freq, 0, 0)) {
+		if (ieee80211_freq_to_channel_ext(freq, 0, CONF_OPER_CHWIDTH_80MHZ,
+				&op_class, &op_channel) == NUM_HOSTAPD_MODES) {
+			p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
+			return -1;
+		}
+	} else if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
 		p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
 		return -1;
 	}
@@ -1566,6 +1574,8 @@  static void p2p_prepare_channel_best(struct p2p_data *p2p)
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
 			unsigned int force_freq, unsigned int pref_freq, int go)
 {
+	struct p2p_channels p2p_chanlist;
+
 	p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
 		force_freq, pref_freq, go);
 	if (force_freq || pref_freq) {
@@ -1575,6 +1585,18 @@  int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
 	} else {
 		p2p_prepare_channel_best(p2p);
 	}
+
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+	    p2p->op_reg_class, p2p->op_channel)) {
+		p2p_dfs_channel_filter(p2p, &p2p->channels, p2p->dfs_ap_list,
+				       p2p->num_dfs_ap, &p2p_chanlist);
+		p2p_channels_dump(p2p, "channel list after filtering DFS "
+				  " channels with WLAN AP info attr channles",
+				  &p2p_chanlist);
+		p2p_copy_channels(&p2p->channels, &p2p_chanlist, p2p->allow_6ghz);
+	}
+
 	p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
 	if (go)
 		p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
@@ -2777,6 +2799,20 @@  int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
 	p2p_buf_add_device_info(tmp, p2p, peer);
 	p2p_buf_update_ie_hdr(tmp, lpos);
 
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0, p2p->op_reg_class,
+				      p2p->op_channel) &&
+	    !is_p2p_dfs_owner(p2p)) {
+		struct wpabuf *p2p2_ie;
+
+		p2p2_ie = wpabuf_alloc(255);
+		if (!p2p2_ie)
+			return -1;
+
+		p2p_group_build_p2p2_ie(p2p, p2p2_ie, 0);
+		tmp = wpabuf_concat(p2p2_ie, tmp);
+	}
+
 	tmplen = wpabuf_len(tmp);
 	if (tmplen > len)
 		res = -1;
@@ -3043,6 +3079,84 @@  bool is_p2p_6ghz_disabled(struct p2p_data *p2p)
 	return false;
 }
 
+
+bool is_p2p_dfs_chan_enabled(struct p2p_data *p2p)
+{
+	if (p2p)
+		return p2p->cfg->dfs_chan_enable;
+	return false;
+}
+
+
+bool is_p2p_dfs_owner(struct p2p_data *p2p)
+{
+	if (p2p)
+		return p2p->cfg->dfs_owner;
+	return false;
+}
+
+
+void p2p_remove_wlan_ap_info(struct p2p_data *p2p, u8 val)
+{
+	p2p->cfg->remove_wlan_ap_info = val;
+}
+
+
+struct ieee80211_dfs_ap_info_list * p2p_dfs_get_ap_info(struct p2p_data *p2p,
+							const u8 *bssid)
+{
+	size_t i;
+
+	if (!p2p->dfs_ap_list)
+		return NULL;
+
+	for (i = 0; i < p2p->num_dfs_ap; i++) {
+		struct ieee80211_dfs_ap_info_list *dfs_ap =
+						 &p2p->dfs_ap_list[i];
+		if (ether_addr_equal(dfs_ap->bssid, bssid))
+			return dfs_ap;
+	}
+	return NULL;
+}
+
+
+void p2p_update_dfs_ap_info(struct p2p_data *p2p, const u8 *bssid, int freq,
+			    int flag)
+{
+	struct ieee80211_dfs_ap_info_list *dfs_ap;
+
+	dfs_ap = p2p_dfs_get_ap_info(p2p, bssid);
+
+	if (dfs_ap) {
+		wpa_printf(MSG_DEBUG, "Update the existing DFS AP info");
+	} else {
+		dfs_ap = os_realloc_array(p2p->dfs_ap_list, p2p->num_dfs_ap + 1,
+				sizeof(struct ieee80211_dfs_ap_info_list));
+		if (!dfs_ap) {
+			wpa_printf(MSG_DEBUG, "Unable to allocate dfs_ap memory");
+			return;
+		}
+
+		p2p->dfs_ap_list = dfs_ap;
+		dfs_ap = &p2p->dfs_ap_list[p2p->num_dfs_ap];
+		p2p->num_dfs_ap++;
+		os_memset(dfs_ap, 0, sizeof(*dfs_ap));
+	}
+	dfs_ap->flag = flag;
+	os_memcpy(dfs_ap->bssid, bssid, ETH_ALEN);
+
+	//TO-DO: update country string correctly
+	dfs_ap->country[0] = 0;
+	dfs_ap->country[1] = 0;
+
+	dfs_ap->country[2] = 0x04;
+	ieee80211_freq_to_channel_ext(freq, 0,
+				      CONF_OPER_CHWIDTH_80MHZ,
+				      &dfs_ap->op_class,
+				      &dfs_ap->op_chan);
+}
+
+
 int p2p_pairing_info_init(struct p2p_data *p2p)
 {
 	struct p2p_pairing_info *pairing_info;
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index b52dbe1..4ada7a2 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -696,6 +696,11 @@  struct p2p_config {
 	bool dfs_owner;
 
 	/**
+	 * dfs_chan_enable - Enable p2p Go to operate on dfs channel
+	 */
+	bool dfs_chan_enable;
+
+	/**
 	 * twt_power_mgmt - Enable TWT based power mgmt for P2P
 	 */
 	bool twt_power_mgmt;
@@ -706,6 +711,10 @@  struct p2p_config {
 	 */
 	u16 comeback_after;
 
+	/**
+	 * remove_wlan_ap_info - Flag not to include wlan ap info in frames
+	 */
+	u8 remove_wlan_ap_info;
 
 	/**
 	 * cb_ctx - Context to use with callback functions
@@ -1313,6 +1322,14 @@  struct p2p_config {
 	int (*pasn_update_extra_ies)(void *ctx, const u8 *peer_addr, bool dira);
 
 	int (*pasn_parse_encrypted_data)(void *ctx, const u8 *data, size_t len);
+
+	/**
+	 *
+	 * is_p2p_dfs_chan - DFS channel check
+	 *
+	 * To check if a channel is DFS channel or not.
+	 */
+	int (*is_p2p_dfs_chan)(void *ctx, int freq, int op_class, int op_chan);
 };
 
 
@@ -2643,4 +2660,9 @@  int p2p_pasn_parse_encrypted_data(struct p2p_data *p2p, const u8 *data,
 				  size_t len);
 int p2p_pasn_auth_tx_status(struct p2p_data *p2p, const u8 *data,
 			    size_t data_len, u8 acked, bool verify);
+bool is_p2p_dfs_chan_enabled(struct p2p_data *p2p);
+bool is_p2p_dfs_owner(struct p2p_data *p2p);
+void p2p_remove_wlan_ap_info(struct p2p_data *p2p, u8 val);
+void p2p_update_dfs_ap_info(struct p2p_data *p2p, const u8 *bssid, int freq,
+			    int flag);
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 33c90ed..f3e5212 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -1052,3 +1052,47 @@  struct wpabuf *p2p_encaps_p2p_vendor_ie(struct p2p_data *p2p,
 
 	return ie;
 }
+
+void p2p_buf_add_wlan_ap_info(struct wpabuf *buf,
+			      struct ieee80211_dfs_ap_info_list *dfs_ap_list,
+			      size_t list_size)
+{
+	u8 *len;
+	size_t i, size;
+
+	if (!list_size)
+		return;
+
+	wpabuf_put_u8(buf, P2P_ATTR_WLAN_AP_INFORMATION);
+	/* IE length to be filled */
+	len = wpabuf_put(buf, 2);
+
+	for (i = 0; i < list_size; i++) {
+		if (dfs_ap_list[i].flag != 1)
+			continue;
+
+		wpabuf_put_u8(buf, dfs_ap_list[i].flag);
+		wpabuf_put_data(buf, dfs_ap_list[i].bssid, ETH_ALEN);
+		wpabuf_put_data(buf, dfs_ap_list[i].country, 3);
+		wpabuf_put_u8(buf, dfs_ap_list[i].op_class);
+		wpabuf_put_u8(buf, dfs_ap_list[i].op_chan);
+	}
+
+	if (list_size > 4)
+		size = 4;
+	else
+		size = list_size;
+
+	for (i = 0; i < size; i++) {
+			if (dfs_ap_list[i].flag == 1)
+				continue;
+		wpabuf_put_u8(buf, dfs_ap_list[i].flag);
+		wpabuf_put_data(buf, dfs_ap_list[i].bssid, ETH_ALEN);
+		wpabuf_put_data(buf, dfs_ap_list[i].country, 3);
+		wpabuf_put_u8(buf, dfs_ap_list[i].op_class);
+		wpabuf_put_u8(buf, dfs_ap_list[i].op_chan);
+	}
+
+	/* Update attribute length */
+	WPA_PUT_LE16(len, (u8 *)wpabuf_put(buf, 0) - len - 2);
+}
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 84f39d2..5a3756c 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -138,6 +138,7 @@  static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
 struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 				     struct p2p_device *peer)
 {
+	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
@@ -227,6 +228,15 @@  struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
 
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
 		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+	    p2p->op_reg_class, p2p->op_channel)) {
+		len = p2p_buf_add_p2p2_ie_hdr(buf2);
+		p2p_buf_add_wlan_ap_info(buf2, p2p->dfs_ap_list,
+					 p2p->num_dfs_ap);
+		p2p_buf_update_p2p2_ie_hdr(buf2, len);
+	}
 	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
@@ -298,6 +308,7 @@  static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 					     u8 dialog_token, u8 status,
 					     u8 tie_breaker)
 {
+	u8 *len;
 	u8 group_capab;
 	size_t extra = 0;
 	u16 pw_id;
@@ -415,6 +426,15 @@  static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
 
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
 		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+	    p2p->op_reg_class, p2p->op_channel)) {
+		len = p2p_buf_add_p2p2_ie_hdr(buf2);
+		p2p_buf_add_wlan_ap_info(buf2, p2p->dfs_ap_list,
+					 p2p->num_dfs_ap);
+		p2p_buf_update_p2p2_ie_hdr(buf2, len);
+	}
 	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
@@ -1089,6 +1109,24 @@  skip:
 		 */
 		p2p_check_pref_chan(p2p, go, dev, &msg);
 
+		if (msg.wlan_ap_info) {
+			u8 *pos = (u8 *)msg.wlan_ap_info, match = 0, i;
+			if (p2p->cfg->is_p2p_dfs_chan &&
+			    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+						      p2p->op_reg_class,
+						      p2p->op_channel)) {
+				for (i = 0; i < msg.wlan_ap_info_len; i += 12) {
+					if (*(pos + 10) == p2p->op_reg_class &&
+					    *(pos + 11) == p2p->op_channel) {
+						match = 1;
+						break;
+					}
+				}
+				if (match == 0)
+					goto fail;
+			}
+		}
+
 		if (msg.config_timeout) {
 			dev->go_timeout = msg.config_timeout[0];
 			dev->client_timeout = msg.config_timeout[1];
@@ -1176,6 +1214,7 @@  static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 					     u8 dialog_token, u8 status,
 					     const u8 *resp_chan, int go)
 {
+	u8 *len;
 	struct p2p_channels res;
 	u8 group_capab;
 	size_t extra = 0;
@@ -1246,6 +1285,15 @@  static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
 		wpabuf_put_buf(buf2, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
 
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+	    p2p->op_reg_class, p2p->op_channel)) {
+		len = p2p_buf_add_p2p2_ie_hdr(buf2);
+		p2p_buf_add_wlan_ap_info(buf2, p2p->dfs_ap_list,
+					 p2p->num_dfs_ap);
+		p2p_buf_update_p2p2_ie_hdr(buf2, len);
+	}
+
 	buf = wpabuf_concat(buf2, buf);
 
 	return buf;
@@ -1485,6 +1533,26 @@  skip:
 	if (go)
 		p2p_check_pref_chan(p2p, go, dev, &msg);
 
+	if (msg.wlan_ap_info) {
+		u16 match = 0, i;
+		const u8 *pos = msg.wlan_ap_info;
+
+		if (p2p->cfg->is_p2p_dfs_chan &&
+		    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+					      p2p->op_reg_class,
+					      p2p->op_channel)) {
+			for (i = 0; i < msg.wlan_ap_info_len; i += 12) {
+				if (*(pos + 10) == p2p->op_reg_class &&
+				    *(pos + 11) == p2p->op_channel) {
+					match = 1;
+					break;
+				}
+			}
+			if (match == 0)
+				goto fail;
+		}
+	}
+
 	p2p_set_state(p2p, P2P_GO_NEG);
 	p2p_clear_timeout(p2p);
 
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 4822c28..d07f41e 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -217,6 +217,17 @@  struct wpabuf * p2p_group_build_p2p2_ie(struct p2p_data *p2p,
 	wpabuf_put_be32(p2p2_ie, P2P2_IE_VENDOR_TYPE);
 	wpa_printf(MSG_DEBUG, "P2P: * P2P2 IE header");
 	p2p_buf_add_pcea(p2p2_ie, p2p);
+	if (p2p->cfg->remove_wlan_ap_info)
+		goto out;
+
+	if (p2p->cfg->is_p2p_dfs_chan &&
+	    p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, freq, p2p->op_reg_class,
+				      p2p->op_channel) &&
+	    !is_p2p_dfs_owner(p2p)) {
+		p2p_buf_add_wlan_ap_info(p2p2_ie, p2p->dfs_ap_list,
+					 p2p->num_dfs_ap);
+	}
+out:
 	*len = (u8 *)wpabuf_put(p2p2_ie, 0) - len - 1;
 
 	return p2p2_ie;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 80e1988..2309dc8 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -698,6 +698,10 @@  struct p2p_data {
 	 * Indicate that auto go is enabled for this device
 	 */
 	u8 auto_go;
+
+	/* list of dfs APs */
+	struct ieee80211_dfs_ap_info_list *dfs_ap_list;
+	size_t num_dfs_ap;
 };
 
 /**
@@ -816,6 +820,9 @@  struct p2p_message {
 
 	const u8 *dira;
 	size_t dira_len;
+
+	const u8 *wlan_ap_info;
+	size_t wlan_ap_info_len;
 };
 
 
@@ -1086,7 +1093,13 @@  void p2p_pref_channel_filter(const struct p2p_channels *a,
 void p2p_sd_query_cb(struct p2p_data *p2p, int success);
 void p2p_pasn_initialize(struct p2p_data *p2p, struct p2p_device *dev,
 			 const u8 *addr, int freq);
-
+void p2p_dfs_channel_filter(struct p2p_data *p2p,
+			    const struct p2p_channels *p2p_chan,
+			    const struct ieee80211_dfs_ap_info_list *ap_list,
+			    size_t num_dfs_ap, struct p2p_channels *res);
+void p2p_buf_add_wlan_ap_info(struct wpabuf *buf,
+			      struct ieee80211_dfs_ap_info_list *dfs_ap_list,
+			      size_t list_size);
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index de2a43f..d863b72 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -457,6 +457,15 @@  static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
 		msg->dira_len = len;
 		wpa_printf(MSG_DEBUG, "P2P: * DIRA (length=%u)", len);
 		break;
+	case P2P_ATTR_WLAN_AP_INFORMATION:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG, "P2P: Too short WLAN AP info (length %d)",
+				   len);
+			return -1;
+		}
+		msg->wlan_ap_info = data;
+		msg->wlan_ap_info_len = len;
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index c1f0084..35cbb16 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -611,3 +611,53 @@  void p2p_pref_channel_filter(const struct p2p_channels *p2p_chan,
 		res_reg->reg_class = reg->reg_class;
 	}
 }
+
+
+static int
+p2p_check_dfs_channel(int channel, u8 op_class,
+		      const struct ieee80211_dfs_ap_info_list *ap_list,
+		      unsigned int num_dfs_ap)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_dfs_ap; i++) {
+		if (op_class == ap_list[i].op_class &&
+		    channel == ap_list[i].op_chan)
+			return 0;
+	}
+	return -1;
+}
+
+
+void p2p_dfs_channel_filter(struct p2p_data *p2p,
+			    const struct p2p_channels *p2p_chan,
+			    const struct ieee80211_dfs_ap_info_list *ap_list,
+			    size_t num_dfs_ap, struct p2p_channels *res)
+{
+	size_t i, j;
+
+	os_memset(res, 0, sizeof(*res));
+
+	for (i = 0; i < p2p_chan->reg_classes; i++) {
+		const struct p2p_reg_class *reg = &p2p_chan->reg_class[i];
+		struct p2p_reg_class *res_reg = &res->reg_class[i];
+
+		for (j = 0; j < reg->channels; j++) {
+			if (p2p->cfg->is_p2p_dfs_chan(p2p->cfg->cb_ctx, 0,
+						      reg->reg_class,
+						      reg->channel[j]) &&
+			    p2p_check_dfs_channel(reg->channel[j],
+						  reg->reg_class, ap_list,
+						  num_dfs_ap) < 0)
+				continue;
+
+			res_reg->channel[res_reg->channels++] =
+				reg->channel[j];
+		}
+
+		if (res_reg->channels == 0)
+			continue;
+		res->reg_classes++;
+		res_reg->reg_class = reg->reg_class;
+	}
+}
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 69a0e5e..9ca266a 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -1072,6 +1072,7 @@  int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
 	hapd_iface->extended_capa_len = wpa_s->extended_capa_len;
 	hapd_iface->drv_max_acl_mac_addrs = wpa_s->drv_max_acl_mac_addrs;
+	hapd_iface->assisted_dfs_go = wpa_s->assisted_dfs_go;
 
 	wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
 	if (conf == NULL) {
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 24861c6..db17c3f 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -5486,6 +5486,8 @@  static const struct global_parse_data global_fields[] = {
 	{ FUNC(p2p_device_persistent_mac_addr), 0 },
 	{ INT(p2p_interface_random_mac_addr), 0 },
 	{ INT(p2p_6ghz_disable), 0 },
+	{ INT(p2p_dfs_chan_enable), 0 },
+	{ INT(p2p_dfs_owner), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 8981305..2dd6b72 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -858,6 +858,8 @@  struct wpa_config {
 	int p2p_optimize_listen_chan;
 
 	int p2p_6ghz_disable;
+	int p2p_dfs_chan_enable;
+	int p2p_dfs_owner;
 
 	struct wpabuf *wps_vendor_ext_m1;
 
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 3f247a7..76ba48b 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -7748,6 +7748,11 @@  static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
 		return 0;
 	}
 
+	if (os_strcmp(cmd, "remove_wlan_ap_info") == 0) {
+		p2p_remove_wlan_ap_info(wpa_s->global->p2p, atoi(param));
+		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index a198674..687591e 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -390,6 +390,7 @@  void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 	sme_clear_on_disassoc(wpa_s);
 	wpa_s->current_bss = NULL;
 	wpa_s->assoc_freq = 0;
+	wpa_s->assisted_dfs_go = 0;
 
 	if (bssid_changed)
 		wpas_notify_bssid_changed(wpa_s);
@@ -2514,6 +2515,7 @@  static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
 	wpas_notify_scan_done(wpa_s, 1);
 
+	wpas_p2p_update_dfs_ap_info_list(wpa_s, scan_res);
 	if (ap) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore scan results in AP mode");
 #ifdef CONFIG_AP
@@ -3699,6 +3701,12 @@  no_pfs:
 	}
 
 	wpa_s->assoc_freq = data->assoc_info.freq;
+	if (ieee80211_is_dfs(wpa_s->assoc_freq, wpa_s->hw.modes,
+			     wpa_s->hw.num_modes)) {
+		wpa_s->assisted_dfs_go = 1;
+		p2p_update_dfs_ap_info(wpa_s->global->p2p, bssid,
+				       wpa_s->assoc_freq, 1);
+	}
 
 #ifndef CONFIG_NO_ROBUST_AV
 	wpas_handle_assoc_resp_qos_mgmt(wpa_s, data->assoc_info.resp_ies,
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 7283719..d55a439 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -2518,6 +2518,7 @@  wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
 
 	wpas_p2p_clone_config(group_wpa_s, wpa_s);
 	group_wpa_s->p2p2 = wpa_s->p2p2;
+	group_wpa_s->assisted_dfs_go = wpa_s->assisted_dfs_go;
 
 	if (wpa_s->conf->p2p_interface_random_mac_addr) {
 		if (wpa_drv_set_mac_addr(group_wpa_s,
@@ -4191,8 +4192,11 @@  static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
 		const struct oper_class_map *o = &global_op_class[op];
 		unsigned int ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
+		bool check_dfs_supported =
+			(is_p2p_dfs_chan_enabled(wpa_s->global->p2p) &&
+			 is_dfs_global_op_class(o->op_class));
 
-		if (o->p2p == NO_P2P_SUPP ||
+		if ((!check_dfs_supported && o->p2p == NO_P2P_SUPP) ||
 		    (is_6ghz_op_class(o->op_class) && p2p_disable_6ghz))
 			continue;
 
@@ -4210,10 +4214,11 @@  static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
 			if ((o->op_class >= 128 && o->op_class <= 130) &&
 			    ch < 149 && ch + o->inc > 149)
 				ch = 149;
-
+			//FIXME
+			wpa_s->p2p_go_allow_dfs = 1;
 			res = wpas_p2p_verify_channel(wpa_s, mode, o->op_class,
 						      ch, o->bw);
-			if (res == ALLOWED) {
+			if (res == ALLOWED || (res == RADAR && check_dfs_supported)) {
 				if (reg == NULL) {
 					if (cla == P2P_MAX_REG_CLASSES)
 						continue;
@@ -5347,6 +5352,19 @@  int wpas_p2p_mac_setup(struct wpa_supplicant *wpa_s)
 }
 
 
+static int wpas_p2p_dfs_chan(void *ctx, int freq, int op_class, int op_chan)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (freq == 0)
+		freq = ieee80211_chan_to_freq(NULL, op_class, op_chan);
+	if (ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes))
+		return 1;
+
+	return 0;
+}
+
+
 /**
  * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -5415,6 +5433,11 @@  int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 	p2p.pasn_send_mgmt = wpas_p2p_pasn_send_mlme;
 	p2p.pasn_update_extra_ies = wpas_p2p_pasn_update_extra_ies;
 	p2p.pasn_parse_encrypted_data = wpas_p2p_pasn_parse_encrypted_data;
+	p2p.is_p2p_dfs_chan = wpas_p2p_dfs_chan;
+	// FIXME: Dont hardcode dfs_chan_enable
+//	p2p.dfs_chan_enable = wpa_s->conf->p2p_dfs_chan_enable;
+	p2p.dfs_chan_enable = 1;
+	p2p.dfs_owner = wpa_s->conf->p2p_dfs_owner;
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -6328,7 +6351,7 @@  static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
 		else
 			ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
 		if (!ret) {
-			if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+			if (is_p2p_dfs_chan_enabled(wpa_s->global->p2p) &&
 			    ieee80211_is_dfs(freq, wpa_s->hw.modes,
 					     wpa_s->hw.num_modes)) {
 				/*
@@ -10874,3 +10897,24 @@  int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 		return -2;
 	return p2p_pasn_auth_rx(p2p, mgmt, len, freq);
 }
+
+void wpas_p2p_update_dfs_ap_info_list(struct wpa_supplicant *wpa_s,
+				      struct wpa_scan_results *scan_res)
+{
+	size_t i;
+
+	for (i = 0; i < scan_res->num; i++) {
+		if (!ieee80211_is_dfs(scan_res->res[i]->freq, wpa_s->hw.modes,
+				      wpa_s->hw.num_modes))
+			continue;
+		if (scan_res->res[i]->flags & BIT(5)) {
+			p2p_update_dfs_ap_info(wpa_s->global->p2p,
+					       scan_res->res[i]->bssid,
+					       scan_res->res[i]->freq, 1);
+		} else {
+			p2p_update_dfs_ap_info(wpa_s->global->p2p,
+					       scan_res->res[i]->bssid,
+					       scan_res->res[i]->freq, 0);
+		}
+	}
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 1e5f77a..9156e5a 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -233,6 +233,8 @@  struct wpabuf * wpas_p2p_usd_elems(struct wpa_supplicant *wpa_s);
 int wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 			  const struct ieee80211_mgmt *mgmt, size_t len,
 			  int freq);
+void wpas_p2p_update_dfs_ap_info_list(struct wpa_supplicant *wpa_s,
+				      struct wpa_scan_results *scan_res);
 #else /* CONFIG_P2P */
 
 static inline int
@@ -370,6 +372,11 @@  wpas_p2p_pasn_auth_rx(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
+static inline void
+wpas_p2p_update_dfs_ap_info_list(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res)
+{
+}
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4e87247..ba4b899 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -1601,6 +1601,7 @@  struct wpa_supplicant {
 	bool last_scan_all_chan;
 	bool last_scan_non_coloc_6ghz;
 	bool support_6ghz;
+	u8 assisted_dfs_go;
 
 	struct wpa_signal_info last_signal_info;