#include "wps/wps_i.h"
#include "p2p/p2p.h"
#include "ap/hostapd.h"
+#include "ap/p2p_hostapd.h"
#include "wpa_supplicant_i.h"
#include "driver_i.h"
#include "ap.h"
wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
int go);
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method);
static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
gtype = "client";
} else
gtype = "GO";
+ if (wpa_s->cross_connect_in_use) {
+ wpa_s->cross_connect_in_use = 0;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ }
wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_REMOVED "%s %s",
wpa_s->ifname, gtype);
if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
"%s GO ssid=\"%s\" psk=%s go_dev_addr=" MACSTR "%s",
wpa_s->ifname, ssid_txt, psk, MAC2STR(go_dev_addr),
persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
} else {
wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
"%s GO ssid=\"%s\" passphrase=\"%s\" go_dev_addr="
ssid && ssid->passphrase ? ssid->passphrase : "",
MAC2STR(go_dev_addr),
persistent ? " [PERSISTENT]" : "");
+ wpas_p2p_cross_connect_setup(wpa_s);
}
if (persistent)
"driver to remain on channel (%u "
"MHz) for Action Frame TX",
wpa_s->pending_action_freq);
- }
+ } else
+ wpa_s->roc_waiting_drv_freq =
+ wpa_s->pending_action_freq;
}
return;
}
}
wpa_s->pending_action_without_roc = 0;
+ if (wpa_s->roc_waiting_drv_freq == freq) {
+ wpa_printf(MSG_DEBUG, "P2P: Already waiting for driver to get "
+ "to frequency %u MHz; continue waiting to send the "
+ "Action frame", freq);
+ return 0;
+ }
+
wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
"once the driver gets to the requested channel");
if (wait_time > wpa_s->max_remain_on_chan)
"Frame TX", freq);
return -1;
}
+ wpa_s->roc_waiting_drv_freq = freq;
return 0;
}
if (wpa_s->off_channel_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
}
}
if (res->wps_method == WPS_PBC)
wpas_wps_start_pbc(wpa_s, NULL /* res->peer_interface_addr */,
1);
- else
+ else {
+ u16 dev_pw_id = DEV_PW_DEFAULT;
+ if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
+ dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
- wpa_s->p2p_pin, 1);
+ wpa_s->p2p_pin, 1, dev_pw_id);
+ }
}
wpas_p2p_store_persistent_group(
wpa_s->parent, ssid,
wpa_s->parent->own_addr);
+ wpas_p2p_cross_connect_setup(wpa_s);
return;
}
if (wpa_s->off_channel_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
}
if (res->status) {
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
}
- if (group_wpa_s != wpa_s)
+ if (group_wpa_s != wpa_s) {
os_memcpy(group_wpa_s->p2p_pin, wpa_s->p2p_pin,
sizeof(group_wpa_s->p2p_pin));
+ group_wpa_s->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
wpa_s->pending_interface_name[0] = '\0';
group_wpa_s->p2p_in_provisioning = 1;
}
-void wpas_go_neg_req_rx(void *ctx, const u8 *src)
+void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
{
struct wpa_supplicant *wpa_s = ctx;
- wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR,
- MAC2STR(src));
+ wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
+ " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
}
wpa_s->pending_listen_freq = 0;
return -1;
}
+ wpa_s->roc_waiting_drv_freq = freq;
return 0;
}
if (wpa_s->off_channel_freq) {
wpa_drv_cancel_remain_on_channel(wpa_s);
wpa_s->off_channel_freq = 0;
+ wpa_s->roc_waiting_drv_freq = 0;
}
wpa_drv_probe_req_report(wpa_s, 0);
}
"available");
/* Status Code */
- wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
/* Response Data: empty */
WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
2);
wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
"available");
/* Status Code */
- wpabuf_put_u8(resp, P2P_SD_QUERY_DATA_NOT_AVAILABLE);
+ wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
/* Response Data: empty */
}
wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
pos, tlv_end - pos);
+
+ if (wpa_s->force_long_sd) {
+ wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+ "response");
+ wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+ wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+ goto done;
+ }
+
switch (srv_proto) {
case P2P_SERV_ALL_SERVICES:
wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
pos = tlv_end;
}
+done:
wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
wpabuf_free(resp);
wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
tlvs, tlvs_len);
- buf_len = 2 * tlvs_len + 1;
- buf = os_malloc(buf_len);
- if (buf) {
- wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
- wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_RESP MACSTR
- " %u %s",
- MAC2STR(sa), update_indic, buf);
- os_free(buf);
+ if (tlvs_len > 1500) {
+ /* TODO: better way for handling this */
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR
+ " %u <long response: %u bytes>",
+ MAC2STR(sa), update_indic,
+ (unsigned int) tlvs_len);
+ } else {
+ buf_len = 2 * tlvs_len + 1;
+ buf = os_malloc(buf_len);
+ if (buf) {
+ wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+ wpa_msg_ctrl(wpa_s, MSG_INFO,
+ P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+ MAC2STR(sa), update_indic, buf);
+ os_free(buf);
+ }
}
while (pos < end) {
if (!persistent_group) {
wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
" to join an active group", MAC2STR(sa));
+ if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
+ (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
+ == 0 ||
+ os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0)) {
+ wpa_printf(MSG_DEBUG, "P2P: Accept previously "
+ "authorized invitation");
+ goto accept_inv;
+ }
/*
* Do not accept the invitation automatically; notify user and
* request approval.
ETH_ALEN);
}
+accept_inv:
if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
wpa_s->assoc_freq) {
wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
if (s) {
wpas_p2p_group_add_persistent(
wpa_s, s, s->mode == WPAS_MODE_P2P_GO, 0);
+ } else {
+ wpas_p2p_join(wpa_s, bssid, go_dev_addr,
+ wpa_s->p2p_wps_method);
}
return;
}
"status=%d ", status);
}
+ if (wpa_s->pending_invite_ssid_id == -1)
+ return; /* Invitation to active group */
+
if (status != P2P_SC_SUCCESS) {
wpas_p2p_remove_pending_group_interface(wpa_s);
return;
p2p.concurrent_operations = !!(wpa_s->drv_flags &
WPA_DRIVER_FLAGS_P2P_CONCURRENT);
- p2p.max_peers = 100;
+ p2p.max_peers = wpa_s->max_stations ? wpa_s->max_stations : 100;
if (wpa_s->conf->p2p_ssid_postfix) {
p2p.ssid_postfix_len =
wpa_s->pending_action_tx = NULL;
eloop_cancel_timeout(wpas_send_action_cb, wpa_s, NULL);
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
wpa_s->p2p_long_listen = 0;
eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
wpas_p2p_remove_pending_group_interface(wpa_s);
/* Remove remaining P2P group interfaces */
wpa_s = global->ifaces;
+ if (wpa_s)
+ wpas_p2p_service_flush(wpa_s);
while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
wpa_s = wpa_s->next;
while (wpa_s) {
}
-static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
- const u8 *dev_addr, enum p2p_wps_method wps_method)
+static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
+ struct wpa_scan_results *scan_res)
{
struct wpa_bss *bss;
- wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
- MACSTR " dev " MACSTR ")",
- MAC2STR(iface_addr), MAC2STR(dev_addr));
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
- os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
- os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
- wpa_s->pending_join_wps_method = wps_method;
+ if (wpa_s->global->p2p_disabled)
+ return;
- /* Make sure we are not running find during connection establishment */
- wpas_p2p_stop_find(wpa_s);
+ wpa_printf(MSG_DEBUG, "P2P: Scan results received (%d BSS) for join",
+ scan_res ? (int) scan_res->num : -1);
- bss = wpa_bss_get_bssid(wpa_s, iface_addr);
+ if (scan_res)
+ wpas_p2p_scan_res_handler(wpa_s, scan_res);
+
+ bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr);
if (bss) {
u16 method;
wpa_printf(MSG_DEBUG, "P2P: Send Provision Discovery Request "
"prior to joining an existing group (GO " MACSTR
" freq=%u MHz)",
- MAC2STR(dev_addr), bss->freq);
+ MAC2STR(wpa_s->pending_join_dev_addr), bss->freq);
wpa_s->pending_pd_before_join = 1;
- switch (wps_method) {
+ switch (wpa_s->pending_join_wps_method) {
case WPS_PIN_LABEL:
case WPS_PIN_DISPLAY:
method = WPS_CONFIG_KEYPAD;
break;
}
- if (p2p_prov_disc_req(wpa_s->global->p2p, dev_addr, method, 1)
+ if (p2p_prov_disc_req(wpa_s->global->p2p,
+ wpa_s->pending_join_dev_addr, method, 1)
< 0) {
wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
"Discovery Request before joining an "
* Actual join operation will be started from the Action frame
* TX status callback.
*/
- return 0;
+ return;
}
wpa_printf(MSG_DEBUG, "P2P: Target BSS/GO not yet in BSS table - "
start:
/* Start join operation immediately */
- return wpas_p2p_join_start(wpa_s);
+ wpas_p2p_join_start(wpa_s);
+}
+
+
+static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ int ret;
+ struct wpa_driver_scan_params params;
+ struct wpabuf *wps_ie, *ies;
+
+ os_memset(¶ms, 0, sizeof(params));
+
+ /* P2P Wildcard SSID */
+ params.num_ssids = 1;
+ params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+ params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+
+ wpa_s->wps->dev.p2p = 1;
+ wps_ie = wps_build_probe_req_ie(0, &wpa_s->wps->dev, wpa_s->wps->uuid,
+ WPS_REQ_ENROLLEE);
+ if (wps_ie == NULL) {
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+
+ ies = wpabuf_alloc(wpabuf_len(wps_ie) + 100);
+ if (ies == NULL) {
+ wpabuf_free(wps_ie);
+ wpas_p2p_scan_res_join(wpa_s, NULL);
+ return;
+ }
+ wpabuf_put_buf(ies, wps_ie);
+ wpabuf_free(wps_ie);
+
+ p2p_scan_ie(wpa_s->global->p2p, ies);
+
+ params.extra_ies = wpabuf_head(ies);
+ params.extra_ies_len = wpabuf_len(ies);
+
+ /*
+ * Run a scan to update BSS table and start Provision Discovery once
+ * the new scan results become available.
+ */
+ wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+ ret = ieee80211_sta_req_scan(wpa_s, ¶ms);
+ else
+ ret = wpa_drv_scan(wpa_s, ¶ms);
+
+ wpabuf_free(ies);
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "P2P: Failed to start scan for join - "
+ "try again later");
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
+ eloop_register_timeout(1, 0, wpas_p2p_join_scan, wpa_s, NULL);
+ }
+}
+
+
+static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
+ const u8 *dev_addr, enum p2p_wps_method wps_method)
+{
+ wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
+ MACSTR " dev " MACSTR ")",
+ MAC2STR(iface_addr), MAC2STR(dev_addr));
+
+ os_memcpy(wpa_s->pending_join_iface_addr, iface_addr, ETH_ALEN);
+ os_memcpy(wpa_s->pending_join_dev_addr, dev_addr, ETH_ALEN);
+ wpa_s->pending_join_wps_method = wps_method;
+
+ /* Make sure we are not running find during connection establishment */
+ wpas_p2p_stop_find(wpa_s);
+
+ wpas_p2p_join_scan(wpa_s, NULL);
+ return 0;
}
group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
if (group == NULL)
return -1;
- if (group != wpa_s)
+ if (group != wpa_s) {
os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
sizeof(group->p2p_pin));
+ group->p2p_wps_method = wpa_s->p2p_wps_method;
+ }
group->p2p_in_provisioning = 1;
if (!auth)
wpa_s->p2p_long_listen = 0;
+ wpa_s->p2p_wps_method = wps_method;
+
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
else if (wps_method == WPS_PIN_DISPLAY) {
wpa_s->p2p_pin[0] = '\0';
if (join) {
- u8 iface_addr[ETH_ALEN];
+ u8 iface_addr[ETH_ALEN], dev_addr[ETH_ALEN];
+ if (auth) {
+ wpa_printf(MSG_DEBUG, "P2P: Authorize invitation to "
+ "connect a running group from " MACSTR,
+ MAC2STR(peer_addr));
+ os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN);
+ return ret;
+ }
+ os_memcpy(dev_addr, peer_addr, ETH_ALEN);
if (p2p_get_interface_addr(wpa_s->global->p2p, peer_addr,
- iface_addr) < 0)
+ iface_addr) < 0) {
os_memcpy(iface_addr, peer_addr, ETH_ALEN);
- if (wpas_p2p_join(wpa_s, iface_addr, peer_addr, wps_method) <
+ p2p_get_dev_addr(wpa_s->global->p2p, peer_addr,
+ dev_addr);
+ }
+ if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method) <
0)
return -1;
return ret;
void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
unsigned int freq, unsigned int duration)
{
+ wpa_s->roc_waiting_drv_freq = 0;
wpa_s->off_channel_freq = freq;
wpas_send_action_cb(wpa_s, NULL);
if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
{
wpa_s->p2p_long_listen = 0;
+ eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
p2p_stop_find(wpa_s->global->p2p);
}
-int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, const u8 *bssid,
+int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
u8 *buf, size_t len, int p2p_group)
{
+ struct wpabuf *p2p_ie;
+ int ret;
+
if (wpa_s->global->p2p_disabled)
return -1;
if (wpa_s->global->p2p == NULL)
return -1;
+ if (bss == NULL)
+ return -1;
+
+ p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+ ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
+ p2p_group, p2p_ie);
+ wpabuf_free(p2p_ie);
- return p2p_assoc_req_ie(wpa_s->global->p2p, bssid, buf, len,
- p2p_group);
+ return ret;
}
role = P2P_INVITE_ROLE_ACTIVE_GO;
bssid = wpa_s->own_addr;
if (go_dev_addr == NULL)
- go_dev_addr = wpa_s->own_addr;
+ go_dev_addr = wpa_s->parent->own_addr;
} else {
role = P2P_INVITE_ROLE_CLIENT;
if (wpa_s->wpa_state < WPA_ASSOCIATED) {
wpa_s->pending_invite_ssid_id = -1;
return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
- ssid->ssid, ssid->ssid_len, 0, go_dev_addr, 0);
+ ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq,
+ go_dev_addr, 0);
}
0);
}
}
+
+
+int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
+ int duration)
+{
+ if (!wpa_s->ap_iface)
+ return -1;
+ return hostapd_p2p_set_noa(wpa_s->ap_iface->bss[0], count, start,
+ duration);
+}
+
+
+int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
+{
+ if (wpa_s->global->p2p_disabled)
+ return -1;
+ if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
+ return -1;
+
+ wpa_s->global->cross_connection = enabled;
+ p2p_set_cross_connect(wpa_s->global->p2p, enabled);
+
+ if (!enabled) {
+ struct wpa_supplicant *iface;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+ {
+ if (iface->cross_connect_enabled == 0)
+ continue;
+
+ iface->cross_connect_enabled = 0;
+ iface->cross_connect_in_use = 0;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+ }
+
+ return 0;
+}
+
+
+static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ if (!uplink->global->cross_connection)
+ return;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (iface->ap_iface == NULL)
+ continue;
+ if (iface->cross_connect_in_use)
+ continue;
+
+ iface->cross_connect_in_use = 1;
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ }
+}
+
+
+static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink)
+{
+ struct wpa_supplicant *iface;
+
+ for (iface = uplink->global->ifaces; iface; iface = iface->next) {
+ if (!iface->cross_connect_enabled)
+ continue;
+ if (os_strcmp(uplink->ifname, iface->cross_connect_uplink) !=
+ 0)
+ continue;
+ if (!iface->cross_connect_in_use)
+ continue;
+
+ wpa_msg(iface->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
+ iface->ifname, iface->cross_connect_uplink);
+ iface->cross_connect_in_use = 0;
+ }
+}
+
+
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->ap_iface || wpa_s->current_ssid == NULL ||
+ wpa_s->current_ssid->mode != WPAS_MODE_INFRA ||
+ wpa_s->cross_connect_disallowed)
+ wpas_p2p_disable_cross_connect(wpa_s);
+ else
+ wpas_p2p_enable_cross_connect(wpa_s);
+}
+
+
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
+{
+ wpas_p2p_disable_cross_connect(wpa_s);
+}
+
+
+static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_supplicant *iface;
+
+ if (!wpa_s->global->cross_connection)
+ return;
+
+ for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+ if (iface == wpa_s)
+ continue;
+ if (iface->drv_flags &
+ WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
+ continue;
+ if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+ continue;
+
+ wpa_s->cross_connect_enabled = 1;
+ os_strlcpy(wpa_s->cross_connect_uplink, iface->ifname,
+ sizeof(wpa_s->cross_connect_uplink));
+ wpa_printf(MSG_DEBUG, "P2P: Enable cross connection from "
+ "%s to %s whenever uplink is available",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+
+ if (iface->ap_iface || iface->current_ssid == NULL ||
+ iface->current_ssid->mode != WPAS_MODE_INFRA ||
+ iface->cross_connect_disallowed ||
+ iface->wpa_state != WPA_COMPLETED)
+ break;
+
+ wpa_s->cross_connect_in_use = 1;
+ wpa_msg(wpa_s->parent, MSG_INFO,
+ P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
+ wpa_s->ifname, wpa_s->cross_connect_uplink);
+ break;
+ }
+}