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 
19 #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
20 
21 
22 int wifi_display_init(struct wpa_global *global)
23 {
24 	global->wifi_display = 1;
25 	return 0;
26 }
27 
28 
29 void wifi_display_deinit(struct wpa_global *global)
30 {
31 	int i;
32 	for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
33 		wpabuf_free(global->wfd_subelem[i]);
34 		global->wfd_subelem[i] = NULL;
35 	}
36 }
37 
38 
39 static int wifi_display_update_wfd_ie(struct wpa_global *global)
40 {
41 	struct wpabuf *ie, *buf;
42 	size_t len, plen;
43 
44 	wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
45 
46 	if (!global->wifi_display) {
47 		wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
48 			   "include WFD IE");
49 		p2p_set_wfd_ie_beacon(global->p2p, NULL);
50 		p2p_set_wfd_ie_probe_req(global->p2p, NULL);
51 		p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
52 		p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
53 		p2p_set_wfd_ie_invitation(global->p2p, NULL);
54 		p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
55 		p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
56 		p2p_set_wfd_ie_go_neg(global->p2p, NULL);
57 		p2p_set_wfd_dev_info(global->p2p, NULL);
58 		p2p_set_wfd_assoc_bssid(global->p2p, NULL);
59 		p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
60 		return 0;
61 	}
62 
63 	p2p_set_wfd_dev_info(global->p2p,
64 			     global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
65 	p2p_set_wfd_assoc_bssid(
66 		global->p2p,
67 		global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
68 	p2p_set_wfd_coupled_sink_info(
69 		global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
70 
71 	/*
72 	 * WFD IE is included in number of management frames. Two different
73 	 * sets of subelements are included depending on the frame:
74 	 *
75 	 * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
76 	 * Provision Discovery Req:
77 	 * WFD Device Info
78 	 * [Associated BSSID]
79 	 * [Coupled Sink Info]
80 	 *
81 	 * Probe Request:
82 	 * WFD Device Info
83 	 * [Associated BSSID]
84 	 * [Coupled Sink Info]
85 	 * [WFD Extended Capability]
86 	 *
87 	 * Probe Response:
88 	 * WFD Device Info
89 	 * [Associated BSSID]
90 	 * [Coupled Sink Info]
91 	 * [WFD Extended Capability]
92 	 * [WFD Session Info]
93 	 *
94 	 * (Re)Association Response, P2P Invitation Req/Resp,
95 	 * Provision Discovery Resp:
96 	 * WFD Device Info
97 	 * [Associated BSSID]
98 	 * [Coupled Sink Info]
99 	 * [WFD Session Info]
100 	 */
101 	len = 0;
102 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
103 		len += wpabuf_len(global->wfd_subelem[
104 					  WFD_SUBELEM_DEVICE_INFO]);
105 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
106 		len += wpabuf_len(global->wfd_subelem[
107 					  WFD_SUBELEM_ASSOCIATED_BSSID]);
108 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
109 		len += wpabuf_len(global->wfd_subelem[
110 					  WFD_SUBELEM_COUPLED_SINK]);
111 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
112 		len += wpabuf_len(global->wfd_subelem[
113 					  WFD_SUBELEM_SESSION_INFO]);
114 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
115 		len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
116 	buf = wpabuf_alloc(len);
117 	if (buf == NULL)
118 		return -1;
119 
120 	if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
121 		wpabuf_put_buf(buf,
122 			       global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
123 	if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
124 		wpabuf_put_buf(buf, global->wfd_subelem[
125 				       WFD_SUBELEM_ASSOCIATED_BSSID]);
126 	if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
127 		wpabuf_put_buf(buf,
128 			       global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
129 
130 	ie = wifi_display_encaps(buf);
131 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
132 	p2p_set_wfd_ie_beacon(global->p2p, ie);
133 
134 	ie = wifi_display_encaps(buf);
135 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
136 			ie);
137 	p2p_set_wfd_ie_assoc_req(global->p2p, ie);
138 
139 	ie = wifi_display_encaps(buf);
140 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
141 	p2p_set_wfd_ie_go_neg(global->p2p, ie);
142 
143 	ie = wifi_display_encaps(buf);
144 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
145 			"Request", ie);
146 	p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
147 
148 	plen = buf->used;
149 	if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
150 		wpabuf_put_buf(buf,
151 			       global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
152 
153 	ie = wifi_display_encaps(buf);
154 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
155 	p2p_set_wfd_ie_probe_req(global->p2p, ie);
156 
157 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
158 		wpabuf_put_buf(buf,
159 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
160 	ie = wifi_display_encaps(buf);
161 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
162 	p2p_set_wfd_ie_probe_resp(global->p2p, ie);
163 
164 	/* Remove WFD Extended Capability from buffer */
165 	buf->used = plen;
166 	if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
167 		wpabuf_put_buf(buf,
168 			       global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
169 
170 	ie = wifi_display_encaps(buf);
171 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
172 	p2p_set_wfd_ie_invitation(global->p2p, ie);
173 
174 	ie = wifi_display_encaps(buf);
175 	wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
176 			"Response", ie);
177 	p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
178 
179 	wpabuf_free(buf);
180 
181 	return 0;
182 }
183 
184 
185 void wifi_display_enable(struct wpa_global *global, int enabled)
186 {
187 	wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
188 		   enabled ? "enabled" : "disabled");
189 	global->wifi_display = enabled;
190 	wifi_display_update_wfd_ie(global);
191 }
192 
193 
194 int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
195 {
196 	char *pos;
197 	int subelem;
198 	size_t len;
199 	struct wpabuf *e;
200 
201 	pos = os_strchr(cmd, ' ');
202 	if (pos == NULL)
203 		return -1;
204 	*pos++ = '\0';
205 	subelem = atoi(cmd);
206 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
207 		return -1;
208 
209 	len = os_strlen(pos);
210 	if (len & 1)
211 		return -1;
212 	len /= 2;
213 
214 	if (len == 0) {
215 		/* Clear subelement */
216 		e = NULL;
217 		wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
218 	} else {
219 		e = wpabuf_alloc(1 + len);
220 		if (e == NULL)
221 			return -1;
222 		wpabuf_put_u8(e, subelem);
223 		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
224 			wpabuf_free(e);
225 			return -1;
226 		}
227 		wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
228 	}
229 
230 	wpabuf_free(global->wfd_subelem[subelem]);
231 	global->wfd_subelem[subelem] = e;
232 	wifi_display_update_wfd_ie(global);
233 
234 	return 0;
235 }
236 
237 
238 int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
239 			     char *buf, size_t buflen)
240 {
241 	int subelem;
242 
243 	subelem = atoi(cmd);
244 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
245 		return -1;
246 
247 	if (global->wfd_subelem[subelem] == NULL)
248 		return 0;
249 
250 	return wpa_snprintf_hex(buf, buflen,
251 				wpabuf_head_u8(global->wfd_subelem[subelem]) +
252 				1,
253 				wpabuf_len(global->wfd_subelem[subelem]) - 1);
254 }
255 
256 
257 char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
258 {
259 	char *subelem = NULL;
260 	const u8 *buf;
261 	size_t buflen;
262 	size_t i = 0;
263 	u16 elen;
264 
265 	if (!wfd_subelems)
266 		return NULL;
267 
268 	buf = wpabuf_head_u8(wfd_subelems);
269 	if (!buf)
270 		return NULL;
271 
272 	buflen = wpabuf_len(wfd_subelems);
273 
274 	while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
275 		elen = WPA_GET_BE16(buf + i + 1);
276 
277 		if (buf[i] == id) {
278 			subelem = os_zalloc(2 * elen + 1);
279 			if (!subelem)
280 				return NULL;
281 			wpa_snprintf_hex(subelem, 2 * elen + 1,
282 					 buf + i +
283 					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
284 					 elen);
285 			break;
286 		}
287 
288 		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
289 	}
290 
291 	return subelem;
292 }
293