diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 1875ca4d4..104f77ba4 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -183,6 +183,14 @@ void p2p_set_state(struct p2p_data *p2p, int new_state) p2p_dbg(p2p, "State %s -> %s", p2p_state_txt(p2p->state), p2p_state_txt(new_state)); p2p->state = new_state; + + if (new_state == P2P_IDLE && p2p->pending_channel) { + p2p_dbg(p2p, "Apply change in listen channel"); + p2p->cfg->reg_class = p2p->pending_reg_class; + p2p->cfg->channel = p2p->pending_channel; + p2p->pending_reg_class = 0; + p2p->pending_channel = 0; + } } @@ -3991,20 +3999,43 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled) } -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel) +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced) { if (p2p_channel_to_freq(reg_class, channel) < 0) return -1; p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u", reg_class, channel); - p2p->cfg->reg_class = reg_class; - p2p->cfg->channel = channel; + + /* + * Listen channel was set in configuration or set by control interface; + * cannot override it. + */ + if (p2p->cfg->channel_forced && forced == 0) + return -1; + + if (p2p->state == P2P_IDLE) { + p2p->cfg->reg_class = reg_class; + p2p->cfg->channel = channel; + p2p->cfg->channel_forced = forced; + } else { + p2p_dbg(p2p, "Defer setting listen channel"); + p2p->pending_reg_class = reg_class; + p2p->pending_channel = channel; + p2p->pending_channel_forced = forced; + } return 0; } +u8 p2p_get_listen_channel(struct p2p_data *p2p) +{ + return p2p->cfg->channel; +} + + int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len) { p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len)); diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index fa8031d07..9cf100fa3 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -266,6 +266,12 @@ struct p2p_config { */ u8 channel; + /** + * channel_forced - the listen channel was forced by configuration + * or by control interface and cannot be overridden + */ + u8 channel_forced; + /** * Regulatory class for own operational channel */ @@ -1669,7 +1675,10 @@ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled); */ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled); -int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel); +int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel, + u8 forced); + +u8 p2p_get_listen_channel(struct p2p_data *p2p); int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 65ff9ef41..39a927a31 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -480,6 +480,10 @@ struct p2p_data { unsigned int search_delay; int in_search_delay; + u8 pending_reg_class; + u8 pending_channel; + u8 pending_channel_forced; + #ifdef CONFIG_WIFI_DISPLAY struct wpabuf *wfd_ie_beacon; struct wpabuf *wfd_ie_probe_req; diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 278ec9b3d..4cea2ef98 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -3256,6 +3256,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; + config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN; config->bss_max_count = DEFAULT_BSS_MAX_COUNT; config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE; config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT; @@ -3838,6 +3839,7 @@ static const struct global_parse_data global_fields[] = { { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN }, { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 }, + { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 }, { INT(p2p_go_ht40), 0 }, { INT(p2p_go_vht), 0 }, { INT(p2p_disabled), 0 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 9b7e21d91..3c2fc4ab2 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -19,6 +19,7 @@ #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 #define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60) +#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0 #define DEFAULT_BSS_MAX_COUNT 200 #define DEFAULT_BSS_EXPIRATION_AGE 180 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 @@ -686,6 +687,7 @@ struct wpa_config { struct wpa_freq_range_list p2p_no_go_freq; int p2p_add_cli_chan; int p2p_ignore_shared_freq; + int p2p_optimize_listen_chan; struct wpabuf *wps_vendor_ext_m1; diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index d09b942ad..58e711187 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -1035,6 +1035,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } if (config->p2p_add_cli_chan) fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan); + if (config->p2p_optimize_listen_chan != + DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN) + fprintf(f, "p2p_optimize_listen_chan=%d\n", + config->p2p_optimize_listen_chan); if (config->p2p_go_ht40) fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); if (config->p2p_go_vht) diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 431120806..33db1139a 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -4760,7 +4760,7 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) if (os_strcmp(cmd, "listen_channel") == 0) { return p2p_set_listen_channel(wpa_s->global->p2p, 81, - atoi(param)); + atoi(param), 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 9eeda94ab..b1c13dab8 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -3872,6 +3872,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) wpa_s->conf->p2p_listen_channel) { p2p.reg_class = wpa_s->conf->p2p_listen_reg_class; p2p.channel = wpa_s->conf->p2p_listen_channel; + p2p.channel_forced = 1; } else { p2p.reg_class = 81; /* @@ -3880,6 +3881,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) */ os_get_random((u8 *) &r, sizeof(r)); p2p.channel = 1 + (r % 3) * 5; + p2p.channel_forced = 0; } wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel); @@ -6270,10 +6272,13 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) u8 reg_class, channel; int ret; unsigned int r; + u8 channel_forced; + if (wpa_s->conf->p2p_listen_reg_class && wpa_s->conf->p2p_listen_channel) { reg_class = wpa_s->conf->p2p_listen_reg_class; channel = wpa_s->conf->p2p_listen_channel; + channel_forced = 1; } else { reg_class = 81; /* @@ -6282,8 +6287,10 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) */ os_get_random((u8 *) &r, sizeof(r)); channel = 1 + (r % 3) * 5; + channel_forced = 0; } - ret = p2p_set_listen_channel(p2p, reg_class, channel); + ret = p2p_set_listen_channel(p2p, reg_class, channel, + channel_forced); if (ret) wpa_printf(MSG_ERROR, "P2P: Own listen channel update " "failed: %d", ret); @@ -7766,3 +7773,60 @@ int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) } #endif /* CONFIG_WPS_NFC */ + + +static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + u8 curr_chan, cand, chan; + unsigned int i; + + 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); + if (curr_chan == chan) { + cand = 0; + break; + } + + if (chan == 1 || chan == 6 || chan == 11) + cand = chan; + } + + if (cand) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Update Listen channel to %u baased on operating channel", + cand); + p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0); + } +} + + +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +{ + struct wpa_used_freq_data *freqs; + unsigned int num = wpa_s->num_multichan_concurrent; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + /* + * 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; + + 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); + os_free(freqs); +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index 0bf3ca9b9..da67e0f76 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -160,6 +160,8 @@ int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled); void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx); +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s); + #ifdef CONFIG_P2P int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s); void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index d6d3b24c0..88862b605 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -737,6 +737,14 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); +#ifdef CONFIG_P2P + /* + * Notify the P2P Device interface about a state change in one + * of the interfaces. + */ + wpas_p2p_indicate_state_change(wpa_s); +#endif /* CONFIG_P2P */ + if (wpa_s->wpa_state == WPA_COMPLETED || old_state == WPA_COMPLETED) wpas_notify_auth_changed(wpa_s);