xref: /freebsd/contrib/wpa/wpa_supplicant/wifi_display.c (revision 67350cb56a69468c118bd4ccf6e361b7ebfa9eb4)
1f05cddf9SRui Paulo /*
2f05cddf9SRui Paulo  * wpa_supplicant - Wi-Fi Display
3f05cddf9SRui Paulo  * Copyright (c) 2011, Atheros Communications, Inc.
4f05cddf9SRui Paulo  * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
5f05cddf9SRui Paulo  *
6f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
7f05cddf9SRui Paulo  * See README for more details.
8f05cddf9SRui Paulo  */
9f05cddf9SRui Paulo 
10f05cddf9SRui Paulo #include "includes.h"
11f05cddf9SRui Paulo 
12f05cddf9SRui Paulo #include "common.h"
13f05cddf9SRui Paulo #include "p2p/p2p.h"
14f05cddf9SRui Paulo #include "common/ieee802_11_defs.h"
15f05cddf9SRui Paulo #include "wpa_supplicant_i.h"
16f05cddf9SRui Paulo #include "wifi_display.h"
17f05cddf9SRui Paulo 
18f05cddf9SRui Paulo 
195b9c547cSRui Paulo #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
205b9c547cSRui Paulo 
215b9c547cSRui Paulo 
wifi_display_init(struct wpa_global * global)22f05cddf9SRui Paulo int wifi_display_init(struct wpa_global *global)
23f05cddf9SRui Paulo {
24f05cddf9SRui Paulo 	global->wifi_display = 1;
25f05cddf9SRui Paulo 	return 0;
26f05cddf9SRui Paulo }
27f05cddf9SRui Paulo 
28f05cddf9SRui Paulo 
wifi_display_deinit(struct wpa_global * global)29f05cddf9SRui Paulo void wifi_display_deinit(struct wpa_global *global)
30f05cddf9SRui Paulo {
31f05cddf9SRui Paulo 	int i;
32f05cddf9SRui Paulo 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
33f05cddf9SRui Paulo 		wpabuf_free(global->wfd_subelem[i]);
34f05cddf9SRui Paulo 		global->wfd_subelem[i] = NULL;
35f05cddf9SRui Paulo 	}
36f05cddf9SRui Paulo }
37f05cddf9SRui Paulo 
38f05cddf9SRui Paulo 
wifi_display_get_wfd_ie(struct wpa_global * global)395b9c547cSRui Paulo struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
405b9c547cSRui Paulo {
415b9c547cSRui Paulo 	struct wpabuf *ie;
425b9c547cSRui Paulo 	size_t len;
435b9c547cSRui Paulo 	int i;
445b9c547cSRui Paulo 
455b9c547cSRui Paulo 	if (global->p2p == NULL)
465b9c547cSRui Paulo 		return NULL;
475b9c547cSRui Paulo 
485b9c547cSRui Paulo 	len = 0;
495b9c547cSRui Paulo 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
505b9c547cSRui Paulo 		if (global->wfd_subelem[i])
515b9c547cSRui Paulo 			len += wpabuf_len(global->wfd_subelem[i]);
525b9c547cSRui Paulo 	}
535b9c547cSRui Paulo 
545b9c547cSRui Paulo 	ie = wpabuf_alloc(len);
555b9c547cSRui Paulo 	if (ie == NULL)
565b9c547cSRui Paulo 		return NULL;
575b9c547cSRui Paulo 
585b9c547cSRui Paulo 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
595b9c547cSRui Paulo 		if (global->wfd_subelem[i])
605b9c547cSRui Paulo 			wpabuf_put_buf(ie, global->wfd_subelem[i]);
615b9c547cSRui Paulo 	}
625b9c547cSRui Paulo 
635b9c547cSRui Paulo 	return ie;
645b9c547cSRui Paulo }
655b9c547cSRui Paulo 
665b9c547cSRui Paulo 
wifi_display_update_wfd_ie(struct wpa_global * global)67f05cddf9SRui Paulo static int wifi_display_update_wfd_ie(struct wpa_global *global)
68f05cddf9SRui Paulo {
69f05cddf9SRui Paulo 	struct wpabuf *ie, *buf;
70f05cddf9SRui Paulo 	size_t len, plen;
71f05cddf9SRui Paulo 
725b9c547cSRui Paulo 	if (global->p2p == NULL)
735b9c547cSRui Paulo 		return 0;
745b9c547cSRui Paulo 
75f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
76f05cddf9SRui Paulo 
77f05cddf9SRui Paulo 	if (!global->wifi_display) {
78f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
79f05cddf9SRui Paulo 			   "include WFD IE");
80f05cddf9SRui Paulo 		p2p_set_wfd_ie_beacon(global->p2p, NULL);
81f05cddf9SRui Paulo 		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
82f05cddf9SRui Paulo 		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
83f05cddf9SRui Paulo 		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
84f05cddf9SRui Paulo 		p2p_set_wfd_ie_invitation(global->p2p, NULL);
85f05cddf9SRui Paulo 		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
86f05cddf9SRui Paulo 		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
87f05cddf9SRui Paulo 		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
88f05cddf9SRui Paulo 		p2p_set_wfd_dev_info(global->p2p, NULL);
89*85732ac8SCy Schubert 		p2p_set_wfd_r2_dev_info(global->p2p, NULL);
90f05cddf9SRui Paulo 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
91f05cddf9SRui Paulo 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
92f05cddf9SRui Paulo 		return 0;
93f05cddf9SRui Paulo 	}
94f05cddf9SRui Paulo 
95f05cddf9SRui Paulo 	p2p_set_wfd_dev_info(global->p2p,
96f05cddf9SRui Paulo 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
97*85732ac8SCy Schubert 	p2p_set_wfd_r2_dev_info(
98*85732ac8SCy Schubert 		global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
99f05cddf9SRui Paulo 	p2p_set_wfd_assoc_bssid(
100f05cddf9SRui Paulo 		global->p2p,
101f05cddf9SRui Paulo 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
102f05cddf9SRui Paulo 	p2p_set_wfd_coupled_sink_info(
103f05cddf9SRui Paulo 		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
104f05cddf9SRui Paulo 
105f05cddf9SRui Paulo 	/*
106f05cddf9SRui Paulo 	 * WFD IE is included in number of management frames. Two different
107f05cddf9SRui Paulo 	 * sets of subelements are included depending on the frame:
108f05cddf9SRui Paulo 	 *
109f05cddf9SRui Paulo 	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
110f05cddf9SRui Paulo 	 * Provision Discovery Req:
111f05cddf9SRui Paulo 	 * WFD Device Info
112f05cddf9SRui Paulo 	 * [Associated BSSID]
113f05cddf9SRui Paulo 	 * [Coupled Sink Info]
114f05cddf9SRui Paulo 	 *
115f05cddf9SRui Paulo 	 * Probe Request:
116f05cddf9SRui Paulo 	 * WFD Device Info
117f05cddf9SRui Paulo 	 * [Associated BSSID]
118f05cddf9SRui Paulo 	 * [Coupled Sink Info]
119f05cddf9SRui Paulo 	 * [WFD Extended Capability]
120f05cddf9SRui Paulo 	 *
121f05cddf9SRui Paulo 	 * Probe Response:
122f05cddf9SRui Paulo 	 * WFD Device Info
123f05cddf9SRui Paulo 	 * [Associated BSSID]
124f05cddf9SRui Paulo 	 * [Coupled Sink Info]
125f05cddf9SRui Paulo 	 * [WFD Extended Capability]
126f05cddf9SRui Paulo 	 * [WFD Session Info]
127f05cddf9SRui Paulo 	 *
128f05cddf9SRui Paulo 	 * (Re)Association Response, P2P Invitation Req/Resp,
129f05cddf9SRui Paulo 	 * Provision Discovery Resp:
130f05cddf9SRui Paulo 	 * WFD Device Info
131f05cddf9SRui Paulo 	 * [Associated BSSID]
132f05cddf9SRui Paulo 	 * [Coupled Sink Info]
133f05cddf9SRui Paulo 	 * [WFD Session Info]
134f05cddf9SRui Paulo 	 */
135f05cddf9SRui Paulo 	len = 0;
136f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
137f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
138f05cddf9SRui Paulo 					  WFD_SUBELEM_DEVICE_INFO]);
139*85732ac8SCy Schubert 
140*85732ac8SCy Schubert 	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
141*85732ac8SCy Schubert 		len += wpabuf_len(global->wfd_subelem[
142*85732ac8SCy Schubert 					  WFD_SUBELEM_R2_DEVICE_INFO]);
143*85732ac8SCy Schubert 
144f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
145f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
146f05cddf9SRui Paulo 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
147f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
148f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
149f05cddf9SRui Paulo 					  WFD_SUBELEM_COUPLED_SINK]);
150f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
151f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
152f05cddf9SRui Paulo 					  WFD_SUBELEM_SESSION_INFO]);
153f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
154f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
155f05cddf9SRui Paulo 	buf = wpabuf_alloc(len);
156f05cddf9SRui Paulo 	if (buf == NULL)
157f05cddf9SRui Paulo 		return -1;
158f05cddf9SRui Paulo 
159f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
160f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
161f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
162*85732ac8SCy Schubert 
163*85732ac8SCy Schubert 	if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
164*85732ac8SCy Schubert 		wpabuf_put_buf(buf,
165*85732ac8SCy Schubert 			       global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
166*85732ac8SCy Schubert 
167f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
168f05cddf9SRui Paulo 		wpabuf_put_buf(buf, global->wfd_subelem[
169f05cddf9SRui Paulo 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
170f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
171f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
172f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
173f05cddf9SRui Paulo 
174f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
175f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
176f05cddf9SRui Paulo 	p2p_set_wfd_ie_beacon(global->p2p, ie);
177f05cddf9SRui Paulo 
178f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
179f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
180f05cddf9SRui Paulo 			ie);
181f05cddf9SRui Paulo 	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
182f05cddf9SRui Paulo 
183f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
184f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
185f05cddf9SRui Paulo 	p2p_set_wfd_ie_go_neg(global->p2p, ie);
186f05cddf9SRui Paulo 
187f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
188f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
189f05cddf9SRui Paulo 			"Request", ie);
190f05cddf9SRui Paulo 	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
191f05cddf9SRui Paulo 
192f05cddf9SRui Paulo 	plen = buf->used;
193f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
194f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
195f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
196f05cddf9SRui Paulo 
197f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
198f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
199f05cddf9SRui Paulo 	p2p_set_wfd_ie_probe_req(global->p2p, ie);
200f05cddf9SRui Paulo 
201f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
202f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
203f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
204f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
205f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
206f05cddf9SRui Paulo 	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
207f05cddf9SRui Paulo 
208f05cddf9SRui Paulo 	/* Remove WFD Extended Capability from buffer */
209f05cddf9SRui Paulo 	buf->used = plen;
210f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
211f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
212f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
213f05cddf9SRui Paulo 
214f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
215f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
216f05cddf9SRui Paulo 	p2p_set_wfd_ie_invitation(global->p2p, ie);
217f05cddf9SRui Paulo 
218f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
219f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
220f05cddf9SRui Paulo 			"Response", ie);
221f05cddf9SRui Paulo 	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
222f05cddf9SRui Paulo 
223f05cddf9SRui Paulo 	wpabuf_free(buf);
224f05cddf9SRui Paulo 
225f05cddf9SRui Paulo 	return 0;
226f05cddf9SRui Paulo }
227f05cddf9SRui Paulo 
228f05cddf9SRui Paulo 
wifi_display_enable(struct wpa_global * global,int enabled)229f05cddf9SRui Paulo void wifi_display_enable(struct wpa_global *global, int enabled)
230f05cddf9SRui Paulo {
231f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
232f05cddf9SRui Paulo 		   enabled ? "enabled" : "disabled");
233f05cddf9SRui Paulo 	global->wifi_display = enabled;
234f05cddf9SRui Paulo 	wifi_display_update_wfd_ie(global);
235f05cddf9SRui Paulo }
236f05cddf9SRui Paulo 
237f05cddf9SRui Paulo 
wifi_display_subelem_set(struct wpa_global * global,char * cmd)238f05cddf9SRui Paulo int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
239f05cddf9SRui Paulo {
240f05cddf9SRui Paulo 	char *pos;
241f05cddf9SRui Paulo 	int subelem;
242f05cddf9SRui Paulo 	size_t len;
243f05cddf9SRui Paulo 	struct wpabuf *e;
244f05cddf9SRui Paulo 
245f05cddf9SRui Paulo 	pos = os_strchr(cmd, ' ');
246f05cddf9SRui Paulo 	if (pos == NULL)
247f05cddf9SRui Paulo 		return -1;
248f05cddf9SRui Paulo 	*pos++ = '\0';
249f05cddf9SRui Paulo 
250f05cddf9SRui Paulo 	len = os_strlen(pos);
251f05cddf9SRui Paulo 	if (len & 1)
252f05cddf9SRui Paulo 		return -1;
253f05cddf9SRui Paulo 	len /= 2;
254f05cddf9SRui Paulo 
2555b9c547cSRui Paulo 	if (os_strcmp(cmd, "all") == 0) {
2565b9c547cSRui Paulo 		int res;
2575b9c547cSRui Paulo 
2585b9c547cSRui Paulo 		e = wpabuf_alloc(len);
2595b9c547cSRui Paulo 		if (e == NULL)
2605b9c547cSRui Paulo 			return -1;
2615b9c547cSRui Paulo 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
2625b9c547cSRui Paulo 			wpabuf_free(e);
2635b9c547cSRui Paulo 			return -1;
2645b9c547cSRui Paulo 		}
2655b9c547cSRui Paulo 		res = wifi_display_subelem_set_from_ies(global, e);
2665b9c547cSRui Paulo 		wpabuf_free(e);
2675b9c547cSRui Paulo 		return res;
2685b9c547cSRui Paulo 	}
2695b9c547cSRui Paulo 
2705b9c547cSRui Paulo 	subelem = atoi(cmd);
2715b9c547cSRui Paulo 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
2725b9c547cSRui Paulo 		return -1;
2735b9c547cSRui Paulo 
274f05cddf9SRui Paulo 	if (len == 0) {
275f05cddf9SRui Paulo 		/* Clear subelement */
276f05cddf9SRui Paulo 		e = NULL;
277f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
278f05cddf9SRui Paulo 	} else {
279f05cddf9SRui Paulo 		e = wpabuf_alloc(1 + len);
280f05cddf9SRui Paulo 		if (e == NULL)
281f05cddf9SRui Paulo 			return -1;
282f05cddf9SRui Paulo 		wpabuf_put_u8(e, subelem);
283f05cddf9SRui Paulo 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
284f05cddf9SRui Paulo 			wpabuf_free(e);
285f05cddf9SRui Paulo 			return -1;
286f05cddf9SRui Paulo 		}
287f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
288f05cddf9SRui Paulo 	}
289f05cddf9SRui Paulo 
290f05cddf9SRui Paulo 	wpabuf_free(global->wfd_subelem[subelem]);
291f05cddf9SRui Paulo 	global->wfd_subelem[subelem] = e;
292f05cddf9SRui Paulo 	wifi_display_update_wfd_ie(global);
293f05cddf9SRui Paulo 
294f05cddf9SRui Paulo 	return 0;
295f05cddf9SRui Paulo }
296f05cddf9SRui Paulo 
297f05cddf9SRui Paulo 
wifi_display_subelem_set_from_ies(struct wpa_global * global,struct wpabuf * ie)2985b9c547cSRui Paulo int wifi_display_subelem_set_from_ies(struct wpa_global *global,
2995b9c547cSRui Paulo 				      struct wpabuf *ie)
3005b9c547cSRui Paulo {
3015b9c547cSRui Paulo 	int subelements[MAX_WFD_SUBELEMS] = {};
3025b9c547cSRui Paulo 	const u8 *pos, *end;
3035b9c547cSRui Paulo 	unsigned int len, subelem;
3045b9c547cSRui Paulo 	struct wpabuf *e;
3055b9c547cSRui Paulo 
3065b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
3075b9c547cSRui Paulo 		   ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
3085b9c547cSRui Paulo 
3095b9c547cSRui Paulo 	if (ie == NULL || wpabuf_len(ie) < 6)
3105b9c547cSRui Paulo 		return -1;
3115b9c547cSRui Paulo 
3125b9c547cSRui Paulo 	pos = wpabuf_head(ie);
3135b9c547cSRui Paulo 	end = pos + wpabuf_len(ie);
3145b9c547cSRui Paulo 
3155b9c547cSRui Paulo 	while (end > pos) {
3165b9c547cSRui Paulo 		if (pos + 3 > end)
3175b9c547cSRui Paulo 			break;
3185b9c547cSRui Paulo 
3195b9c547cSRui Paulo 		len = WPA_GET_BE16(pos + 1) + 3;
3205b9c547cSRui Paulo 
3215b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
3225b9c547cSRui Paulo 			   *pos, len - 3);
3235b9c547cSRui Paulo 
3245b9c547cSRui Paulo 		if (len > (unsigned int) (end - pos))
3255b9c547cSRui Paulo 			break;
3265b9c547cSRui Paulo 
3275b9c547cSRui Paulo 		subelem = *pos;
3285b9c547cSRui Paulo 		if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
3295b9c547cSRui Paulo 			e = wpabuf_alloc_copy(pos, len);
3305b9c547cSRui Paulo 			if (e == NULL)
3315b9c547cSRui Paulo 				return -1;
3325b9c547cSRui Paulo 
3335b9c547cSRui Paulo 			wpabuf_free(global->wfd_subelem[subelem]);
3345b9c547cSRui Paulo 			global->wfd_subelem[subelem] = e;
3355b9c547cSRui Paulo 			subelements[subelem] = 1;
3365b9c547cSRui Paulo 		}
3375b9c547cSRui Paulo 
3385b9c547cSRui Paulo 		pos += len;
3395b9c547cSRui Paulo 	}
3405b9c547cSRui Paulo 
3415b9c547cSRui Paulo 	for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
3425b9c547cSRui Paulo 		if (subelements[subelem] == 0) {
3435b9c547cSRui Paulo 			wpabuf_free(global->wfd_subelem[subelem]);
3445b9c547cSRui Paulo 			global->wfd_subelem[subelem] = NULL;
3455b9c547cSRui Paulo 		}
3465b9c547cSRui Paulo 	}
3475b9c547cSRui Paulo 
3485b9c547cSRui Paulo 	return wifi_display_update_wfd_ie(global);
3495b9c547cSRui Paulo }
3505b9c547cSRui Paulo 
3515b9c547cSRui Paulo 
wifi_display_subelem_get(struct wpa_global * global,char * cmd,char * buf,size_t buflen)352f05cddf9SRui Paulo int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
353f05cddf9SRui Paulo 			     char *buf, size_t buflen)
354f05cddf9SRui Paulo {
355f05cddf9SRui Paulo 	int subelem;
356f05cddf9SRui Paulo 
3575b9c547cSRui Paulo 	if (os_strcmp(cmd, "all") == 0) {
3585b9c547cSRui Paulo 		struct wpabuf *ie;
3595b9c547cSRui Paulo 		int res;
3605b9c547cSRui Paulo 
3615b9c547cSRui Paulo 		ie = wifi_display_get_wfd_ie(global);
3625b9c547cSRui Paulo 		if (ie == NULL)
3635b9c547cSRui Paulo 			return 0;
3645b9c547cSRui Paulo 		res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
3655b9c547cSRui Paulo 				       wpabuf_len(ie));
3665b9c547cSRui Paulo 		wpabuf_free(ie);
3675b9c547cSRui Paulo 		return res;
3685b9c547cSRui Paulo 	}
3695b9c547cSRui Paulo 
370f05cddf9SRui Paulo 	subelem = atoi(cmd);
371f05cddf9SRui Paulo 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
372f05cddf9SRui Paulo 		return -1;
373f05cddf9SRui Paulo 
374f05cddf9SRui Paulo 	if (global->wfd_subelem[subelem] == NULL)
375f05cddf9SRui Paulo 		return 0;
376f05cddf9SRui Paulo 
377f05cddf9SRui Paulo 	return wpa_snprintf_hex(buf, buflen,
378f05cddf9SRui Paulo 				wpabuf_head_u8(global->wfd_subelem[subelem]) +
379f05cddf9SRui Paulo 				1,
380f05cddf9SRui Paulo 				wpabuf_len(global->wfd_subelem[subelem]) - 1);
381f05cddf9SRui Paulo }
3825b9c547cSRui Paulo 
3835b9c547cSRui Paulo 
wifi_display_subelem_hex(const struct wpabuf * wfd_subelems,u8 id)3845b9c547cSRui Paulo char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
3855b9c547cSRui Paulo {
3865b9c547cSRui Paulo 	char *subelem = NULL;
3875b9c547cSRui Paulo 	const u8 *buf;
3885b9c547cSRui Paulo 	size_t buflen;
3895b9c547cSRui Paulo 	size_t i = 0;
3905b9c547cSRui Paulo 	u16 elen;
3915b9c547cSRui Paulo 
3925b9c547cSRui Paulo 	if (!wfd_subelems)
3935b9c547cSRui Paulo 		return NULL;
3945b9c547cSRui Paulo 
3955b9c547cSRui Paulo 	buf = wpabuf_head_u8(wfd_subelems);
3965b9c547cSRui Paulo 	if (!buf)
3975b9c547cSRui Paulo 		return NULL;
3985b9c547cSRui Paulo 
3995b9c547cSRui Paulo 	buflen = wpabuf_len(wfd_subelems);
4005b9c547cSRui Paulo 
4015b9c547cSRui Paulo 	while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
4025b9c547cSRui Paulo 		elen = WPA_GET_BE16(buf + i + 1);
4035b9c547cSRui Paulo 		if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
4045b9c547cSRui Paulo 			break; /* truncated subelement */
4055b9c547cSRui Paulo 
4065b9c547cSRui Paulo 		if (buf[i] == id) {
4075b9c547cSRui Paulo 			/*
4085b9c547cSRui Paulo 			 * Limit explicitly to an arbitrary length to avoid
4095b9c547cSRui Paulo 			 * unnecessarily large allocations. In practice, this
4105b9c547cSRui Paulo 			 * is limited to maximum frame length anyway, so the
4115b9c547cSRui Paulo 			 * maximum memory allocation here is not really that
4125b9c547cSRui Paulo 			 * large. Anyway, the Wi-Fi Display subelements that
4135b9c547cSRui Paulo 			 * are fetched with this function are even shorter.
4145b9c547cSRui Paulo 			 */
4155b9c547cSRui Paulo 			if (elen > 1000)
4165b9c547cSRui Paulo 				break;
4175b9c547cSRui Paulo 			subelem = os_zalloc(2 * elen + 1);
4185b9c547cSRui Paulo 			if (!subelem)
4195b9c547cSRui Paulo 				return NULL;
4205b9c547cSRui Paulo 			wpa_snprintf_hex(subelem, 2 * elen + 1,
4215b9c547cSRui Paulo 					 buf + i +
4225b9c547cSRui Paulo 					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
4235b9c547cSRui Paulo 					 elen);
4245b9c547cSRui Paulo 			break;
4255b9c547cSRui Paulo 		}
4265b9c547cSRui Paulo 
4275b9c547cSRui Paulo 		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
4285b9c547cSRui Paulo 	}
4295b9c547cSRui Paulo 
4305b9c547cSRui Paulo 	return subelem;
4315b9c547cSRui Paulo }
432