P2P: Add wfd_dev_info= field for device found event
[mech_eap.git] / wpa_supplicant / wifi_display.c
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 }