#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
};
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);
/*
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
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;
}
-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)
- return 1; /* Assume no restrictions */
- return p2p_channels_includes_freq(channels, 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)
+{
+ 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;
}
-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 == 125 ||
- 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;
}
} 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;
}
-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);
}
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) {