xref: /freebsd/contrib/wpa/wpa_supplicant/wifi_display.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
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 
19*5b9c547cSRui Paulo #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
20*5b9c547cSRui Paulo 
21*5b9c547cSRui Paulo 
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 
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 
39*5b9c547cSRui Paulo struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
40*5b9c547cSRui Paulo {
41*5b9c547cSRui Paulo 	struct wpabuf *ie;
42*5b9c547cSRui Paulo 	size_t len;
43*5b9c547cSRui Paulo 	int i;
44*5b9c547cSRui Paulo 
45*5b9c547cSRui Paulo 	if (global->p2p == NULL)
46*5b9c547cSRui Paulo 		return NULL;
47*5b9c547cSRui Paulo 
48*5b9c547cSRui Paulo 	len = 0;
49*5b9c547cSRui Paulo 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
50*5b9c547cSRui Paulo 		if (global->wfd_subelem[i])
51*5b9c547cSRui Paulo 			len += wpabuf_len(global->wfd_subelem[i]);
52*5b9c547cSRui Paulo 	}
53*5b9c547cSRui Paulo 
54*5b9c547cSRui Paulo 	ie = wpabuf_alloc(len);
55*5b9c547cSRui Paulo 	if (ie == NULL)
56*5b9c547cSRui Paulo 		return NULL;
57*5b9c547cSRui Paulo 
58*5b9c547cSRui Paulo 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
59*5b9c547cSRui Paulo 		if (global->wfd_subelem[i])
60*5b9c547cSRui Paulo 			wpabuf_put_buf(ie, global->wfd_subelem[i]);
61*5b9c547cSRui Paulo 	}
62*5b9c547cSRui Paulo 
63*5b9c547cSRui Paulo 	return ie;
64*5b9c547cSRui Paulo }
65*5b9c547cSRui Paulo 
66*5b9c547cSRui Paulo 
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 
72*5b9c547cSRui Paulo 	if (global->p2p == NULL)
73*5b9c547cSRui Paulo 		return 0;
74*5b9c547cSRui 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);
89f05cddf9SRui Paulo 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
90f05cddf9SRui Paulo 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
91f05cddf9SRui Paulo 		return 0;
92f05cddf9SRui Paulo 	}
93f05cddf9SRui Paulo 
94f05cddf9SRui Paulo 	p2p_set_wfd_dev_info(global->p2p,
95f05cddf9SRui Paulo 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
96f05cddf9SRui Paulo 	p2p_set_wfd_assoc_bssid(
97f05cddf9SRui Paulo 		global->p2p,
98f05cddf9SRui Paulo 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
99f05cddf9SRui Paulo 	p2p_set_wfd_coupled_sink_info(
100f05cddf9SRui Paulo 		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
101f05cddf9SRui Paulo 
102f05cddf9SRui Paulo 	/*
103f05cddf9SRui Paulo 	 * WFD IE is included in number of management frames. Two different
104f05cddf9SRui Paulo 	 * sets of subelements are included depending on the frame:
105f05cddf9SRui Paulo 	 *
106f05cddf9SRui Paulo 	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
107f05cddf9SRui Paulo 	 * Provision Discovery Req:
108f05cddf9SRui Paulo 	 * WFD Device Info
109f05cddf9SRui Paulo 	 * [Associated BSSID]
110f05cddf9SRui Paulo 	 * [Coupled Sink Info]
111f05cddf9SRui Paulo 	 *
112f05cddf9SRui Paulo 	 * Probe Request:
113f05cddf9SRui Paulo 	 * WFD Device Info
114f05cddf9SRui Paulo 	 * [Associated BSSID]
115f05cddf9SRui Paulo 	 * [Coupled Sink Info]
116f05cddf9SRui Paulo 	 * [WFD Extended Capability]
117f05cddf9SRui Paulo 	 *
118f05cddf9SRui Paulo 	 * Probe Response:
119f05cddf9SRui Paulo 	 * WFD Device Info
120f05cddf9SRui Paulo 	 * [Associated BSSID]
121f05cddf9SRui Paulo 	 * [Coupled Sink Info]
122f05cddf9SRui Paulo 	 * [WFD Extended Capability]
123f05cddf9SRui Paulo 	 * [WFD Session Info]
124f05cddf9SRui Paulo 	 *
125f05cddf9SRui Paulo 	 * (Re)Association Response, P2P Invitation Req/Resp,
126f05cddf9SRui Paulo 	 * Provision Discovery Resp:
127f05cddf9SRui Paulo 	 * WFD Device Info
128f05cddf9SRui Paulo 	 * [Associated BSSID]
129f05cddf9SRui Paulo 	 * [Coupled Sink Info]
130f05cddf9SRui Paulo 	 * [WFD Session Info]
131f05cddf9SRui Paulo 	 */
132f05cddf9SRui Paulo 	len = 0;
133f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
134f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
135f05cddf9SRui Paulo 					  WFD_SUBELEM_DEVICE_INFO]);
136f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
137f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
138f05cddf9SRui Paulo 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
139f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
140f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
141f05cddf9SRui Paulo 					  WFD_SUBELEM_COUPLED_SINK]);
142f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
143f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[
144f05cddf9SRui Paulo 					  WFD_SUBELEM_SESSION_INFO]);
145f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
146f05cddf9SRui Paulo 		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
147f05cddf9SRui Paulo 	buf = wpabuf_alloc(len);
148f05cddf9SRui Paulo 	if (buf == NULL)
149f05cddf9SRui Paulo 		return -1;
150f05cddf9SRui Paulo 
151f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
152f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
153f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
154f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
155f05cddf9SRui Paulo 		wpabuf_put_buf(buf, global->wfd_subelem[
156f05cddf9SRui Paulo 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
157f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
158f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
159f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
160f05cddf9SRui Paulo 
161f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
162f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
163f05cddf9SRui Paulo 	p2p_set_wfd_ie_beacon(global->p2p, ie);
164f05cddf9SRui Paulo 
165f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
166f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
167f05cddf9SRui Paulo 			ie);
168f05cddf9SRui Paulo 	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
169f05cddf9SRui Paulo 
170f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
171f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
172f05cddf9SRui Paulo 	p2p_set_wfd_ie_go_neg(global->p2p, ie);
173f05cddf9SRui Paulo 
174f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
175f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
176f05cddf9SRui Paulo 			"Request", ie);
177f05cddf9SRui Paulo 	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
178f05cddf9SRui Paulo 
179f05cddf9SRui Paulo 	plen = buf->used;
180f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
181f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
182f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
183f05cddf9SRui Paulo 
184f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
185f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
186f05cddf9SRui Paulo 	p2p_set_wfd_ie_probe_req(global->p2p, ie);
187f05cddf9SRui Paulo 
188f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
189f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
190f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
191f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
192f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
193f05cddf9SRui Paulo 	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
194f05cddf9SRui Paulo 
195f05cddf9SRui Paulo 	/* Remove WFD Extended Capability from buffer */
196f05cddf9SRui Paulo 	buf->used = plen;
197f05cddf9SRui Paulo 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
198f05cddf9SRui Paulo 		wpabuf_put_buf(buf,
199f05cddf9SRui Paulo 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
200f05cddf9SRui Paulo 
201f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
202f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
203f05cddf9SRui Paulo 	p2p_set_wfd_ie_invitation(global->p2p, ie);
204f05cddf9SRui Paulo 
205f05cddf9SRui Paulo 	ie = wifi_display_encaps(buf);
206f05cddf9SRui Paulo 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
207f05cddf9SRui Paulo 			"Response", ie);
208f05cddf9SRui Paulo 	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
209f05cddf9SRui Paulo 
210f05cddf9SRui Paulo 	wpabuf_free(buf);
211f05cddf9SRui Paulo 
212f05cddf9SRui Paulo 	return 0;
213f05cddf9SRui Paulo }
214f05cddf9SRui Paulo 
215f05cddf9SRui Paulo 
216f05cddf9SRui Paulo void wifi_display_enable(struct wpa_global *global, int enabled)
217f05cddf9SRui Paulo {
218f05cddf9SRui Paulo 	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
219f05cddf9SRui Paulo 		   enabled ? "enabled" : "disabled");
220f05cddf9SRui Paulo 	global->wifi_display = enabled;
221f05cddf9SRui Paulo 	wifi_display_update_wfd_ie(global);
222f05cddf9SRui Paulo }
223f05cddf9SRui Paulo 
224f05cddf9SRui Paulo 
225f05cddf9SRui Paulo int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
226f05cddf9SRui Paulo {
227f05cddf9SRui Paulo 	char *pos;
228f05cddf9SRui Paulo 	int subelem;
229f05cddf9SRui Paulo 	size_t len;
230f05cddf9SRui Paulo 	struct wpabuf *e;
231f05cddf9SRui Paulo 
232f05cddf9SRui Paulo 	pos = os_strchr(cmd, ' ');
233f05cddf9SRui Paulo 	if (pos == NULL)
234f05cddf9SRui Paulo 		return -1;
235f05cddf9SRui Paulo 	*pos++ = '\0';
236f05cddf9SRui Paulo 
237f05cddf9SRui Paulo 	len = os_strlen(pos);
238f05cddf9SRui Paulo 	if (len & 1)
239f05cddf9SRui Paulo 		return -1;
240f05cddf9SRui Paulo 	len /= 2;
241f05cddf9SRui Paulo 
242*5b9c547cSRui Paulo 	if (os_strcmp(cmd, "all") == 0) {
243*5b9c547cSRui Paulo 		int res;
244*5b9c547cSRui Paulo 
245*5b9c547cSRui Paulo 		e = wpabuf_alloc(len);
246*5b9c547cSRui Paulo 		if (e == NULL)
247*5b9c547cSRui Paulo 			return -1;
248*5b9c547cSRui Paulo 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
249*5b9c547cSRui Paulo 			wpabuf_free(e);
250*5b9c547cSRui Paulo 			return -1;
251*5b9c547cSRui Paulo 		}
252*5b9c547cSRui Paulo 		res = wifi_display_subelem_set_from_ies(global, e);
253*5b9c547cSRui Paulo 		wpabuf_free(e);
254*5b9c547cSRui Paulo 		return res;
255*5b9c547cSRui Paulo 	}
256*5b9c547cSRui Paulo 
257*5b9c547cSRui Paulo 	subelem = atoi(cmd);
258*5b9c547cSRui Paulo 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
259*5b9c547cSRui Paulo 		return -1;
260*5b9c547cSRui Paulo 
261f05cddf9SRui Paulo 	if (len == 0) {
262f05cddf9SRui Paulo 		/* Clear subelement */
263f05cddf9SRui Paulo 		e = NULL;
264f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
265f05cddf9SRui Paulo 	} else {
266f05cddf9SRui Paulo 		e = wpabuf_alloc(1 + len);
267f05cddf9SRui Paulo 		if (e == NULL)
268f05cddf9SRui Paulo 			return -1;
269f05cddf9SRui Paulo 		wpabuf_put_u8(e, subelem);
270f05cddf9SRui Paulo 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
271f05cddf9SRui Paulo 			wpabuf_free(e);
272f05cddf9SRui Paulo 			return -1;
273f05cddf9SRui Paulo 		}
274f05cddf9SRui Paulo 		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
275f05cddf9SRui Paulo 	}
276f05cddf9SRui Paulo 
277f05cddf9SRui Paulo 	wpabuf_free(global->wfd_subelem[subelem]);
278f05cddf9SRui Paulo 	global->wfd_subelem[subelem] = e;
279f05cddf9SRui Paulo 	wifi_display_update_wfd_ie(global);
280f05cddf9SRui Paulo 
281f05cddf9SRui Paulo 	return 0;
282f05cddf9SRui Paulo }
283f05cddf9SRui Paulo 
284f05cddf9SRui Paulo 
285*5b9c547cSRui Paulo int wifi_display_subelem_set_from_ies(struct wpa_global *global,
286*5b9c547cSRui Paulo 				      struct wpabuf *ie)
287*5b9c547cSRui Paulo {
288*5b9c547cSRui Paulo 	int subelements[MAX_WFD_SUBELEMS] = {};
289*5b9c547cSRui Paulo 	const u8 *pos, *end;
290*5b9c547cSRui Paulo 	unsigned int len, subelem;
291*5b9c547cSRui Paulo 	struct wpabuf *e;
292*5b9c547cSRui Paulo 
293*5b9c547cSRui Paulo 	wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
294*5b9c547cSRui Paulo 		   ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
295*5b9c547cSRui Paulo 
296*5b9c547cSRui Paulo 	if (ie == NULL || wpabuf_len(ie) < 6)
297*5b9c547cSRui Paulo 		return -1;
298*5b9c547cSRui Paulo 
299*5b9c547cSRui Paulo 	pos = wpabuf_head(ie);
300*5b9c547cSRui Paulo 	end = pos + wpabuf_len(ie);
301*5b9c547cSRui Paulo 
302*5b9c547cSRui Paulo 	while (end > pos) {
303*5b9c547cSRui Paulo 		if (pos + 3 > end)
304*5b9c547cSRui Paulo 			break;
305*5b9c547cSRui Paulo 
306*5b9c547cSRui Paulo 		len = WPA_GET_BE16(pos + 1) + 3;
307*5b9c547cSRui Paulo 
308*5b9c547cSRui Paulo 		wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
309*5b9c547cSRui Paulo 			   *pos, len - 3);
310*5b9c547cSRui Paulo 
311*5b9c547cSRui Paulo 		if (len > (unsigned int) (end - pos))
312*5b9c547cSRui Paulo 			break;
313*5b9c547cSRui Paulo 
314*5b9c547cSRui Paulo 		subelem = *pos;
315*5b9c547cSRui Paulo 		if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
316*5b9c547cSRui Paulo 			e = wpabuf_alloc_copy(pos, len);
317*5b9c547cSRui Paulo 			if (e == NULL)
318*5b9c547cSRui Paulo 				return -1;
319*5b9c547cSRui Paulo 
320*5b9c547cSRui Paulo 			wpabuf_free(global->wfd_subelem[subelem]);
321*5b9c547cSRui Paulo 			global->wfd_subelem[subelem] = e;
322*5b9c547cSRui Paulo 			subelements[subelem] = 1;
323*5b9c547cSRui Paulo 		}
324*5b9c547cSRui Paulo 
325*5b9c547cSRui Paulo 		pos += len;
326*5b9c547cSRui Paulo 	}
327*5b9c547cSRui Paulo 
328*5b9c547cSRui Paulo 	for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
329*5b9c547cSRui Paulo 		if (subelements[subelem] == 0) {
330*5b9c547cSRui Paulo 			wpabuf_free(global->wfd_subelem[subelem]);
331*5b9c547cSRui Paulo 			global->wfd_subelem[subelem] = NULL;
332*5b9c547cSRui Paulo 		}
333*5b9c547cSRui Paulo 	}
334*5b9c547cSRui Paulo 
335*5b9c547cSRui Paulo 	return wifi_display_update_wfd_ie(global);
336*5b9c547cSRui Paulo }
337*5b9c547cSRui Paulo 
338*5b9c547cSRui Paulo 
339f05cddf9SRui Paulo int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
340f05cddf9SRui Paulo 			     char *buf, size_t buflen)
341f05cddf9SRui Paulo {
342f05cddf9SRui Paulo 	int subelem;
343f05cddf9SRui Paulo 
344*5b9c547cSRui Paulo 	if (os_strcmp(cmd, "all") == 0) {
345*5b9c547cSRui Paulo 		struct wpabuf *ie;
346*5b9c547cSRui Paulo 		int res;
347*5b9c547cSRui Paulo 
348*5b9c547cSRui Paulo 		ie = wifi_display_get_wfd_ie(global);
349*5b9c547cSRui Paulo 		if (ie == NULL)
350*5b9c547cSRui Paulo 			return 0;
351*5b9c547cSRui Paulo 		res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
352*5b9c547cSRui Paulo 				       wpabuf_len(ie));
353*5b9c547cSRui Paulo 		wpabuf_free(ie);
354*5b9c547cSRui Paulo 		return res;
355*5b9c547cSRui Paulo 	}
356*5b9c547cSRui Paulo 
357f05cddf9SRui Paulo 	subelem = atoi(cmd);
358f05cddf9SRui Paulo 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
359f05cddf9SRui Paulo 		return -1;
360f05cddf9SRui Paulo 
361f05cddf9SRui Paulo 	if (global->wfd_subelem[subelem] == NULL)
362f05cddf9SRui Paulo 		return 0;
363f05cddf9SRui Paulo 
364f05cddf9SRui Paulo 	return wpa_snprintf_hex(buf, buflen,
365f05cddf9SRui Paulo 				wpabuf_head_u8(global->wfd_subelem[subelem]) +
366f05cddf9SRui Paulo 				1,
367f05cddf9SRui Paulo 				wpabuf_len(global->wfd_subelem[subelem]) - 1);
368f05cddf9SRui Paulo }
369*5b9c547cSRui Paulo 
370*5b9c547cSRui Paulo 
371*5b9c547cSRui Paulo char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
372*5b9c547cSRui Paulo {
373*5b9c547cSRui Paulo 	char *subelem = NULL;
374*5b9c547cSRui Paulo 	const u8 *buf;
375*5b9c547cSRui Paulo 	size_t buflen;
376*5b9c547cSRui Paulo 	size_t i = 0;
377*5b9c547cSRui Paulo 	u16 elen;
378*5b9c547cSRui Paulo 
379*5b9c547cSRui Paulo 	if (!wfd_subelems)
380*5b9c547cSRui Paulo 		return NULL;
381*5b9c547cSRui Paulo 
382*5b9c547cSRui Paulo 	buf = wpabuf_head_u8(wfd_subelems);
383*5b9c547cSRui Paulo 	if (!buf)
384*5b9c547cSRui Paulo 		return NULL;
385*5b9c547cSRui Paulo 
386*5b9c547cSRui Paulo 	buflen = wpabuf_len(wfd_subelems);
387*5b9c547cSRui Paulo 
388*5b9c547cSRui Paulo 	while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
389*5b9c547cSRui Paulo 		elen = WPA_GET_BE16(buf + i + 1);
390*5b9c547cSRui Paulo 		if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
391*5b9c547cSRui Paulo 			break; /* truncated subelement */
392*5b9c547cSRui Paulo 
393*5b9c547cSRui Paulo 		if (buf[i] == id) {
394*5b9c547cSRui Paulo 			/*
395*5b9c547cSRui Paulo 			 * Limit explicitly to an arbitrary length to avoid
396*5b9c547cSRui Paulo 			 * unnecessarily large allocations. In practice, this
397*5b9c547cSRui Paulo 			 * is limited to maximum frame length anyway, so the
398*5b9c547cSRui Paulo 			 * maximum memory allocation here is not really that
399*5b9c547cSRui Paulo 			 * large. Anyway, the Wi-Fi Display subelements that
400*5b9c547cSRui Paulo 			 * are fetched with this function are even shorter.
401*5b9c547cSRui Paulo 			 */
402*5b9c547cSRui Paulo 			if (elen > 1000)
403*5b9c547cSRui Paulo 				break;
404*5b9c547cSRui Paulo 			subelem = os_zalloc(2 * elen + 1);
405*5b9c547cSRui Paulo 			if (!subelem)
406*5b9c547cSRui Paulo 				return NULL;
407*5b9c547cSRui Paulo 			wpa_snprintf_hex(subelem, 2 * elen + 1,
408*5b9c547cSRui Paulo 					 buf + i +
409*5b9c547cSRui Paulo 					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
410*5b9c547cSRui Paulo 					 elen);
411*5b9c547cSRui Paulo 			break;
412*5b9c547cSRui Paulo 		}
413*5b9c547cSRui Paulo 
414*5b9c547cSRui Paulo 		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
415*5b9c547cSRui Paulo 	}
416*5b9c547cSRui Paulo 
417*5b9c547cSRui Paulo 	return subelem;
418*5b9c547cSRui Paulo }
419