|
@@ -47,6 +47,12 @@
|
|
|
|
|
|
#define P2P_AUTO_PD_SCAN_ATTEMPTS 5
|
|
#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
|
|
#ifndef P2P_MAX_CLIENT_IDLE
|
|
/*
|
|
/*
|
|
* How many seconds to try to reconnect to the GO when connection in P2P client
|
|
* How many seconds to try to reconnect to the GO when connection in P2P client
|
|
@@ -94,7 +100,8 @@ enum p2p_group_removal_reason {
|
|
P2P_GROUP_REMOVAL_UNAVAILABLE,
|
|
P2P_GROUP_REMOVAL_UNAVAILABLE,
|
|
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
|
|
P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
|
|
P2P_GROUP_REMOVAL_PSK_FAILURE,
|
|
P2P_GROUP_REMOVAL_PSK_FAILURE,
|
|
- P2P_GROUP_REMOVAL_FREQ_CONFLICT
|
|
|
|
|
|
+ P2P_GROUP_REMOVAL_FREQ_CONFLICT,
|
|
|
|
+ P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
@@ -128,6 +135,7 @@ static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
|
|
enum wpa_driver_if_type type);
|
|
enum wpa_driver_if_type type);
|
|
static 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);
|
|
int already_deleted);
|
|
|
|
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
|
|
|
|
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -876,6 +884,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
|
|
}
|
|
}
|
|
|
|
|
|
wpa_s->p2p_in_invitation = 0;
|
|
wpa_s->p2p_in_invitation = 0;
|
|
|
|
+ eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Make sure wait for the first client does not remain active after the
|
|
* Make sure wait for the first client does not remain active after the
|
|
@@ -1866,6 +1875,7 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
|
|
d->num_sec_device_types = s->num_sec_device_types;
|
|
d->num_sec_device_types = s->num_sec_device_types;
|
|
|
|
|
|
d->p2p_group_idle = s->p2p_group_idle;
|
|
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->p2p_intra_bss = s->p2p_intra_bss;
|
|
d->persistent_reconnect = s->persistent_reconnect;
|
|
d->persistent_reconnect = s->persistent_reconnect;
|
|
d->max_num_sta = s->max_num_sta;
|
|
d->max_num_sta = s->max_num_sta;
|
|
@@ -2596,6 +2606,28 @@ static int freq_included(const struct p2p_channels *channels, unsigned int freq)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * 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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Pick the best frequency to use from all the currently used frequencies.
|
|
* Pick the best frequency to use from all the currently used frequencies.
|
|
*/
|
|
*/
|
|
@@ -8249,6 +8281,181 @@ static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
|
|
|
|
+{
|
|
|
|
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
|
|
|
|
+ wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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 (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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* 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;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * 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;
|
|
|
|
+
|
|
|
|
+ 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 (!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)
|
|
|
|
+{
|
|
|
|
+ struct wpa_supplicant *ifs;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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;
|
|
|
|
+
|
|
|
|
+ wpas_p2p_consider_moving_one_go(ifs, freqs, num);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
|
|
void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
|
|
{
|
|
{
|
|
struct wpa_used_freq_data *freqs;
|
|
struct wpa_used_freq_data *freqs;
|
|
@@ -8274,6 +8481,16 @@ void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
|
|
num = get_shared_radio_freqs_data(wpa_s, freqs, num);
|
|
|
|
|
|
wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
|
|
wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 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 the group to a frequency already used by other station
|
|
|
|
+ * interfaces.
|
|
|
|
+ */
|
|
|
|
+ wpas_p2p_consider_moving_gos(wpa_s, freqs, num);
|
|
|
|
+
|
|
os_free(freqs);
|
|
os_free(freqs);
|
|
}
|
|
}
|
|
|
|
|