Home | History | Annotate | Download | only in wpa_supplicant
      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 	if (global->p2p == NULL)
     45 		return 0;
     46 
     47 	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 
    188 void 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 
    197 int 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 
    241 int 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 }
    258 
    259 
    260 char * 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);
    279 		if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
    280 			break; /* truncated subelement */
    281 
    282 		if (buf[i] == id) {
    283 			/*
    284 			 * Limit explicitly to an arbitrary length to avoid
    285 			 * unnecessarily large allocations. In practice, this
    286 			 * is limited to maximum frame length anyway, so the
    287 			 * maximum memory allocation here is not really that
    288 			 * large. Anyway, the Wi-Fi Display subelements that
    289 			 * are fetched with this function are even shorter.
    290 			 */
    291 			if (elen > 1000)
    292 				break;
    293 			subelem = os_zalloc(2 * elen + 1);
    294 			if (!subelem)
    295 				return NULL;
    296 			wpa_snprintf_hex(subelem, 2 * elen + 1,
    297 					 buf + i +
    298 					 WIFI_DISPLAY_SUBELEM_HEADER_LEN,
    299 					 elen);
    300 			break;
    301 		}
    302 
    303 		i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
    304 	}
    305 
    306 	return subelem;
    307 }
    308