int go);
static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+ int *force_freq, int *pref_freq, int go,
+ unsigned int *pref_freq_list,
+ unsigned int *num_pref_freq);
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len);
static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
}
+static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
+{
+ return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
+ !wpa_s->current_ssid->disabled &&
+ wpa_s->current_ssid->p2p_group &&
+ wpa_s->current_ssid->p2p_persistent_group;
+}
+
+
+static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
+{
+ return p2p_is_active_persistent_group(wpa_s) &&
+ wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
+}
+
+
/* Find an interface for a P2P group where we are the GO */
static struct wpa_supplicant *
wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
{
struct wpa_supplicant *save = NULL;
- struct wpa_ssid *s;
if (!wpa_s)
return NULL;
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- for (s = wpa_s->conf->ssid; s; s = s->next) {
- if (s->disabled || !s->p2p_group ||
- s->mode != WPAS_MODE_P2P_GO)
- continue;
+ if (!p2p_is_active_persistent_go(wpa_s))
+ continue;
- /* Prefer a group with connected clients */
- if (p2p_get_group_num_members(wpa_s->p2p_group))
- return wpa_s;
- save = wpa_s;
- }
+ /* Prefer a group with connected clients */
+ if (p2p_get_group_num_members(wpa_s->p2p_group))
+ return wpa_s;
+ save = wpa_s;
}
/* No group with connected clients, so pick the one without (if any) */
}
-/* Find an active P2P group where we are the GO */
-static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
- u8 *bssid)
+static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
{
- struct wpa_ssid *s, *empty = NULL;
+ return p2p_is_active_persistent_group(wpa_s) &&
+ wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
- if (!wpa_s)
- return 0;
+/* Find an interface for a P2P group where we are the P2P Client */
+static struct wpa_supplicant *
+wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
+{
for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
- for (s = wpa_s->conf->ssid; s; s = s->next) {
- if (s->disabled || !s->p2p_group ||
- s->mode != WPAS_MODE_P2P_GO)
- continue;
-
- os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
- if (p2p_get_group_num_members(wpa_s->p2p_group))
- return s;
- empty = s;
- }
+ if (p2p_is_active_persistent_cli(wpa_s))
+ return wpa_s;
}
- return empty;
+ return NULL;
}
}
-static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
+ unsigned int *force_freq,
+ unsigned int *pref_freq)
{
- struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+ struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *s;
u8 conncap = P2PS_SETUP_NONE;
unsigned int owned_members = 0;
- unsigned int owner = 0;
- unsigned int client = 0;
- struct wpa_supplicant *go_wpa_s;
+ struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
struct wpa_ssid *persistent_go;
int p2p_no_group_iface;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+ if (force_freq)
+ *force_freq = 0;
+ if (pref_freq)
+ *pref_freq = 0;
+
+ size = P2P_MAX_PREF_CHANNELS;
+ if (force_freq && pref_freq &&
+ !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
+ (int *) pref_freq, 0, pref_freq_list, &size))
+ wpas_p2p_set_own_freq_preference(wpa_s,
+ *force_freq ? *force_freq :
+ *pref_freq);
+
/*
* For non-concurrent capable devices:
* If persistent_go, then no new.
* If client, then no GO.
*/
go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+ if (go_wpa_s)
+ owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
persistent_go = wpas_p2p_get_persistent_go(wpa_s);
p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+ cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
- wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
- go_wpa_s, persistent_go);
-
- for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
- tmp_wpa_s = tmp_wpa_s->next) {
- for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
- wpa_printf(MSG_DEBUG,
- "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
- tmp_wpa_s, s, s->disabled,
- s->p2p_group, s->mode);
- if (!s->disabled && s->p2p_group) {
- if (s->mode == WPAS_MODE_P2P_GO) {
- owned_members +=
- p2p_get_group_num_members(
- tmp_wpa_s->p2p_group);
- owner++;
- } else
- client++;
- }
- }
- }
+ wpa_printf(MSG_DEBUG,
+ "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
+ go_wpa_s, owned_members, cli_wpa_s, persistent_go);
/* If not concurrent, restrict our choices */
if (p2p_no_group_iface) {
wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
- if (client)
+ if (cli_wpa_s)
return P2PS_SETUP_NONE;
if (go_wpa_s) {
/* If a required role has been specified, handle it here */
if (role && role != P2PS_SETUP_NEW) {
switch (incoming) {
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+ case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+ /*
+ * Peer has an active GO, so if the role allows it and
+ * we do not have any active roles, become client.
+ */
+ if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
+ !cli_wpa_s)
+ return P2PS_SETUP_CLIENT;
+
+ /* fall through */
+
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
- case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
- case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
conncap = role;
goto grp_owner;
* Must be a complimentary role - cannot be a client to
* more than one peer.
*/
- if (incoming == role || client)
+ if (incoming == role || cli_wpa_s)
return P2PS_SETUP_NONE;
return P2PS_SETUP_CLIENT;
switch (incoming) {
case P2PS_SETUP_NONE:
case P2PS_SETUP_NEW:
- if (client)
+ if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else if (!owned_members)
conncap = P2PS_SETUP_NEW;
break;
case P2PS_SETUP_GROUP_OWNER:
- if (!client)
+ if (!cli_wpa_s)
conncap = P2PS_SETUP_CLIENT;
break;
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
- if (client)
+ if (cli_wpa_s)
conncap = P2PS_SETUP_GROUP_OWNER;
else {
u8 r;
(!incoming && (conncap & P2PS_SETUP_NEW))) {
if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
conncap &= ~P2PS_SETUP_GROUP_OWNER;
- wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
- owner, owned_members, conncap);
s = wpas_p2p_get_persistent_go(wpa_s);
-
- if (!s && !owner && p2p_no_group_iface) {
+ if (!s && !go_wpa_s && p2p_no_group_iface) {
p2p_set_intended_addr(wpa_s->global->p2p,
wpa_s->own_addr);
- } else if (!s && !owner) {
+ } else if (!s && !go_wpa_s) {
if (wpas_p2p_add_group_interface(wpa_s,
WPA_IF_P2P_GO) < 0) {
wpa_printf(MSG_ERROR,
};
+static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
+{
+ struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
+
+ wpa_printf(MSG_DEBUG,
+ "P2P: Free Action frame radio work @%p (freq=%u dst="
+ MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
+ wpa_s->p2p_send_action_work, awork->freq,
+ MAC2STR(awork->dst), MAC2STR(awork->src),
+ MAC2STR(awork->bssid), awork->wait_time);
+ wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
+ awork->buf, awork->len);
+ os_free(awork);
+ wpa_s->p2p_send_action_work->ctx = NULL;
+ radio_work_done(wpa_s->p2p_send_action_work);
+ wpa_s->p2p_send_action_work = NULL;
+}
+
+
static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
void *timeout_ctx)
{
return;
wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
- os_free(wpa_s->p2p_send_action_work->ctx);
- radio_work_done(wpa_s->p2p_send_action_work);
- wpa_s->p2p_send_action_work = NULL;
+ wpas_p2p_free_send_action_work(wpa_s);
}
{
if (wpa_s->p2p_send_action_work) {
struct send_action_work *awork;
+
awork = wpa_s->p2p_send_action_work->ctx;
+ wpa_printf(MSG_DEBUG,
+ "P2P: Clear Action TX work @%p (wait_time=%u)",
+ wpa_s->p2p_send_action_work, awork->wait_time);
if (awork->wait_time == 0) {
- os_free(awork);
- radio_work_done(wpa_s->p2p_send_action_work);
- wpa_s->p2p_send_action_work = NULL;
+ wpas_p2p_free_send_action_work(wpa_s);
} else {
/*
* In theory, this should not be needed, but number of
struct wpa_ssid *ssid;
int network_id = -1;
+ wpa_s->ap_configured_cb = NULL;
+ wpa_s->ap_configured_cb_ctx = NULL;
+ wpa_s->ap_configured_cb_data = NULL;
+ if (!wpa_s->go_params) {
+ wpa_printf(MSG_ERROR,
+ "P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
+ return;
+ }
+
p2p_go_save_group_common_freqs(wpa_s, params);
p2p_go_dump_common_freqs(wpa_s);
ssid->frequency = params->freq;
ssid->ht40 = params->ht40;
ssid->vht = params->vht;
+ ssid->max_oper_chwidth = params->max_oper_chwidth;
+ ssid->vht_center_freq2 = params->vht_center_freq2;
ssid->ssid = os_zalloc(params->ssid_len + 1);
if (ssid->ssid) {
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
*/
ssid->pairwise_cipher = WPA_CIPHER_GCMP;
ssid->group_cipher = WPA_CIPHER_GCMP;
+ /* P2P GO in 60 GHz is always a PCP (PBSS) */
+ ssid->pbss = 1;
}
if (os_strlen(params->passphrase) > 0) {
ssid->passphrase = os_strdup(params->passphrase);
res->ht40 = 1;
if (wpa_s->p2p_go_vht)
res->vht = 1;
+ res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
+ res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
if (s) {
int go = s->mode == WPAS_MODE_P2P_GO;
wpas_p2p_group_add_persistent(
- wpa_s, s, go, 0, op_freq, 0, 0, NULL,
+ wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL,
go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
1);
} else if (bssid) {
wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
status, MAC2STR(peer));
if (wpa_s->pending_invite_ssid_id == -1) {
+ struct wpa_supplicant *group_if =
+ wpa_s->global->p2p_invite_group;
+
if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
wpas_remove_persistent_client(wpa_s, peer);
- return; /* Invitation to active group */
+
+ /*
+ * Invitation to an active group. If this is successful and we
+ * are the GO, set the client wait to postpone some concurrent
+ * operations and to allow provisioning and connection to happen
+ * more quickly.
+ */
+ if (status == P2P_SC_SUCCESS &&
+ group_if && group_if->current_ssid &&
+ group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
+ os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (group_if->p2p_go_csa_on_inv) {
+ wpa_printf(MSG_DEBUG,
+ "Testing: force P2P GO CSA after invitation");
+ eloop_cancel_timeout(
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ eloop_register_timeout(
+ 0, 50000,
+ wpas_p2p_reconsider_moving_go,
+ wpa_s, NULL);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ }
+ return;
}
if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth,
channels,
ssid->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
u8 min_chan;
u8 max_chan;
u8 inc;
- enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
+ enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
};
static const struct p2p_oper_class_map op_class[] = {
* removing invalid channels.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80 },
+ { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160 },
{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
{ -1, 0, 0, 0, 0, BW20 }
};
}
+static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel)
+{
+ u8 center_channels[] = { 50, 114 };
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+ /*
+ * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+ * so the center channel is 14 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 14 &&
+ channel <= center_channels[i] + 14)
+ return center_channels[i];
+
+ return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ u8 center_chan;
+ int i, flags;
+ enum chan_allowed res, ret = ALLOWED;
+
+ center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+ /* VHT 160 MHz uses DFS channels in most countries. */
+
+ /* Check all the channels are available */
+ for (i = 0; i < 8; i++) {
+ int adj_chan = center_chan - 14 + i * 4;
+
+ res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+ if (res == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (res == NO_IR)
+ ret = NO_IR;
+
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+ return NOT_ALLOWED;
+ if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+ return NOT_ALLOWED;
+ if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+ return NOT_ALLOWED;
+ if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+ return NOT_ALLOWED;
+ if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+ return NOT_ALLOWED;
+ }
+
+ return ret;
+}
+
+
static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel, u8 bw)
res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
} else if (bw == BW80) {
res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+ } else if (bw == BW160) {
+ res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
}
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel)
+{
+ if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160))
+ return 0;
+ return wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+}
+
+
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
size_t buf_len)
{
static int wpas_get_go_info(void *ctx, u8 *intended_addr,
- u8 *ssid, size_t *ssid_len, int *group_iface)
+ u8 *ssid, size_t *ssid_len, int *group_iface,
+ unsigned int *freq)
{
struct wpa_supplicant *wpa_s = ctx;
+ struct wpa_supplicant *go;
struct wpa_ssid *s;
- u8 bssid[ETH_ALEN];
/*
* group_iface will be set to 1 only if a dedicated interface for P2P
* that the pending interface should be used.
*/
*group_iface = 0;
- s = wpas_p2p_group_go_ssid(wpa_s, bssid);
- if (!s) {
+
+ if (freq)
+ *freq = 0;
+
+ go = wpas_p2p_get_go_group(wpa_s);
+ if (!go) {
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);
+ os_memcpy(intended_addr, s->bssid, ETH_ALEN);
else
return 0;
+ } else {
+ s = go->current_ssid;
+ os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
+ if (freq)
+ *freq = go->assoc_freq;
}
- os_memcpy(intended_addr, bssid, ETH_ALEN);
os_memcpy(ssid, s->ssid, s->ssid_len);
*ssid_len = s->ssid_len;
const u8 *persist_ssid,
size_t persist_ssid_size, int response_done,
int prov_start, const char *session_info,
- const u8 *feat_cap, size_t feat_cap_len)
+ const u8 *feat_cap, size_t feat_cap_len,
+ unsigned int freq,
+ const u8 *group_ssid, size_t group_ssid_len)
{
struct wpa_supplicant *wpa_s = ctx;
u8 mac[ETH_ALEN];
- struct wpa_ssid *persistent_go, *stale, *s;
+ struct wpa_ssid *persistent_go, *stale, *s = NULL;
int save_config = 0;
struct wpa_supplicant *go_wpa_s;
char feat_cap_str[256];
}
/* Clean up stale persistent groups with this device */
- s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
- persist_ssid_size);
+ if (persist_ssid && persist_ssid_size)
+ s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+ persist_ssid_size);
if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
is_zero_ether_addr(grp_mac)) {
go_ifname[0] = '\0';
if (!go_wpa_s) {
wpa_s->global->pending_p2ps_group = 1;
+ wpa_s->global->pending_p2ps_group_freq = freq;
if (!wpas_p2p_create_iface(wpa_s))
os_memcpy(go_ifname, wpa_s->ifname,
wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
dev, adv_mac, ses_mac,
grp_mac, adv_id, ses_id, 0, 0,
- NULL, 0, 0, 0, NULL, NULL, 0);
+ NULL, 0, 0, 0, NULL, NULL, 0, 0,
+ NULL, 0);
return;
}
if (response_done && persistent_go) {
wpas_p2p_group_add_persistent(
wpa_s, persistent_go,
- 0, 0, 0, 0, 0, NULL,
+ 0, 0, freq, 0, 0, 0, 0, NULL,
persistent_go->mode ==
WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
0, 0);
} else if (response_done) {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
}
if (passwd_id == DEV_PW_P2PS_DEFAULT) {
}
if (conncap == P2PS_SETUP_CLIENT) {
+ char ssid_hex[32 * 2 + 1];
+
+ if (group_ssid)
+ wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+ group_ssid, group_ssid_len);
+ else
+ ssid_hex[0] = '\0';
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 join=" MACSTR "%s",
+ " dev_passwd_id=%d join=" MACSTR "%s%s%s",
MAC2STR(dev), status, conncap,
adv_id, MAC2STR(adv_mac),
ses_id, MAC2STR(ses_mac),
- passwd_id, MAC2STR(grp_mac), feat_cap_str);
+ passwd_id, MAC2STR(grp_mac), feat_cap_str,
+ group_ssid ? " group_ssid=" : "", ssid_hex);
} else {
wpa_msg_global(wpa_s, MSG_INFO,
P2P_EVENT_P2PS_PROVISION_DONE MACSTR
{
struct wpa_supplicant *wpa_s = ctx;
struct wpa_ssid *persistent_go;
+ unsigned int freq;
if (!wpa_s->global->pending_p2ps_group)
return 0;
+ freq = wpa_s->global->pending_p2ps_group_freq;
+ wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->global->pending_p2ps_group = 0;
if (wpas_p2p_get_go_group(wpa_s))
if (persistent_go) {
wpas_p2p_group_add_persistent(
- wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+ wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL,
persistent_go->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
} else {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
}
return 1;
wpa_s->p2p_persistent_group, 0, 0, 0,
wpa_s->p2p_go_intent,
wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth,
+ NULL, 0);
return;
}
bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
wpa_s->p2p_join_ssid,
wpa_s->p2p_join_ssid_len);
- }
- if (!bss) {
+ } else if (!bss) {
wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
bss = wpa_bss_get_bssid_latest(wpa_s,
start:
/* Start join operation immediately */
- wpas_p2p_join_start(wpa_s, 0, NULL, 0);
+ wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid,
+ wpa_s->p2p_join_ssid_len);
}
res.ssid_len = ssid_len;
os_memcpy(res.ssid, ssid, ssid_len);
} else {
- bss = wpa_bss_get_bssid_latest(wpa_s,
- wpa_s->pending_join_iface_addr);
+ if (ssid && ssid_len) {
+ bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
+ ssid, ssid_len);
+ } else {
+ bss = wpa_bss_get_bssid_latest(
+ wpa_s, wpa_s->pending_join_iface_addr);
+ }
if (bss) {
res.freq = bss->freq;
res.ssid_len = bss->ssid_len;
wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
bss->freq,
wpa_ssid_txt(bss->ssid, bss->ssid_len));
+ } else if (ssid && ssid_len) {
+ res.ssid_len = ssid_len;
+ os_memcpy(res.ssid, ssid, ssid_len);
+ wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)",
+ wpa_ssid_txt(ssid, ssid_len));
}
}
* initiating Group Owner negotiation
* @go_intent: GO Intent or -1 to use default
* @freq: Frequency for the group or 0 for auto-selection
+ * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
* @persistent_id: Persistent group credentials to use for forcing GO
* parameters or -1 to generate new values (SSID/passphrase)
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
* interoperability workaround when initiating group formation
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: Channel width supported by GO operating with VHT support
+ * (VHT_CHANWIDTH_*).
+ * @group_ssid: Specific Group SSID for join or %NULL if not set
+ * @group_ssid_len: Length of @group_ssid in octets
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
* failure, -2 on failure due to channel not currently available,
* -3 if forced channel is not supported
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
- int go_intent, int freq, int persistent_id, int pd,
- int ht40, int vht)
+ int go_intent, int freq, unsigned int vht_center_freq2,
+ int persistent_id, int pd, int ht40, int vht,
+ unsigned int vht_chwidth, const u8 *group_ssid,
+ size_t group_ssid_len)
{
int force_freq = 0, pref_freq = 0;
int ret = 0, res;
wpa_s->global->p2p_fail_on_wps_complete = 0;
wpa_s->global->pending_p2ps_group = 0;
+ wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->p2ps_method_config_any = 0;
if (go_intent < 0)
wpa_s->p2p_pd_before_go_neg = !!pd;
wpa_s->p2p_go_ht40 = !!ht40;
wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
+ wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
}
wpa_s->user_initiated_pd = 1;
if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
- auto_join, freq, NULL, 0) < 0)
+ auto_join, freq,
+ group_ssid, group_ssid_len) < 0)
return -1;
return ret;
}
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
- int freq, int ht40, int vht,
+ int freq, int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
const struct p2p_channels *channels)
{
struct wpa_used_freq_data *freqs;
params->role_go = 1;
params->ht40 = ht40;
params->vht = vht;
+ params->max_oper_chwidth = max_oper_chwidth;
+ params->vht_center_freq2 = vht_center_freq2;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @persistent_group: Whether to create a persistent group
* @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: channel bandwidth for GO operating with VHT support
* Returns: 0 on success, -1 on failure
*
* This function creates a new P2P group with the local end as the Group Owner,
* i.e., without using Group Owner Negotiation.
*/
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40, int vht)
+ int freq, int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth)
{
struct p2p_go_neg_results params;
if (freq < 0)
return -1;
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, NULL))
return -1;
if (params.freq &&
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
wpa_s->show_group_started = 1;
wpa_s->p2p_in_invitation = 1;
wpa_s->p2p_invite_go_freq = freq;
+ wpa_s->p2p_go_group_formation_completed = 0;
+ wpa_s->global->p2p_group_formation = wpa_s;
eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
NULL);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int force_freq, int neg_freq, int ht40,
- int vht, const struct p2p_channels *channels,
+ int force_freq, int neg_freq,
+ int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
+ const struct p2p_channels *channels,
int connection_timeout, int force_scan)
{
struct p2p_go_neg_results params;
return -1;
}
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels))
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, channels))
return -1;
params.role_go = 1;
eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
wpas_p2p_group_formation_timeout,
wpa_s->parent, NULL);
+ /* Complete group formation on successful data connection. */
+ wpa_s->p2p_go_group_formation_completed = 0;
} else if (ssid) {
/*
* Use a separate timeout for initial data connection to
u16 config_methods;
wpa_s->global->pending_p2ps_group = 0;
+ wpa_s->global->pending_p2ps_group_freq = 0;
wpa_s->p2p_fallback_to_go_neg = 0;
wpa_s->pending_pd_use = NORMAL_PD;
if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
p2ps_prov->conncap = p2ps_group_capability(
- wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+ wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
+ &p2ps_prov->force_freq, &p2ps_prov->pref_freq);
+
wpa_printf(MSG_DEBUG,
"P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
__func__, p2ps_prov->conncap,
if (!offchannel_pending_action_tx(wpa_s))
return;
- wpas_p2p_action_tx_clear(wpa_s);
+ if (wpa_s->p2p_send_action_work) {
+ wpas_p2p_free_send_action_work(wpa_s);
+ eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+ wpa_s, NULL);
+ offchannel_send_action_done(wpa_s);
+ }
wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
"operation request");
/* Invite to reinvoke a persistent group */
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int vht, int pref_freq)
+ int vht_center_freq2, int ht40, int vht, int max_chwidth,
+ int pref_freq)
{
enum p2p_invite_role role;
u8 *bssid = NULL;
wpa_s->p2p_persistent_go_freq = freq;
wpa_s->p2p_go_ht40 = !!ht40;
+ wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_GO;
if (peer_addr == NULL) {
pref_freq_list, &size);
if (res)
return res;
- p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
no_pref_freq_given && pref_freq > 0 &&
wpa_s->num_multichan_concurrent > 1 &&
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
wpa_s->p2p_go_vht = 0;
+ wpa_s->p2p_go_vht_center_freq2 = 0;
+ wpa_s->p2p_go_max_oper_chwidth = 0;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
return;
wpa_s->show_group_started = 0;
+ if (!wpa_s->p2p_go_group_formation_completed &&
+ wpa_s->global->p2p_group_formation == wpa_s) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "P2P: Marking group formation completed on client on data connection");
+ wpa_s->p2p_go_group_formation_completed = 1;
+ wpa_s->global->p2p_group_formation = NULL;
+ wpa_s->p2p_in_provisioning = 0;
+ wpa_s->p2p_in_invitation = 0;
+ }
os_memset(go_dev_addr, 0, ETH_ALEN);
if (ssid->bssid_set)
wpa_s->ifname);
found = 1;
wpas_p2p_group_formation_failed(wpa_s, 0);
+ break;
}
}
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth, NULL, 0);
return ret;
}
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
- params->go_freq, -1, 0, 1, 1);
+ params->go_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+ params->go_ssid_len ? params->go_ssid : NULL,
+ params->go_ssid_len);
}
"connection handover");
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+ NULL, 0);
}
"connection handover");
res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+ NULL, 0);
if (res)
return res;
* TODO: This function may not always work correctly. For example,
* when we have a running GO and a BSS on a DFS channel.
*/
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) {
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, 0, 0, NULL)) {
wpa_dbg(wpa_s, MSG_DEBUG,
"P2P CSA: Failed to select new frequency for GO");
return -1;
wpa_supplicant_ap_deinit(wpa_s);
/* Reselect the GO frequency */
- if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 0, NULL)) {
+ if (wpas_p2p_init_go_params(wpa_s, ¶ms, 0, 0, 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);