WFD: Add Wi-Fi Display support
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 2 Mar 2012 15:26:01 +0000 (17:26 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 29 Aug 2012 16:51:29 +0000 (19:51 +0300)
This commit adds control interface commands and internal storage of
Wi-Fi Display related configuration. In addition, WFD IE is now added
to various P2P frames, Probe Request/Response, and (Re)Association
Request/Response frames. WFD subelements from peers are stored in the
P2P peer table.

Following control interface commands are now available:
SET wifi_display <0/1>
GET wifi_display
WFD_SUBELEM_SET <subelem> [hexdump of length+body]
WFD_SUBELEM_GET <subelem>

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

19 files changed:
src/ap/ap_drv_ops.c
src/common/ieee802_11_common.c
src/common/ieee802_11_common.h
src/common/ieee802_11_defs.h
src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_go_neg.c
src/p2p/p2p_group.c
src/p2p/p2p_i.h
src/p2p/p2p_invitation.c
src/p2p/p2p_parse.c
src/p2p/p2p_pd.c
wpa_supplicant/Makefile
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wifi_display.c [new file with mode: 0644]
wpa_supplicant/wifi_display.h [new file with mode: 0644]
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 64c9c20..02da25b 100644 (file)
@@ -12,6 +12,7 @@
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "wps/wps.h"
+#include "p2p/p2p.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
 #include "sta_info.h"
@@ -148,6 +149,16 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_P2P_MANAGER */
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (hapd->p2p_group) {
+               struct wpabuf *a;
+               a = p2p_group_assoc_resp_ie(hapd->p2p_group, P2P_SC_SUCCESS);
+               if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
+                       wpabuf_put_buf(assocresp, a);
+               wpabuf_free(a);
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
+
 #ifdef CONFIG_HS20
        pos = buf;
        pos = hostapd_eid_hs20_indication(hapd, pos);
index 0f50c17..d9d3cd0 100644 (file)
@@ -97,6 +97,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                        elems->p2p = pos;
                        elems->p2p_len = elen;
                        break;
+               case WFD_OUI_TYPE:
+                       /* Wi-Fi Alliance - WFD IE */
+                       elems->wfd = pos;
+                       elems->wfd_len = elen;
+                       break;
                case HS20_INDICATION_OUI_TYPE:
                        /* Hotspot 2.0 */
                        elems->hs20 = pos;
index ac0843a..bfc3eb2 100644 (file)
@@ -37,6 +37,7 @@ struct ieee802_11_elems {
        const u8 *vht_operation;
        const u8 *vendor_ht_cap;
        const u8 *p2p;
+       const u8 *wfd;
        const u8 *link_id;
        const u8 *interworking;
        const u8 *hs20;
@@ -69,6 +70,7 @@ struct ieee802_11_elems {
        u8 vht_operation_len;
        u8 vendor_ht_cap_len;
        u8 p2p_len;
+       u8 wfd_len;
        u8 interworking_len;
        u8 hs20_len;
        u8 ext_capab_len;
index cb5ff18..ad4f260 100644 (file)
@@ -701,6 +701,8 @@ struct ieee80211_vht_operation {
 #define WPS_IE_VENDOR_TYPE 0x0050f204
 #define OUI_WFA 0x506f9a
 #define P2P_IE_VENDOR_TYPE 0x506f9a09
+#define WFD_IE_VENDOR_TYPE 0x506f9a0a
+#define WFD_OUI_TYPE 10
 #define HS20_IE_VENDOR_TYPE 0x506f9a10
 
 #define WMM_OUI_TYPE 2
@@ -934,6 +936,20 @@ enum p2p_sd_status {
 };
 
 
+enum wifi_display_subelem {
+       WFD_SUBELEM_DEVICE_INFO = 0,
+       WFD_SUBELEM_ASSOCIATED_BSSID = 1,
+       WFD_SUBELEM_AUDIO_FORMATS = 2,
+       WFD_SUBELEM_VIDEO_FORMATS = 3,
+       WFD_SUBELEM_3D_VIDEO_FORMATS = 4,
+       WFD_SUBELEM_CONTENT_PROTECTION = 5,
+       WFD_SUBELEM_COUPLED_SINK = 6,
+       WFD_SUBELEM_EXT_CAPAB = 7,
+       WFD_SUBELEM_LOCAL_IP_ADDRESS = 8,
+       WFD_SUBELEM_SESSION_INFO = 9
+};
+
+
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
index 311aad3..365d5fd 100644 (file)
@@ -680,6 +680,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
                        break;
        }
 
+       if (msg.wfd_subelems) {
+               wpabuf_free(dev->info.wfd_subelems);
+               dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+       }
+
        if (scan_res) {
                p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
                                      msg.group_info, msg.group_info_len);
@@ -737,6 +742,8 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
                dev->info.wps_vendor_ext[i] = NULL;
        }
 
+       wpabuf_free(dev->info.wfd_subelems);
+
        os_free(dev);
 }
 
@@ -1375,6 +1382,11 @@ void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
                }
        }
 
+       if (msg->wfd_subelems) {
+               wpabuf_free(dev->info.wfd_subelems);
+               dev->info.wfd_subelems = wpabuf_dup(msg->wfd_subelems);
+       }
+
        if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
                dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
                wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1712,6 +1724,11 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
 
        p2p_copy_wps_info(dev, 1, &msg);
 
+       if (msg.wfd_subelems) {
+               wpabuf_free(dev->info.wfd_subelems);
+               dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+       }
+
        p2p_parse_free(&msg);
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
@@ -1810,8 +1827,14 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
        struct wpabuf *buf;
        u8 *len;
        int pw_id = -1;
+       size_t extra = 0;
 
-       buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_probe_resp)
+               extra = wpabuf_len(p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -1822,6 +1845,11 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
 
        p2p_build_wps_ie(p2p, buf, pw_id, 1);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_probe_resp)
+               wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        /* P2P IE */
        len = p2p_buf_add_ie_hdr(buf);
        p2p_buf_add_capability(buf, p2p->dev_capab &
@@ -2106,20 +2134,31 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
        struct p2p_device *peer;
        size_t tmplen;
        int res;
+       size_t extra = 0;
 
        if (!p2p_group)
                return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_assoc_req)
+               extra = wpabuf_len(p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        /*
         * (Re)Association Request - P2P IE
         * P2P Capability attribute (shall be present)
         * Extended Listen Timing (may be present)
         * P2P Device Info attribute (shall be present)
         */
-       tmp = wpabuf_alloc(200);
+       tmp = wpabuf_alloc(200 + extra);
        if (tmp == NULL)
                return -1;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_assoc_req)
+               wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
 
        lpos = p2p_buf_add_ie_hdr(tmp);
@@ -2303,6 +2342,20 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 
 void p2p_deinit(struct p2p_data *p2p)
 {
+#ifdef CONFIG_WIFI_DISPLAY
+       wpabuf_free(p2p->wfd_ie_beacon);
+       wpabuf_free(p2p->wfd_ie_probe_req);
+       wpabuf_free(p2p->wfd_ie_probe_resp);
+       wpabuf_free(p2p->wfd_ie_assoc_req);
+       wpabuf_free(p2p->wfd_ie_invitation);
+       wpabuf_free(p2p->wfd_ie_prov_disc_req);
+       wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+       wpabuf_free(p2p->wfd_ie_go_neg);
+       wpabuf_free(p2p->wfd_dev_info);
+       wpabuf_free(p2p->wfd_assoc_bssid);
+       wpabuf_free(p2p->wfd_coupled_sink_info);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
        eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
        eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
@@ -2675,7 +2728,14 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
 
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
-       u8 *len = p2p_buf_add_ie_hdr(ies);
+       u8 *len;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_probe_req)
+               wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       len = p2p_buf_add_ie_hdr(ies);
        p2p_buf_add_capability(ies, p2p->dev_capab &
                               ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
        if (dev_id)
@@ -2694,7 +2754,14 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 
 size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
 {
-       return 100;
+       size_t len = 100;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p && p2p->wfd_ie_probe_req)
+               len += wpabuf_len(p2p->wfd_ie_probe_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       return len;
 }
 
 
@@ -3398,6 +3465,24 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                pos += res;
        }
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (dev->info.wfd_subelems) {
+               res = os_snprintf(pos, end - pos, "wfd_subelems=");
+               if (res < 0 || res >= end - pos)
+                       return pos - buf;
+               pos += res;
+
+               pos += wpa_snprintf_hex(pos, end - pos,
+                                       wpabuf_head(dev->info.wfd_subelems),
+                                       wpabuf_len(dev->info.wfd_subelems));
+
+               res = os_snprintf(pos, end - pos, "\n");
+               if (res < 0 || res >= end - pos)
+                       return pos - buf;
+               pos += res;
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return pos - buf;
 }
 
@@ -4018,3 +4103,128 @@ void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
        if (p2p && p2p->search_delay < delay)
                p2p->search_delay = delay;
 }
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
+{
+       size_t g;
+       struct p2p_group *group;
+
+       for (g = 0; g < p2p->num_groups; g++) {
+               group = p2p->groups[g];
+               p2p_group_update_ies(group);
+       }
+}
+
+
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_beacon);
+       p2p->wfd_ie_beacon = ie;
+       p2p_update_wfd_ie_groups(p2p);
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_probe_req);
+       p2p->wfd_ie_probe_req = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_probe_resp);
+       p2p->wfd_ie_probe_resp = ie;
+       p2p_update_wfd_ie_groups(p2p);
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_assoc_req);
+       p2p->wfd_ie_assoc_req = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_invitation);
+       p2p->wfd_ie_invitation = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_prov_disc_req);
+       p2p->wfd_ie_prov_disc_req = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_prov_disc_resp);
+       p2p->wfd_ie_prov_disc_resp = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie)
+{
+       wpabuf_free(p2p->wfd_ie_go_neg);
+       p2p->wfd_ie_go_neg = ie;
+       return 0;
+}
+
+
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+       wpabuf_free(p2p->wfd_dev_info);
+       if (elem) {
+               p2p->wfd_dev_info = wpabuf_dup(elem);
+               if (p2p->wfd_dev_info == NULL)
+                       return -1;
+       } else
+               p2p->wfd_dev_info = NULL;
+
+       return 0;
+}
+
+
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem)
+{
+       wpabuf_free(p2p->wfd_assoc_bssid);
+       if (elem) {
+               p2p->wfd_assoc_bssid = wpabuf_dup(elem);
+               if (p2p->wfd_assoc_bssid == NULL)
+                       return -1;
+       } else
+               p2p->wfd_assoc_bssid = NULL;
+
+       return 0;
+}
+
+
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+                                 const struct wpabuf *elem)
+{
+       wpabuf_free(p2p->wfd_coupled_sink_info);
+       if (elem) {
+               p2p->wfd_coupled_sink_info = wpabuf_dup(elem);
+               if (p2p->wfd_coupled_sink_info == NULL)
+                       return -1;
+       } else
+               p2p->wfd_coupled_sink_info = NULL;
+
+       return 0;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
index 2f35deb..b2ecc22 100644 (file)
@@ -211,6 +211,11 @@ struct p2p_peer_info {
        size_t wps_sec_dev_type_list_len;
 
        struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT];
+
+       /**
+        * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
+        */
+       struct wpabuf *wfd_subelems;
 };
 
 enum p2p_prov_disc_status {
@@ -1710,4 +1715,18 @@ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 
 void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
 
+int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_assoc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_invitation(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie);
+int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem);
+int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p,
+                                 const struct wpabuf *elem);
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
+
 #endif /* P2P_H */
index 719dca4..031b3a1 100644 (file)
@@ -134,8 +134,14 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        struct wpabuf *buf;
        u8 *len;
        u8 group_capab;
+       size_t extra = 0;
 
-       buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -177,6 +183,11 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        /* WPS IE with Device Password ID attribute */
        p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
@@ -246,10 +257,17 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        struct wpabuf *buf;
        u8 *len;
        u8 group_capab;
+       size_t extra = 0;
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                "P2P: Building GO Negotiation Response");
-       buf = wpabuf_alloc(1000);
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -308,6 +326,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
                         p2p_wps_method_pw_id(peer ? peer->wps_method :
                                              WPS_NOT_READY), 0);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
        return buf;
 }
 
@@ -710,10 +734,17 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
        u8 *len;
        struct p2p_channels res;
        u8 group_capab;
+       size_t extra = 0;
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
                "P2P: Building GO Negotiation Confirm");
-       buf = wpabuf_alloc(1000);
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               extra = wpabuf_len(p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -752,6 +783,11 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
        }
        p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_go_neg)
+               wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
index 5ff2627..8687320 100644 (file)
@@ -22,6 +22,7 @@ struct p2p_group_member {
        u8 addr[ETH_ALEN]; /* P2P Interface Address */
        u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
        struct wpabuf *p2p_ie;
+       struct wpabuf *wfd_ie;
        struct wpabuf *client_info;
        u8 dev_capab;
 };
@@ -37,12 +38,10 @@ struct p2p_group {
        int group_formation;
        int beacon_update;
        struct wpabuf *noa;
+       struct wpabuf *wfd_ie;
 };
 
 
-static void p2p_group_update_ies(struct p2p_group *group);
-
-
 struct p2p_group * p2p_group_init(struct p2p_data *p2p,
                                  struct p2p_group_config *config)
 {
@@ -74,6 +73,7 @@ struct p2p_group * p2p_group_init(struct p2p_data *p2p,
 
 static void p2p_group_free_member(struct p2p_group_member *m)
 {
+       wpabuf_free(m->wfd_ie);
        wpabuf_free(m->p2p_ie);
        wpabuf_free(m->client_info);
        os_free(m);
@@ -118,6 +118,7 @@ void p2p_group_deinit(struct p2p_group *group)
        p2p_group_free_members(group);
        os_free(group->cfg);
        wpabuf_free(group->noa);
+       wpabuf_free(group->wfd_ie);
        os_free(group);
 }
 
@@ -172,11 +173,22 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 {
        struct wpabuf *ie;
        u8 *len;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->p2p->wfd_ie_beacon)
+               extra = wpabuf_len(group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       ie = wpabuf_alloc(257);
+       ie = wpabuf_alloc(257 + extra);
        if (ie == NULL)
                return NULL;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->p2p->wfd_ie_beacon)
+               wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        len = p2p_buf_add_ie_hdr(ie);
        p2p_group_add_common_ies(group, ie);
        p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
@@ -187,17 +199,193 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
 }
 
 
+#ifdef CONFIG_WIFI_DISPLAY
+
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g)
+{
+       return g->wfd_ie;
+}
+
+
+struct wpabuf * wifi_display_encaps(struct wpabuf *subelems)
+{
+       struct wpabuf *ie;
+       const u8 *pos, *end;
+
+       if (subelems == NULL)
+               return NULL;
+
+       ie = wpabuf_alloc(wpabuf_len(subelems) + 100);
+       if (ie == NULL)
+               return NULL;
+
+       pos = wpabuf_head(subelems);
+       end = pos + wpabuf_len(subelems);
+
+       while (end > pos) {
+               size_t frag_len = end - pos;
+               if (frag_len > 251)
+                       frag_len = 251;
+               wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+               wpabuf_put_u8(ie, 4 + frag_len);
+               wpabuf_put_be32(ie, WFD_IE_VENDOR_TYPE);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       return ie;
+}
+
+
+static int wifi_display_add_dev_info_descr(struct wpabuf *buf,
+                                          struct p2p_group_member *m)
+{
+       const u8 *pos, *end;
+       const u8 *dev_info = NULL;
+       const u8 *assoc_bssid = NULL;
+       const u8 *coupled_sink = NULL;
+       u8 zero_addr[ETH_ALEN];
+
+       if (m->wfd_ie == NULL)
+               return 0;
+
+       os_memset(zero_addr, 0, ETH_ALEN);
+       pos = wpabuf_head_u8(m->wfd_ie);
+       end = pos + wpabuf_len(m->wfd_ie);
+       while (pos + 1 < end) {
+               u8 id;
+               u16 len;
+
+               id = *pos++;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + len > end)
+                       break;
+
+               switch (id) {
+               case WFD_SUBELEM_DEVICE_INFO:
+                       if (len < 6)
+                               break;
+                       dev_info = pos;
+                       break;
+               case WFD_SUBELEM_ASSOCIATED_BSSID:
+                       if (len < ETH_ALEN)
+                               break;
+                       assoc_bssid = pos;
+                       break;
+               case WFD_SUBELEM_COUPLED_SINK:
+                       if (len < 1 + ETH_ALEN)
+                               break;
+                       coupled_sink = pos;
+                       break;
+               }
+
+               pos += len;
+       }
+
+       if (dev_info == NULL)
+               return 0;
+
+       wpabuf_put_u8(buf, 23);
+       wpabuf_put_data(buf, m->dev_addr, ETH_ALEN);
+       if (assoc_bssid)
+               wpabuf_put_data(buf, assoc_bssid, ETH_ALEN);
+       else
+               wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+       wpabuf_put_data(buf, dev_info, 2); /* WFD Device Info */
+       wpabuf_put_data(buf, dev_info + 4, 2); /* WFD Device Max Throughput */
+       if (coupled_sink) {
+               wpabuf_put_data(buf, coupled_sink, 1 + ETH_ALEN);
+       } else {
+               wpabuf_put_u8(buf, 0);
+               wpabuf_put_data(buf, zero_addr, ETH_ALEN);
+       }
+
+       return 1;
+}
+
+
+static struct wpabuf *
+wifi_display_build_go_ie(struct p2p_group *group)
+{
+       struct wpabuf *wfd_subelems, *wfd_ie;
+       struct p2p_group_member *m;
+       u8 *len;
+       unsigned int count = 0;
+
+       if (!group->p2p->wfd_ie_probe_resp)
+               return NULL;
+
+       wfd_subelems = wpabuf_alloc(wpabuf_len(group->p2p->wfd_ie_probe_resp) +
+                                   group->num_members * 24 + 100);
+       if (wfd_subelems == NULL)
+               return NULL;
+       if (group->p2p->wfd_dev_info)
+               wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info);
+       if (group->p2p->wfd_assoc_bssid)
+               wpabuf_put_buf(wfd_subelems,
+                              group->p2p->wfd_assoc_bssid);
+       if (group->p2p->wfd_coupled_sink_info)
+               wpabuf_put_buf(wfd_subelems,
+                              group->p2p->wfd_coupled_sink_info);
+
+       /* Build WFD Session Info */
+       wpabuf_put_u8(wfd_subelems, WFD_SUBELEM_SESSION_INFO);
+       len = wpabuf_put(wfd_subelems, 2);
+       m = group->members;
+       while (m) {
+               if (wifi_display_add_dev_info_descr(wfd_subelems, m))
+                       count++;
+               m = m->next;
+       }
+
+       if (count == 0) {
+               /* No Wi-Fi Display clients - do not include subelement */
+               wfd_subelems->used -= 3;
+       } else {
+               WPA_PUT_BE16(len, (u8 *) wpabuf_put(wfd_subelems, 0) - len -
+                            2);
+               wpa_printf(MSG_DEBUG, "WFD: WFD Session Info: %u descriptors",
+                          count);
+       }
+
+       wfd_ie = wifi_display_encaps(wfd_subelems);
+       wpabuf_free(wfd_subelems);
+
+       return wfd_ie;
+}
+
+static void wifi_display_group_update(struct p2p_group *group)
+{
+       wpabuf_free(group->wfd_ie);
+       group->wfd_ie = wifi_display_build_go_ie(group);
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
        u8 *group_info;
        struct wpabuf *ie;
        struct p2p_group_member *m;
        u8 *len;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               extra += wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       ie = wpabuf_alloc(257);
+       ie = wpabuf_alloc(257 + extra);
        if (ie == NULL)
                return NULL;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               wpabuf_put_buf(ie, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        len = p2p_buf_add_ie_hdr(ie);
 
        p2p_group_add_common_ies(group, ie);
@@ -216,15 +404,20 @@ static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
                     (u8 *) wpabuf_put(ie, 0) - group_info - 3);
 
        p2p_buf_update_ie_hdr(ie, len);
+
        return ie;
 }
 
 
-static void p2p_group_update_ies(struct p2p_group *group)
+void p2p_group_update_ies(struct p2p_group *group)
 {
        struct wpabuf *beacon_ie;
        struct wpabuf *probe_resp_ie;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       wifi_display_group_update(group);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        probe_resp_ie = p2p_group_build_probe_resp_ie(group);
        if (probe_resp_ie == NULL)
                return;
@@ -354,6 +547,9 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
                                                       &m->dev_capab,
                                                       m->dev_addr);
        }
+#ifdef CONFIG_WIFI_DISPLAY
+       m->wfd_ie = ieee802_11_vendor_ie_concat(ie, len, WFD_IE_VENDOR_TYPE);
+#endif /* CONFIG_WIFI_DISPLAY */
 
        p2p_group_remove_member(group, addr);
 
@@ -361,8 +557,9 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
        group->members = m;
        group->num_members++;
        wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
-               " to group (p2p=%d client_info=%d); num_members=%u/%u",
-               MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0,
+               " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
+               MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
+               m->client_info ? 1 : 0,
                group->num_members, group->cfg->max_clients);
        if (group->num_members == group->cfg->max_clients)
                group->beacon_update = 1;
@@ -378,6 +575,12 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
 {
        struct wpabuf *resp;
        u8 *rlen;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               extra = wpabuf_len(group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
        /*
         * (Re)Association Response - P2P IE
@@ -385,9 +588,15 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
         *      denied)
         * Extended Listen Timing (may be present)
         */
-       resp = wpabuf_alloc(20);
+       resp = wpabuf_alloc(20 + extra);
        if (resp == NULL)
                return NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (group->wfd_ie)
+               wpabuf_put_buf(resp, group->wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        rlen = p2p_buf_add_ie_hdr(resp);
        if (status != P2P_SC_SUCCESS)
                p2p_buf_add_status(resp, status);
index 7dbdf80..f4646ee 100644 (file)
@@ -436,6 +436,20 @@ struct p2p_data {
        /* Extra delay in milliseconds between search iterations */
        unsigned int search_delay;
        int in_search_delay;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       struct wpabuf *wfd_ie_beacon;
+       struct wpabuf *wfd_ie_probe_req;
+       struct wpabuf *wfd_ie_probe_resp;
+       struct wpabuf *wfd_ie_assoc_req;
+       struct wpabuf *wfd_ie_invitation;
+       struct wpabuf *wfd_ie_prov_disc_req;
+       struct wpabuf *wfd_ie_prov_disc_resp;
+       struct wpabuf *wfd_ie_go_neg;
+       struct wpabuf *wfd_dev_info;
+       struct wpabuf *wfd_assoc_bssid;
+       struct wpabuf *wfd_coupled_sink_info;
+#endif /* CONFIG_WIFI_DISPLAY */
 };
 
 /**
@@ -444,6 +458,7 @@ struct p2p_data {
 struct p2p_message {
        struct wpabuf *p2p_attributes;
        struct wpabuf *wps_attributes;
+       struct wpabuf *wfd_subelems;
 
        u8 dialog_token;
 
@@ -564,6 +579,8 @@ u8 p2p_group_presence_req(struct p2p_group *group,
                          const u8 *noa, size_t noa_len);
 int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
                                size_t group_id_len);
+void p2p_group_update_ies(struct p2p_group *group);
+struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
index caedfb3..df24c64 100644 (file)
@@ -21,8 +21,27 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
        struct wpabuf *buf;
        u8 *len;
        const u8 *dev_addr;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+       if (wfd_ie && p2p->inv_role == P2P_INVITE_ROLE_ACTIVE_GO) {
+               size_t i;
+               for (i = 0; i < p2p->num_groups; i++) {
+                       struct p2p_group *g = p2p->groups[i];
+                       struct wpabuf *ie;
+                       ie = p2p_group_get_wfd_ie(g);
+                       if (ie) {
+                               wfd_ie = ie;
+                               break;
+                       }
+               }
+       }
+       if (wfd_ie)
+               extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       buf = wpabuf_alloc(1000);
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -55,6 +74,11 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
        p2p_buf_add_device_info(buf, p2p, peer);
        p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (wfd_ie)
+               wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
@@ -68,8 +92,30 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
 {
        struct wpabuf *buf;
        u8 *len;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       struct wpabuf *wfd_ie = p2p->wfd_ie_invitation;
+       if (wfd_ie && group_bssid) {
+               size_t i;
+               for (i = 0; i < p2p->num_groups; i++) {
+                       struct p2p_group *g = p2p->groups[i];
+                       struct wpabuf *ie;
+                       if (!p2p_group_is_group_id_match(g, group_bssid,
+                                                        ETH_ALEN))
+                               continue;
+                       ie = p2p_group_get_wfd_ie(g);
+                       if (ie) {
+                               wfd_ie = ie;
+                               break;
+                       }
+               }
+       }
+       if (wfd_ie)
+               extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       buf = wpabuf_alloc(1000);
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -88,6 +134,11 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
                p2p_buf_add_channel_list(buf, p2p->cfg->country, channels);
        p2p_buf_update_ie_hdr(buf, len);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (wfd_ie)
+               wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
index a3ec57d..097a31d 100644 (file)
@@ -414,6 +414,13 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
                return -1;
        }
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (elems.wfd) {
+               msg->wfd_subelems = ieee802_11_vendor_ie_concat(
+                       data, len, WFD_IE_VENDOR_TYPE);
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return 0;
 }
 
@@ -453,6 +460,10 @@ void p2p_parse_free(struct p2p_message *msg)
        msg->p2p_attributes = NULL;
        wpabuf_free(msg->wps_attributes);
        msg->wps_attributes = NULL;
+#ifdef CONFIG_WIFI_DISPLAY
+       wpabuf_free(msg->wfd_subelems);
+       msg->wfd_subelems = NULL;
+#endif /* CONFIG_WIFI_DISPLAY */
 }
 
 
index f391ff7..a2d5aee 100644 (file)
@@ -46,8 +46,14 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
 {
        struct wpabuf *buf;
        u8 *len;
+       size_t extra = 0;
 
-       buf = wpabuf_alloc(1000);
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_prov_disc_req)
+               extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -66,17 +72,46 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
        /* WPS IE with Config Methods attribute */
        p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (p2p->wfd_ie_prov_disc_req)
+               wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
 
 static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
                                                u8 dialog_token,
-                                               u16 config_methods)
+                                               u16 config_methods,
+                                               const u8 *group_id,
+                                               size_t group_id_len)
 {
        struct wpabuf *buf;
+       size_t extra = 0;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
+       if (wfd_ie && group_id) {
+               size_t i;
+               for (i = 0; i < p2p->num_groups; i++) {
+                       struct p2p_group *g = p2p->groups[i];
+                       struct wpabuf *ie;
+                       if (!p2p_group_is_group_id_match(g, group_id,
+                                                        group_id_len))
+                               continue;
+                       ie = p2p_group_get_wfd_ie(g);
+                       if (ie) {
+                               wfd_ie = ie;
+                               break;
+                       }
+               }
+       }
+       if (wfd_ie)
+               extra = wpabuf_len(wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
 
-       buf = wpabuf_alloc(100);
+       buf = wpabuf_alloc(100 + extra);
        if (buf == NULL)
                return NULL;
 
@@ -85,6 +120,11 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
        /* WPS IE with Config Methods attribute */
        p2p_build_wps_ie_config_methods(buf, config_methods);
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (wfd_ie)
+               wpabuf_put_buf(buf, wfd_ie);
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return buf;
 }
 
@@ -117,6 +157,9 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                                "P2P: Provision Discovery Request add device "
                                "failed " MACSTR, MAC2STR(sa));
                }
+       } else if (msg.wfd_subelems) {
+               wpabuf_free(dev->info.wfd_subelems);
+               dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
        }
 
        if (!(msg.wps_config_methods &
@@ -162,7 +205,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 
 out:
        resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
-                                       reject ? 0 : msg.wps_config_methods);
+                                       reject ? 0 : msg.wps_config_methods,
+                                       msg.group_id, msg.group_id_len);
        if (resp == NULL) {
                p2p_parse_free(&msg);
                return;
index 4b399df..227fb4f 100644 (file)
@@ -237,6 +237,11 @@ CFLAGS += -DCONFIG_P2P_STRICT
 endif
 endif
 
+ifdef CONFIG_WIFI_DISPLAY
+CFLAGS += -DCONFIG_WIFI_DISPLAY
+OBJS += wifi_display.o
+endif
+
 ifdef CONFIG_HS20
 OBJS += hs20_supplicant.o
 CFLAGS += -DCONFIG_HS20
index 4dcba59..4723189 100644 (file)
@@ -29,6 +29,7 @@
 #include "p2p_supplicant.h"
 #include "p2p/p2p.h"
 #include "hs20_supplicant.h"
+#include "wifi_display.h"
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
@@ -283,6 +284,10 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                }
        } else if (os_strcasecmp(cmd, "ps") == 0) {
                ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
+#ifdef CONFIG_WIFI_DISPLAY
+       } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+               wifi_display_enable(wpa_s->global, !!atoi(value));
+#endif /* CONFIG_WIFI_DISPLAY */
        } else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
                ret = set_bssid_filter(wpa_s, value);
        } else {
@@ -310,6 +315,14 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
                        res = os_snprintf(buf, buflen, "%c%c",
                                          wpa_s->conf->country[0],
                                          wpa_s->conf->country[1]);
+#ifdef CONFIG_WIFI_DISPLAY
+       } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
+               res = os_snprintf(buf, buflen, "%d",
+                                 wpa_s->global->wifi_display);
+               if (res < 0 || (unsigned int) res >= buflen)
+                       return -1;
+               return res;
+#endif /* CONFIG_WIFI_DISPLAY */
        }
 
        if (res < 0 || (unsigned int) res >= buflen)
@@ -4555,6 +4568,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
                        reply_len = -1;
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_WIFI_DISPLAY
+       } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
+               if (wifi_display_subelem_set(wpa_s->global, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) {
+               reply_len = wifi_display_subelem_get(wpa_s->global, buf + 16,
+                                                    reply, reply_size);
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_INTERWORKING
        } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
                if (interworking_fetch_anqp(wpa_s) < 0)
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
new file mode 100644 (file)
index 0000000..92ca536
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * 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);
+}
diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h
new file mode 100644 (file)
index 0000000..b75d4f2
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#ifndef WIFI_DISPLAY_H
+#define WIFI_DISPLAY_H
+
+int wifi_display_init(struct wpa_global *global);
+void wifi_display_deinit(struct wpa_global *global);
+void wifi_display_enable(struct wpa_global *global, int enabled);
+int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
+                            char *buf, size_t buflen);
+
+#endif /* WIFI_DISPLAY_H */
index 11b6066..2cb9513 100644 (file)
@@ -2014,6 +2014,50 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
 
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_WIFI_DISPLAY
+
+static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 1 && argc != 2) {
+               printf("Invalid WFD_SUBELEM_SET command: needs one or two "
+                      "arguments (subelem, hexdump)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
+                         argv[0], argc > 1 ? argv[1] : "");
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       char cmd[100];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid WFD_SUBELEM_GET command: needs one "
+                      "argument (subelem)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
+                         argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
 
 #ifdef CONFIG_INTERWORKING
 static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc,
@@ -2521,7 +2565,14 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_none,
          "[<period> <interval>] = set extended listen timing" },
 #endif /* CONFIG_P2P */
-
+#ifdef CONFIG_WIFI_DISPLAY
+       { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
+         cli_cmd_flag_none,
+         "<subelem> [contents] = set Wi-Fi Display subelement" },
+       { "wfd_subelem_get", wpa_cli_cmd_wfd_subelem_get, NULL,
+         cli_cmd_flag_none,
+         "<subelem> = get Wi-Fi Display subelement" },
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_INTERWORKING
        { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
          "= fetch ANQP information for all APs" },
index 423bd5c..820cd5e 100644 (file)
@@ -41,6 +41,7 @@
 #include "gas_query.h"
 #include "ap.h"
 #include "p2p_supplicant.h"
+#include "wifi_display.h"
 #include "notify.h"
 #include "bgscan.h"
 #include "autoscan.h"
@@ -3190,6 +3191,14 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
                return NULL;
        }
 
+#ifdef CONFIG_WIFI_DISPLAY
+       if (wifi_display_init(global) < 0) {
+               wpa_printf(MSG_ERROR, "Failed to initialize Wi-Fi Display");
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
+
        return global;
 }
 
@@ -3241,6 +3250,9 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        if (global == NULL)
                return;
 
+#ifdef CONFIG_WIFI_DISPLAY
+       wifi_display_deinit(global);
+#endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_P2P
        wpas_p2p_deinit_global(global);
 #endif /* CONFIG_P2P */
index 01d67d9..523b50d 100644 (file)
@@ -239,6 +239,12 @@ struct wpa_global {
                WPA_CONC_PREF_STA,
                WPA_CONC_PREF_P2P
        } conc_pref;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       int wifi_display;
+#define MAX_WFD_SUBELEMS 10
+       struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
+#endif /* CONFIG_WIFI_DISPLAY */
 };