| /* |
| * wpa_supplicant - Wi-Fi Display |
| * Copyright (c) 2011, Atheros Communications, Inc. |
| * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. |
| * |
| * This software may be distributed under the terms of the BSD license. |
| * See README for more details. |
| */ |
| |
| #include "includes.h" |
| |
| #include "common.h" |
| #include "p2p/p2p.h" |
| #include "common/ieee802_11_defs.h" |
| #include "wpa_supplicant_i.h" |
| #include "wifi_display.h" |
| |
| |
| int wifi_display_init(struct wpa_global *global) |
| { |
| global->wifi_display = 1; |
| return 0; |
| } |
| |
| |
| void wifi_display_deinit(struct wpa_global *global) |
| { |
| int i; |
| for (i = 0; i < MAX_WFD_SUBELEMS; i++) { |
| wpabuf_free(global->wfd_subelem[i]); |
| global->wfd_subelem[i] = NULL; |
| } |
| } |
| |
| |
| static int wifi_display_update_wfd_ie(struct wpa_global *global) |
| { |
| struct wpabuf *ie, *buf; |
| size_t len, plen; |
| |
| wpa_printf(MSG_DEBUG, "WFD: Update WFD IE"); |
| |
| if (!global->wifi_display) { |
| wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display disabled - do not " |
| "include WFD IE"); |
| p2p_set_wfd_ie_beacon(global->p2p, NULL); |
| p2p_set_wfd_ie_probe_req(global->p2p, NULL); |
| p2p_set_wfd_ie_probe_resp(global->p2p, NULL); |
| p2p_set_wfd_ie_assoc_req(global->p2p, NULL); |
| p2p_set_wfd_ie_invitation(global->p2p, NULL); |
| p2p_set_wfd_ie_prov_disc_req(global->p2p, NULL); |
| p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL); |
| p2p_set_wfd_ie_go_neg(global->p2p, NULL); |
| p2p_set_wfd_dev_info(global->p2p, NULL); |
| p2p_set_wfd_assoc_bssid(global->p2p, NULL); |
| p2p_set_wfd_coupled_sink_info(global->p2p, NULL); |
| return 0; |
| } |
| |
| p2p_set_wfd_dev_info(global->p2p, |
| global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); |
| p2p_set_wfd_assoc_bssid( |
| global->p2p, |
| global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]); |
| p2p_set_wfd_coupled_sink_info( |
| global->p2p, global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); |
| |
| /* |
| * WFD IE is included in number of management frames. Two different |
| * sets of subelements are included depending on the frame: |
| * |
| * Beacon, (Re)Association Request, GO Negotiation Req/Resp/Conf, |
| * Provision Discovery Req: |
| * WFD Device Info |
| * [Associated BSSID] |
| * [Coupled Sink Info] |
| * |
| * Probe Request: |
| * WFD Device Info |
| * [Associated BSSID] |
| * [Coupled Sink Info] |
| * [WFD Extended Capability] |
| * |
| * Probe Response: |
| * WFD Device Info |
| * [Associated BSSID] |
| * [Coupled Sink Info] |
| * [WFD Extended Capability] |
| * [WFD Session Info] |
| * |
| * (Re)Association Response, P2P Invitation Req/Resp, |
| * Provision Discovery Resp: |
| * WFD Device Info |
| * [Associated BSSID] |
| * [Coupled Sink Info] |
| * [WFD Session Info] |
| */ |
| len = 0; |
| if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) |
| len += wpabuf_len(global->wfd_subelem[ |
| WFD_SUBELEM_DEVICE_INFO]); |
| if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) |
| len += wpabuf_len(global->wfd_subelem[ |
| WFD_SUBELEM_ASSOCIATED_BSSID]); |
| if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) |
| len += wpabuf_len(global->wfd_subelem[ |
| WFD_SUBELEM_COUPLED_SINK]); |
| if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) |
| len += wpabuf_len(global->wfd_subelem[ |
| WFD_SUBELEM_SESSION_INFO]); |
| if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) |
| len += wpabuf_len(global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); |
| buf = wpabuf_alloc(len); |
| if (buf == NULL) |
| return -1; |
| |
| if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) |
| wpabuf_put_buf(buf, |
| global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); |
| if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) |
| wpabuf_put_buf(buf, global->wfd_subelem[ |
| WFD_SUBELEM_ASSOCIATED_BSSID]); |
| if (global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]) |
| wpabuf_put_buf(buf, |
| global->wfd_subelem[WFD_SUBELEM_COUPLED_SINK]); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Beacon", ie); |
| p2p_set_wfd_ie_beacon(global->p2p, ie); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for (Re)Association Request", |
| ie); |
| p2p_set_wfd_ie_assoc_req(global->p2p, ie); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for GO Negotiation", ie); |
| p2p_set_wfd_ie_go_neg(global->p2p, ie); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " |
| "Request", ie); |
| p2p_set_wfd_ie_prov_disc_req(global->p2p, ie); |
| |
| plen = buf->used; |
| if (global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]) |
| wpabuf_put_buf(buf, |
| global->wfd_subelem[WFD_SUBELEM_EXT_CAPAB]); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Request", ie); |
| p2p_set_wfd_ie_probe_req(global->p2p, ie); |
| |
| if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) |
| wpabuf_put_buf(buf, |
| global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Probe Response", ie); |
| p2p_set_wfd_ie_probe_resp(global->p2p, ie); |
| |
| /* Remove WFD Extended Capability from buffer */ |
| buf->used = plen; |
| if (global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]) |
| wpabuf_put_buf(buf, |
| global->wfd_subelem[WFD_SUBELEM_SESSION_INFO]); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for P2P Invitation", ie); |
| p2p_set_wfd_ie_invitation(global->p2p, ie); |
| |
| ie = wifi_display_encaps(buf); |
| wpa_hexdump_buf(MSG_DEBUG, "WFD: WFD IE for Provision Discovery " |
| "Response", ie); |
| p2p_set_wfd_ie_prov_disc_resp(global->p2p, ie); |
| |
| wpabuf_free(buf); |
| |
| return 0; |
| } |
| |
| |
| void wifi_display_enable(struct wpa_global *global, int enabled) |
| { |
| wpa_printf(MSG_DEBUG, "WFD: Wi-Fi Display %s", |
| enabled ? "enabled" : "disabled"); |
| global->wifi_display = enabled; |
| wifi_display_update_wfd_ie(global); |
| } |
| |
| |
| int wifi_display_subelem_set(struct wpa_global *global, char *cmd) |
| { |
| char *pos; |
| int subelem; |
| size_t len; |
| struct wpabuf *e; |
| |
| pos = os_strchr(cmd, ' '); |
| if (pos == NULL) |
| return -1; |
| *pos++ = '\0'; |
| subelem = atoi(cmd); |
| if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) |
| return -1; |
| |
| len = os_strlen(pos); |
| if (len & 1) |
| return -1; |
| len /= 2; |
| |
| if (len == 0) { |
| /* Clear subelement */ |
| e = NULL; |
| wpa_printf(MSG_DEBUG, "WFD: Clear subelement %d", subelem); |
| } else { |
| e = wpabuf_alloc(1 + len); |
| if (e == NULL) |
| return -1; |
| wpabuf_put_u8(e, subelem); |
| if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { |
| wpabuf_free(e); |
| return -1; |
| } |
| wpa_printf(MSG_DEBUG, "WFD: Set subelement %d", subelem); |
| } |
| |
| wpabuf_free(global->wfd_subelem[subelem]); |
| global->wfd_subelem[subelem] = e; |
| wifi_display_update_wfd_ie(global); |
| |
| return 0; |
| } |
| |
| |
| int wifi_display_subelem_get(struct wpa_global *global, char *cmd, |
| char *buf, size_t buflen) |
| { |
| int subelem; |
| |
| subelem = atoi(cmd); |
| if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) |
| return -1; |
| |
| if (global->wfd_subelem[subelem] == NULL) |
| return 0; |
| |
| return wpa_snprintf_hex(buf, buflen, |
| wpabuf_head_u8(global->wfd_subelem[subelem]) + |
| 1, |
| wpabuf_len(global->wfd_subelem[subelem]) - 1); |
| } |