1e604d861Schristos /*
2e604d861Schristos * wpa_supplicant - Wi-Fi Display
3e604d861Schristos * Copyright (c) 2011, Atheros Communications, Inc.
4e604d861Schristos * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
5e604d861Schristos *
6e604d861Schristos * This software may be distributed under the terms of the BSD license.
7e604d861Schristos * See README for more details.
8e604d861Schristos */
9e604d861Schristos
10e604d861Schristos #include "includes.h"
11e604d861Schristos
12e604d861Schristos #include "common.h"
13e604d861Schristos #include "p2p/p2p.h"
14e604d861Schristos #include "common/ieee802_11_defs.h"
15e604d861Schristos #include "wpa_supplicant_i.h"
16e604d861Schristos #include "wifi_display.h"
17e604d861Schristos
18e604d861Schristos
193c260e60Schristos #define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
203c260e60Schristos
213c260e60Schristos
wifi_display_init(struct wpa_global * global)22e604d861Schristos int wifi_display_init(struct wpa_global *global)
23e604d861Schristos {
24e604d861Schristos global->wifi_display = 1;
25e604d861Schristos return 0;
26e604d861Schristos }
27e604d861Schristos
28e604d861Schristos
wifi_display_deinit(struct wpa_global * global)29e604d861Schristos void wifi_display_deinit(struct wpa_global *global)
30e604d861Schristos {
31e604d861Schristos int i;
32e604d861Schristos for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
33e604d861Schristos wpabuf_free(global->wfd_subelem[i]);
34e604d861Schristos global->wfd_subelem[i] = NULL;
35e604d861Schristos }
36e604d861Schristos }
37e604d861Schristos
38e604d861Schristos
wifi_display_get_wfd_ie(struct wpa_global * global)393c260e60Schristos struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
403c260e60Schristos {
413c260e60Schristos struct wpabuf *ie;
423c260e60Schristos size_t len;
433c260e60Schristos int i;
443c260e60Schristos
453c260e60Schristos if (global->p2p == NULL)
463c260e60Schristos return NULL;
473c260e60Schristos
483c260e60Schristos len = 0;
493c260e60Schristos for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
503c260e60Schristos if (global->wfd_subelem[i])
513c260e60Schristos len += wpabuf_len(global->wfd_subelem[i]);
523c260e60Schristos }
533c260e60Schristos
543c260e60Schristos ie = wpabuf_alloc(len);
553c260e60Schristos if (ie == NULL)
563c260e60Schristos return NULL;
573c260e60Schristos
583c260e60Schristos for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
593c260e60Schristos if (global->wfd_subelem[i])
603c260e60Schristos wpabuf_put_buf(ie, global->wfd_subelem[i]);
613c260e60Schristos }
623c260e60Schristos
633c260e60Schristos return ie;
643c260e60Schristos }
653c260e60Schristos
663c260e60Schristos
wifi_display_update_wfd_ie(struct wpa_global * global)67e604d861Schristos static int wifi_display_update_wfd_ie(struct wpa_global *global)
68e604d861Schristos {
69e604d861Schristos struct wpabuf *ie, *buf;
70e604d861Schristos size_t len, plen;
71e604d861Schristos
723c260e60Schristos if (global->p2p == NULL)
733c260e60Schristos return 0;
743c260e60Schristos
75e604d861Schristos wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
76e604d861Schristos
77e604d861Schristos if (!global->wifi_display) {
78e604d861Schristos wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not "
79e604d861Schristos "include WFD IE");
80e604d861Schristos p2p_set_wfd_ie_beacon(global->p2p, NULL);
81e604d861Schristos p2p_set_wfd_ie_probe_req(global->p2p, NULL);
82e604d861Schristos p2p_set_wfd_ie_probe_resp(global->p2p, NULL);
83e604d861Schristos p2p_set_wfd_ie_assoc_req(global->p2p, NULL);
84e604d861Schristos p2p_set_wfd_ie_invitation(global->p2p, NULL);
85e604d861Schristos p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL);
86e604d861Schristos p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL);
87e604d861Schristos p2p_set_wfd_ie_go_neg(global->p2p, NULL);
88e604d861Schristos p2p_set_wfd_dev_info(global->p2p, NULL);
89*0a73ee0aSchristos p2p_set_wfd_r2_dev_info(global->p2p, NULL);
90e604d861Schristos p2p_set_wfd_assoc_bssid(global->p2p, NULL);
91e604d861Schristos p2p_set_wfd_coupled_sink_info(global->p2p, NULL);
92e604d861Schristos return 0;
93e604d861Schristos }
94e604d861Schristos
95e604d861Schristos p2p_set_wfd_dev_info(global->p2p,
96e604d861Schristos global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
97*0a73ee0aSchristos p2p_set_wfd_r2_dev_info(
98*0a73ee0aSchristos global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
99e604d861Schristos p2p_set_wfd_assoc_bssid(
100e604d861Schristos global->p2p,
101e604d861Schristos global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]);
102e604d861Schristos p2p_set_wfd_coupled_sink_info(
103e604d861Schristos global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
104e604d861Schristos
105e604d861Schristos /*
106e604d861Schristos * WFD IE is included in number of management frames. Two different
107e604d861Schristos * sets of subelements are included depending on the frame:
108e604d861Schristos *
109e604d861Schristos * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf,
110e604d861Schristos * Provision Discovery Req:
111e604d861Schristos * WFD Device Info
112e604d861Schristos * [Associated BSSID]
113e604d861Schristos * [Coupled Sink Info]
114e604d861Schristos *
115e604d861Schristos * Probe Request:
116e604d861Schristos * WFD Device Info
117e604d861Schristos * [Associated BSSID]
118e604d861Schristos * [Coupled Sink Info]
119e604d861Schristos * [WFD Extended Capability]
120e604d861Schristos *
121e604d861Schristos * Probe Response:
122e604d861Schristos * WFD Device Info
123e604d861Schristos * [Associated BSSID]
124e604d861Schristos * [Coupled Sink Info]
125e604d861Schristos * [WFD Extended Capability]
126e604d861Schristos * [WFD Session Info]
127e604d861Schristos *
128e604d861Schristos * (Re)Association Response, P2P Invitation Req/Resp,
129e604d861Schristos * Provision Discovery Resp:
130e604d861Schristos * WFD Device Info
131e604d861Schristos * [Associated BSSID]
132e604d861Schristos * [Coupled Sink Info]
133e604d861Schristos * [WFD Session Info]
134e604d861Schristos */
135e604d861Schristos len = 0;
136e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
137e604d861Schristos len += wpabuf_len(global->wfd_subelem[
138e604d861Schristos WFD_SUBELEM_DEVICE_INFO]);
139*0a73ee0aSchristos
140*0a73ee0aSchristos if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
141*0a73ee0aSchristos len += wpabuf_len(global->wfd_subelem[
142*0a73ee0aSchristos WFD_SUBELEM_R2_DEVICE_INFO]);
143*0a73ee0aSchristos
144e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
145e604d861Schristos len += wpabuf_len(global->wfd_subelem[
146e604d861Schristos WFD_SUBELEM_ASSOCIATED_BSSID]);
147e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
148e604d861Schristos len += wpabuf_len(global->wfd_subelem[
149e604d861Schristos WFD_SUBELEM_COUPLED_SINK]);
150e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
151e604d861Schristos len += wpabuf_len(global->wfd_subelem[
152e604d861Schristos WFD_SUBELEM_SESSION_INFO]);
153e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
154e604d861Schristos len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
155e604d861Schristos buf = wpabuf_alloc(len);
156e604d861Schristos if (buf == NULL)
157e604d861Schristos return -1;
158e604d861Schristos
159e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO])
160e604d861Schristos wpabuf_put_buf(buf,
161e604d861Schristos global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]);
162*0a73ee0aSchristos
163*0a73ee0aSchristos if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO])
164*0a73ee0aSchristos wpabuf_put_buf(buf,
165*0a73ee0aSchristos global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]);
166*0a73ee0aSchristos
167e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID])
168e604d861Schristos wpabuf_put_buf(buf, global->wfd_subelem[
169e604d861Schristos WFD_SUBELEM_ASSOCIATED_BSSID]);
170e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK])
171e604d861Schristos wpabuf_put_buf(buf,
172e604d861Schristos global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]);
173e604d861Schristos
174e604d861Schristos ie = wifi_display_encaps(buf);
175e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie);
176e604d861Schristos p2p_set_wfd_ie_beacon(global->p2p, ie);
177e604d861Schristos
178e604d861Schristos ie = wifi_display_encaps(buf);
179e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request",
180e604d861Schristos ie);
181e604d861Schristos p2p_set_wfd_ie_assoc_req(global->p2p, ie);
182e604d861Schristos
183e604d861Schristos ie = wifi_display_encaps(buf);
184e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie);
185e604d861Schristos p2p_set_wfd_ie_go_neg(global->p2p, ie);
186e604d861Schristos
187e604d861Schristos ie = wifi_display_encaps(buf);
188e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
189e604d861Schristos "Request", ie);
190e604d861Schristos p2p_set_wfd_ie_prov_disc_req(global->p2p, ie);
191e604d861Schristos
192e604d861Schristos plen = buf->used;
193e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB])
194e604d861Schristos wpabuf_put_buf(buf,
195e604d861Schristos global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]);
196e604d861Schristos
197e604d861Schristos ie = wifi_display_encaps(buf);
198e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie);
199e604d861Schristos p2p_set_wfd_ie_probe_req(global->p2p, ie);
200e604d861Schristos
201e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
202e604d861Schristos wpabuf_put_buf(buf,
203e604d861Schristos global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
204e604d861Schristos ie = wifi_display_encaps(buf);
205e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie);
206e604d861Schristos p2p_set_wfd_ie_probe_resp(global->p2p, ie);
207e604d861Schristos
208e604d861Schristos /* Remove WFD Extended Capability from buffer */
209e604d861Schristos buf->used = plen;
210e604d861Schristos if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO])
211e604d861Schristos wpabuf_put_buf(buf,
212e604d861Schristos global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]);
213e604d861Schristos
214e604d861Schristos ie = wifi_display_encaps(buf);
215e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie);
216e604d861Schristos p2p_set_wfd_ie_invitation(global->p2p, ie);
217e604d861Schristos
218e604d861Schristos ie = wifi_display_encaps(buf);
219e604d861Schristos wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery "
220e604d861Schristos "Response", ie);
221e604d861Schristos p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie);
222e604d861Schristos
223e604d861Schristos wpabuf_free(buf);
224e604d861Schristos
225e604d861Schristos return 0;
226e604d861Schristos }
227e604d861Schristos
228e604d861Schristos
wifi_display_enable(struct wpa_global * global,int enabled)229e604d861Schristos void wifi_display_enable(struct wpa_global *global, int enabled)
230e604d861Schristos {
231e604d861Schristos wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s",
232e604d861Schristos enabled ? "enabled" : "disabled");
233e604d861Schristos global->wifi_display = enabled;
234e604d861Schristos wifi_display_update_wfd_ie(global);
235e604d861Schristos }
236e604d861Schristos
237e604d861Schristos
wifi_display_subelem_set(struct wpa_global * global,char * cmd)238e604d861Schristos int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
239e604d861Schristos {
240e604d861Schristos char *pos;
241e604d861Schristos int subelem;
242e604d861Schristos size_t len;
243e604d861Schristos struct wpabuf *e;
244e604d861Schristos
245e604d861Schristos pos = os_strchr(cmd, ' ');
246e604d861Schristos if (pos == NULL)
247e604d861Schristos return -1;
248e604d861Schristos *pos++ = '\0';
249e604d861Schristos
250e604d861Schristos len = os_strlen(pos);
251e604d861Schristos if (len & 1)
252e604d861Schristos return -1;
253e604d861Schristos len /= 2;
254e604d861Schristos
255bb610346Schristos if (os_strcmp(cmd, "all") == 0) {
256bb610346Schristos int res;
257bb610346Schristos
258bb610346Schristos e = wpabuf_alloc(len);
259bb610346Schristos if (e == NULL)
260bb610346Schristos return -1;
261bb610346Schristos if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
262bb610346Schristos wpabuf_free(e);
263bb610346Schristos return -1;
264bb610346Schristos }
265bb610346Schristos res = wifi_display_subelem_set_from_ies(global, e);
266bb610346Schristos wpabuf_free(e);
267bb610346Schristos return res;
268bb610346Schristos }
269bb610346Schristos
270bb610346Schristos subelem = atoi(cmd);
271bb610346Schristos if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
272bb610346Schristos return -1;
273bb610346Schristos
274e604d861Schristos if (len == 0) {
275e604d861Schristos /* Clear subelement */
276e604d861Schristos e = NULL;
277e604d861Schristos wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem);
278e604d861Schristos } else {
279e604d861Schristos e = wpabuf_alloc(1 + len);
280e604d861Schristos if (e == NULL)
281e604d861Schristos return -1;
282e604d861Schristos wpabuf_put_u8(e, subelem);
283e604d861Schristos if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
284e604d861Schristos wpabuf_free(e);
285e604d861Schristos return -1;
286e604d861Schristos }
287e604d861Schristos wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem);
288e604d861Schristos }
289e604d861Schristos
290e604d861Schristos wpabuf_free(global->wfd_subelem[subelem]);
291e604d861Schristos global->wfd_subelem[subelem] = e;
292e604d861Schristos wifi_display_update_wfd_ie(global);
293e604d861Schristos
294e604d861Schristos return 0;
295e604d861Schristos }
296e604d861Schristos
297e604d861Schristos
wifi_display_subelem_set_from_ies(struct wpa_global * global,struct wpabuf * ie)2983c260e60Schristos int wifi_display_subelem_set_from_ies(struct wpa_global *global,
2993c260e60Schristos struct wpabuf *ie)
3003c260e60Schristos {
3013c260e60Schristos int subelements[MAX_WFD_SUBELEMS] = {};
3023c260e60Schristos const u8 *pos, *end;
303bb610346Schristos unsigned int len, subelem;
3043c260e60Schristos struct wpabuf *e;
3053c260e60Schristos
3063c260e60Schristos wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
3073c260e60Schristos ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
3083c260e60Schristos
3093c260e60Schristos if (ie == NULL || wpabuf_len(ie) < 6)
3103c260e60Schristos return -1;
3113c260e60Schristos
3123c260e60Schristos pos = wpabuf_head(ie);
3133c260e60Schristos end = pos + wpabuf_len(ie);
3143c260e60Schristos
3153c260e60Schristos while (end > pos) {
3163c260e60Schristos if (pos + 3 > end)
3173c260e60Schristos break;
3183c260e60Schristos
3193c260e60Schristos len = WPA_GET_BE16(pos + 1) + 3;
3203c260e60Schristos
3213c260e60Schristos wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
3223c260e60Schristos *pos, len - 3);
3233c260e60Schristos
324bb610346Schristos if (len > (unsigned int) (end - pos))
3253c260e60Schristos break;
3263c260e60Schristos
3273c260e60Schristos subelem = *pos;
3283c260e60Schristos if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
3293c260e60Schristos e = wpabuf_alloc_copy(pos, len);
3303c260e60Schristos if (e == NULL)
3313c260e60Schristos return -1;
3323c260e60Schristos
3333c260e60Schristos wpabuf_free(global->wfd_subelem[subelem]);
3343c260e60Schristos global->wfd_subelem[subelem] = e;
3353c260e60Schristos subelements[subelem] = 1;
3363c260e60Schristos }
3373c260e60Schristos
3383c260e60Schristos pos += len;
3393c260e60Schristos }
3403c260e60Schristos
3413c260e60Schristos for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
3423c260e60Schristos if (subelements[subelem] == 0) {
3433c260e60Schristos wpabuf_free(global->wfd_subelem[subelem]);
3443c260e60Schristos global->wfd_subelem[subelem] = NULL;
3453c260e60Schristos }
3463c260e60Schristos }
3473c260e60Schristos
3483c260e60Schristos return wifi_display_update_wfd_ie(global);
3493c260e60Schristos }
3503c260e60Schristos
3513c260e60Schristos
wifi_display_subelem_get(struct wpa_global * global,char * cmd,char * buf,size_t buflen)352e604d861Schristos int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
353e604d861Schristos char *buf, size_t buflen)
354e604d861Schristos {
355e604d861Schristos int subelem;
356e604d861Schristos
357bb610346Schristos if (os_strcmp(cmd, "all") == 0) {
358bb610346Schristos struct wpabuf *ie;
359bb610346Schristos int res;
360bb610346Schristos
361bb610346Schristos ie = wifi_display_get_wfd_ie(global);
362bb610346Schristos if (ie == NULL)
363bb610346Schristos return 0;
364bb610346Schristos res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
365bb610346Schristos wpabuf_len(ie));
366bb610346Schristos wpabuf_free(ie);
367bb610346Schristos return res;
368bb610346Schristos }
369bb610346Schristos
370e604d861Schristos subelem = atoi(cmd);
371e604d861Schristos if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
372e604d861Schristos return -1;
373e604d861Schristos
374e604d861Schristos if (global->wfd_subelem[subelem] == NULL)
375e604d861Schristos return 0;
376e604d861Schristos
377e604d861Schristos return wpa_snprintf_hex(buf, buflen,
378e604d861Schristos wpabuf_head_u8(global->wfd_subelem[subelem]) +
379e604d861Schristos 1,
380e604d861Schristos wpabuf_len(global->wfd_subelem[subelem]) - 1);
381e604d861Schristos }
3823c260e60Schristos
3833c260e60Schristos
wifi_display_subelem_hex(const struct wpabuf * wfd_subelems,u8 id)3843c260e60Schristos char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
3853c260e60Schristos {
3863c260e60Schristos char *subelem = NULL;
3873c260e60Schristos const u8 *buf;
3883c260e60Schristos size_t buflen;
3893c260e60Schristos size_t i = 0;
3903c260e60Schristos u16 elen;
3913c260e60Schristos
3923c260e60Schristos if (!wfd_subelems)
3933c260e60Schristos return NULL;
3943c260e60Schristos
3953c260e60Schristos buf = wpabuf_head_u8(wfd_subelems);
3963c260e60Schristos if (!buf)
3973c260e60Schristos return NULL;
3983c260e60Schristos
3993c260e60Schristos buflen = wpabuf_len(wfd_subelems);
4003c260e60Schristos
4013c260e60Schristos while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
4023c260e60Schristos elen = WPA_GET_BE16(buf + i + 1);
4033c260e60Schristos if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
4043c260e60Schristos break; /* truncated subelement */
4053c260e60Schristos
4063c260e60Schristos if (buf[i] == id) {
4073c260e60Schristos /*
4083c260e60Schristos * Limit explicitly to an arbitrary length to avoid
4093c260e60Schristos * unnecessarily large allocations. In practice, this
4103c260e60Schristos * is limited to maximum frame length anyway, so the
4113c260e60Schristos * maximum memory allocation here is not really that
4123c260e60Schristos * large. Anyway, the Wi-Fi Display subelements that
4133c260e60Schristos * are fetched with this function are even shorter.
4143c260e60Schristos */
4153c260e60Schristos if (elen > 1000)
4163c260e60Schristos break;
4173c260e60Schristos subelem = os_zalloc(2 * elen + 1);
4183c260e60Schristos if (!subelem)
4193c260e60Schristos return NULL;
4203c260e60Schristos wpa_snprintf_hex(subelem, 2 * elen + 1,
4213c260e60Schristos buf + i +
4223c260e60Schristos WIFI_DISPLAY_SUBELEM_HEADER_LEN,
4233c260e60Schristos elen);
4243c260e60Schristos break;
4253c260e60Schristos }
4263c260e60Schristos
4273c260e60Schristos i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
4283c260e60Schristos }
4293c260e60Schristos
4303c260e60Schristos return subelem;
4313c260e60Schristos }
432