#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
+/**
+ * Defines time interval in seconds when a GO needs to evacuate a frequency that
+ * it is currently using, but is no longer valid for P2P use cases.
+ */
+#define P2P_GO_FREQ_CHANGE_TIME 5
+
#ifndef P2P_MAX_CLIENT_IDLE
/*
* How many seconds to try to reconnect to the GO when connection in P2P client
#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-"
+/*
+ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
+ * was not possible.
+ */
+#define P2P_RECONSIDER_GO_MOVE_DELAY 30
+
enum p2p_group_removal_reason {
P2P_GROUP_REMOVAL_UNKNOWN,
P2P_GROUP_REMOVAL_SILENT,
P2P_GROUP_REMOVAL_UNAVAILABLE,
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
P2P_GROUP_REMOVAL_PSK_FAILURE,
- P2P_GROUP_REMOVAL_FREQ_CONFLICT
+ P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
};
static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type type);
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+ int already_deleted);
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num);
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
+static void
+wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs, unsigned int num,
+ enum wpas_p2p_channel_update_trig trig);
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
/*
*/
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
- p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+ p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
go_wpa_s, persistent_go);
wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
"timeout");
wpa_s->p2p_in_provisioning = 0;
+ wpas_p2p_group_formation_failed(wpa_s, 1);
}
wpa_s->p2p_in_invitation = 0;
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
/*
* Make sure wait for the first client does not remain active after the
else
wpa_drv_deinit_p2p_cli(wpa_s);
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
return 0;
}
static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
- int success)
+ int success, int already_deleted)
{
struct wpa_ssid *ssid;
int client;
if (!success) {
wpa_msg_global(wpa_s->parent, MSG_INFO,
P2P_EVENT_GROUP_FORMATION_FAILURE);
+ if (already_deleted)
+ return;
wpas_p2p_group_delete(wpa_s,
P2P_GROUP_REMOVAL_FORMATION_FAILED);
return;
wpa_s->show_group_started = 0;
wpa_s->p2p_go_group_formation_completed = 0;
wpa_s->group_formation_reported = 0;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
d->num_sec_device_types = s->num_sec_device_types;
d->p2p_group_idle = s->p2p_group_idle;
+ d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
d->p2p_intra_bss = s->p2p_intra_bss;
d->persistent_reconnect = s->persistent_reconnect;
d->max_num_sta = s->max_num_sta;
d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
}
+ d->p2p_cli_probe = s->p2p_cli_probe;
}
{
struct wpa_supplicant *wpa_s = eloop_ctx;
wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
- wpas_p2p_group_formation_failed(wpa_s);
+ wpas_p2p_group_formation_failed(wpa_s, 0);
}
-void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+ int already_deleted)
{
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
if (wpa_s->global->p2p)
p2p_group_formation_failed(wpa_s->global->p2p);
- wpas_group_formation_completed(wpa_s, 0);
+ wpas_group_formation_completed(wpa_s, 0, already_deleted);
}
return;
}
+ if (!res->role_go) {
+ /* Inform driver of the operating channel of GO. */
+ wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
+ }
+
if (wpa_s->p2p_go_ht40)
res->ht40 = 1;
if (wpa_s->p2p_go_vht)
wpas_p2p_remove_pending_group_interface(wpa_s);
eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
wpa_s, NULL);
- wpas_p2p_group_formation_failed(wpa_s);
+ wpas_p2p_group_formation_failed(wpa_s, 1);
return;
}
if (group_wpa_s != wpa_s) {
}
-static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
+ u8 go_intent)
{
struct wpa_supplicant *wpa_s = ctx;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
- " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
+ " dev_passwd_id=%u go_intent=%u", MAC2STR(src),
+ dev_passwd_id, go_intent);
- wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+ wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
}
wpa_s->roc_waiting_drv_freq = 0;
}
wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
- wpa_drv_probe_req_report(wpa_s, 0);
+
+ /*
+ * Don't cancel Probe Request RX reporting for a connected P2P Client
+ * handling Probe Request frames.
+ */
+ if (!wpa_s->p2p_cli_probe)
+ wpa_drv_probe_req_report(wpa_s, 0);
+
wpas_p2p_listen_work_done(wpa_s);
}
-static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
+ unsigned int freq)
{
struct wpa_supplicant *wpa_s = ctx;
- return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1);
+ return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
+ freq);
}
}
-static int freq_included(const struct p2p_channels *channels, unsigned int freq)
+static int freq_included(struct wpa_supplicant *wpa_s,
+ const struct p2p_channels *channels,
+ unsigned int freq)
+{
+ if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
+ wpas_p2p_go_is_peer_freq(wpa_s, freq))
+ return 1;
+ return 0;
+}
+
+
+static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
{
- if (channels == NULL)
- return 1; /* Assume no restrictions */
- return p2p_channels_includes_freq(channels, freq);
+ unsigned int num = P2P_MAX_CHANNELS;
+ int *common_freqs;
+ int ret;
+ p2p_go_dump_common_freqs(wpa_s);
+ common_freqs = os_calloc(num, sizeof(int));
+ if (!common_freqs)
+ return;
+
+ ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
+ if (ret < 0) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Failed to get group common freqs");
+ os_free(common_freqs);
+ return;
+ }
+
+ os_free(wpa_s->p2p_group_common_freqs);
+ wpa_s->p2p_group_common_freqs = common_freqs;
+ wpa_s->p2p_group_common_freqs_num = num;
+ p2p_go_dump_common_freqs(wpa_s);
+}
+
+
+/*
+ * Check if the given frequency is one of the possible operating frequencies
+ * set after the completion of the GO Negotiation.
+ */
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+ unsigned int i;
+
+ p2p_go_dump_common_freqs(wpa_s);
+
+ /* assume no restrictions */
+ if (!wpa_s->p2p_group_common_freqs_num)
+ return 1;
+
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ if (wpa_s->p2p_group_common_freqs[i] == freq)
+ return 1;
+ }
+ return 0;
}
"running a GO but we are capable of MCC, "
"figure out the best channel to use");
*force_freq = 0;
- } else if (!freq_included(channels, *force_freq)) {
+ } else if (!freq_included(wpa_s, channels, *force_freq)) {
/* We are the GO, and *force_freq is not in the
* intersection */
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
os_sleep(0, 50000);
if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
- freq_included(channels, neg_freq))
+ freq_included(wpa_s, channels, neg_freq))
freq = neg_freq;
else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
- freq_included(channels, peer_oper_freq))
+ freq_included(wpa_s, channels, peer_oper_freq))
freq = peer_oper_freq;
else
freq = 0;
#endif
{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
+ { HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 },
{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
if (o->mode != HOSTAPD_MODE_IEEE80211A ||
- o->bw == BW20 || ch != channel)
+ (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
+ ch != channel)
continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
if (ret == ALLOWED)
{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
struct wpa_ssid *ssid = wpa_s->current_ssid;
- if (ssid == NULL)
- continue;
- if (ssid->mode != WPAS_MODE_INFRA)
- continue;
- if (wpa_s->wpa_state != WPA_COMPLETED &&
- wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+ if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
continue;
if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
return wpa_s;
iface.confname = wpa_s->confname;
iface.ctrl_interface = wpa_s->conf->ctrl_interface;
}
- iface.conf_p2p_dev = NULL;
p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
if (!p2pdev_wpa_s) {
static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
size_t ssid_len, u8 *go_dev_addr,
- u8 *ret_ssid, size_t *ret_ssid_len)
+ u8 *ret_ssid, size_t *ret_ssid_len,
+ u8 *intended_iface_addr)
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
os_memcpy(ret_ssid, s->ssid, s->ssid_len);
*ret_ssid_len = s->ssid_len;
os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+
+ if (s->mode != WPAS_MODE_P2P_GO) {
+ os_memset(intended_iface_addr, 0, ETH_ALEN);
+ } else if (wpas_p2p_create_iface(wpa_s)) {
+ if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
+ return 0;
+
+ os_memcpy(intended_iface_addr,
+ wpa_s->pending_interface_addr, ETH_ALEN);
+ } else {
+ os_memcpy(intended_iface_addr, wpa_s->own_addr,
+ ETH_ALEN);
+ }
return 1;
}
struct wpa_ssid *s;
u8 bssid[ETH_ALEN];
+ /*
+ * group_iface will be set to 1 only if a dedicated interface for P2P
+ * role is required. First, we try to reuse an active GO. However,
+ * if it is not present, we will try to reactivate an existing
+ * persistent group and set group_iface to 1, so the caller will know
+ * that the pending interface should be used.
+ */
+ *group_iface = 0;
s = wpas_p2p_group_go_ssid(wpa_s, bssid);
if (!s) {
s = wpas_p2p_get_persistent_go(wpa_s);
+ *group_iface = wpas_p2p_create_iface(wpa_s);
if (s)
os_memcpy(bssid, s->bssid, ETH_ALEN);
+ else
+ return 0;
}
- *group_iface = wpas_p2p_create_iface(wpa_s);
- if (!s)
- return 0;
-
os_memcpy(intended_addr, bssid, ETH_ALEN);
os_memcpy(ssid, s->ssid, s->ssid_len);
*ssid_len = s->ssid_len;
}
+static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
+ const u8 *feat_cap, size_t feat_cap_len)
+{
+ static const char pref[] = " feature_cap=";
+ int ret;
+
+ buf[0] = '\0';
+
+ /*
+ * We expect a feature capability to contain at least one byte to be
+ * reported. The string buffer provided by the caller function is
+ * expected to be big enough to contain all bytes of the attribute for
+ * known specifications. This function truncates the reported bytes if
+ * the feature capability data exceeds the string buffer size.
+ */
+ if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
+ return;
+
+ os_memcpy(buf, pref, sizeof(pref));
+ ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
+ buf_len - sizeof(pref) + 1,
+ feat_cap, feat_cap_len);
+
+ if (ret != (2 * (int) feat_cap_len))
+ wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
+}
+
+
static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
const u8 *adv_mac, const u8 *ses_mac,
const u8 *grp_mac, u32 adv_id, u32 ses_id,
u8 conncap, int passwd_id,
const u8 *persist_ssid,
size_t persist_ssid_size, int response_done,
- int prov_start, const char *session_info)
+ int prov_start, const char *session_info,
+ const u8 *feat_cap, size_t feat_cap_len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 mac[ETH_ALEN];
struct wpa_ssid *persistent_go, *stale, *s;
int save_config = 0;
struct wpa_supplicant *go_wpa_s;
+ char feat_cap_str[256];
if (!dev)
return;
if (!grp_mac)
grp_mac = mac;
+ wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
+ feat_cap, feat_cap_len);
+
if (prov_start) {
if (session_info == NULL) {
wpa_msg_global(wpa_s, MSG_INFO,
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d",
+ " dev_passwd_id=%d%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id);
+ passwd_id, feat_cap_str);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_START MACSTR
" adv_id=%x conncap=%x"
" adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d info='%s'",
+ " dev_passwd_id=%d info='%s'%s",
MAC2STR(dev), adv_id, conncap,
MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, session_info);
+ passwd_id, session_info, feat_cap_str);
}
return;
}
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d"
" adv_id=%x adv_mac=" MACSTR
- " session=%x mac=" MACSTR,
+ " session=%x mac=" MACSTR "%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
- ses_id, MAC2STR(ses_mac));
+ ses_id, MAC2STR(ses_mac), feat_cap_str);
return;
}
/* Clean up stale persistent groups with this device */
s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
persist_ssid_size);
+
+ if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
+ os_memcmp(grp_mac, mac, ETH_ALEN) == 0) {
+ wpa_dbg(wpa_s, MSG_ERROR,
+ "P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
+ return;
+ }
+
for (;;) {
stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
if (!stale)
" status=%d"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " persist=%d",
+ " persist=%d%s",
MAC2STR(dev), status,
adv_id, MAC2STR(adv_mac),
- ses_id, MAC2STR(ses_mac), s->id);
+ ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
return;
}
if (conncap == P2PS_SETUP_GROUP_OWNER) {
- const char *go_ifname = NULL;
+ /*
+ * We need to copy the interface name. Simply saving a
+ * pointer isn't enough, since if we use pending_interface_name
+ * it will be overwritten when the group is added.
+ */
+ char go_ifname[100];
+
+ go_ifname[0] = '\0';
if (!go_wpa_s) {
wpa_s->global->pending_p2ps_group = 1;
- if (wpa_s->conf->p2p_no_group_iface)
- go_ifname = wpa_s->ifname;
+ if (!wpas_p2p_create_iface(wpa_s))
+ os_memcpy(go_ifname, wpa_s->ifname,
+ sizeof(go_ifname));
else if (wpa_s->pending_interface_name[0])
- go_ifname = wpa_s->pending_interface_name;
+ os_memcpy(go_ifname,
+ wpa_s->pending_interface_name,
+ sizeof(go_ifname));
- if (!go_ifname) {
+ if (!go_ifname[0]) {
wpas_p2ps_prov_complete(
wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
dev, adv_mac, ses_mac,
NULL, adv_id, ses_id, 0, 0,
- NULL, 0, 0, 0, NULL);
+ NULL, 0, 0, 0, NULL, NULL, 0);
return;
}
MAC2STR(dev));
}
} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
- go_ifname = go_wpa_s->ifname;
+ os_memcpy(go_ifname, go_wpa_s->ifname,
+ sizeof(go_ifname));
wpa_dbg(go_wpa_s, MSG_DEBUG,
"P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d go=%s",
+ " dev_passwd_id=%d go=%s%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, go_ifname);
+ passwd_id, go_ifname, feat_cap_str);
return;
}
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d join=" MACSTR,
+ " dev_passwd_id=%d join=" MACSTR "%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, MAC2STR(grp_mac));
+ passwd_id, MAC2STR(grp_mac), feat_cap_str);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
" status=%d conncap=%x"
" adv_id=%x adv_mac=" MACSTR
" session=%x mac=" MACSTR
- " dev_passwd_id=%d",
+ " dev_passwd_id=%d%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id);
+ passwd_id, feat_cap_str);
}
}
}
+static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
+ unsigned int *len,
+ unsigned int *freq_list)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT, len, freq_list);
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
p2p.p2ps_group_capability = p2ps_group_capability;
+ p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
wpa_s->pending_join_iface_addr);
}
if (bss) {
+ u8 dev_addr[ETH_ALEN];
+
freq = bss->freq;
wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
"from BSS table: %d MHz (SSID %s)", freq,
wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+ dev_addr) == 0 &&
+ os_memcmp(wpa_s->pending_join_dev_addr,
+ wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
+ os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
+ ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
+ MAC2STR(dev_addr),
+ MAC2STR(wpa_s->pending_join_dev_addr));
+ os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
+ ETH_ALEN);
+ }
}
if (freq > 0) {
u16 method;
static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
- int *force_freq, int *pref_freq, int go)
+ int *force_freq, int *pref_freq, int go,
+ unsigned int *pref_freq_list,
+ unsigned int *num_pref_freq)
{
struct wpa_used_freq_data *freqs;
int res, best_freq, num_unused;
- unsigned int freq_in_use = 0, num, i;
+ unsigned int freq_in_use = 0, num, i, max_pref_freq;
+
+ max_pref_freq = *num_pref_freq;
+ *num_pref_freq = 0;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
+ enum wpa_driver_if_type iface_type;
+
+ if (go)
+ iface_type = WPA_IF_P2P_GO;
+ else
+ iface_type = WPA_IF_P2P_CLIENT;
+
+ wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
+ best_freq, go);
+
+ res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
+ &max_pref_freq,
+ pref_freq_list);
+ if (!res && max_pref_freq > 0) {
+ *num_pref_freq = max_pref_freq;
+ i = 0;
+ while (wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]) &&
+ i < *num_pref_freq) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: preferred_freq_list[%d]=%d is disallowed",
+ i, pref_freq_list[i]);
+ i++;
+ }
+ if (i != *num_pref_freq) {
+ best_freq = pref_freq_list[i];
+ wpa_printf(MSG_DEBUG,
+ "P2P: Using preferred_freq_list[%d]=%d",
+ i, best_freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All driver preferred frequencies are disallowed for P2P use");
+ *num_pref_freq = 0;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: No preferred frequency list available");
+ }
+ }
+
/* We have a candidate frequency to use */
if (best_freq > 0) {
if (*pref_freq == 0 && num_unused > 0) {
enum wpa_driver_if_type iftype;
const u8 *if_addr;
struct wpa_ssid *ssid = NULL;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
return ret;
}
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- go_intent == 15);
+ go_intent == 15, pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s,
force_freq ? force_freq : pref_freq);
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
if (wpa_s->create_p2p_iface) {
}
if_addr = wpa_s->pending_interface_addr;
- } else
+ } else {
if_addr = wpa_s->own_addr;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+ }
if (auth) {
if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
}
-static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
- struct p2p_go_neg_results *params,
- const struct p2p_channels *channels)
+static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
+ const struct p2p_channels *channels,
+ int freq)
+{
+ if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
+ p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+ freq_included(wpa_s, channels, freq))
+ return 1;
+ return 0;
+}
+
+
+static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
+ struct p2p_go_neg_results *params,
+ const struct p2p_channels *channels)
{
unsigned int i, r;
/* first try some random selection of the social channels */
if (os_get_random((u8 *) &r, sizeof(r)) < 0)
- return -1;
+ return;
for (i = 0; i < 3; i++) {
params->freq = 2412 + ((r + i) % 3) * 25;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
- /* try all channels in reg. class 81 */
+ /* try all other channels in operating class 81 */
for (i = 0; i < 11; i++) {
params->freq = 2412 + i * 5;
- if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
- p2p_supported_freq(wpa_s->global->p2p, params->freq))
+
+ /* skip social channels; covered in the previous loop */
+ if (params->freq == 2412 ||
+ params->freq == 2437 ||
+ params->freq == 2462)
+ continue;
+
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
goto out;
}
for (i = 0; i < 4; i++) {
params->freq = 5180 + i * 20;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
for (i = 0; i < 4; i++) {
params->freq = 5745 + i * 20;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
/* try social channel class 180 channel 2 */
params->freq = 58320 + 1 * 2160;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
for (i = 0; i < 4; i++) {
params->freq = 58320 + i * 2160;
if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
- freq_included(channels, params->freq) &&
+ freq_included(wpa_s, channels, params->freq) &&
p2p_supported_freq(wpa_s->global->p2p, params->freq))
goto out;
}
+ params->freq = 0;
wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
- return -1;
+ return;
out:
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
params->freq);
- return 0;
}
const struct p2p_channels *channels)
{
struct wpa_used_freq_data *freqs;
- unsigned int pref_freq, cand_freq;
+ unsigned int cand;
unsigned int num, i;
os_memset(params, 0, sizeof(*params));
params->role_go = 1;
params->ht40 = ht40;
params->vht = vht;
+
+ if (wpa_s->p2p_group_common_freqs_num)
+ wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+ __func__);
+
+ freqs = os_calloc(wpa_s->num_multichan_concurrent,
+ sizeof(struct wpa_used_freq_data));
+ if (!freqs)
+ return -1;
+
+ num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+ wpa_s->num_multichan_concurrent);
+
+ /* try using the forced freq */
if (freq) {
- if (!freq_included(channels, freq)) {
- wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
- "accepted", freq);
- return -1;
+ if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Forced GO freq %d MHz not accepted",
+ freq);
+ goto fail;
}
- wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
- "frequency %d MHz", freq);
+
+ for (i = 0; i < num; i++) {
+ if (freqs[i].freq == freq) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: forced freq (%d MHz) is also shared",
+ freq);
+ params->freq = freq;
+ goto success;
+ }
+ }
+
+ if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
+ freq);
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "P2P: force GO freq (%d MHz) on a free channel",
+ freq);
params->freq = freq;
- } else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
- wpa_s->conf->p2p_oper_channel >= 1 &&
- wpa_s->conf->p2p_oper_channel <= 11 &&
- freq_included(channels,
- 2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
+ goto success;
+ }
+
+ /* consider using one of the shared frequencies */
+ if (num) {
+ cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use shared freq (%d MHz) for GO",
+ freq);
+ params->freq = cand;
+ goto success;
+ }
+
+ /* try using one of the shared freqs */
+ for (i = 0; i < num; i++) {
+ if (wpas_p2p_supported_freq_go(wpa_s, channels,
+ freqs[i].freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use shared freq (%d MHz) for GO",
+ freq);
+ params->freq = freqs[i].freq;
+ goto success;
+ }
+ }
+ }
+
+ if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Cannot force GO on any of the channels we are already using");
+ goto fail;
+ }
+
+ /* try using the setting from the configuration file */
+ if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+ wpa_s->conf->p2p_oper_channel >= 1 &&
+ wpa_s->conf->p2p_oper_channel <= 11 &&
+ wpas_p2p_supported_freq_go(
+ wpa_s, channels,
+ 2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
- } else if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
- wpa_s->conf->p2p_oper_reg_class == 116 ||
- wpa_s->conf->p2p_oper_reg_class == 117 ||
- wpa_s->conf->p2p_oper_reg_class == 124 ||
- wpa_s->conf->p2p_oper_reg_class == 126 ||
- wpa_s->conf->p2p_oper_reg_class == 127) &&
- freq_included(channels,
- 5000 + 5 * wpa_s->conf->p2p_oper_channel)) {
+ goto success;
+ }
+
+ if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
+ wpa_s->conf->p2p_oper_reg_class == 116 ||
+ wpa_s->conf->p2p_oper_reg_class == 117 ||
+ wpa_s->conf->p2p_oper_reg_class == 124 ||
+ wpa_s->conf->p2p_oper_reg_class == 125 ||
+ wpa_s->conf->p2p_oper_reg_class == 126 ||
+ wpa_s->conf->p2p_oper_reg_class == 127) &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ 5000 +
+ 5 * wpa_s->conf->p2p_oper_channel)) {
params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
"frequency %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_overall_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_overall_freq) &&
- freq_included(channels, wpa_s->best_overall_freq)) {
+ goto success;
+ }
+
+ /* Try using best channels */
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_overall_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_overall_freq)) {
params->freq = wpa_s->best_overall_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
"channel %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_24_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_24_freq) &&
- freq_included(channels, wpa_s->best_24_freq)) {
+ goto success;
+ }
+
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_24_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_24_freq)) {
params->freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
"channel %d MHz", params->freq);
- } else if (wpa_s->conf->p2p_oper_channel == 0 &&
- wpa_s->best_5_freq > 0 &&
- p2p_supported_freq_go(wpa_s->global->p2p,
- wpa_s->best_5_freq) &&
- freq_included(channels, wpa_s->best_5_freq)) {
+ goto success;
+ }
+
+ if (wpa_s->conf->p2p_oper_channel == 0 &&
+ wpa_s->best_5_freq > 0 &&
+ wpas_p2p_supported_freq_go(wpa_s, channels,
+ wpa_s->best_5_freq)) {
params->freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
"channel %d MHz", params->freq);
- } else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
- channels))) {
- params->freq = pref_freq;
+ goto success;
+ }
+
+ /* try using preferred channels */
+ cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
+ if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
"channels", params->freq);
- } else {
- /* no preference, select some channel */
- if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
- return -1;
+ goto success;
}
- freqs = os_calloc(wpa_s->num_multichan_concurrent,
- sizeof(struct wpa_used_freq_data));
- if (!freqs)
- return -1;
-
- num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
- wpa_s->num_multichan_concurrent);
-
- cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
-
- /* First try the best used frequency if possible */
- if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
- params->freq = cand_freq;
- } else if (!freq) {
- /* Try any of the used frequencies */
- for (i = 0; i < num; i++) {
- if (freq_included(channels, freqs[i].freq)) {
- wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
- freqs[i].freq);
- params->freq = freqs[i].freq;
- break;
+ /* Try using one of the group common freqs */
+ if (wpa_s->p2p_group_common_freqs) {
+ for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+ cand = wpa_s->p2p_group_common_freqs[i];
+ if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+ params->freq = cand;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Use freq %d MHz common with the peer",
+ params->freq);
+ goto success;
}
}
+ }
- if (i == num) {
- if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
- os_free(freqs);
- return -1;
- } else {
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
- }
- }
- } else {
- for (i = 0; i < num; i++) {
- if (freqs[i].freq == freq)
- break;
- }
+ /* no preference, select some channel */
+ wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
- if (i == num) {
- if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
- if (freq)
- wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
- os_free(freqs);
- return -1;
- } else {
- wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
- }
- }
+ if (params->freq == 0) {
+ wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
+ goto fail;
}
+success:
os_free(freqs);
return 0;
+fail:
+ os_free(freqs);
+ return -1;
}
ssid = wpa_config_add_network(wpa_s->conf);
if (ssid == NULL)
return -1;
+ os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
wpa_config_set_network_defaults(ssid);
ssid->temporary = 1;
ssid->proto = WPA_PROTO_RSN;
go == (ssid->mode == WPAS_MODE_P2P_GO)) {
wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
"already running");
+ if (go == 0 &&
+ eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL)) {
+ /*
+ * This can happen if Invitation Response frame was lost
+ * and the peer (GO of a persistent group) tries to
+ * invite us again. Reschedule the timeout to avoid
+ * terminating the wait for the connection too early
+ * since we now know that the peer is still trying to
+ * invite us instead of having already started the GO.
+ */
+ wpa_printf(MSG_DEBUG,
+ "P2P: Reschedule group formation timeout since peer is still trying to invite us");
+ eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+ wpas_p2p_group_formation_timeout,
+ wpa_s->parent, NULL);
+ }
return 0;
}
} else {
freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
if (freq < 0 ||
- (freq > 0 && !freq_included(channels, freq)))
+ (freq > 0 && !freq_included(wpa_s, channels, freq)))
freq = 0;
}
} else if (ssid->mode == WPAS_MODE_INFRA) {
freq = neg_freq;
- if (freq <= 0 || !freq_included(channels, freq)) {
+ if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
struct os_reltime now;
struct wpa_bss *bss =
wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
os_get_reltime(&now);
if (bss &&
!os_reltime_expired(&now, &bss->last_update, 5) &&
- freq_included(channels, bss->freq))
+ freq_included(wpa_s, channels, bss->freq))
freq = bss->freq;
else
freq = 0;
}
if (wpa_s->global->p2p)
p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
- wpas_group_formation_completed(wpa_s, 1);
+ wpas_group_formation_completed(wpa_s, 1, 0);
}
int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
const u8 *dst, const u8 *bssid,
- const u8 *ie, size_t ie_len, int ssi_signal)
+ const u8 *ie, size_t ie_len,
+ unsigned int rx_freq, int ssi_signal)
{
if (wpa_s->global->p2p_disabled)
return 0;
return 0;
switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
- ie, ie_len)) {
+ ie, ie_len, rx_freq)) {
case P2P_PREQ_NOT_P2P:
wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
ssi_signal);
int force_freq = 0;
int res;
int no_pref_freq_given = pref_freq == 0;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->global->p2p_invite_group = NULL;
if (peer_addr)
}
wpa_s->pending_invite_ssid_id = ssid->id;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_GO);
+ role == P2P_INVITE_ROLE_GO,
+ pref_freq_list, &size);
if (res)
return res;
int persistent;
int freq = 0, force_freq = 0, pref_freq = 0;
int res;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_ACTIVE_GO);
+ role == P2P_INVITE_ROLE_ACTIVE_GO,
+ pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
"session overlap");
if (wpa_s != wpa_s->parent)
wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
- wpas_p2p_group_formation_failed(wpa_s);
+ wpas_p2p_group_formation_failed(wpa_s, 0);
return 1;
}
}
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+ enum wpas_p2p_channel_update_trig trig)
{
struct p2p_channels chan, cli_chan;
- struct wpa_supplicant *ifs;
+ struct wpa_used_freq_data *freqs = NULL;
+ unsigned int num = wpa_s->num_multichan_concurrent;
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
return;
+ freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+ if (!freqs)
+ return;
+
+ num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
os_memset(&chan, 0, sizeof(chan));
os_memset(&cli_chan, 0, sizeof(cli_chan));
if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
- for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
- int freq;
- if (!ifs->current_ssid ||
- !ifs->current_ssid->p2p_group ||
- (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
- ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
- continue;
- freq = ifs->current_ssid->frequency;
- if (freq_included(&chan, freq)) {
- wpa_dbg(ifs, MSG_DEBUG,
- "P2P GO operating frequency %d MHz in valid range",
- freq);
- continue;
- }
+ wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
- wpa_dbg(ifs, MSG_DEBUG,
- "P2P GO operating in invalid frequency %d MHz", freq);
- /* TODO: Consider using CSA or removing the group within
- * wpa_supplicant */
- wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
- }
+ /*
+ * The used frequencies map changed, so it is possible that a GO is
+ * using a channel that is no longer valid for P2P use. It is also
+ * possible that due to policy consideration, it would be preferable to
+ * move it to a frequency already used by other station interfaces.
+ */
+ wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
+
+ os_free(freqs);
}
eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
if (wpa_s->p2p_in_provisioning) {
- wpas_group_formation_completed(wpa_s, 0);
+ wpas_group_formation_completed(wpa_s, 0, 0);
break;
}
wpas_p2p_group_delete(wpa_s,
wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
wpa_s->ifname);
found = 1;
- wpas_p2p_group_formation_failed(wpa_s);
+ wpas_p2p_group_formation_failed(wpa_s, 0);
}
}
*/
if (wpa_s->global->p2p)
p2p_wps_success_cb(wpa_s->global->p2p, addr);
- wpas_group_formation_completed(wpa_s, 1);
+ wpas_group_formation_completed(wpa_s, 1, 0);
}
}
if (!wpa_s->p2p_go_group_formation_completed) {
if (wpa_s->global->p2p_group_formation)
group = wpa_s->global->p2p_group_formation;
- wpa_s = wpa_s->parent;
+ wpa_s = wpa_s->global->p2p_init_wpa_s;
offchannel_send_action_done(wpa_s);
if (group_added)
ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
u8 curr_chan, cand, chan;
unsigned int i;
+ /*
+ * If possible, optimize the Listen channel to be a channel that is
+ * already used by one of the other interfaces.
+ */
+ if (!wpa_s->conf->p2p_optimize_listen_chan)
+ return;
+
+ if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+ return;
+
curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
for (i = 0, cand = 0; i < num; i++) {
ieee80211_freq_to_chan(freqs[i].freq, &chan);
}
-void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
{
- struct wpa_used_freq_data *freqs;
- unsigned int num = wpa_s->num_multichan_concurrent;
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+ return -1;
+ }
- if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ /* TODO: Add CSA support */
+ wpa_dbg(wpa_s, MSG_DEBUG, "Moving GO with CSA is not implemented");
+ return -1;
+}
+
+
+static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
+{
+ struct p2p_go_neg_results params;
+ struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+ wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
+ current_ssid->frequency);
+
+ /* Stop the AP functionality */
+ /* TODO: Should do this in a way that does not indicated to possible
+ * P2P Clients in the group that the group is terminated. */
+ wpa_supplicant_ap_deinit(wpa_s);
+
+ /* Reselect the GO frequency */
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) {
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
return;
+ }
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
+ params.freq);
- /*
- * If possible, optimize the Listen channel to be a channel that is
- * already used by one of the other interfaces.
- */
- if (!wpa_s->conf->p2p_optimize_listen_chan)
+ if (params.freq &&
+ !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: Selected freq (%u MHz) is not valid for P2P",
+ params.freq);
+ wpas_p2p_group_delete(wpa_s,
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
return;
+ }
- if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+ /* Update the frequency */
+ current_ssid->frequency = params.freq;
+ wpa_s->connect_without_scan = current_ssid;
+ wpa_s->reassociate = 1;
+ wpa_s->disconnected = 0;
+ wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ if (!wpa_s->ap_iface || !wpa_s->current_ssid)
return;
+ wpas_p2p_go_update_common_freqs(wpa_s);
+
+ /*
+ * First, try a channel switch flow. If it is not supported or fails,
+ * take down the GO and bring it up again.
+ */
+ if (wpas_p2p_move_go_csa(wpa_s) < 0)
+ wpas_p2p_move_go_no_csa(wpa_s);
+}
+
+
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_used_freq_data *freqs = NULL;
+ unsigned int num = wpa_s->num_multichan_concurrent;
+
freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
if (!freqs)
return;
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
- wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+ /* Previous attempt to move a GO was not possible -- try again. */
+ wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
+ WPAS_P2P_CHANNEL_UPDATE_ANY);
+
os_free(freqs);
}
+/*
+ * Consider moving a GO from its currently used frequency:
+ * 1. It is possible that due to regulatory consideration the frequency
+ * can no longer be used and there is a need to evacuate the GO.
+ * 2. It is possible that due to MCC considerations, it would be preferable
+ * to move the GO to a channel that is currently used by some other
+ * station interface.
+ *
+ * In case a frequency that became invalid is once again valid, cancel a
+ * previously initiated GO frequency change.
+ */
+static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num)
+{
+ unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
+ unsigned int timeout;
+ int freq;
+
+ wpas_p2p_go_update_common_freqs(wpa_s);
+
+ freq = wpa_s->current_ssid->frequency;
+ for (i = 0, invalid_freq = 0; i < num; i++) {
+ if (freqs[i].freq == freq) {
+ flags = freqs[i].flags;
+
+ /* The channel is invalid, must change it */
+ if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Freq=%d MHz no longer valid for GO",
+ freq);
+ invalid_freq = 1;
+ }
+ } else if (freqs[i].flags == 0) {
+ /* Freq is not used by any other station interface */
+ continue;
+ } else if (!p2p_supported_freq(wpa_s->global->p2p,
+ freqs[i].freq)) {
+ /* Freq is not valid for P2P use cases */
+ continue;
+ } else if (wpa_s->conf->p2p_go_freq_change_policy ==
+ P2P_GO_FREQ_MOVE_SCM) {
+ policy_move = 1;
+ } else if (wpa_s->conf->p2p_go_freq_change_policy ==
+ P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
+ wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+ policy_move = 1;
+ }
+ }
+
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
+ invalid_freq, policy_move, flags);
+
+ /*
+ * The channel is valid, or we are going to have a policy move, so
+ * cancel timeout.
+ */
+ if (!invalid_freq || policy_move) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Cancel a GO move from freq=%d MHz", freq);
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+
+ if (wpas_p2p_in_progress(wpa_s)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ return;
+ }
+ }
+
+ if (!invalid_freq && (!policy_move || flags != 0)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Not initiating a GO frequency change");
+ return;
+ }
+
+ if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
+ timeout = P2P_GO_FREQ_CHANGE_TIME;
+ else
+ timeout = 0;
+
+ wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
+ freq, timeout);
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+ eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
+}
+
+
+static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+ struct wpa_used_freq_data *freqs,
+ unsigned int num,
+ enum wpas_p2p_channel_update_trig trig)
+{
+ struct wpa_supplicant *ifs;
+
+ eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
+ NULL);
+
+ /*
+ * Travers all the radio interfaces, and for each GO interface, check
+ * if there is a need to move the GO from the frequency it is using,
+ * or in case the frequency is valid again, cancel the evacuation flow.
+ */
+ dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+ radio_list) {
+ if (ifs->current_ssid == NULL ||
+ ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+ continue;
+
+ /*
+ * The GO was just started or completed channel switch, no need
+ * to move it.
+ */
+ if (wpa_s == ifs &&
+ (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
+ trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: GO move - schedule re-consideration");
+ eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ continue;
+ }
+
+ wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+ }
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+ if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+ return;
+
+ wpas_p2p_update_channel_list(wpa_s,
+ WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
+}
+
+
void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
{
if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {