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