WFD: Add Wi-Fi Display support

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>
This commit is contained in:
Jouni Malinen 2012-03-02 17:26:01 +02:00 committed by Jouni Malinen
parent eb7719ff22
commit 9675ce354a
19 changed files with 1020 additions and 23 deletions

View file

@ -12,6 +12,7 @@
#include "drivers/driver.h" #include "drivers/driver.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "wps/wps.h" #include "wps/wps.h"
#include "p2p/p2p.h"
#include "hostapd.h" #include "hostapd.h"
#include "ieee802_11.h" #include "ieee802_11.h"
#include "sta_info.h" #include "sta_info.h"
@ -148,6 +149,16 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
} }
#endif /* CONFIG_P2P_MANAGER */ #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 #ifdef CONFIG_HS20
pos = buf; pos = buf;
pos = hostapd_eid_hs20_indication(hapd, pos); pos = hostapd_eid_hs20_indication(hapd, pos);

View file

@ -97,6 +97,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->p2p = pos; elems->p2p = pos;
elems->p2p_len = elen; elems->p2p_len = elen;
break; break;
case WFD_OUI_TYPE:
/* Wi-Fi Alliance - WFD IE */
elems->wfd = pos;
elems->wfd_len = elen;
break;
case HS20_INDICATION_OUI_TYPE: case HS20_INDICATION_OUI_TYPE:
/* Hotspot 2.0 */ /* Hotspot 2.0 */
elems->hs20 = pos; elems->hs20 = pos;

View file

@ -37,6 +37,7 @@ struct ieee802_11_elems {
const u8 *vht_operation; const u8 *vht_operation;
const u8 *vendor_ht_cap; const u8 *vendor_ht_cap;
const u8 *p2p; const u8 *p2p;
const u8 *wfd;
const u8 *link_id; const u8 *link_id;
const u8 *interworking; const u8 *interworking;
const u8 *hs20; const u8 *hs20;
@ -69,6 +70,7 @@ struct ieee802_11_elems {
u8 vht_operation_len; u8 vht_operation_len;
u8 vendor_ht_cap_len; u8 vendor_ht_cap_len;
u8 p2p_len; u8 p2p_len;
u8 wfd_len;
u8 interworking_len; u8 interworking_len;
u8 hs20_len; u8 hs20_len;
u8 ext_capab_len; u8 ext_capab_len;

View file

@ -701,6 +701,8 @@ struct ieee80211_vht_operation {
#define WPS_IE_VENDOR_TYPE 0x0050f204 #define WPS_IE_VENDOR_TYPE 0x0050f204
#define OUI_WFA 0x506f9a #define OUI_WFA 0x506f9a
#define P2P_IE_VENDOR_TYPE 0x506f9a09 #define P2P_IE_VENDOR_TYPE 0x506f9a09
#define WFD_IE_VENDOR_TYPE 0x506f9a0a
#define WFD_OUI_TYPE 10
#define HS20_IE_VENDOR_TYPE 0x506f9a10 #define HS20_IE_VENDOR_TYPE 0x506f9a10
#define WMM_OUI_TYPE 2 #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 OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */

View file

@ -680,6 +680,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, int level,
break; break;
} }
if (msg.wfd_subelems) {
wpabuf_free(dev->info.wfd_subelems);
dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
}
if (scan_res) { if (scan_res) {
p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq, p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
msg.group_info, msg.group_info_len); 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; dev->info.wps_vendor_ext[i] = NULL;
} }
wpabuf_free(dev->info.wfd_subelems);
os_free(dev); 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) { if (dev->flags & P2P_DEV_PROBE_REQ_ONLY) {
dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY; dev->flags &= ~P2P_DEV_PROBE_REQ_ONLY;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 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); 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); p2p_parse_free(&msg);
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, 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; struct wpabuf *buf;
u8 *len; u8 *len;
int pw_id = -1; 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) if (buf == NULL)
return 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); 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 */ /* P2P IE */
len = p2p_buf_add_ie_hdr(buf); len = p2p_buf_add_ie_hdr(buf);
p2p_buf_add_capability(buf, p2p->dev_capab & 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; struct p2p_device *peer;
size_t tmplen; size_t tmplen;
int res; int res;
size_t extra = 0;
if (!p2p_group) if (!p2p_group)
return p2p_assoc_req_ie_wlan_ap(p2p, bssid, buf, len, p2p_ie); 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 * (Re)Association Request - P2P IE
* P2P Capability attribute (shall be present) * P2P Capability attribute (shall be present)
* Extended Listen Timing (may be present) * Extended Listen Timing (may be present)
* P2P Device Info attribute (shall be present) * P2P Device Info attribute (shall be present)
*/ */
tmp = wpabuf_alloc(200); tmp = wpabuf_alloc(200 + extra);
if (tmp == NULL) if (tmp == NULL)
return -1; 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; peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
lpos = p2p_buf_add_ie_hdr(tmp); 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) 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_expiration_timeout, p2p, NULL);
eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL); eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
eloop_cancel_timeout(p2p_scan_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) 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_buf_add_capability(ies, p2p->dev_capab &
~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0); ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
if (dev_id) 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) 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; 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; 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) if (p2p && p2p->search_delay < delay)
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 */

View file

@ -211,6 +211,11 @@ struct p2p_peer_info {
size_t wps_sec_dev_type_list_len; size_t wps_sec_dev_type_list_len;
struct wpabuf *wps_vendor_ext[P2P_MAX_WPS_VENDOR_EXT]; 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 { 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); 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 */ #endif /* P2P_H */

View file

@ -134,8 +134,14 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
struct wpabuf *buf; struct wpabuf *buf;
u8 *len; u8 *len;
u8 group_capab; 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) if (buf == NULL)
return 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 */ /* WPS IE with Device Password ID attribute */
p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0); 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; return buf;
} }
@ -246,10 +257,17 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
struct wpabuf *buf; struct wpabuf *buf;
u8 *len; u8 *len;
u8 group_capab; u8 group_capab;
size_t extra = 0;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Building GO Negotiation Response"); "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) if (buf == NULL)
return 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 : p2p_wps_method_pw_id(peer ? peer->wps_method :
WPS_NOT_READY), 0); 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; return buf;
} }
@ -710,10 +734,17 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
u8 *len; u8 *len;
struct p2p_channels res; struct p2p_channels res;
u8 group_capab; u8 group_capab;
size_t extra = 0;
wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
"P2P: Building GO Negotiation Confirm"); "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) if (buf == NULL)
return 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); 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; return buf;
} }

View file

@ -22,6 +22,7 @@ struct p2p_group_member {
u8 addr[ETH_ALEN]; /* P2P Interface Address */ u8 addr[ETH_ALEN]; /* P2P Interface Address */
u8 dev_addr[ETH_ALEN]; /* P2P Device Address */ u8 dev_addr[ETH_ALEN]; /* P2P Device Address */
struct wpabuf *p2p_ie; struct wpabuf *p2p_ie;
struct wpabuf *wfd_ie;
struct wpabuf *client_info; struct wpabuf *client_info;
u8 dev_capab; u8 dev_capab;
}; };
@ -37,12 +38,10 @@ struct p2p_group {
int group_formation; int group_formation;
int beacon_update; int beacon_update;
struct wpabuf *noa; 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 * p2p_group_init(struct p2p_data *p2p,
struct p2p_group_config *config) 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) static void p2p_group_free_member(struct p2p_group_member *m)
{ {
wpabuf_free(m->wfd_ie);
wpabuf_free(m->p2p_ie); wpabuf_free(m->p2p_ie);
wpabuf_free(m->client_info); wpabuf_free(m->client_info);
os_free(m); os_free(m);
@ -118,6 +118,7 @@ void p2p_group_deinit(struct p2p_group *group)
p2p_group_free_members(group); p2p_group_free_members(group);
os_free(group->cfg); os_free(group->cfg);
wpabuf_free(group->noa); wpabuf_free(group->noa);
wpabuf_free(group->wfd_ie);
os_free(group); os_free(group);
} }
@ -172,11 +173,22 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
{ {
struct wpabuf *ie; struct wpabuf *ie;
u8 *len; u8 *len;
size_t extra = 0;
ie = wpabuf_alloc(257); #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 + extra);
if (ie == NULL) if (ie == NULL)
return 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); len = p2p_buf_add_ie_hdr(ie);
p2p_group_add_common_ies(group, ie); p2p_group_add_common_ies(group, ie);
p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr); 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) static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
{ {
u8 *group_info; u8 *group_info;
struct wpabuf *ie; struct wpabuf *ie;
struct p2p_group_member *m; struct p2p_group_member *m;
u8 *len; u8 *len;
size_t extra = 0;
ie = wpabuf_alloc(257); #ifdef CONFIG_WIFI_DISPLAY
if (group->wfd_ie)
extra += wpabuf_len(group->wfd_ie);
#endif /* CONFIG_WIFI_DISPLAY */
ie = wpabuf_alloc(257 + extra);
if (ie == NULL) if (ie == NULL)
return 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); len = p2p_buf_add_ie_hdr(ie);
p2p_group_add_common_ies(group, 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); (u8 *) wpabuf_put(ie, 0) - group_info - 3);
p2p_buf_update_ie_hdr(ie, len); p2p_buf_update_ie_hdr(ie, len);
return ie; 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 *beacon_ie;
struct wpabuf *probe_resp_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); probe_resp_ie = p2p_group_build_probe_resp_ie(group);
if (probe_resp_ie == NULL) if (probe_resp_ie == NULL)
return; return;
@ -354,6 +547,9 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
&m->dev_capab, &m->dev_capab,
m->dev_addr); 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); 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->members = m;
group->num_members++; group->num_members++;
wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR wpa_msg(group->p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Add client " MACSTR
" to group (p2p=%d client_info=%d); num_members=%u/%u", " to group (p2p=%d wfd=%d client_info=%d); num_members=%u/%u",
MAC2STR(addr), m->p2p_ie ? 1 : 0, m->client_info ? 1 : 0, MAC2STR(addr), m->p2p_ie ? 1 : 0, m->wfd_ie ? 1 : 0,
m->client_info ? 1 : 0,
group->num_members, group->cfg->max_clients); group->num_members, group->cfg->max_clients);
if (group->num_members == group->cfg->max_clients) if (group->num_members == group->cfg->max_clients)
group->beacon_update = 1; group->beacon_update = 1;
@ -378,6 +575,12 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
{ {
struct wpabuf *resp; struct wpabuf *resp;
u8 *rlen; 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 * (Re)Association Response - P2P IE
@ -385,9 +588,15 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
* denied) * denied)
* Extended Listen Timing (may be present) * Extended Listen Timing (may be present)
*/ */
resp = wpabuf_alloc(20); resp = wpabuf_alloc(20 + extra);
if (resp == NULL) if (resp == NULL)
return 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); rlen = p2p_buf_add_ie_hdr(resp);
if (status != P2P_SC_SUCCESS) if (status != P2P_SC_SUCCESS)
p2p_buf_add_status(resp, status); p2p_buf_add_status(resp, status);

View file

@ -436,6 +436,20 @@ struct p2p_data {
/* Extra delay in milliseconds between search iterations */ /* Extra delay in milliseconds between search iterations */
unsigned int search_delay; unsigned int search_delay;
int in_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 p2p_message {
struct wpabuf *p2p_attributes; struct wpabuf *p2p_attributes;
struct wpabuf *wps_attributes; struct wpabuf *wps_attributes;
struct wpabuf *wfd_subelems;
u8 dialog_token; u8 dialog_token;
@ -564,6 +579,8 @@ u8 p2p_group_presence_req(struct p2p_group *group,
const u8 *noa, size_t noa_len); const u8 *noa, size_t noa_len);
int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id, int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
size_t group_id_len); 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); void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);

View file

@ -21,8 +21,27 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
struct wpabuf *buf; struct wpabuf *buf;
u8 *len; u8 *len;
const u8 *dev_addr; const u8 *dev_addr;
size_t extra = 0;
buf = wpabuf_alloc(1000); #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 + extra);
if (buf == NULL) if (buf == NULL)
return 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_add_device_info(buf, p2p, peer);
p2p_buf_update_ie_hdr(buf, len); 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; return buf;
} }
@ -68,8 +92,30 @@ static struct wpabuf * p2p_build_invitation_resp(struct p2p_data *p2p,
{ {
struct wpabuf *buf; struct wpabuf *buf;
u8 *len; u8 *len;
size_t extra = 0;
buf = wpabuf_alloc(1000); #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 + extra);
if (buf == NULL) if (buf == NULL)
return 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_add_channel_list(buf, p2p->cfg->country, channels);
p2p_buf_update_ie_hdr(buf, len); 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; return buf;
} }

View file

@ -414,6 +414,13 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
return -1; 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; return 0;
} }
@ -453,6 +460,10 @@ void p2p_parse_free(struct p2p_message *msg)
msg->p2p_attributes = NULL; msg->p2p_attributes = NULL;
wpabuf_free(msg->wps_attributes); wpabuf_free(msg->wps_attributes);
msg->wps_attributes = NULL; msg->wps_attributes = NULL;
#ifdef CONFIG_WIFI_DISPLAY
wpabuf_free(msg->wfd_subelems);
msg->wfd_subelems = NULL;
#endif /* CONFIG_WIFI_DISPLAY */
} }

View file

@ -46,8 +46,14 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
{ {
struct wpabuf *buf; struct wpabuf *buf;
u8 *len; 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) if (buf == NULL)
return 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 */ /* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods); 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; return buf;
} }
static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p, static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
u8 dialog_token, u8 dialog_token,
u16 config_methods) u16 config_methods,
const u8 *group_id,
size_t group_id_len)
{ {
struct wpabuf *buf; struct wpabuf *buf;
size_t extra = 0;
buf = wpabuf_alloc(100); #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 + extra);
if (buf == NULL) if (buf == NULL)
return 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 */ /* WPS IE with Config Methods attribute */
p2p_build_wps_ie_config_methods(buf, config_methods); 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; 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 " "P2P: Provision Discovery Request add device "
"failed " MACSTR, MAC2STR(sa)); "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 & if (!(msg.wps_config_methods &
@ -162,7 +205,8 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
out: out:
resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token, 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) { if (resp == NULL) {
p2p_parse_free(&msg); p2p_parse_free(&msg);
return; return;

View file

@ -237,6 +237,11 @@ CFLAGS += -DCONFIG_P2P_STRICT
endif endif
endif endif
ifdef CONFIG_WIFI_DISPLAY
CFLAGS += -DCONFIG_WIFI_DISPLAY
OBJS += wifi_display.o
endif
ifdef CONFIG_HS20 ifdef CONFIG_HS20
OBJS += hs20_supplicant.o OBJS += hs20_supplicant.o
CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_HS20

View file

@ -29,6 +29,7 @@
#include "p2p_supplicant.h" #include "p2p_supplicant.h"
#include "p2p/p2p.h" #include "p2p/p2p.h"
#include "hs20_supplicant.h" #include "hs20_supplicant.h"
#include "wifi_display.h"
#include "notify.h" #include "notify.h"
#include "bss.h" #include "bss.h"
#include "scan.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) { } else if (os_strcasecmp(cmd, "ps") == 0) {
ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); 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) { } else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
ret = set_bssid_filter(wpa_s, value); ret = set_bssid_filter(wpa_s, value);
} else { } else {
@ -310,6 +315,14 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
res = os_snprintf(buf, buflen, "%c%c", res = os_snprintf(buf, buflen, "%c%c",
wpa_s->conf->country[0], wpa_s->conf->country[0],
wpa_s->conf->country[1]); 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) 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) if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
reply_len = -1; reply_len = -1;
#endif /* CONFIG_P2P */ #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 #ifdef CONFIG_INTERWORKING
} else if (os_strcmp(buf, "FETCH_ANQP") == 0) { } else if (os_strcmp(buf, "FETCH_ANQP") == 0) {
if (interworking_fetch_anqp(wpa_s) < 0) if (interworking_fetch_anqp(wpa_s) < 0)

View file

@ -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);
}

View file

@ -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 */

View file

@ -2014,6 +2014,50 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
#endif /* CONFIG_P2P */ #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 #ifdef CONFIG_INTERWORKING
static int wpa_cli_cmd_fetch_anqp(struct wpa_ctrl *ctrl, int argc, 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, cli_cmd_flag_none,
"[<period> <interval>] = set extended listen timing" }, "[<period> <interval>] = set extended listen timing" },
#endif /* CONFIG_P2P */ #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 #ifdef CONFIG_INTERWORKING
{ "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none, { "fetch_anqp", wpa_cli_cmd_fetch_anqp, NULL, cli_cmd_flag_none,
"= fetch ANQP information for all APs" }, "= fetch ANQP information for all APs" },

View file

@ -41,6 +41,7 @@
#include "gas_query.h" #include "gas_query.h"
#include "ap.h" #include "ap.h"
#include "p2p_supplicant.h" #include "p2p_supplicant.h"
#include "wifi_display.h"
#include "notify.h" #include "notify.h"
#include "bgscan.h" #include "bgscan.h"
#include "autoscan.h" #include "autoscan.h"
@ -3190,6 +3191,14 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
return NULL; 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; return global;
} }
@ -3241,6 +3250,9 @@ void wpa_supplicant_deinit(struct wpa_global *global)
if (global == NULL) if (global == NULL)
return; return;
#ifdef CONFIG_WIFI_DISPLAY
wifi_display_deinit(global);
#endif /* CONFIG_WIFI_DISPLAY */
#ifdef CONFIG_P2P #ifdef CONFIG_P2P
wpas_p2p_deinit_global(global); wpas_p2p_deinit_global(global);
#endif /* CONFIG_P2P */ #endif /* CONFIG_P2P */

View file

@ -239,6 +239,12 @@ struct wpa_global {
WPA_CONC_PREF_STA, WPA_CONC_PREF_STA,
WPA_CONC_PREF_P2P WPA_CONC_PREF_P2P
} conc_pref; } 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 */
}; };