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