1/*
2 * wpa_supplicant - Wi-Fi Display
3 * Copyright (c) 2011, Atheros Communications, Inc.
4 * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "includes.h"
11
12#include "common.h"
13#include "p2p/p2p.h"
14#include "common/ieee802_11_defs.h"
15#include "wpa_supplicant_i.h"
16#include "wifi_display.h"
17
18
19int wifi_display_init(struct wpa_global *global)
20{
21	global->wifi_display = 1;
22	return 0;
23}
24
25
26void wifi_display_deinit(struct wpa_global *global)
27{
28	int i;
29	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
30		wpabuf_free(global->wfd_subelem[i]);
31		global->wfd_subelem[i] = NULL;
32	}
33}
34
35
36static int wifi_display_update_wfd_ie(struct wpa_global *global)
37{
38	struct wpabuf *ie, *buf;
39	size_t len, plen;
40
41	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
42
43	if (!global->wifi_display) {
44		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
45			   "include WFD IE");
46		p2p_set_wfd_ie_beacon(global->p2p, NULL);
47		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
48		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
49		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
50		p2p_set_wfd_ie_invitation(global->p2p, NULL);
51		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
52		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
53		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
54		p2p_set_wfd_dev_info(global->p2p, NULL);
55		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
56		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
57		return 0;
58	}
59
60	p2p_set_wfd_dev_info(global->p2p,
61			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
62	p2p_set_wfd_assoc_bssid(
63		global->p2p,
64		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
65	p2p_set_wfd_coupled_sink_info(
66		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
67
68	/*
69	 * WFD IE is included in number of management frames. Two different
70	 * sets of subelements are included depending on the frame:
71	 *
72	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
73	 * Provision Discovery Req:
74	 * WFD Device Info
75	 * [Associated BSSID]
76	 * [Coupled Sink Info]
77	 *
78	 * Probe Request:
79	 * WFD Device Info
80	 * [Associated BSSID]
81	 * [Coupled Sink Info]
82	 * [WFD Extended Capability]
83	 *
84	 * Probe Response:
85	 * WFD Device Info
86	 * [Associated BSSID]
87	 * [Coupled Sink Info]
88	 * [WFD Extended Capability]
89	 * [WFD Session Info]
90	 *
91	 * (Re)Association Response, P2P Invitation Req/Resp,
92	 * Provision Discovery Resp:
93	 * WFD Device Info
94	 * [Associated BSSID]
95	 * [Coupled Sink Info]
96	 * [WFD Session Info]
97	 */
98	len = 0;
99	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
100		len += wpabuf_len(global->wfd_subelem[
101					  WFD_SUBELEM_DEVICE_INFO]);
102	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
103		len += wpabuf_len(global->wfd_subelem[
104					  WFD_SUBELEM_ASSOCIATED_BSSID]);
105	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
106		len += wpabuf_len(global->wfd_subelem[
107					  WFD_SUBELEM_COUPLED_SINK]);
108	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
109		len += wpabuf_len(global->wfd_subelem[
110					  WFD_SUBELEM_SESSION_INFO]);
111	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
112		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
113	buf = wpabuf_alloc(len);
114	if (buf == NULL)
115		return -1;
116
117	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
118		wpabuf_put_buf(buf,
119			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
120	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
121		wpabuf_put_buf(buf, global->wfd_subelem[
122				       WFD_SUBELEM_ASSOCIATED_BSSID]);
123	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
124		wpabuf_put_buf(buf,
125			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
126
127	ie = wifi_display_encaps(buf);
128	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
129	p2p_set_wfd_ie_beacon(global->p2p, ie);
130
131	ie = wifi_display_encaps(buf);
132	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
133			ie);
134	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
135
136	ie = wifi_display_encaps(buf);
137	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
138	p2p_set_wfd_ie_go_neg(global->p2p, ie);
139
140	ie = wifi_display_encaps(buf);
141	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
142			"Request", ie);
143	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
144
145	plen = buf->used;
146	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
147		wpabuf_put_buf(buf,
148			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
149
150	ie = wifi_display_encaps(buf);
151	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
152	p2p_set_wfd_ie_probe_req(global->p2p, ie);
153
154	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
155		wpabuf_put_buf(buf,
156			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
157	ie = wifi_display_encaps(buf);
158	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
159	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
160
161	/* Remove WFD Extended Capability from buffer */
162	buf->used = plen;
163	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
164		wpabuf_put_buf(buf,
165			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
166
167	ie = wifi_display_encaps(buf);
168	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
169	p2p_set_wfd_ie_invitation(global->p2p, ie);
170
171	ie = wifi_display_encaps(buf);
172	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
173			"Response", ie);
174	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
175
176	wpabuf_free(buf);
177
178	return 0;
179}
180
181
182void wifi_display_enable(struct wpa_global *global, int enabled)
183{
184	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
185		   enabled ? "enabled" : "disabled");
186	global->wifi_display = enabled;
187	wifi_display_update_wfd_ie(global);
188}
189
190
191int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
192{
193	char *pos;
194	int subelem;
195	size_t len;
196	struct wpabuf *e;
197
198	pos = os_strchr(cmd, ' ');
199	if (pos == NULL)
200		return -1;
201	*pos++ = '\0';
202	subelem = atoi(cmd);
203	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
204		return -1;
205
206	len = os_strlen(pos);
207	if (len & 1)
208		return -1;
209	len /= 2;
210
211	if (len == 0) {
212		/* Clear subelement */
213		e = NULL;
214		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
215	} else {
216		e = wpabuf_alloc(1 + len);
217		if (e == NULL)
218			return -1;
219		wpabuf_put_u8(e, subelem);
220		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
221			wpabuf_free(e);
222			return -1;
223		}
224		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
225	}
226
227	wpabuf_free(global->wfd_subelem[subelem]);
228	global->wfd_subelem[subelem] = e;
229	wifi_display_update_wfd_ie(global);
230
231	return 0;
232}
233
234
235int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
236			     char *buf, size_t buflen)
237{
238	int subelem;
239
240	subelem = atoi(cmd);
241	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
242		return -1;
243
244	if (global->wfd_subelem[subelem] == NULL)
245		return 0;
246
247	return wpa_snprintf_hex(buf, buflen,
248				wpabuf_head_u8(global->wfd_subelem[subelem]) +
249				1,
250				wpabuf_len(global->wfd_subelem[subelem]) - 1);
251}
252