1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * Wi-Fi Direct - P2P provision discovery
3e5b75505Sopenharmony_ci * Copyright (c) 2009-2010, Atheros Communications
4e5b75505Sopenharmony_ci *
5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
6e5b75505Sopenharmony_ci * See README for more details.
7e5b75505Sopenharmony_ci */
8e5b75505Sopenharmony_ci
9e5b75505Sopenharmony_ci#include "includes.h"
10e5b75505Sopenharmony_ci
11e5b75505Sopenharmony_ci#include "common.h"
12e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h"
13e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h"
14e5b75505Sopenharmony_ci#include "wps/wps_defs.h"
15e5b75505Sopenharmony_ci#include "p2p_i.h"
16e5b75505Sopenharmony_ci#include "p2p.h"
17e5b75505Sopenharmony_ci
18e5b75505Sopenharmony_ci
19e5b75505Sopenharmony_ci/*
20e5b75505Sopenharmony_ci * Number of retries to attempt for provision discovery requests
21e5b75505Sopenharmony_ci * in case the peer is not listening.
22e5b75505Sopenharmony_ci */
23e5b75505Sopenharmony_ci#define MAX_PROV_DISC_REQ_RETRIES 120
24e5b75505Sopenharmony_ci
25e5b75505Sopenharmony_ci
26e5b75505Sopenharmony_cistatic void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
27e5b75505Sopenharmony_ci					    u16 config_methods)
28e5b75505Sopenharmony_ci{
29e5b75505Sopenharmony_ci	u8 *len;
30e5b75505Sopenharmony_ci	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
31e5b75505Sopenharmony_ci	len = wpabuf_put(buf, 1);
32e5b75505Sopenharmony_ci	wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
33e5b75505Sopenharmony_ci
34e5b75505Sopenharmony_ci	/* Config Methods */
35e5b75505Sopenharmony_ci	wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
36e5b75505Sopenharmony_ci	wpabuf_put_be16(buf, 2);
37e5b75505Sopenharmony_ci	wpabuf_put_be16(buf, config_methods);
38e5b75505Sopenharmony_ci
39e5b75505Sopenharmony_ci	p2p_buf_update_ie_hdr(buf, len);
40e5b75505Sopenharmony_ci}
41e5b75505Sopenharmony_ci
42e5b75505Sopenharmony_ci
43e5b75505Sopenharmony_cistatic void p2ps_add_new_group_info(struct p2p_data *p2p,
44e5b75505Sopenharmony_ci				    struct p2p_device *dev,
45e5b75505Sopenharmony_ci				    struct wpabuf *buf)
46e5b75505Sopenharmony_ci{
47e5b75505Sopenharmony_ci	int found;
48e5b75505Sopenharmony_ci	u8 intended_addr[ETH_ALEN];
49e5b75505Sopenharmony_ci	u8 ssid[SSID_MAX_LEN];
50e5b75505Sopenharmony_ci	size_t ssid_len;
51e5b75505Sopenharmony_ci	int group_iface;
52e5b75505Sopenharmony_ci	unsigned int force_freq;
53e5b75505Sopenharmony_ci
54e5b75505Sopenharmony_ci	if (!p2p->cfg->get_go_info)
55e5b75505Sopenharmony_ci		return;
56e5b75505Sopenharmony_ci
57e5b75505Sopenharmony_ci	found = p2p->cfg->get_go_info(
58e5b75505Sopenharmony_ci		p2p->cfg->cb_ctx, intended_addr, ssid,
59e5b75505Sopenharmony_ci		&ssid_len, &group_iface, &force_freq);
60e5b75505Sopenharmony_ci	if (found) {
61e5b75505Sopenharmony_ci		if (force_freq > 0) {
62e5b75505Sopenharmony_ci			p2p->p2ps_prov->force_freq = force_freq;
63e5b75505Sopenharmony_ci			p2p->p2ps_prov->pref_freq = 0;
64e5b75505Sopenharmony_ci
65e5b75505Sopenharmony_ci			if (dev)
66e5b75505Sopenharmony_ci				p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
67e5b75505Sopenharmony_ci		}
68e5b75505Sopenharmony_ci		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
69e5b75505Sopenharmony_ci				     ssid, ssid_len);
70e5b75505Sopenharmony_ci
71e5b75505Sopenharmony_ci		if (group_iface)
72e5b75505Sopenharmony_ci			p2p_buf_add_intended_addr(buf, p2p->intended_addr);
73e5b75505Sopenharmony_ci		else
74e5b75505Sopenharmony_ci			p2p_buf_add_intended_addr(buf, intended_addr);
75e5b75505Sopenharmony_ci	} else {
76e5b75505Sopenharmony_ci		if (!p2p->ssid_set) {
77e5b75505Sopenharmony_ci			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
78e5b75505Sopenharmony_ci			p2p->ssid_set = 1;
79e5b75505Sopenharmony_ci		}
80e5b75505Sopenharmony_ci
81e5b75505Sopenharmony_ci		/* Add pre-composed P2P Group ID */
82e5b75505Sopenharmony_ci		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
83e5b75505Sopenharmony_ci				     p2p->ssid, p2p->ssid_len);
84e5b75505Sopenharmony_ci
85e5b75505Sopenharmony_ci		if (group_iface)
86e5b75505Sopenharmony_ci			p2p_buf_add_intended_addr(
87e5b75505Sopenharmony_ci				buf, p2p->intended_addr);
88e5b75505Sopenharmony_ci		else
89e5b75505Sopenharmony_ci			p2p_buf_add_intended_addr(
90e5b75505Sopenharmony_ci				buf, p2p->cfg->dev_addr);
91e5b75505Sopenharmony_ci	}
92e5b75505Sopenharmony_ci}
93e5b75505Sopenharmony_ci
94e5b75505Sopenharmony_ci
95e5b75505Sopenharmony_cistatic void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
96e5b75505Sopenharmony_ci				  struct wpabuf *buf, u16 config_methods)
97e5b75505Sopenharmony_ci{
98e5b75505Sopenharmony_ci	struct p2ps_provision *prov = p2p->p2ps_prov;
99e5b75505Sopenharmony_ci	struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
100e5b75505Sopenharmony_ci	int shared_group = 0;
101e5b75505Sopenharmony_ci	u8 ssid[SSID_MAX_LEN];
102e5b75505Sopenharmony_ci	size_t ssid_len;
103e5b75505Sopenharmony_ci	u8 go_dev_addr[ETH_ALEN];
104e5b75505Sopenharmony_ci	u8 intended_addr[ETH_ALEN];
105e5b75505Sopenharmony_ci	int follow_on_req_fail = prov->status >= 0 &&
106e5b75505Sopenharmony_ci		prov->status != P2P_SC_SUCCESS_DEFERRED;
107e5b75505Sopenharmony_ci
108e5b75505Sopenharmony_ci	/* If we might be explicite group owner, add GO details */
109e5b75505Sopenharmony_ci	if (!follow_on_req_fail &&
110e5b75505Sopenharmony_ci	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
111e5b75505Sopenharmony_ci		p2ps_add_new_group_info(p2p, dev, buf);
112e5b75505Sopenharmony_ci
113e5b75505Sopenharmony_ci	if (prov->status >= 0)
114e5b75505Sopenharmony_ci		p2p_buf_add_status(buf, (u8) prov->status);
115e5b75505Sopenharmony_ci	else
116e5b75505Sopenharmony_ci		prov->method = config_methods;
117e5b75505Sopenharmony_ci
118e5b75505Sopenharmony_ci	if (!follow_on_req_fail) {
119e5b75505Sopenharmony_ci		if (p2p->cfg->get_persistent_group) {
120e5b75505Sopenharmony_ci			shared_group = p2p->cfg->get_persistent_group(
121e5b75505Sopenharmony_ci				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
122e5b75505Sopenharmony_ci				NULL, 0, go_dev_addr, ssid, &ssid_len,
123e5b75505Sopenharmony_ci				intended_addr);
124e5b75505Sopenharmony_ci		}
125e5b75505Sopenharmony_ci
126e5b75505Sopenharmony_ci		if (shared_group ||
127e5b75505Sopenharmony_ci		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
128e5b75505Sopenharmony_ci			p2p_buf_add_channel_list(buf, p2p->cfg->country,
129e5b75505Sopenharmony_ci						 &p2p->channels);
130e5b75505Sopenharmony_ci
131e5b75505Sopenharmony_ci		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
132e5b75505Sopenharmony_ci		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
133e5b75505Sopenharmony_ci			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
134e5b75505Sopenharmony_ci						      p2p->op_reg_class,
135e5b75505Sopenharmony_ci						      p2p->op_channel);
136e5b75505Sopenharmony_ci	}
137e5b75505Sopenharmony_ci
138e5b75505Sopenharmony_ci	if (prov->status < 0 && prov->info[0])
139e5b75505Sopenharmony_ci		p2p_buf_add_session_info(buf, prov->info);
140e5b75505Sopenharmony_ci
141e5b75505Sopenharmony_ci	if (!follow_on_req_fail)
142e5b75505Sopenharmony_ci		p2p_buf_add_connection_capability(buf, prov->conncap);
143e5b75505Sopenharmony_ci
144e5b75505Sopenharmony_ci	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
145e5b75505Sopenharmony_ci
146e5b75505Sopenharmony_ci	if (!follow_on_req_fail) {
147e5b75505Sopenharmony_ci		if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
148e5b75505Sopenharmony_ci		    prov->conncap ==
149e5b75505Sopenharmony_ci		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
150e5b75505Sopenharmony_ci		    prov->conncap ==
151e5b75505Sopenharmony_ci		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
152e5b75505Sopenharmony_ci			/* Add Config Timeout */
153e5b75505Sopenharmony_ci			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
154e5b75505Sopenharmony_ci						   p2p->client_timeout);
155e5b75505Sopenharmony_ci		}
156e5b75505Sopenharmony_ci
157e5b75505Sopenharmony_ci		p2p_buf_add_listen_channel(buf, p2p->cfg->country,
158e5b75505Sopenharmony_ci					   p2p->cfg->reg_class,
159e5b75505Sopenharmony_ci					   p2p->cfg->channel);
160e5b75505Sopenharmony_ci	}
161e5b75505Sopenharmony_ci
162e5b75505Sopenharmony_ci	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
163e5b75505Sopenharmony_ci
164e5b75505Sopenharmony_ci	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
165e5b75505Sopenharmony_ci
166e5b75505Sopenharmony_ci	if (shared_group) {
167e5b75505Sopenharmony_ci		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
168e5b75505Sopenharmony_ci						  ssid, ssid_len);
169e5b75505Sopenharmony_ci		/* Add intended interface address if it is not added yet */
170e5b75505Sopenharmony_ci		if ((prov->conncap == P2PS_SETUP_NONE ||
171e5b75505Sopenharmony_ci		     prov->conncap == P2PS_SETUP_CLIENT) &&
172e5b75505Sopenharmony_ci		    !is_zero_ether_addr(intended_addr))
173e5b75505Sopenharmony_ci			p2p_buf_add_intended_addr(buf, intended_addr);
174e5b75505Sopenharmony_ci	}
175e5b75505Sopenharmony_ci}
176e5b75505Sopenharmony_ci
177e5b75505Sopenharmony_ci
178e5b75505Sopenharmony_cistatic struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
179e5b75505Sopenharmony_ci					       struct p2p_device *dev,
180e5b75505Sopenharmony_ci					       int join)
181e5b75505Sopenharmony_ci{
182e5b75505Sopenharmony_ci	struct wpabuf *buf;
183e5b75505Sopenharmony_ci	u8 *len;
184e5b75505Sopenharmony_ci	size_t extra = 0;
185e5b75505Sopenharmony_ci	u8 dialog_token = dev->dialog_token;
186e5b75505Sopenharmony_ci	u16 config_methods = dev->req_config_methods;
187e5b75505Sopenharmony_ci	struct p2p_device *go = join ? dev : NULL;
188e5b75505Sopenharmony_ci	u8 group_capab;
189e5b75505Sopenharmony_ci
190e5b75505Sopenharmony_ci#ifdef CONFIG_WIFI_DISPLAY
191e5b75505Sopenharmony_ci	if (p2p->wfd_ie_prov_disc_req)
192e5b75505Sopenharmony_ci		extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
193e5b75505Sopenharmony_ci#endif /* CONFIG_WIFI_DISPLAY */
194e5b75505Sopenharmony_ci
195e5b75505Sopenharmony_ci	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
196e5b75505Sopenharmony_ci		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
197e5b75505Sopenharmony_ci
198e5b75505Sopenharmony_ci	if (p2p->p2ps_prov)
199e5b75505Sopenharmony_ci		extra += os_strlen(p2p->p2ps_prov->info) + 1 +
200e5b75505Sopenharmony_ci			sizeof(struct p2ps_provision);
201e5b75505Sopenharmony_ci
202e5b75505Sopenharmony_ci	buf = wpabuf_alloc(1000 + extra);
203e5b75505Sopenharmony_ci	if (buf == NULL)
204e5b75505Sopenharmony_ci		return NULL;
205e5b75505Sopenharmony_ci
206e5b75505Sopenharmony_ci	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
207e5b75505Sopenharmony_ci
208e5b75505Sopenharmony_ci	len = p2p_buf_add_ie_hdr(buf);
209e5b75505Sopenharmony_ci
210e5b75505Sopenharmony_ci	group_capab = 0;
211e5b75505Sopenharmony_ci	if (p2p->p2ps_prov) {
212e5b75505Sopenharmony_ci		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
213e5b75505Sopenharmony_ci		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
214e5b75505Sopenharmony_ci		if (p2p->cross_connect)
215e5b75505Sopenharmony_ci			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
216e5b75505Sopenharmony_ci		if (p2p->cfg->p2p_intra_bss)
217e5b75505Sopenharmony_ci			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
218e5b75505Sopenharmony_ci	}
219e5b75505Sopenharmony_ci	p2p_buf_add_capability(buf, p2p->dev_capab &
220e5b75505Sopenharmony_ci			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
221e5b75505Sopenharmony_ci			       group_capab);
222e5b75505Sopenharmony_ci	p2p_buf_add_device_info(buf, p2p, NULL);
223e5b75505Sopenharmony_ci	if (p2p->p2ps_prov) {
224e5b75505Sopenharmony_ci		p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
225e5b75505Sopenharmony_ci	} else if (go) {
226e5b75505Sopenharmony_ci		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
227e5b75505Sopenharmony_ci				     go->oper_ssid, go->oper_ssid_len);
228e5b75505Sopenharmony_ci	}
229e5b75505Sopenharmony_ci	p2p_buf_update_ie_hdr(buf, len);
230e5b75505Sopenharmony_ci
231e5b75505Sopenharmony_ci	/* WPS IE with Config Methods attribute */
232e5b75505Sopenharmony_ci	p2p_build_wps_ie_config_methods(buf, config_methods);
233e5b75505Sopenharmony_ci
234e5b75505Sopenharmony_ci#ifdef CONFIG_WIFI_DISPLAY
235e5b75505Sopenharmony_ci	if (p2p->wfd_ie_prov_disc_req)
236e5b75505Sopenharmony_ci		wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
237e5b75505Sopenharmony_ci#endif /* CONFIG_WIFI_DISPLAY */
238e5b75505Sopenharmony_ci
239e5b75505Sopenharmony_ci	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
240e5b75505Sopenharmony_ci		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
241e5b75505Sopenharmony_ci
242e5b75505Sopenharmony_ci	return buf;
243e5b75505Sopenharmony_ci}
244e5b75505Sopenharmony_ci
245e5b75505Sopenharmony_ci
246e5b75505Sopenharmony_cistatic struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
247e5b75505Sopenharmony_ci						struct p2p_device *dev,
248e5b75505Sopenharmony_ci						u8 dialog_token,
249e5b75505Sopenharmony_ci						enum p2p_status_code status,
250e5b75505Sopenharmony_ci						u16 config_methods,
251e5b75505Sopenharmony_ci						u32 adv_id,
252e5b75505Sopenharmony_ci						const u8 *group_id,
253e5b75505Sopenharmony_ci						size_t group_id_len,
254e5b75505Sopenharmony_ci						const u8 *persist_ssid,
255e5b75505Sopenharmony_ci						size_t persist_ssid_len,
256e5b75505Sopenharmony_ci						const u8 *fcap,
257e5b75505Sopenharmony_ci						u16 fcap_len)
258e5b75505Sopenharmony_ci{
259e5b75505Sopenharmony_ci	struct wpabuf *buf;
260e5b75505Sopenharmony_ci	size_t extra = 0;
261e5b75505Sopenharmony_ci	int persist = 0;
262e5b75505Sopenharmony_ci
263e5b75505Sopenharmony_ci#ifdef CONFIG_WIFI_DISPLAY
264e5b75505Sopenharmony_ci	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
265e5b75505Sopenharmony_ci	if (wfd_ie && group_id) {
266e5b75505Sopenharmony_ci		size_t i;
267e5b75505Sopenharmony_ci		for (i = 0; i < p2p->num_groups; i++) {
268e5b75505Sopenharmony_ci			struct p2p_group *g = p2p->groups[i];
269e5b75505Sopenharmony_ci			struct wpabuf *ie;
270e5b75505Sopenharmony_ci			if (!p2p_group_is_group_id_match(g, group_id,
271e5b75505Sopenharmony_ci							 group_id_len))
272e5b75505Sopenharmony_ci				continue;
273e5b75505Sopenharmony_ci			ie = p2p_group_get_wfd_ie(g);
274e5b75505Sopenharmony_ci			if (ie) {
275e5b75505Sopenharmony_ci				wfd_ie = ie;
276e5b75505Sopenharmony_ci				break;
277e5b75505Sopenharmony_ci			}
278e5b75505Sopenharmony_ci		}
279e5b75505Sopenharmony_ci	}
280e5b75505Sopenharmony_ci	if (wfd_ie)
281e5b75505Sopenharmony_ci		extra = wpabuf_len(wfd_ie);
282e5b75505Sopenharmony_ci#endif /* CONFIG_WIFI_DISPLAY */
283e5b75505Sopenharmony_ci
284e5b75505Sopenharmony_ci	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
285e5b75505Sopenharmony_ci		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
286e5b75505Sopenharmony_ci
287e5b75505Sopenharmony_ci	buf = wpabuf_alloc(1000 + extra);
288e5b75505Sopenharmony_ci	if (buf == NULL)
289e5b75505Sopenharmony_ci		return NULL;
290e5b75505Sopenharmony_ci
291e5b75505Sopenharmony_ci	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
292e5b75505Sopenharmony_ci
293e5b75505Sopenharmony_ci	/* Add P2P IE for P2PS */
294e5b75505Sopenharmony_ci	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
295e5b75505Sopenharmony_ci		u8 *len = p2p_buf_add_ie_hdr(buf);
296e5b75505Sopenharmony_ci		struct p2ps_provision *prov = p2p->p2ps_prov;
297e5b75505Sopenharmony_ci		u8 group_capab;
298e5b75505Sopenharmony_ci		u8 conncap = 0;
299e5b75505Sopenharmony_ci
300e5b75505Sopenharmony_ci		if (status == P2P_SC_SUCCESS ||
301e5b75505Sopenharmony_ci		    status == P2P_SC_SUCCESS_DEFERRED)
302e5b75505Sopenharmony_ci			conncap = prov->conncap;
303e5b75505Sopenharmony_ci
304e5b75505Sopenharmony_ci		if (!status && prov->status != -1)
305e5b75505Sopenharmony_ci			status = prov->status;
306e5b75505Sopenharmony_ci
307e5b75505Sopenharmony_ci		p2p_buf_add_status(buf, status);
308e5b75505Sopenharmony_ci		group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
309e5b75505Sopenharmony_ci			P2P_GROUP_CAPAB_PERSISTENT_RECONN;
310e5b75505Sopenharmony_ci		if (p2p->cross_connect)
311e5b75505Sopenharmony_ci			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
312e5b75505Sopenharmony_ci		if (p2p->cfg->p2p_intra_bss)
313e5b75505Sopenharmony_ci			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
314e5b75505Sopenharmony_ci		p2p_buf_add_capability(buf, p2p->dev_capab &
315e5b75505Sopenharmony_ci				       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
316e5b75505Sopenharmony_ci				       group_capab);
317e5b75505Sopenharmony_ci		p2p_buf_add_device_info(buf, p2p, NULL);
318e5b75505Sopenharmony_ci
319e5b75505Sopenharmony_ci		if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
320e5b75505Sopenharmony_ci		    (status == P2P_SC_SUCCESS ||
321e5b75505Sopenharmony_ci		     status == P2P_SC_SUCCESS_DEFERRED)) {
322e5b75505Sopenharmony_ci			u8 ssid[SSID_MAX_LEN];
323e5b75505Sopenharmony_ci			size_t ssid_len;
324e5b75505Sopenharmony_ci			u8 go_dev_addr[ETH_ALEN];
325e5b75505Sopenharmony_ci			u8 intended_addr[ETH_ALEN];
326e5b75505Sopenharmony_ci
327e5b75505Sopenharmony_ci			persist = p2p->cfg->get_persistent_group(
328e5b75505Sopenharmony_ci				p2p->cfg->cb_ctx,
329e5b75505Sopenharmony_ci				dev->info.p2p_device_addr,
330e5b75505Sopenharmony_ci				persist_ssid, persist_ssid_len, go_dev_addr,
331e5b75505Sopenharmony_ci				ssid, &ssid_len, intended_addr);
332e5b75505Sopenharmony_ci			if (persist) {
333e5b75505Sopenharmony_ci				p2p_buf_add_persistent_group_info(
334e5b75505Sopenharmony_ci					buf, go_dev_addr, ssid, ssid_len);
335e5b75505Sopenharmony_ci				if (!is_zero_ether_addr(intended_addr))
336e5b75505Sopenharmony_ci					p2p_buf_add_intended_addr(
337e5b75505Sopenharmony_ci						buf, intended_addr);
338e5b75505Sopenharmony_ci			}
339e5b75505Sopenharmony_ci		}
340e5b75505Sopenharmony_ci
341e5b75505Sopenharmony_ci		if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
342e5b75505Sopenharmony_ci			p2ps_add_new_group_info(p2p, dev, buf);
343e5b75505Sopenharmony_ci
344e5b75505Sopenharmony_ci		/* Add Operating Channel if conncap indicates GO */
345e5b75505Sopenharmony_ci		if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
346e5b75505Sopenharmony_ci			if (p2p->op_reg_class && p2p->op_channel)
347e5b75505Sopenharmony_ci				p2p_buf_add_operating_channel(
348e5b75505Sopenharmony_ci					buf, p2p->cfg->country,
349e5b75505Sopenharmony_ci					p2p->op_reg_class,
350e5b75505Sopenharmony_ci					p2p->op_channel);
351e5b75505Sopenharmony_ci			else
352e5b75505Sopenharmony_ci				p2p_buf_add_operating_channel(
353e5b75505Sopenharmony_ci					buf, p2p->cfg->country,
354e5b75505Sopenharmony_ci					p2p->cfg->op_reg_class,
355e5b75505Sopenharmony_ci					p2p->cfg->op_channel);
356e5b75505Sopenharmony_ci		}
357e5b75505Sopenharmony_ci
358e5b75505Sopenharmony_ci		if (persist ||
359e5b75505Sopenharmony_ci		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
360e5b75505Sopenharmony_ci			p2p_buf_add_channel_list(buf, p2p->cfg->country,
361e5b75505Sopenharmony_ci						 &p2p->channels);
362e5b75505Sopenharmony_ci
363e5b75505Sopenharmony_ci		if (!persist && conncap)
364e5b75505Sopenharmony_ci			p2p_buf_add_connection_capability(buf, conncap);
365e5b75505Sopenharmony_ci
366e5b75505Sopenharmony_ci		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
367e5b75505Sopenharmony_ci
368e5b75505Sopenharmony_ci		if (persist ||
369e5b75505Sopenharmony_ci		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
370e5b75505Sopenharmony_ci			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
371e5b75505Sopenharmony_ci						   p2p->client_timeout);
372e5b75505Sopenharmony_ci
373e5b75505Sopenharmony_ci		p2p_buf_add_session_id(buf, prov->session_id,
374e5b75505Sopenharmony_ci				       prov->session_mac);
375e5b75505Sopenharmony_ci
376e5b75505Sopenharmony_ci		p2p_buf_add_feature_capability(buf, fcap_len, fcap);
377e5b75505Sopenharmony_ci		p2p_buf_update_ie_hdr(buf, len);
378e5b75505Sopenharmony_ci	} else if (status != P2P_SC_SUCCESS || adv_id) {
379e5b75505Sopenharmony_ci		u8 *len = p2p_buf_add_ie_hdr(buf);
380e5b75505Sopenharmony_ci
381e5b75505Sopenharmony_ci		p2p_buf_add_status(buf, status);
382e5b75505Sopenharmony_ci
383e5b75505Sopenharmony_ci		if (p2p->p2ps_prov)
384e5b75505Sopenharmony_ci			p2p_buf_add_advertisement_id(buf, adv_id,
385e5b75505Sopenharmony_ci						     p2p->p2ps_prov->adv_mac);
386e5b75505Sopenharmony_ci
387e5b75505Sopenharmony_ci		p2p_buf_update_ie_hdr(buf, len);
388e5b75505Sopenharmony_ci	}
389e5b75505Sopenharmony_ci
390e5b75505Sopenharmony_ci	/* WPS IE with Config Methods attribute */
391e5b75505Sopenharmony_ci	p2p_build_wps_ie_config_methods(buf, config_methods);
392e5b75505Sopenharmony_ci
393e5b75505Sopenharmony_ci#ifdef CONFIG_WIFI_DISPLAY
394e5b75505Sopenharmony_ci	if (wfd_ie)
395e5b75505Sopenharmony_ci		wpabuf_put_buf(buf, wfd_ie);
396e5b75505Sopenharmony_ci#endif /* CONFIG_WIFI_DISPLAY */
397e5b75505Sopenharmony_ci
398e5b75505Sopenharmony_ci	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
399e5b75505Sopenharmony_ci		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
400e5b75505Sopenharmony_ci
401e5b75505Sopenharmony_ci	return buf;
402e5b75505Sopenharmony_ci}
403e5b75505Sopenharmony_ci
404e5b75505Sopenharmony_ci
405e5b75505Sopenharmony_cistatic int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
406e5b75505Sopenharmony_ci				u32 session_id, u16 method,
407e5b75505Sopenharmony_ci				const u8 *session_mac, const u8 *adv_mac)
408e5b75505Sopenharmony_ci{
409e5b75505Sopenharmony_ci	struct p2ps_provision *tmp;
410e5b75505Sopenharmony_ci
411e5b75505Sopenharmony_ci	if (!p2p->p2ps_prov) {
412e5b75505Sopenharmony_ci		p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
413e5b75505Sopenharmony_ci		if (!p2p->p2ps_prov)
414e5b75505Sopenharmony_ci			return -1;
415e5b75505Sopenharmony_ci	} else {
416e5b75505Sopenharmony_ci		os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
417e5b75505Sopenharmony_ci	}
418e5b75505Sopenharmony_ci
419e5b75505Sopenharmony_ci	tmp = p2p->p2ps_prov;
420e5b75505Sopenharmony_ci	tmp->adv_id = adv_id;
421e5b75505Sopenharmony_ci	tmp->session_id = session_id;
422e5b75505Sopenharmony_ci	tmp->method = method;
423e5b75505Sopenharmony_ci	os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
424e5b75505Sopenharmony_ci	os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
425e5b75505Sopenharmony_ci	tmp->info[0] = '\0';
426e5b75505Sopenharmony_ci
427e5b75505Sopenharmony_ci	return 0;
428e5b75505Sopenharmony_ci}
429e5b75505Sopenharmony_ci
430e5b75505Sopenharmony_ci
431e5b75505Sopenharmony_cistatic u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
432e5b75505Sopenharmony_ci{
433e5b75505Sopenharmony_ci	int i;
434e5b75505Sopenharmony_ci
435e5b75505Sopenharmony_ci	for (i = 0; cpt_priority[i]; i++)
436e5b75505Sopenharmony_ci		if (req_cpt_mask & cpt_priority[i])
437e5b75505Sopenharmony_ci			return cpt_priority[i];
438e5b75505Sopenharmony_ci
439e5b75505Sopenharmony_ci	return 0;
440e5b75505Sopenharmony_ci}
441e5b75505Sopenharmony_ci
442e5b75505Sopenharmony_ci
443e5b75505Sopenharmony_ci/* Check if the message contains a valid P2PS PD Request */
444e5b75505Sopenharmony_cistatic int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
445e5b75505Sopenharmony_ci				const u8 *addr)
446e5b75505Sopenharmony_ci{
447e5b75505Sopenharmony_ci	u8 group_id = 0;
448e5b75505Sopenharmony_ci	u8 intended_addr = 0;
449e5b75505Sopenharmony_ci	u8 operating_channel = 0;
450e5b75505Sopenharmony_ci	u8 channel_list = 0;
451e5b75505Sopenharmony_ci	u8 config_timeout = 0;
452e5b75505Sopenharmony_ci	u8 listen_channel = 0;
453e5b75505Sopenharmony_ci
454e5b75505Sopenharmony_ci#define P2PS_PD_REQ_CHECK(_val, _attr) \
455e5b75505Sopenharmony_cido { \
456e5b75505Sopenharmony_ci	if ((_val) && !msg->_attr) { \
457e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
458e5b75505Sopenharmony_ci		return -1; \
459e5b75505Sopenharmony_ci	} \
460e5b75505Sopenharmony_ci} while (0)
461e5b75505Sopenharmony_ci
462e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, adv_id);
463e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, session_id);
464e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, session_mac);
465e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, adv_mac);
466e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, capability);
467e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, p2p_device_info);
468e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, feature_cap);
469e5b75505Sopenharmony_ci
470e5b75505Sopenharmony_ci	/*
471e5b75505Sopenharmony_ci	 * We don't need to check Connection Capability, Persistent Group,
472e5b75505Sopenharmony_ci	 * and related attributes for follow-on PD Request with a status
473e5b75505Sopenharmony_ci	 * other than SUCCESS_DEFERRED.
474e5b75505Sopenharmony_ci	 */
475e5b75505Sopenharmony_ci	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
476e5b75505Sopenharmony_ci		return 0;
477e5b75505Sopenharmony_ci
478e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(1, conn_cap);
479e5b75505Sopenharmony_ci
480e5b75505Sopenharmony_ci	/*
481e5b75505Sopenharmony_ci	 * Note 1: A feature capability attribute structure can be changed
482e5b75505Sopenharmony_ci	 * in the future. The assumption is that such modifications are
483e5b75505Sopenharmony_ci	 * backward compatible, therefore we allow processing of msg.feature_cap
484e5b75505Sopenharmony_ci	 * exceeding the size of the p2ps_feature_capab structure.
485e5b75505Sopenharmony_ci	 * Note 2: Verification of msg.feature_cap_len below has to be changed
486e5b75505Sopenharmony_ci	 * to allow 2 byte feature capability processing if
487e5b75505Sopenharmony_ci	 * struct p2ps_feature_capab is extended to include additional fields
488e5b75505Sopenharmony_ci	 * and it affects the structure size.
489e5b75505Sopenharmony_ci	 */
490e5b75505Sopenharmony_ci	if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
491e5b75505Sopenharmony_ci		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
492e5b75505Sopenharmony_ci		return -1;
493e5b75505Sopenharmony_ci	}
494e5b75505Sopenharmony_ci
495e5b75505Sopenharmony_ci	switch (*msg->conn_cap) {
496e5b75505Sopenharmony_ci	case P2PS_SETUP_NEW:
497e5b75505Sopenharmony_ci		group_id = 1;
498e5b75505Sopenharmony_ci		intended_addr = 1;
499e5b75505Sopenharmony_ci		operating_channel = 1;
500e5b75505Sopenharmony_ci		channel_list = 1;
501e5b75505Sopenharmony_ci		config_timeout = 1;
502e5b75505Sopenharmony_ci		listen_channel = 1;
503e5b75505Sopenharmony_ci		break;
504e5b75505Sopenharmony_ci	case P2PS_SETUP_CLIENT:
505e5b75505Sopenharmony_ci		channel_list = 1;
506e5b75505Sopenharmony_ci		listen_channel = 1;
507e5b75505Sopenharmony_ci		break;
508e5b75505Sopenharmony_ci	case P2PS_SETUP_GROUP_OWNER:
509e5b75505Sopenharmony_ci		group_id = 1;
510e5b75505Sopenharmony_ci		intended_addr = 1;
511e5b75505Sopenharmony_ci		operating_channel = 1;
512e5b75505Sopenharmony_ci		break;
513e5b75505Sopenharmony_ci	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
514e5b75505Sopenharmony_ci		group_id = 1;
515e5b75505Sopenharmony_ci		operating_channel = 1;
516e5b75505Sopenharmony_ci		intended_addr = 1;
517e5b75505Sopenharmony_ci		channel_list = 1;
518e5b75505Sopenharmony_ci		config_timeout = 1;
519e5b75505Sopenharmony_ci		break;
520e5b75505Sopenharmony_ci	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
521e5b75505Sopenharmony_ci		group_id = 1;
522e5b75505Sopenharmony_ci		intended_addr = 1;
523e5b75505Sopenharmony_ci		operating_channel = 1;
524e5b75505Sopenharmony_ci		channel_list = 1;
525e5b75505Sopenharmony_ci		config_timeout = 1;
526e5b75505Sopenharmony_ci		break;
527e5b75505Sopenharmony_ci	default:
528e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
529e5b75505Sopenharmony_ci		return -1;
530e5b75505Sopenharmony_ci	}
531e5b75505Sopenharmony_ci
532e5b75505Sopenharmony_ci	if (msg->persistent_dev) {
533e5b75505Sopenharmony_ci		channel_list = 1;
534e5b75505Sopenharmony_ci		config_timeout = 1;
535e5b75505Sopenharmony_ci		if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
536e5b75505Sopenharmony_ci			intended_addr = 1;
537e5b75505Sopenharmony_ci			operating_channel = 1;
538e5b75505Sopenharmony_ci		}
539e5b75505Sopenharmony_ci	}
540e5b75505Sopenharmony_ci
541e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(group_id, group_id);
542e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
543e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
544e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(channel_list, channel_list);
545e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
546e5b75505Sopenharmony_ci	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
547e5b75505Sopenharmony_ci
548e5b75505Sopenharmony_ci#undef P2PS_PD_REQ_CHECK
549e5b75505Sopenharmony_ci
550e5b75505Sopenharmony_ci	return 0;
551e5b75505Sopenharmony_ci}
552e5b75505Sopenharmony_ci
553e5b75505Sopenharmony_ci
554e5b75505Sopenharmony_civoid p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
555e5b75505Sopenharmony_ci			       const u8 *data, size_t len, int rx_freq)
556e5b75505Sopenharmony_ci{
557e5b75505Sopenharmony_ci	struct p2p_message msg;
558e5b75505Sopenharmony_ci	struct p2p_device *dev;
559e5b75505Sopenharmony_ci	int freq;
560e5b75505Sopenharmony_ci	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
561e5b75505Sopenharmony_ci	struct wpabuf *resp;
562e5b75505Sopenharmony_ci	u32 adv_id = 0;
563e5b75505Sopenharmony_ci	struct p2ps_advertisement *p2ps_adv = NULL;
564e5b75505Sopenharmony_ci	u8 conncap = P2PS_SETUP_NEW;
565e5b75505Sopenharmony_ci	u8 auto_accept = 0;
566e5b75505Sopenharmony_ci	u32 session_id = 0;
567e5b75505Sopenharmony_ci	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
568e5b75505Sopenharmony_ci	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
569e5b75505Sopenharmony_ci	const u8 *group_mac;
570e5b75505Sopenharmony_ci	int passwd_id = DEV_PW_DEFAULT;
571e5b75505Sopenharmony_ci	u16 config_methods;
572e5b75505Sopenharmony_ci	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
573e5b75505Sopenharmony_ci	struct p2ps_feature_capab resp_fcap = { 0, 0 };
574e5b75505Sopenharmony_ci	struct p2ps_feature_capab *req_fcap = NULL;
575e5b75505Sopenharmony_ci	u8 remote_conncap;
576e5b75505Sopenharmony_ci	u16 method;
577e5b75505Sopenharmony_ci
578e5b75505Sopenharmony_ci	if (p2p_parse(data, len, &msg))
579e5b75505Sopenharmony_ci		return;
580e5b75505Sopenharmony_ci
581e5b75505Sopenharmony_ci	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
582e5b75505Sopenharmony_ci		" with config methods 0x%x (freq=%d)",
583e5b75505Sopenharmony_ci		MAC2STR(sa), msg.wps_config_methods, rx_freq);
584e5b75505Sopenharmony_ci	group_mac = msg.intended_addr;
585e5b75505Sopenharmony_ci
586e5b75505Sopenharmony_ci	dev = p2p_get_device(p2p, sa);
587e5b75505Sopenharmony_ci	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
588e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Provision Discovery Request from unknown peer "
589e5b75505Sopenharmony_ci			MACSTR, MAC2STR(sa));
590e5b75505Sopenharmony_ci
591e5b75505Sopenharmony_ci		if (p2p_add_device(p2p, sa, rx_freq, NULL, 0, data + 1, len - 1,
592e5b75505Sopenharmony_ci				   0)) {
593e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Provision Discovery Request add device failed "
594e5b75505Sopenharmony_ci				MACSTR, MAC2STR(sa));
595e5b75505Sopenharmony_ci			goto out;
596e5b75505Sopenharmony_ci		}
597e5b75505Sopenharmony_ci
598e5b75505Sopenharmony_ci		dev = p2p_get_device(p2p, sa);
599e5b75505Sopenharmony_ci		if (!dev) {
600e5b75505Sopenharmony_ci			p2p_dbg(p2p,
601e5b75505Sopenharmony_ci				"Provision Discovery device not found "
602e5b75505Sopenharmony_ci				MACSTR, MAC2STR(sa));
603e5b75505Sopenharmony_ci			goto out;
604e5b75505Sopenharmony_ci		}
605e5b75505Sopenharmony_ci	} else if (msg.wfd_subelems) {
606e5b75505Sopenharmony_ci		wpabuf_free(dev->info.wfd_subelems);
607e5b75505Sopenharmony_ci		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
608e5b75505Sopenharmony_ci	}
609e5b75505Sopenharmony_ci
610e5b75505Sopenharmony_ci	if (!msg.adv_id) {
611e5b75505Sopenharmony_ci		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
612e5b75505Sopenharmony_ci		if (!(msg.wps_config_methods & allowed_config_methods)) {
613e5b75505Sopenharmony_ci			p2p_dbg(p2p,
614e5b75505Sopenharmony_ci				"Unsupported Config Methods in Provision Discovery Request");
615e5b75505Sopenharmony_ci			goto out;
616e5b75505Sopenharmony_ci		}
617e5b75505Sopenharmony_ci
618e5b75505Sopenharmony_ci		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
619e5b75505Sopenharmony_ci		if (msg.group_id) {
620e5b75505Sopenharmony_ci			size_t i;
621e5b75505Sopenharmony_ci
622e5b75505Sopenharmony_ci			for (i = 0; i < p2p->num_groups; i++) {
623e5b75505Sopenharmony_ci				if (p2p_group_is_group_id_match(
624e5b75505Sopenharmony_ci					    p2p->groups[i],
625e5b75505Sopenharmony_ci					    msg.group_id, msg.group_id_len))
626e5b75505Sopenharmony_ci					break;
627e5b75505Sopenharmony_ci			}
628e5b75505Sopenharmony_ci			if (i == p2p->num_groups) {
629e5b75505Sopenharmony_ci				p2p_dbg(p2p,
630e5b75505Sopenharmony_ci					"PD request for unknown P2P Group ID - reject");
631e5b75505Sopenharmony_ci				goto out;
632e5b75505Sopenharmony_ci			}
633e5b75505Sopenharmony_ci		}
634e5b75505Sopenharmony_ci	} else {
635e5b75505Sopenharmony_ci		allowed_config_methods |= WPS_CONFIG_P2PS;
636e5b75505Sopenharmony_ci
637e5b75505Sopenharmony_ci		/*
638e5b75505Sopenharmony_ci		 * Set adv_id here, so in case of an error, a P2PS PD Response
639e5b75505Sopenharmony_ci		 * will be sent.
640e5b75505Sopenharmony_ci		 */
641e5b75505Sopenharmony_ci		adv_id = WPA_GET_LE32(msg.adv_id);
642e5b75505Sopenharmony_ci		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
643e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INVALID_PARAMS;
644e5b75505Sopenharmony_ci			goto out;
645e5b75505Sopenharmony_ci		}
646e5b75505Sopenharmony_ci
647e5b75505Sopenharmony_ci		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
648e5b75505Sopenharmony_ci
649e5b75505Sopenharmony_ci		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
650e5b75505Sopenharmony_ci		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
651e5b75505Sopenharmony_ci
652e5b75505Sopenharmony_ci		session_id = WPA_GET_LE32(msg.session_id);
653e5b75505Sopenharmony_ci
654e5b75505Sopenharmony_ci		if (msg.conn_cap)
655e5b75505Sopenharmony_ci			conncap = *msg.conn_cap;
656e5b75505Sopenharmony_ci
657e5b75505Sopenharmony_ci		/*
658e5b75505Sopenharmony_ci		 * We need to verify a P2PS config methog in an initial PD
659e5b75505Sopenharmony_ci		 * request or in a follow-on PD request with the status
660e5b75505Sopenharmony_ci		 * SUCCESS_DEFERRED.
661e5b75505Sopenharmony_ci		 */
662e5b75505Sopenharmony_ci		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
663e5b75505Sopenharmony_ci		    !(msg.wps_config_methods & allowed_config_methods)) {
664e5b75505Sopenharmony_ci			p2p_dbg(p2p,
665e5b75505Sopenharmony_ci				"Unsupported Config Methods in Provision Discovery Request");
666e5b75505Sopenharmony_ci			goto out;
667e5b75505Sopenharmony_ci		}
668e5b75505Sopenharmony_ci
669e5b75505Sopenharmony_ci		/*
670e5b75505Sopenharmony_ci		 * TODO: since we don't support multiple PD, reject PD request
671e5b75505Sopenharmony_ci		 * if we are in the middle of P2PS PD with some other peer
672e5b75505Sopenharmony_ci		 */
673e5b75505Sopenharmony_ci	}
674e5b75505Sopenharmony_ci
675e5b75505Sopenharmony_ci	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
676e5b75505Sopenharmony_ci			P2P_DEV_PD_PEER_KEYPAD |
677e5b75505Sopenharmony_ci			P2P_DEV_PD_PEER_P2PS);
678e5b75505Sopenharmony_ci
679e5b75505Sopenharmony_ci	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
680e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR
681e5b75505Sopenharmony_ci			" requested us to show a PIN on display", MAC2STR(sa));
682e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
683e5b75505Sopenharmony_ci		passwd_id = DEV_PW_USER_SPECIFIED;
684e5b75505Sopenharmony_ci	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
685e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR
686e5b75505Sopenharmony_ci			" requested us to write its PIN using keypad",
687e5b75505Sopenharmony_ci			MAC2STR(sa));
688e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
689e5b75505Sopenharmony_ci		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
690e5b75505Sopenharmony_ci	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
691e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
692e5b75505Sopenharmony_ci			MAC2STR(sa));
693e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_P2PS;
694e5b75505Sopenharmony_ci		passwd_id = DEV_PW_P2PS_DEFAULT;
695e5b75505Sopenharmony_ci	}
696e5b75505Sopenharmony_ci
697e5b75505Sopenharmony_ci	/* Remove stale persistent groups */
698e5b75505Sopenharmony_ci	if (p2p->cfg->remove_stale_groups) {
699e5b75505Sopenharmony_ci		p2p->cfg->remove_stale_groups(
700e5b75505Sopenharmony_ci			p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
701e5b75505Sopenharmony_ci			msg.persistent_dev,
702e5b75505Sopenharmony_ci			msg.persistent_ssid, msg.persistent_ssid_len);
703e5b75505Sopenharmony_ci	}
704e5b75505Sopenharmony_ci
705e5b75505Sopenharmony_ci	reject = P2P_SC_SUCCESS;
706e5b75505Sopenharmony_ci
707e5b75505Sopenharmony_ci	/*
708e5b75505Sopenharmony_ci	 * End of a legacy P2P PD Request processing, from this point continue
709e5b75505Sopenharmony_ci	 * with P2PS one.
710e5b75505Sopenharmony_ci	 */
711e5b75505Sopenharmony_ci	if (!msg.adv_id)
712e5b75505Sopenharmony_ci		goto out;
713e5b75505Sopenharmony_ci
714e5b75505Sopenharmony_ci	remote_conncap = conncap;
715e5b75505Sopenharmony_ci
716e5b75505Sopenharmony_ci	if (!msg.status) {
717e5b75505Sopenharmony_ci		unsigned int forced_freq, pref_freq;
718e5b75505Sopenharmony_ci
719e5b75505Sopenharmony_ci		if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
720e5b75505Sopenharmony_ci			p2p_dbg(p2p,
721e5b75505Sopenharmony_ci				"P2PS PD adv mac does not match the local one");
722e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
723e5b75505Sopenharmony_ci			goto out;
724e5b75505Sopenharmony_ci		}
725e5b75505Sopenharmony_ci
726e5b75505Sopenharmony_ci		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
727e5b75505Sopenharmony_ci		if (!p2ps_adv) {
728e5b75505Sopenharmony_ci			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
729e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
730e5b75505Sopenharmony_ci			goto out;
731e5b75505Sopenharmony_ci		}
732e5b75505Sopenharmony_ci		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
733e5b75505Sopenharmony_ci
734e5b75505Sopenharmony_ci		auto_accept = p2ps_adv->auto_accept;
735e5b75505Sopenharmony_ci		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
736e5b75505Sopenharmony_ci							  conncap, auto_accept,
737e5b75505Sopenharmony_ci							  &forced_freq,
738e5b75505Sopenharmony_ci							  &pref_freq);
739e5b75505Sopenharmony_ci
740e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
741e5b75505Sopenharmony_ci			auto_accept, remote_conncap, conncap);
742e5b75505Sopenharmony_ci
743e5b75505Sopenharmony_ci		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
744e5b75505Sopenharmony_ci
745e5b75505Sopenharmony_ci		resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
746e5b75505Sopenharmony_ci						       req_fcap->cpt);
747e5b75505Sopenharmony_ci
748e5b75505Sopenharmony_ci		p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
749e5b75505Sopenharmony_ci			p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
750e5b75505Sopenharmony_ci
751e5b75505Sopenharmony_ci		if (!resp_fcap.cpt) {
752e5b75505Sopenharmony_ci			p2p_dbg(p2p,
753e5b75505Sopenharmony_ci				"Incompatible P2PS feature capability CPT bitmask");
754e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
755e5b75505Sopenharmony_ci		} else if (p2ps_adv->config_methods &&
756e5b75505Sopenharmony_ci			   !(msg.wps_config_methods &
757e5b75505Sopenharmony_ci			     p2ps_adv->config_methods)) {
758e5b75505Sopenharmony_ci			p2p_dbg(p2p,
759e5b75505Sopenharmony_ci				"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
760e5b75505Sopenharmony_ci				p2ps_adv->config_methods,
761e5b75505Sopenharmony_ci				msg.wps_config_methods);
762e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
763e5b75505Sopenharmony_ci		} else if (!p2ps_adv->state) {
764e5b75505Sopenharmony_ci			p2p_dbg(p2p, "P2PS state unavailable");
765e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
766e5b75505Sopenharmony_ci		} else if (!conncap) {
767e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Conncap resolution failed");
768e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
769e5b75505Sopenharmony_ci		}
770e5b75505Sopenharmony_ci
771e5b75505Sopenharmony_ci		if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
772e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Keypad - always defer");
773e5b75505Sopenharmony_ci			auto_accept = 0;
774e5b75505Sopenharmony_ci		}
775e5b75505Sopenharmony_ci
776e5b75505Sopenharmony_ci		if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
777e5b75505Sopenharmony_ci		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
778e5b75505Sopenharmony_ci		    msg.channel_list && msg.channel_list_len &&
779e5b75505Sopenharmony_ci		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
780e5b75505Sopenharmony_ci					    msg.channel_list,
781e5b75505Sopenharmony_ci					    msg.channel_list_len) < 0) {
782e5b75505Sopenharmony_ci			p2p_dbg(p2p,
783e5b75505Sopenharmony_ci				"No common channels - force deferred flow");
784e5b75505Sopenharmony_ci			auto_accept = 0;
785e5b75505Sopenharmony_ci		}
786e5b75505Sopenharmony_ci
787e5b75505Sopenharmony_ci		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
788e5b75505Sopenharmony_ci		     msg.persistent_dev) && msg.operating_channel) {
789e5b75505Sopenharmony_ci			struct p2p_channels intersect;
790e5b75505Sopenharmony_ci
791e5b75505Sopenharmony_ci			/*
792e5b75505Sopenharmony_ci			 * There are cases where only the operating channel is
793e5b75505Sopenharmony_ci			 * provided. This requires saving the channel as the
794e5b75505Sopenharmony_ci			 * supported channel list, and verifying that it is
795e5b75505Sopenharmony_ci			 * supported.
796e5b75505Sopenharmony_ci			 */
797e5b75505Sopenharmony_ci			if (dev->channels.reg_classes == 0 ||
798e5b75505Sopenharmony_ci			    !p2p_channels_includes(&dev->channels,
799e5b75505Sopenharmony_ci						   msg.operating_channel[3],
800e5b75505Sopenharmony_ci						   msg.operating_channel[4])) {
801e5b75505Sopenharmony_ci				struct p2p_channels *ch = &dev->channels;
802e5b75505Sopenharmony_ci
803e5b75505Sopenharmony_ci				os_memset(ch, 0, sizeof(*ch));
804e5b75505Sopenharmony_ci				ch->reg_class[0].reg_class =
805e5b75505Sopenharmony_ci					msg.operating_channel[3];
806e5b75505Sopenharmony_ci				ch->reg_class[0].channel[0] =
807e5b75505Sopenharmony_ci					msg.operating_channel[4];
808e5b75505Sopenharmony_ci				ch->reg_class[0].channels = 1;
809e5b75505Sopenharmony_ci				ch->reg_classes = 1;
810e5b75505Sopenharmony_ci			}
811e5b75505Sopenharmony_ci
812e5b75505Sopenharmony_ci			p2p_channels_intersect(&p2p->channels, &dev->channels,
813e5b75505Sopenharmony_ci					       &intersect);
814e5b75505Sopenharmony_ci
815e5b75505Sopenharmony_ci			if (intersect.reg_classes == 0) {
816e5b75505Sopenharmony_ci				p2p_dbg(p2p,
817e5b75505Sopenharmony_ci					"No common channels - force deferred flow");
818e5b75505Sopenharmony_ci				auto_accept = 0;
819e5b75505Sopenharmony_ci			}
820e5b75505Sopenharmony_ci		}
821e5b75505Sopenharmony_ci
822e5b75505Sopenharmony_ci		if (auto_accept || reject != P2P_SC_SUCCESS) {
823e5b75505Sopenharmony_ci			struct p2ps_provision *tmp;
824e5b75505Sopenharmony_ci
825e5b75505Sopenharmony_ci			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
826e5b75505Sopenharmony_ci						 msg.wps_config_methods,
827e5b75505Sopenharmony_ci						 session_mac, adv_mac) < 0) {
828e5b75505Sopenharmony_ci				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
829e5b75505Sopenharmony_ci				goto out;
830e5b75505Sopenharmony_ci			}
831e5b75505Sopenharmony_ci
832e5b75505Sopenharmony_ci			tmp = p2p->p2ps_prov;
833e5b75505Sopenharmony_ci			tmp->force_freq = forced_freq;
834e5b75505Sopenharmony_ci			tmp->pref_freq = pref_freq;
835e5b75505Sopenharmony_ci			if (conncap) {
836e5b75505Sopenharmony_ci				tmp->conncap = conncap;
837e5b75505Sopenharmony_ci				tmp->status = P2P_SC_SUCCESS;
838e5b75505Sopenharmony_ci			} else {
839e5b75505Sopenharmony_ci				tmp->conncap = auto_accept;
840e5b75505Sopenharmony_ci				tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
841e5b75505Sopenharmony_ci			}
842e5b75505Sopenharmony_ci
843e5b75505Sopenharmony_ci			if (reject != P2P_SC_SUCCESS)
844e5b75505Sopenharmony_ci				goto out;
845e5b75505Sopenharmony_ci		}
846e5b75505Sopenharmony_ci	}
847e5b75505Sopenharmony_ci
848e5b75505Sopenharmony_ci	if (!msg.status && !auto_accept &&
849e5b75505Sopenharmony_ci	    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
850e5b75505Sopenharmony_ci		struct p2ps_provision *tmp;
851e5b75505Sopenharmony_ci
852e5b75505Sopenharmony_ci		if (!conncap) {
853e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
854e5b75505Sopenharmony_ci			goto out;
855e5b75505Sopenharmony_ci		}
856e5b75505Sopenharmony_ci
857e5b75505Sopenharmony_ci		if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
858e5b75505Sopenharmony_ci					 msg.wps_config_methods,
859e5b75505Sopenharmony_ci					 session_mac, adv_mac) < 0) {
860e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
861e5b75505Sopenharmony_ci			goto out;
862e5b75505Sopenharmony_ci		}
863e5b75505Sopenharmony_ci		tmp = p2p->p2ps_prov;
864e5b75505Sopenharmony_ci		reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
865e5b75505Sopenharmony_ci		tmp->status = reject;
866e5b75505Sopenharmony_ci	}
867e5b75505Sopenharmony_ci
868e5b75505Sopenharmony_ci	/* Not a P2PS Follow-on PD */
869e5b75505Sopenharmony_ci	if (!msg.status)
870e5b75505Sopenharmony_ci		goto out;
871e5b75505Sopenharmony_ci
872e5b75505Sopenharmony_ci	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
873e5b75505Sopenharmony_ci		reject = *msg.status;
874e5b75505Sopenharmony_ci		goto out;
875e5b75505Sopenharmony_ci	}
876e5b75505Sopenharmony_ci
877e5b75505Sopenharmony_ci	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
878e5b75505Sopenharmony_ci		goto out;
879e5b75505Sopenharmony_ci
880e5b75505Sopenharmony_ci	if (p2p->p2ps_prov->adv_id != adv_id ||
881e5b75505Sopenharmony_ci	    os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
882e5b75505Sopenharmony_ci		p2p_dbg(p2p,
883e5b75505Sopenharmony_ci			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
884e5b75505Sopenharmony_ci		goto out;
885e5b75505Sopenharmony_ci	}
886e5b75505Sopenharmony_ci
887e5b75505Sopenharmony_ci	if (p2p->p2ps_prov->session_id != session_id ||
888e5b75505Sopenharmony_ci	    os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
889e5b75505Sopenharmony_ci		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
890e5b75505Sopenharmony_ci		goto out;
891e5b75505Sopenharmony_ci	}
892e5b75505Sopenharmony_ci
893e5b75505Sopenharmony_ci	method = p2p->p2ps_prov->method;
894e5b75505Sopenharmony_ci
895e5b75505Sopenharmony_ci	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
896e5b75505Sopenharmony_ci						  remote_conncap,
897e5b75505Sopenharmony_ci						  p2p->p2ps_prov->conncap,
898e5b75505Sopenharmony_ci						  &p2p->p2ps_prov->force_freq,
899e5b75505Sopenharmony_ci						  &p2p->p2ps_prov->pref_freq);
900e5b75505Sopenharmony_ci
901e5b75505Sopenharmony_ci	resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
902e5b75505Sopenharmony_ci					       req_fcap->cpt);
903e5b75505Sopenharmony_ci
904e5b75505Sopenharmony_ci	p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
905e5b75505Sopenharmony_ci		p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
906e5b75505Sopenharmony_ci
907e5b75505Sopenharmony_ci	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
908e5b75505Sopenharmony_ci			    p2p->p2ps_prov->pref_freq, 0);
909e5b75505Sopenharmony_ci
910e5b75505Sopenharmony_ci	/*
911e5b75505Sopenharmony_ci	 * Ensure that if we asked for PIN originally, our method is consistent
912e5b75505Sopenharmony_ci	 * with original request.
913e5b75505Sopenharmony_ci	 */
914e5b75505Sopenharmony_ci	if (method & WPS_CONFIG_DISPLAY)
915e5b75505Sopenharmony_ci		method = WPS_CONFIG_KEYPAD;
916e5b75505Sopenharmony_ci	else if (method & WPS_CONFIG_KEYPAD)
917e5b75505Sopenharmony_ci		method = WPS_CONFIG_DISPLAY;
918e5b75505Sopenharmony_ci
919e5b75505Sopenharmony_ci	if (!conncap || !(msg.wps_config_methods & method)) {
920e5b75505Sopenharmony_ci		/*
921e5b75505Sopenharmony_ci		 * Reject this "Deferred Accept*
922e5b75505Sopenharmony_ci		 * if incompatible conncap or method
923e5b75505Sopenharmony_ci		 */
924e5b75505Sopenharmony_ci		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
925e5b75505Sopenharmony_ci	} else if (!resp_fcap.cpt) {
926e5b75505Sopenharmony_ci		p2p_dbg(p2p,
927e5b75505Sopenharmony_ci			"Incompatible P2PS feature capability CPT bitmask");
928e5b75505Sopenharmony_ci		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
929e5b75505Sopenharmony_ci	} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
930e5b75505Sopenharmony_ci		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
931e5b75505Sopenharmony_ci		   msg.channel_list && msg.channel_list_len &&
932e5b75505Sopenharmony_ci		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
933e5b75505Sopenharmony_ci					   msg.channel_list,
934e5b75505Sopenharmony_ci					   msg.channel_list_len) < 0) {
935e5b75505Sopenharmony_ci		p2p_dbg(p2p,
936e5b75505Sopenharmony_ci			"No common channels in Follow-On Provision Discovery Request");
937e5b75505Sopenharmony_ci		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
938e5b75505Sopenharmony_ci	} else {
939e5b75505Sopenharmony_ci		reject = P2P_SC_SUCCESS;
940e5b75505Sopenharmony_ci	}
941e5b75505Sopenharmony_ci
942e5b75505Sopenharmony_ci	dev->oper_freq = 0;
943e5b75505Sopenharmony_ci	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
944e5b75505Sopenharmony_ci		u8 tmp;
945e5b75505Sopenharmony_ci
946e5b75505Sopenharmony_ci		if (msg.operating_channel)
947e5b75505Sopenharmony_ci			dev->oper_freq =
948e5b75505Sopenharmony_ci				p2p_channel_to_freq(msg.operating_channel[3],
949e5b75505Sopenharmony_ci						    msg.operating_channel[4]);
950e5b75505Sopenharmony_ci
951e5b75505Sopenharmony_ci		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
952e5b75505Sopenharmony_ci		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
953e5b75505Sopenharmony_ci			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
954e5b75505Sopenharmony_ci	}
955e5b75505Sopenharmony_ci
956e5b75505Sopenharmony_ci	p2p->p2ps_prov->status = reject;
957e5b75505Sopenharmony_ci	p2p->p2ps_prov->conncap = conncap;
958e5b75505Sopenharmony_ci
959e5b75505Sopenharmony_ciout:
960e5b75505Sopenharmony_ci	if (reject == P2P_SC_SUCCESS ||
961e5b75505Sopenharmony_ci	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
962e5b75505Sopenharmony_ci		config_methods = msg.wps_config_methods;
963e5b75505Sopenharmony_ci	else
964e5b75505Sopenharmony_ci		config_methods = 0;
965e5b75505Sopenharmony_ci
966e5b75505Sopenharmony_ci	/*
967e5b75505Sopenharmony_ci	 * Send PD Response for an initial PD Request or for follow-on
968e5b75505Sopenharmony_ci	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
969e5b75505Sopenharmony_ci	 */
970e5b75505Sopenharmony_ci	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
971e5b75505Sopenharmony_ci		resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
972e5b75505Sopenharmony_ci						reject, config_methods, adv_id,
973e5b75505Sopenharmony_ci						msg.group_id, msg.group_id_len,
974e5b75505Sopenharmony_ci						msg.persistent_ssid,
975e5b75505Sopenharmony_ci						msg.persistent_ssid_len,
976e5b75505Sopenharmony_ci						(const u8 *) &resp_fcap,
977e5b75505Sopenharmony_ci						sizeof(resp_fcap));
978e5b75505Sopenharmony_ci		if (!resp) {
979e5b75505Sopenharmony_ci			p2p_parse_free(&msg);
980e5b75505Sopenharmony_ci			return;
981e5b75505Sopenharmony_ci		}
982e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Sending Provision Discovery Response");
983e5b75505Sopenharmony_ci		if (rx_freq > 0)
984e5b75505Sopenharmony_ci			freq = rx_freq;
985e5b75505Sopenharmony_ci		else
986e5b75505Sopenharmony_ci			freq = p2p_channel_to_freq(p2p->cfg->reg_class,
987e5b75505Sopenharmony_ci						   p2p->cfg->channel);
988e5b75505Sopenharmony_ci		if (freq < 0) {
989e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Unknown regulatory class/channel");
990e5b75505Sopenharmony_ci			wpabuf_free(resp);
991e5b75505Sopenharmony_ci			p2p_parse_free(&msg);
992e5b75505Sopenharmony_ci			return;
993e5b75505Sopenharmony_ci		}
994e5b75505Sopenharmony_ci		p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
995e5b75505Sopenharmony_ci		if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
996e5b75505Sopenharmony_ci				    p2p->cfg->dev_addr,
997e5b75505Sopenharmony_ci				    wpabuf_head(resp), wpabuf_len(resp),
998e5b75505Sopenharmony_ci				    50) < 0)
999e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Failed to send Action frame");
1000e5b75505Sopenharmony_ci		else
1001e5b75505Sopenharmony_ci			p2p->send_action_in_progress = 1;
1002e5b75505Sopenharmony_ci
1003e5b75505Sopenharmony_ci		wpabuf_free(resp);
1004e5b75505Sopenharmony_ci	}
1005e5b75505Sopenharmony_ci
1006e5b75505Sopenharmony_ci	if (!dev) {
1007e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1008e5b75505Sopenharmony_ci		return;
1009e5b75505Sopenharmony_ci	}
1010e5b75505Sopenharmony_ci
1011e5b75505Sopenharmony_ci	freq = 0;
1012e5b75505Sopenharmony_ci	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
1013e5b75505Sopenharmony_ci		freq = p2p_channel_to_freq(p2p->op_reg_class,
1014e5b75505Sopenharmony_ci					   p2p->op_channel);
1015e5b75505Sopenharmony_ci		if (freq < 0)
1016e5b75505Sopenharmony_ci			freq = 0;
1017e5b75505Sopenharmony_ci	}
1018e5b75505Sopenharmony_ci
1019e5b75505Sopenharmony_ci	if (!p2p->cfg->p2ps_prov_complete) {
1020e5b75505Sopenharmony_ci		/* Don't emit anything */
1021e5b75505Sopenharmony_ci	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
1022e5b75505Sopenharmony_ci		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
1023e5b75505Sopenharmony_ci		reject = *msg.status;
1024e5b75505Sopenharmony_ci		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
1025e5b75505Sopenharmony_ci					     sa, adv_mac, session_mac,
1026e5b75505Sopenharmony_ci					     NULL, adv_id, session_id,
1027e5b75505Sopenharmony_ci					     0, 0, msg.persistent_ssid,
1028e5b75505Sopenharmony_ci					     msg.persistent_ssid_len,
1029e5b75505Sopenharmony_ci					     0, 0, NULL, NULL, 0, freq,
1030e5b75505Sopenharmony_ci					     NULL, 0);
1031e5b75505Sopenharmony_ci	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
1032e5b75505Sopenharmony_ci		   p2p->p2ps_prov) {
1033e5b75505Sopenharmony_ci		p2p->p2ps_prov->status = reject;
1034e5b75505Sopenharmony_ci		p2p->p2ps_prov->conncap = conncap;
1035e5b75505Sopenharmony_ci
1036e5b75505Sopenharmony_ci		if (reject != P2P_SC_SUCCESS)
1037e5b75505Sopenharmony_ci			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
1038e5b75505Sopenharmony_ci						     sa, adv_mac, session_mac,
1039e5b75505Sopenharmony_ci						     NULL, adv_id,
1040e5b75505Sopenharmony_ci						     session_id, conncap, 0,
1041e5b75505Sopenharmony_ci						     msg.persistent_ssid,
1042e5b75505Sopenharmony_ci						     msg.persistent_ssid_len, 0,
1043e5b75505Sopenharmony_ci						     0, NULL, NULL, 0, freq,
1044e5b75505Sopenharmony_ci						     NULL, 0);
1045e5b75505Sopenharmony_ci		else
1046e5b75505Sopenharmony_ci			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
1047e5b75505Sopenharmony_ci						     *msg.status,
1048e5b75505Sopenharmony_ci						     sa, adv_mac, session_mac,
1049e5b75505Sopenharmony_ci						     group_mac, adv_id,
1050e5b75505Sopenharmony_ci						     session_id, conncap,
1051e5b75505Sopenharmony_ci						     passwd_id,
1052e5b75505Sopenharmony_ci						     msg.persistent_ssid,
1053e5b75505Sopenharmony_ci						     msg.persistent_ssid_len, 0,
1054e5b75505Sopenharmony_ci						     0, NULL,
1055e5b75505Sopenharmony_ci						     (const u8 *) &resp_fcap,
1056e5b75505Sopenharmony_ci						     sizeof(resp_fcap), freq,
1057e5b75505Sopenharmony_ci						     NULL, 0);
1058e5b75505Sopenharmony_ci	} else if (msg.status && p2p->p2ps_prov) {
1059e5b75505Sopenharmony_ci		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
1060e5b75505Sopenharmony_ci		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
1061e5b75505Sopenharmony_ci					     adv_mac, session_mac, group_mac,
1062e5b75505Sopenharmony_ci					     adv_id, session_id, conncap,
1063e5b75505Sopenharmony_ci					     passwd_id,
1064e5b75505Sopenharmony_ci					     msg.persistent_ssid,
1065e5b75505Sopenharmony_ci					     msg.persistent_ssid_len,
1066e5b75505Sopenharmony_ci					     0, 0, NULL,
1067e5b75505Sopenharmony_ci					     (const u8 *) &resp_fcap,
1068e5b75505Sopenharmony_ci					     sizeof(resp_fcap), freq, NULL, 0);
1069e5b75505Sopenharmony_ci	} else if (msg.status) {
1070e5b75505Sopenharmony_ci	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
1071e5b75505Sopenharmony_ci		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
1072e5b75505Sopenharmony_ci					     sa, adv_mac, session_mac,
1073e5b75505Sopenharmony_ci					     group_mac, adv_id, session_id,
1074e5b75505Sopenharmony_ci					     conncap, passwd_id,
1075e5b75505Sopenharmony_ci					     msg.persistent_ssid,
1076e5b75505Sopenharmony_ci					     msg.persistent_ssid_len,
1077e5b75505Sopenharmony_ci					     0, 0, NULL,
1078e5b75505Sopenharmony_ci					     (const u8 *) &resp_fcap,
1079e5b75505Sopenharmony_ci					     sizeof(resp_fcap), freq,
1080e5b75505Sopenharmony_ci					     msg.group_id ?
1081e5b75505Sopenharmony_ci					     msg.group_id + ETH_ALEN : NULL,
1082e5b75505Sopenharmony_ci					     msg.group_id ?
1083e5b75505Sopenharmony_ci					     msg.group_id_len - ETH_ALEN : 0);
1084e5b75505Sopenharmony_ci	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1085e5b75505Sopenharmony_ci		   (!msg.session_info || !msg.session_info_len)) {
1086e5b75505Sopenharmony_ci		p2p->p2ps_prov->method = msg.wps_config_methods;
1087e5b75505Sopenharmony_ci
1088e5b75505Sopenharmony_ci		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
1089e5b75505Sopenharmony_ci					     sa, adv_mac, session_mac,
1090e5b75505Sopenharmony_ci					     group_mac, adv_id, session_id,
1091e5b75505Sopenharmony_ci					     conncap, passwd_id,
1092e5b75505Sopenharmony_ci					     msg.persistent_ssid,
1093e5b75505Sopenharmony_ci					     msg.persistent_ssid_len,
1094e5b75505Sopenharmony_ci					     0, 1, NULL,
1095e5b75505Sopenharmony_ci					     (const u8 *) &resp_fcap,
1096e5b75505Sopenharmony_ci					     sizeof(resp_fcap), freq, NULL, 0);
1097e5b75505Sopenharmony_ci	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
1098e5b75505Sopenharmony_ci		size_t buf_len = msg.session_info_len;
1099e5b75505Sopenharmony_ci		char *buf = os_malloc(2 * buf_len + 1);
1100e5b75505Sopenharmony_ci
1101e5b75505Sopenharmony_ci		if (buf) {
1102e5b75505Sopenharmony_ci			p2p->p2ps_prov->method = msg.wps_config_methods;
1103e5b75505Sopenharmony_ci
1104e5b75505Sopenharmony_ci			utf8_escape((char *) msg.session_info, buf_len,
1105e5b75505Sopenharmony_ci				    buf, 2 * buf_len + 1);
1106e5b75505Sopenharmony_ci
1107e5b75505Sopenharmony_ci			p2p->cfg->p2ps_prov_complete(
1108e5b75505Sopenharmony_ci				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
1109e5b75505Sopenharmony_ci				adv_mac, session_mac, group_mac, adv_id,
1110e5b75505Sopenharmony_ci				session_id, conncap, passwd_id,
1111e5b75505Sopenharmony_ci				msg.persistent_ssid, msg.persistent_ssid_len,
1112e5b75505Sopenharmony_ci				0, 1, buf,
1113e5b75505Sopenharmony_ci				(const u8 *) &resp_fcap, sizeof(resp_fcap),
1114e5b75505Sopenharmony_ci				freq, NULL, 0);
1115e5b75505Sopenharmony_ci
1116e5b75505Sopenharmony_ci			os_free(buf);
1117e5b75505Sopenharmony_ci		}
1118e5b75505Sopenharmony_ci	}
1119e5b75505Sopenharmony_ci
1120e5b75505Sopenharmony_ci	/*
1121e5b75505Sopenharmony_ci	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1122e5b75505Sopenharmony_ci	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1123e5b75505Sopenharmony_ci	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
1124e5b75505Sopenharmony_ci	 * enter/show PIN.
1125e5b75505Sopenharmony_ci	 *
1126e5b75505Sopenharmony_ci	 * The callback is called in the following cases:
1127e5b75505Sopenharmony_ci	 * 1. Legacy P2P PD request, response status SUCCESS
1128e5b75505Sopenharmony_ci	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
1129e5b75505Sopenharmony_ci	 *    response status: SUCCESS
1130e5b75505Sopenharmony_ci	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
1131e5b75505Sopenharmony_ci	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1132e5b75505Sopenharmony_ci	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
1133e5b75505Sopenharmony_ci	 *    response status: INFO_CURRENTLY_UNAVAILABLE
1134e5b75505Sopenharmony_ci	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
1135e5b75505Sopenharmony_ci	 *    advertiser role: DISPLAY, autoaccept: FALSE,
1136e5b75505Sopenharmony_ci	 *    seeker: KEYPAD, response status: SUCCESS
1137e5b75505Sopenharmony_ci	 */
1138e5b75505Sopenharmony_ci	if (p2p->cfg->prov_disc_req &&
1139e5b75505Sopenharmony_ci	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
1140e5b75505Sopenharmony_ci	     (!msg.status &&
1141e5b75505Sopenharmony_ci	     (reject == P2P_SC_SUCCESS ||
1142e5b75505Sopenharmony_ci	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
1143e5b75505Sopenharmony_ci	      passwd_id == DEV_PW_USER_SPECIFIED) ||
1144e5b75505Sopenharmony_ci	     (!msg.status &&
1145e5b75505Sopenharmony_ci	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1146e5b75505Sopenharmony_ci	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1147e5b75505Sopenharmony_ci	     (reject == P2P_SC_SUCCESS &&
1148e5b75505Sopenharmony_ci	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
1149e5b75505Sopenharmony_ci	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
1150e5b75505Sopenharmony_ci		const u8 *dev_addr = sa;
1151e5b75505Sopenharmony_ci
1152e5b75505Sopenharmony_ci		if (msg.p2p_device_addr)
1153e5b75505Sopenharmony_ci			dev_addr = msg.p2p_device_addr;
1154e5b75505Sopenharmony_ci		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
1155e5b75505Sopenharmony_ci					msg.wps_config_methods,
1156e5b75505Sopenharmony_ci					dev_addr, msg.pri_dev_type,
1157e5b75505Sopenharmony_ci					msg.device_name, msg.config_methods,
1158e5b75505Sopenharmony_ci					msg.capability ? msg.capability[0] : 0,
1159e5b75505Sopenharmony_ci					msg.capability ? msg.capability[1] :
1160e5b75505Sopenharmony_ci					0,
1161e5b75505Sopenharmony_ci					msg.group_id, msg.group_id_len);
1162e5b75505Sopenharmony_ci	}
1163e5b75505Sopenharmony_ci
1164e5b75505Sopenharmony_ci	if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
1165e5b75505Sopenharmony_ci		p2ps_prov_free(p2p);
1166e5b75505Sopenharmony_ci
1167e5b75505Sopenharmony_ci	if (reject == P2P_SC_SUCCESS) {
1168e5b75505Sopenharmony_ci		switch (config_methods) {
1169e5b75505Sopenharmony_ci		case WPS_CONFIG_DISPLAY:
1170e5b75505Sopenharmony_ci			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
1171e5b75505Sopenharmony_ci			break;
1172e5b75505Sopenharmony_ci		case WPS_CONFIG_KEYPAD:
1173e5b75505Sopenharmony_ci			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
1174e5b75505Sopenharmony_ci			break;
1175e5b75505Sopenharmony_ci		case WPS_CONFIG_PUSHBUTTON:
1176e5b75505Sopenharmony_ci			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
1177e5b75505Sopenharmony_ci			break;
1178e5b75505Sopenharmony_ci		case WPS_CONFIG_P2PS:
1179e5b75505Sopenharmony_ci			dev->wps_prov_info = WPS_CONFIG_P2PS;
1180e5b75505Sopenharmony_ci			break;
1181e5b75505Sopenharmony_ci		default:
1182e5b75505Sopenharmony_ci			dev->wps_prov_info = 0;
1183e5b75505Sopenharmony_ci			break;
1184e5b75505Sopenharmony_ci		}
1185e5b75505Sopenharmony_ci
1186e5b75505Sopenharmony_ci		if (msg.intended_addr)
1187e5b75505Sopenharmony_ci			os_memcpy(dev->interface_addr, msg.intended_addr,
1188e5b75505Sopenharmony_ci				  ETH_ALEN);
1189e5b75505Sopenharmony_ci	}
1190e5b75505Sopenharmony_ci	p2p_parse_free(&msg);
1191e5b75505Sopenharmony_ci}
1192e5b75505Sopenharmony_ci
1193e5b75505Sopenharmony_ci
1194e5b75505Sopenharmony_cistatic int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
1195e5b75505Sopenharmony_ci				     struct p2p_message *msg)
1196e5b75505Sopenharmony_ci{
1197e5b75505Sopenharmony_ci	u8 conn_cap_go = 0;
1198e5b75505Sopenharmony_ci	u8 conn_cap_cli = 0;
1199e5b75505Sopenharmony_ci	u32 session_id;
1200e5b75505Sopenharmony_ci	u32 adv_id;
1201e5b75505Sopenharmony_ci
1202e5b75505Sopenharmony_ci#define P2PS_PD_RESP_CHECK(_val, _attr) \
1203e5b75505Sopenharmony_ci	do { \
1204e5b75505Sopenharmony_ci		if ((_val) && !msg->_attr) { \
1205e5b75505Sopenharmony_ci			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
1206e5b75505Sopenharmony_ci			return -1; \
1207e5b75505Sopenharmony_ci		} \
1208e5b75505Sopenharmony_ci	} while (0)
1209e5b75505Sopenharmony_ci
1210e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, status);
1211e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, adv_id);
1212e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, adv_mac);
1213e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, capability);
1214e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, p2p_device_info);
1215e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, session_id);
1216e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, session_mac);
1217e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(1, feature_cap);
1218e5b75505Sopenharmony_ci
1219e5b75505Sopenharmony_ci	session_id = WPA_GET_LE32(msg->session_id);
1220e5b75505Sopenharmony_ci	adv_id = WPA_GET_LE32(msg->adv_id);
1221e5b75505Sopenharmony_ci
1222e5b75505Sopenharmony_ci	if (p2p->p2ps_prov->session_id != session_id) {
1223e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1224e5b75505Sopenharmony_ci			"Ignore PD Response with unexpected Session ID");
1225e5b75505Sopenharmony_ci		return -1;
1226e5b75505Sopenharmony_ci	}
1227e5b75505Sopenharmony_ci
1228e5b75505Sopenharmony_ci	if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
1229e5b75505Sopenharmony_ci		      ETH_ALEN)) {
1230e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1231e5b75505Sopenharmony_ci			"Ignore PD Response with unexpected Session MAC");
1232e5b75505Sopenharmony_ci		return -1;
1233e5b75505Sopenharmony_ci	}
1234e5b75505Sopenharmony_ci
1235e5b75505Sopenharmony_ci	if (p2p->p2ps_prov->adv_id != adv_id) {
1236e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1237e5b75505Sopenharmony_ci			"Ignore PD Response with unexpected Advertisement ID");
1238e5b75505Sopenharmony_ci		return -1;
1239e5b75505Sopenharmony_ci	}
1240e5b75505Sopenharmony_ci
1241e5b75505Sopenharmony_ci	if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
1242e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1243e5b75505Sopenharmony_ci			"Ignore PD Response with unexpected Advertisement MAC");
1244e5b75505Sopenharmony_ci		return -1;
1245e5b75505Sopenharmony_ci	}
1246e5b75505Sopenharmony_ci
1247e5b75505Sopenharmony_ci	if (msg->listen_channel) {
1248e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1249e5b75505Sopenharmony_ci			"Ignore malformed PD Response - unexpected Listen Channel");
1250e5b75505Sopenharmony_ci		return -1;
1251e5b75505Sopenharmony_ci	}
1252e5b75505Sopenharmony_ci
1253e5b75505Sopenharmony_ci	if (*msg->status == P2P_SC_SUCCESS &&
1254e5b75505Sopenharmony_ci	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
1255e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1256e5b75505Sopenharmony_ci			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
1257e5b75505Sopenharmony_ci		return -1;
1258e5b75505Sopenharmony_ci	}
1259e5b75505Sopenharmony_ci
1260e5b75505Sopenharmony_ci	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
1261e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1262e5b75505Sopenharmony_ci			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
1263e5b75505Sopenharmony_ci		return -1;
1264e5b75505Sopenharmony_ci	}
1265e5b75505Sopenharmony_ci
1266e5b75505Sopenharmony_ci	if (msg->conn_cap) {
1267e5b75505Sopenharmony_ci		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
1268e5b75505Sopenharmony_ci		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
1269e5b75505Sopenharmony_ci	}
1270e5b75505Sopenharmony_ci
1271e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1272e5b75505Sopenharmony_ci			   channel_list);
1273e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
1274e5b75505Sopenharmony_ci			   config_timeout);
1275e5b75505Sopenharmony_ci
1276e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
1277e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
1278e5b75505Sopenharmony_ci	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
1279e5b75505Sopenharmony_ci	/*
1280e5b75505Sopenharmony_ci	 * TODO: Also validate that operating channel is present if the device
1281e5b75505Sopenharmony_ci	 * is a GO in a persistent group. We can't do it here since we don't
1282e5b75505Sopenharmony_ci	 * know what is the role of the peer. It should be probably done in
1283e5b75505Sopenharmony_ci	 * p2ps_prov_complete callback, but currently operating channel isn't
1284e5b75505Sopenharmony_ci	 * passed to it.
1285e5b75505Sopenharmony_ci	 */
1286e5b75505Sopenharmony_ci
1287e5b75505Sopenharmony_ci#undef P2PS_PD_RESP_CHECK
1288e5b75505Sopenharmony_ci
1289e5b75505Sopenharmony_ci	return 0;
1290e5b75505Sopenharmony_ci}
1291e5b75505Sopenharmony_ci
1292e5b75505Sopenharmony_ci
1293e5b75505Sopenharmony_civoid p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
1294e5b75505Sopenharmony_ci				const u8 *data, size_t len)
1295e5b75505Sopenharmony_ci{
1296e5b75505Sopenharmony_ci	struct p2p_message msg;
1297e5b75505Sopenharmony_ci	struct p2p_device *dev;
1298e5b75505Sopenharmony_ci	u16 report_config_methods = 0, req_config_methods;
1299e5b75505Sopenharmony_ci	u8 status = P2P_SC_SUCCESS;
1300e5b75505Sopenharmony_ci	u32 adv_id = 0;
1301e5b75505Sopenharmony_ci	u8 conncap = P2PS_SETUP_NEW;
1302e5b75505Sopenharmony_ci	u8 adv_mac[ETH_ALEN];
1303e5b75505Sopenharmony_ci	const u8 *group_mac;
1304e5b75505Sopenharmony_ci	int passwd_id = DEV_PW_DEFAULT;
1305e5b75505Sopenharmony_ci	int p2ps_seeker;
1306e5b75505Sopenharmony_ci
1307e5b75505Sopenharmony_ci	if (p2p_parse(data, len, &msg))
1308e5b75505Sopenharmony_ci		return;
1309e5b75505Sopenharmony_ci
1310e5b75505Sopenharmony_ci	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
1311e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1312e5b75505Sopenharmony_ci		return;
1313e5b75505Sopenharmony_ci	}
1314e5b75505Sopenharmony_ci
1315e5b75505Sopenharmony_ci	/* Parse the P2PS members present */
1316e5b75505Sopenharmony_ci	if (msg.status)
1317e5b75505Sopenharmony_ci		status = *msg.status;
1318e5b75505Sopenharmony_ci
1319e5b75505Sopenharmony_ci	group_mac = msg.intended_addr;
1320e5b75505Sopenharmony_ci
1321e5b75505Sopenharmony_ci	if (msg.adv_mac)
1322e5b75505Sopenharmony_ci		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
1323e5b75505Sopenharmony_ci	else
1324e5b75505Sopenharmony_ci		os_memset(adv_mac, 0, ETH_ALEN);
1325e5b75505Sopenharmony_ci
1326e5b75505Sopenharmony_ci	if (msg.adv_id)
1327e5b75505Sopenharmony_ci		adv_id = WPA_GET_LE32(msg.adv_id);
1328e5b75505Sopenharmony_ci
1329e5b75505Sopenharmony_ci	if (msg.conn_cap) {
1330e5b75505Sopenharmony_ci		conncap = *msg.conn_cap;
1331e5b75505Sopenharmony_ci
1332e5b75505Sopenharmony_ci		/* Switch bits to local relative */
1333e5b75505Sopenharmony_ci		switch (conncap) {
1334e5b75505Sopenharmony_ci		case P2PS_SETUP_GROUP_OWNER:
1335e5b75505Sopenharmony_ci			conncap = P2PS_SETUP_CLIENT;
1336e5b75505Sopenharmony_ci			break;
1337e5b75505Sopenharmony_ci		case P2PS_SETUP_CLIENT:
1338e5b75505Sopenharmony_ci			conncap = P2PS_SETUP_GROUP_OWNER;
1339e5b75505Sopenharmony_ci			break;
1340e5b75505Sopenharmony_ci		}
1341e5b75505Sopenharmony_ci	}
1342e5b75505Sopenharmony_ci
1343e5b75505Sopenharmony_ci	p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
1344e5b75505Sopenharmony_ci		" with config methods 0x%x",
1345e5b75505Sopenharmony_ci		MAC2STR(sa), msg.wps_config_methods);
1346e5b75505Sopenharmony_ci
1347e5b75505Sopenharmony_ci	dev = p2p_get_device(p2p, sa);
1348e5b75505Sopenharmony_ci	if (dev == NULL || !dev->req_config_methods) {
1349e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Ignore Provision Discovery Response from " MACSTR
1350e5b75505Sopenharmony_ci			" with no pending request", MAC2STR(sa));
1351e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1352e5b75505Sopenharmony_ci		return;
1353e5b75505Sopenharmony_ci	} else if (msg.wfd_subelems) {
1354e5b75505Sopenharmony_ci		wpabuf_free(dev->info.wfd_subelems);
1355e5b75505Sopenharmony_ci		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
1356e5b75505Sopenharmony_ci	}
1357e5b75505Sopenharmony_ci
1358e5b75505Sopenharmony_ci	if (dev->dialog_token != msg.dialog_token) {
1359e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Ignore Provision Discovery Response with unexpected Dialog Token %u (expected %u)",
1360e5b75505Sopenharmony_ci			msg.dialog_token, dev->dialog_token);
1361e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1362e5b75505Sopenharmony_ci		return;
1363e5b75505Sopenharmony_ci	}
1364e5b75505Sopenharmony_ci
1365e5b75505Sopenharmony_ci	if (p2p->pending_action_state == P2P_PENDING_PD) {
1366e5b75505Sopenharmony_ci		os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
1367e5b75505Sopenharmony_ci		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
1368e5b75505Sopenharmony_ci	}
1369e5b75505Sopenharmony_ci
1370e5b75505Sopenharmony_ci	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
1371e5b75505Sopenharmony_ci
1372e5b75505Sopenharmony_ci	/*
1373e5b75505Sopenharmony_ci	 * Use a local copy of the requested config methods since
1374e5b75505Sopenharmony_ci	 * p2p_reset_pending_pd() can clear this in the peer entry.
1375e5b75505Sopenharmony_ci	 */
1376e5b75505Sopenharmony_ci	req_config_methods = dev->req_config_methods;
1377e5b75505Sopenharmony_ci
1378e5b75505Sopenharmony_ci	/*
1379e5b75505Sopenharmony_ci	 * If the response is from the peer to whom a user initiated request
1380e5b75505Sopenharmony_ci	 * was sent earlier, we reset that state info here.
1381e5b75505Sopenharmony_ci	 */
1382e5b75505Sopenharmony_ci	if (p2p->user_initiated_pd &&
1383e5b75505Sopenharmony_ci	    os_memcmp(p2p->pending_pd_devaddr, sa, ETH_ALEN) == 0)
1384e5b75505Sopenharmony_ci		p2p_reset_pending_pd(p2p);
1385e5b75505Sopenharmony_ci
1386e5b75505Sopenharmony_ci	if (msg.wps_config_methods != req_config_methods) {
1387e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request (received config_methods 0x%x expected 0x%x",
1388e5b75505Sopenharmony_ci			msg.wps_config_methods, req_config_methods);
1389e5b75505Sopenharmony_ci		if (p2p->cfg->prov_disc_fail)
1390e5b75505Sopenharmony_ci			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
1391e5b75505Sopenharmony_ci						 P2P_PROV_DISC_REJECTED,
1392e5b75505Sopenharmony_ci						 adv_id, adv_mac, NULL);
1393e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1394e5b75505Sopenharmony_ci		p2ps_prov_free(p2p);
1395e5b75505Sopenharmony_ci		goto out;
1396e5b75505Sopenharmony_ci	}
1397e5b75505Sopenharmony_ci
1398e5b75505Sopenharmony_ci	report_config_methods = req_config_methods;
1399e5b75505Sopenharmony_ci	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
1400e5b75505Sopenharmony_ci			P2P_DEV_PD_PEER_KEYPAD |
1401e5b75505Sopenharmony_ci			P2P_DEV_PD_PEER_P2PS);
1402e5b75505Sopenharmony_ci	if (req_config_methods & WPS_CONFIG_DISPLAY) {
1403e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR
1404e5b75505Sopenharmony_ci			" accepted to show a PIN on display", MAC2STR(sa));
1405e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
1406e5b75505Sopenharmony_ci		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
1407e5b75505Sopenharmony_ci	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
1408e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR
1409e5b75505Sopenharmony_ci			" accepted to write our PIN using keypad",
1410e5b75505Sopenharmony_ci			MAC2STR(sa));
1411e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
1412e5b75505Sopenharmony_ci		passwd_id = DEV_PW_USER_SPECIFIED;
1413e5b75505Sopenharmony_ci	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
1414e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
1415e5b75505Sopenharmony_ci			MAC2STR(sa));
1416e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_PEER_P2PS;
1417e5b75505Sopenharmony_ci		passwd_id = DEV_PW_P2PS_DEFAULT;
1418e5b75505Sopenharmony_ci	}
1419e5b75505Sopenharmony_ci
1420e5b75505Sopenharmony_ci	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
1421e5b75505Sopenharmony_ci	    p2p->p2ps_prov) {
1422e5b75505Sopenharmony_ci		dev->oper_freq = 0;
1423e5b75505Sopenharmony_ci
1424e5b75505Sopenharmony_ci		/*
1425e5b75505Sopenharmony_ci		 * Save the reported channel list and operating frequency.
1426e5b75505Sopenharmony_ci		 * Note that the specification mandates that the responder
1427e5b75505Sopenharmony_ci		 * should include in the channel list only channels reported by
1428e5b75505Sopenharmony_ci		 * the initiator, so this is only a sanity check, and if this
1429e5b75505Sopenharmony_ci		 * fails the flow would continue, although it would probably
1430e5b75505Sopenharmony_ci		 * fail. Same is true for the operating channel.
1431e5b75505Sopenharmony_ci		 */
1432e5b75505Sopenharmony_ci		if (msg.channel_list && msg.channel_list_len &&
1433e5b75505Sopenharmony_ci		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
1434e5b75505Sopenharmony_ci					    msg.channel_list,
1435e5b75505Sopenharmony_ci					    msg.channel_list_len) < 0)
1436e5b75505Sopenharmony_ci			p2p_dbg(p2p, "P2PS PD Response - no common channels");
1437e5b75505Sopenharmony_ci
1438e5b75505Sopenharmony_ci		if (msg.operating_channel) {
1439e5b75505Sopenharmony_ci			if (p2p_channels_includes(&p2p->channels,
1440e5b75505Sopenharmony_ci						  msg.operating_channel[3],
1441e5b75505Sopenharmony_ci						  msg.operating_channel[4]) &&
1442e5b75505Sopenharmony_ci			    p2p_channels_includes(&dev->channels,
1443e5b75505Sopenharmony_ci						  msg.operating_channel[3],
1444e5b75505Sopenharmony_ci						  msg.operating_channel[4])) {
1445e5b75505Sopenharmony_ci				dev->oper_freq =
1446e5b75505Sopenharmony_ci					p2p_channel_to_freq(
1447e5b75505Sopenharmony_ci						msg.operating_channel[3],
1448e5b75505Sopenharmony_ci						msg.operating_channel[4]);
1449e5b75505Sopenharmony_ci			} else {
1450e5b75505Sopenharmony_ci				p2p_dbg(p2p,
1451e5b75505Sopenharmony_ci					"P2PS PD Response - invalid operating channel");
1452e5b75505Sopenharmony_ci			}
1453e5b75505Sopenharmony_ci		}
1454e5b75505Sopenharmony_ci
1455e5b75505Sopenharmony_ci		if (p2p->cfg->p2ps_prov_complete) {
1456e5b75505Sopenharmony_ci			int freq = 0;
1457e5b75505Sopenharmony_ci
1458e5b75505Sopenharmony_ci			if (conncap == P2PS_SETUP_GROUP_OWNER) {
1459e5b75505Sopenharmony_ci				u8 tmp;
1460e5b75505Sopenharmony_ci
1461e5b75505Sopenharmony_ci				/*
1462e5b75505Sopenharmony_ci				 * Re-select the operating channel as it is
1463e5b75505Sopenharmony_ci				 * possible that original channel is no longer
1464e5b75505Sopenharmony_ci				 * valid. This should not really fail.
1465e5b75505Sopenharmony_ci				 */
1466e5b75505Sopenharmony_ci				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
1467e5b75505Sopenharmony_ci					p2p_dbg(p2p,
1468e5b75505Sopenharmony_ci						"P2PS PD channel selection failed");
1469e5b75505Sopenharmony_ci
1470e5b75505Sopenharmony_ci				freq = p2p_channel_to_freq(p2p->op_reg_class,
1471e5b75505Sopenharmony_ci							   p2p->op_channel);
1472e5b75505Sopenharmony_ci				if (freq < 0)
1473e5b75505Sopenharmony_ci					freq = 0;
1474e5b75505Sopenharmony_ci			}
1475e5b75505Sopenharmony_ci
1476e5b75505Sopenharmony_ci			p2p->cfg->p2ps_prov_complete(
1477e5b75505Sopenharmony_ci				p2p->cfg->cb_ctx, status, sa, adv_mac,
1478e5b75505Sopenharmony_ci				p2p->p2ps_prov->session_mac,
1479e5b75505Sopenharmony_ci				group_mac, adv_id, p2p->p2ps_prov->session_id,
1480e5b75505Sopenharmony_ci				conncap, passwd_id, msg.persistent_ssid,
1481e5b75505Sopenharmony_ci				msg.persistent_ssid_len, 1, 0, NULL,
1482e5b75505Sopenharmony_ci				msg.feature_cap, msg.feature_cap_len, freq,
1483e5b75505Sopenharmony_ci				msg.group_id ? msg.group_id + ETH_ALEN : NULL,
1484e5b75505Sopenharmony_ci				msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
1485e5b75505Sopenharmony_ci		}
1486e5b75505Sopenharmony_ci		p2ps_prov_free(p2p);
1487e5b75505Sopenharmony_ci	} else if (status != P2P_SC_SUCCESS &&
1488e5b75505Sopenharmony_ci		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1489e5b75505Sopenharmony_ci		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
1490e5b75505Sopenharmony_ci		if (p2p->cfg->p2ps_prov_complete)
1491e5b75505Sopenharmony_ci			p2p->cfg->p2ps_prov_complete(
1492e5b75505Sopenharmony_ci				p2p->cfg->cb_ctx, status, sa, adv_mac,
1493e5b75505Sopenharmony_ci				p2p->p2ps_prov->session_mac,
1494e5b75505Sopenharmony_ci				group_mac, adv_id, p2p->p2ps_prov->session_id,
1495e5b75505Sopenharmony_ci				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0);
1496e5b75505Sopenharmony_ci		p2ps_prov_free(p2p);
1497e5b75505Sopenharmony_ci	}
1498e5b75505Sopenharmony_ci
1499e5b75505Sopenharmony_ci	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
1500e5b75505Sopenharmony_ci		if (p2p->cfg->remove_stale_groups) {
1501e5b75505Sopenharmony_ci			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
1502e5b75505Sopenharmony_ci						      dev->info.p2p_device_addr,
1503e5b75505Sopenharmony_ci						      NULL, NULL, 0);
1504e5b75505Sopenharmony_ci		}
1505e5b75505Sopenharmony_ci
1506e5b75505Sopenharmony_ci		if (msg.session_info && msg.session_info_len) {
1507e5b75505Sopenharmony_ci			size_t info_len = msg.session_info_len;
1508e5b75505Sopenharmony_ci			char *deferred_sess_resp = os_malloc(2 * info_len + 1);
1509e5b75505Sopenharmony_ci
1510e5b75505Sopenharmony_ci			if (!deferred_sess_resp) {
1511e5b75505Sopenharmony_ci				p2p_parse_free(&msg);
1512e5b75505Sopenharmony_ci				p2ps_prov_free(p2p);
1513e5b75505Sopenharmony_ci				goto out;
1514e5b75505Sopenharmony_ci			}
1515e5b75505Sopenharmony_ci			utf8_escape((char *) msg.session_info, info_len,
1516e5b75505Sopenharmony_ci				    deferred_sess_resp, 2 * info_len + 1);
1517e5b75505Sopenharmony_ci
1518e5b75505Sopenharmony_ci			if (p2p->cfg->prov_disc_fail)
1519e5b75505Sopenharmony_ci				p2p->cfg->prov_disc_fail(
1520e5b75505Sopenharmony_ci					p2p->cfg->cb_ctx, sa,
1521e5b75505Sopenharmony_ci					P2P_PROV_DISC_INFO_UNAVAILABLE,
1522e5b75505Sopenharmony_ci					adv_id, adv_mac,
1523e5b75505Sopenharmony_ci					deferred_sess_resp);
1524e5b75505Sopenharmony_ci			os_free(deferred_sess_resp);
1525e5b75505Sopenharmony_ci		} else
1526e5b75505Sopenharmony_ci			if (p2p->cfg->prov_disc_fail)
1527e5b75505Sopenharmony_ci				p2p->cfg->prov_disc_fail(
1528e5b75505Sopenharmony_ci					p2p->cfg->cb_ctx, sa,
1529e5b75505Sopenharmony_ci					P2P_PROV_DISC_INFO_UNAVAILABLE,
1530e5b75505Sopenharmony_ci					adv_id, adv_mac, NULL);
1531e5b75505Sopenharmony_ci	} else if (status != P2P_SC_SUCCESS) {
1532e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
1533e5b75505Sopenharmony_ci		if (p2p->cfg->prov_disc_fail)
1534e5b75505Sopenharmony_ci			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
1535e5b75505Sopenharmony_ci						 P2P_PROV_DISC_REJECTED,
1536e5b75505Sopenharmony_ci						 adv_id, adv_mac, NULL);
1537e5b75505Sopenharmony_ci		p2p_parse_free(&msg);
1538e5b75505Sopenharmony_ci		p2ps_prov_free(p2p);
1539e5b75505Sopenharmony_ci		goto out;
1540e5b75505Sopenharmony_ci	}
1541e5b75505Sopenharmony_ci
1542e5b75505Sopenharmony_ci	/* Store the provisioning info */
1543e5b75505Sopenharmony_ci	dev->wps_prov_info = msg.wps_config_methods;
1544e5b75505Sopenharmony_ci	if (msg.intended_addr)
1545e5b75505Sopenharmony_ci		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
1546e5b75505Sopenharmony_ci
1547e5b75505Sopenharmony_ci	p2p_parse_free(&msg);
1548e5b75505Sopenharmony_ci
1549e5b75505Sopenharmony_ciout:
1550e5b75505Sopenharmony_ci	dev->req_config_methods = 0;
1551e5b75505Sopenharmony_ci	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
1552e5b75505Sopenharmony_ci	if (dev->flags & P2P_DEV_PD_BEFORE_GO_NEG) {
1553e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Start GO Neg after the PD-before-GO-Neg workaround with "
1554e5b75505Sopenharmony_ci			MACSTR, MAC2STR(dev->info.p2p_device_addr));
1555e5b75505Sopenharmony_ci		dev->flags &= ~P2P_DEV_PD_BEFORE_GO_NEG;
1556e5b75505Sopenharmony_ci		p2p_connect_send(p2p, dev);
1557e5b75505Sopenharmony_ci		return;
1558e5b75505Sopenharmony_ci	}
1559e5b75505Sopenharmony_ci
1560e5b75505Sopenharmony_ci	/*
1561e5b75505Sopenharmony_ci	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
1562e5b75505Sopenharmony_ci	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
1563e5b75505Sopenharmony_ci	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
1564e5b75505Sopenharmony_ci	 * show/enter PIN events are needed.
1565e5b75505Sopenharmony_ci	 *
1566e5b75505Sopenharmony_ci	 * The callback is called in the following cases:
1567e5b75505Sopenharmony_ci	 * 1. Legacy P2P PD response with a status SUCCESS
1568e5b75505Sopenharmony_ci	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
1569e5b75505Sopenharmony_ci	 *    response status: SUCCESS, local method KEYPAD
1570e5b75505Sopenharmony_ci	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
1571e5b75505Sopenharmony_ci	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
1572e5b75505Sopenharmony_ci	 *    local method: DISPLAY
1573e5b75505Sopenharmony_ci	 */
1574e5b75505Sopenharmony_ci	if (p2p->cfg->prov_disc_resp &&
1575e5b75505Sopenharmony_ci	    ((status == P2P_SC_SUCCESS && !adv_id) ||
1576e5b75505Sopenharmony_ci	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
1577e5b75505Sopenharmony_ci	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
1578e5b75505Sopenharmony_ci	     (p2ps_seeker &&
1579e5b75505Sopenharmony_ci	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
1580e5b75505Sopenharmony_ci	      passwd_id == DEV_PW_USER_SPECIFIED)))
1581e5b75505Sopenharmony_ci		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
1582e5b75505Sopenharmony_ci					 report_config_methods);
1583e5b75505Sopenharmony_ci
1584e5b75505Sopenharmony_ci	if (p2p->state == P2P_PD_DURING_FIND) {
1585e5b75505Sopenharmony_ci		p2p_stop_listen_for_freq(p2p, 0);
1586e5b75505Sopenharmony_ci		p2p_continue_find(p2p);
1587e5b75505Sopenharmony_ci	}
1588e5b75505Sopenharmony_ci}
1589e5b75505Sopenharmony_ci
1590e5b75505Sopenharmony_ci
1591e5b75505Sopenharmony_ciint p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
1592e5b75505Sopenharmony_ci			   int join, int force_freq)
1593e5b75505Sopenharmony_ci{
1594e5b75505Sopenharmony_ci	struct wpabuf *req;
1595e5b75505Sopenharmony_ci	int freq;
1596e5b75505Sopenharmony_ci
1597e5b75505Sopenharmony_ci	if (force_freq > 0)
1598e5b75505Sopenharmony_ci		freq = force_freq;
1599e5b75505Sopenharmony_ci	else
1600e5b75505Sopenharmony_ci		freq = dev->listen_freq > 0 ? dev->listen_freq :
1601e5b75505Sopenharmony_ci			dev->oper_freq;
1602e5b75505Sopenharmony_ci	if (freq <= 0) {
1603e5b75505Sopenharmony_ci		p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
1604e5b75505Sopenharmony_ci			MACSTR " to send Provision Discovery Request",
1605e5b75505Sopenharmony_ci			MAC2STR(dev->info.p2p_device_addr));
1606e5b75505Sopenharmony_ci		return -1;
1607e5b75505Sopenharmony_ci	}
1608e5b75505Sopenharmony_ci
1609e5b75505Sopenharmony_ci	if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
1610e5b75505Sopenharmony_ci		if (!(dev->info.dev_capab &
1611e5b75505Sopenharmony_ci		      P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY)) {
1612e5b75505Sopenharmony_ci			p2p_dbg(p2p, "Cannot use PD with P2P Device " MACSTR
1613e5b75505Sopenharmony_ci				" that is in a group and is not discoverable",
1614e5b75505Sopenharmony_ci				MAC2STR(dev->info.p2p_device_addr));
1615e5b75505Sopenharmony_ci			return -1;
1616e5b75505Sopenharmony_ci		}
1617e5b75505Sopenharmony_ci		/* TODO: use device discoverability request through GO */
1618e5b75505Sopenharmony_ci	}
1619e5b75505Sopenharmony_ci
1620e5b75505Sopenharmony_ci	if (p2p->p2ps_prov) {
1621e5b75505Sopenharmony_ci		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
1622e5b75505Sopenharmony_ci			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
1623e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_KEYPAD;
1624e5b75505Sopenharmony_ci			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
1625e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_DISPLAY;
1626e5b75505Sopenharmony_ci			else
1627e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_P2PS;
1628e5b75505Sopenharmony_ci		} else {
1629e5b75505Sopenharmony_ci			/* Order of preference, based on peer's capabilities */
1630e5b75505Sopenharmony_ci			if (p2p->p2ps_prov->method)
1631e5b75505Sopenharmony_ci				dev->req_config_methods =
1632e5b75505Sopenharmony_ci					p2p->p2ps_prov->method;
1633e5b75505Sopenharmony_ci			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
1634e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_P2PS;
1635e5b75505Sopenharmony_ci			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
1636e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_DISPLAY;
1637e5b75505Sopenharmony_ci			else
1638e5b75505Sopenharmony_ci				dev->req_config_methods = WPS_CONFIG_KEYPAD;
1639e5b75505Sopenharmony_ci		}
1640e5b75505Sopenharmony_ci		p2p_dbg(p2p,
1641e5b75505Sopenharmony_ci			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
1642e5b75505Sopenharmony_ci			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
1643e5b75505Sopenharmony_ci			dev->req_config_methods);
1644e5b75505Sopenharmony_ci
1645e5b75505Sopenharmony_ci		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
1646e5b75505Sopenharmony_ci					p2p->p2ps_prov->pref_freq, 1) < 0)
1647e5b75505Sopenharmony_ci			return -1;
1648e5b75505Sopenharmony_ci	}
1649e5b75505Sopenharmony_ci
1650e5b75505Sopenharmony_ci	req = p2p_build_prov_disc_req(p2p, dev, join);
1651e5b75505Sopenharmony_ci	if (req == NULL)
1652e5b75505Sopenharmony_ci		return -1;
1653e5b75505Sopenharmony_ci
1654e5b75505Sopenharmony_ci	if (p2p->state != P2P_IDLE)
1655e5b75505Sopenharmony_ci		p2p_stop_listen_for_freq(p2p, freq);
1656e5b75505Sopenharmony_ci	p2p->pending_action_state = P2P_PENDING_PD;
1657e5b75505Sopenharmony_ci	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
1658e5b75505Sopenharmony_ci			    p2p->cfg->dev_addr, dev->info.p2p_device_addr,
1659e5b75505Sopenharmony_ci			    wpabuf_head(req), wpabuf_len(req), 200) < 0) {
1660e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Failed to send Action frame");
1661e5b75505Sopenharmony_ci		wpabuf_free(req);
1662e5b75505Sopenharmony_ci		return -1;
1663e5b75505Sopenharmony_ci	}
1664e5b75505Sopenharmony_ci
1665e5b75505Sopenharmony_ci	os_memcpy(p2p->pending_pd_devaddr, dev->info.p2p_device_addr, ETH_ALEN);
1666e5b75505Sopenharmony_ci
1667e5b75505Sopenharmony_ci	wpabuf_free(req);
1668e5b75505Sopenharmony_ci	return 0;
1669e5b75505Sopenharmony_ci}
1670e5b75505Sopenharmony_ci
1671e5b75505Sopenharmony_ci
1672e5b75505Sopenharmony_ciint p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
1673e5b75505Sopenharmony_ci		      struct p2ps_provision *p2ps_prov,
1674e5b75505Sopenharmony_ci		      u16 config_methods, int join, int force_freq,
1675e5b75505Sopenharmony_ci		      int user_initiated_pd)
1676e5b75505Sopenharmony_ci{
1677e5b75505Sopenharmony_ci	struct p2p_device *dev;
1678e5b75505Sopenharmony_ci
1679e5b75505Sopenharmony_ci	dev = p2p_get_device(p2p, peer_addr);
1680e5b75505Sopenharmony_ci	if (dev == NULL)
1681e5b75505Sopenharmony_ci		dev = p2p_get_device_interface(p2p, peer_addr);
1682e5b75505Sopenharmony_ci	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
1683e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
1684e5b75505Sopenharmony_ci			" not yet known", MAC2STR(peer_addr));
1685e5b75505Sopenharmony_ci		os_free(p2ps_prov);
1686e5b75505Sopenharmony_ci		return -1;
1687e5b75505Sopenharmony_ci	}
1688e5b75505Sopenharmony_ci
1689e5b75505Sopenharmony_ci	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
1690e5b75505Sopenharmony_ci		" (config methods 0x%x)",
1691e5b75505Sopenharmony_ci		MAC2STR(peer_addr), config_methods);
1692e5b75505Sopenharmony_ci	if (config_methods == 0 && !p2ps_prov) {
1693e5b75505Sopenharmony_ci		os_free(p2ps_prov);
1694e5b75505Sopenharmony_ci		return -1;
1695e5b75505Sopenharmony_ci	}
1696e5b75505Sopenharmony_ci
1697e5b75505Sopenharmony_ci	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
1698e5b75505Sopenharmony_ci	    p2p->p2ps_prov) {
1699e5b75505Sopenharmony_ci		/* Use cached method from deferred provisioning */
1700e5b75505Sopenharmony_ci		p2ps_prov->method = p2p->p2ps_prov->method;
1701e5b75505Sopenharmony_ci	}
1702e5b75505Sopenharmony_ci
1703e5b75505Sopenharmony_ci	/* Reset provisioning info */
1704e5b75505Sopenharmony_ci	dev->wps_prov_info = 0;
1705e5b75505Sopenharmony_ci	p2ps_prov_free(p2p);
1706e5b75505Sopenharmony_ci	p2p->p2ps_prov = p2ps_prov;
1707e5b75505Sopenharmony_ci
1708e5b75505Sopenharmony_ci	dev->req_config_methods = config_methods;
1709e5b75505Sopenharmony_ci	if (join)
1710e5b75505Sopenharmony_ci		dev->flags |= P2P_DEV_PD_FOR_JOIN;
1711e5b75505Sopenharmony_ci	else
1712e5b75505Sopenharmony_ci		dev->flags &= ~P2P_DEV_PD_FOR_JOIN;
1713e5b75505Sopenharmony_ci
1714e5b75505Sopenharmony_ci	if (p2p->state != P2P_IDLE && p2p->state != P2P_SEARCH &&
1715e5b75505Sopenharmony_ci	    p2p->state != P2P_LISTEN_ONLY) {
1716e5b75505Sopenharmony_ci		p2p_dbg(p2p, "Busy with other operations; postpone Provision Discovery Request with "
1717e5b75505Sopenharmony_ci			MACSTR " (config methods 0x%x)",
1718e5b75505Sopenharmony_ci			MAC2STR(peer_addr), config_methods);
1719e5b75505Sopenharmony_ci		return 0;
1720e5b75505Sopenharmony_ci	}
1721e5b75505Sopenharmony_ci
1722e5b75505Sopenharmony_ci	p2p->user_initiated_pd = user_initiated_pd;
1723e5b75505Sopenharmony_ci	p2p->pd_force_freq = force_freq;
1724e5b75505Sopenharmony_ci
1725e5b75505Sopenharmony_ci	if (p2p->user_initiated_pd)
1726e5b75505Sopenharmony_ci		p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
1727e5b75505Sopenharmony_ci
1728e5b75505Sopenharmony_ci	/*
1729e5b75505Sopenharmony_ci	 * Assign dialog token here to use the same value in each retry within
1730e5b75505Sopenharmony_ci	 * the same PD exchange.
1731e5b75505Sopenharmony_ci	 */
1732e5b75505Sopenharmony_ci	dev->dialog_token++;
1733e5b75505Sopenharmony_ci	if (dev->dialog_token == 0)
1734e5b75505Sopenharmony_ci		dev->dialog_token = 1;
1735e5b75505Sopenharmony_ci
1736e5b75505Sopenharmony_ci	return p2p_send_prov_disc_req(p2p, dev, join, force_freq);
1737e5b75505Sopenharmony_ci}
1738e5b75505Sopenharmony_ci
1739e5b75505Sopenharmony_ci
1740e5b75505Sopenharmony_civoid p2p_reset_pending_pd(struct p2p_data *p2p)
1741e5b75505Sopenharmony_ci{
1742e5b75505Sopenharmony_ci	struct p2p_device *dev;
1743e5b75505Sopenharmony_ci
1744e5b75505Sopenharmony_ci	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
1745e5b75505Sopenharmony_ci		if (os_memcmp(p2p->pending_pd_devaddr,
1746e5b75505Sopenharmony_ci			      dev->info.p2p_device_addr, ETH_ALEN))
1747e5b75505Sopenharmony_ci			continue;
1748e5b75505Sopenharmony_ci		if (!dev->req_config_methods)
1749e5b75505Sopenharmony_ci			continue;
1750e5b75505Sopenharmony_ci		if (dev->flags & P2P_DEV_PD_FOR_JOIN)
1751e5b75505Sopenharmony_ci			continue;
1752e5b75505Sopenharmony_ci		/* Reset the config methods of the device */
1753e5b75505Sopenharmony_ci		dev->req_config_methods = 0;
1754e5b75505Sopenharmony_ci	}
1755e5b75505Sopenharmony_ci
1756e5b75505Sopenharmony_ci	p2p->user_initiated_pd = 0;
1757e5b75505Sopenharmony_ci	os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
1758e5b75505Sopenharmony_ci	p2p->pd_retries = 0;
1759e5b75505Sopenharmony_ci	p2p->pd_force_freq = 0;
1760e5b75505Sopenharmony_ci}
1761e5b75505Sopenharmony_ci
1762e5b75505Sopenharmony_ci
1763e5b75505Sopenharmony_civoid p2ps_prov_free(struct p2p_data *p2p)
1764e5b75505Sopenharmony_ci{
1765e5b75505Sopenharmony_ci	os_free(p2p->p2ps_prov);
1766e5b75505Sopenharmony_ci	p2p->p2ps_prov = NULL;
1767e5b75505Sopenharmony_ci}
1768