diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 50ee1e08c..d2cb7af32 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -151,29 +151,32 @@ static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s) * Get the frequencies that are currently in use by one or more of the virtual * interfaces, and that are also valid for P2P operation. */ -static int wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s, - int *p2p_freqs, unsigned int len) +static unsigned int +wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *p2p_freqs, + unsigned int len) { - int *freqs; + struct wpa_used_freq_data *freqs; unsigned int num, i, j; - freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); if (!freqs) - return -1; + return 0; - num = get_shared_radio_freqs(wpa_s, freqs, - wpa_s->num_multichan_concurrent); + num = get_shared_radio_freqs_data(wpa_s, freqs, + wpa_s->num_multichan_concurrent); - os_memset(p2p_freqs, 0, sizeof(int) * len); + os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len); for (i = 0, j = 0; i < num && j < len; i++) { - if (p2p_supported_freq(wpa_s->global->p2p, freqs[i])) + if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) p2p_freqs[j++] = freqs[i]; } os_free(freqs); - dump_freq_array(wpa_s, "valid for P2P", p2p_freqs, j); + dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j); return j; } @@ -2914,6 +2917,45 @@ static int freq_included(const struct p2p_channels *channels, unsigned int freq) } +/** + * Pick the best frequency to use from all the currently used frequencies. + */ +static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + unsigned int i, c; + + /* find a candidate freq that is supported by P2P */ + for (c = 0; c < num; c++) + if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq)) + break; + + if (c == num) + return 0; + + /* once we have a candidate, try to find a 'better' one */ + for (i = c + 1; i < num; i++) { + if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) + continue; + + /* + * 1. Infrastructure station interfaces have higher preference. + * 2. P2P Clients have higher preference. + * 3. All others. + */ + if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) { + c = i; + break; + } + + if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT)) + c = i; + } + return freqs[c].freq; +} + + static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, const u8 *go_dev_addr, const u8 *ssid, size_t ssid_len, int *go, u8 *group_bssid, @@ -2923,8 +2965,9 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; - int res; + struct wpa_used_freq_data *freqs; struct wpa_supplicant *grp; + int best_freq; if (!persistent_group) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR @@ -3016,15 +3059,25 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, accept_inv: wpas_p2p_set_own_freq_preference(wpa_s, 0); + best_freq = 0; + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (freqs) { + int num_channels = wpa_s->num_multichan_concurrent; + int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels); + best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + os_free(freqs); + } + /* Get one of the frequencies currently in use */ - if (wpas_p2p_valid_oper_freqs(wpa_s, &res, 1) > 0) { + if (best_freq > 0) { wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces"); - wpas_p2p_set_own_freq_preference(wpa_s, res); + wpas_p2p_set_own_freq_preference(wpa_s, best_freq); if (wpa_s->num_multichan_concurrent < 2 || wpas_p2p_num_unused_channels(wpa_s) < 1) { wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces"); - *force_freq = res; + *force_freq = best_freq; } } @@ -4095,26 +4148,25 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) { - int *freqs, res, num, i; + int res; + unsigned int num, i; + struct wpa_used_freq_data *freqs; if (wpas_p2p_num_unused_channels(wpa_s) > 0) { /* Multiple channels are supported and not all are in use */ return 0; } - freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + 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); - if (num < 0) { - res = 1; - goto exit_free; - } for (i = 0; i < num; i++) { - if (freqs[i] == freq) { + if (freqs[i].freq == freq) { wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used", freq); res = 0; @@ -4122,6 +4174,7 @@ static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) } } + wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies"); res = 1; exit_free: @@ -4555,18 +4608,28 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, int *force_freq, int *pref_freq, int go) { - int *freqs, res; + struct wpa_used_freq_data *freqs; + int res, best_freq, num_unused; unsigned int freq_in_use = 0, num, i; - freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; - num = get_shared_radio_freqs(wpa_s, freqs, - wpa_s->num_multichan_concurrent); + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + /* + * It is possible that the total number of used frequencies is bigger + * than the number of frequencies used for P2P, so get the system wide + * number of unused frequencies. + */ + num_unused = wpas_p2p_num_unused_channels(wpa_s); + wpa_printf(MSG_DEBUG, - "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u", - freq, wpa_s->num_multichan_concurrent, num); + "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d", + freq, wpa_s->num_multichan_concurrent, num, num_unused); if (freq > 0) { int ret; @@ -4583,11 +4646,11 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, } for (i = 0; i < num; i++) { - if (freqs[i] == freq) + if (freqs[i].freq == freq) freq_in_use = 1; } - if (num == wpa_s->num_multichan_concurrent && !freq_in_use) { + if (num_unused <= 0 && !freq_in_use) { wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels", freq); res = -2; @@ -4599,34 +4662,28 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, goto exit_ok; } - for (i = 0; i < num; i++) { - if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i])) - continue; + best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); - if (*pref_freq == 0 && num < wpa_s->num_multichan_concurrent) { + /* We have a candidate frequency to use */ + if (best_freq > 0) { + if (*pref_freq == 0 && num_unused > 0) { wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using", - freqs[i]); - *pref_freq = freqs[i]; + best_freq); + *pref_freq = best_freq; } else { wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use", - freqs[i]); - *force_freq = freqs[i]; - } - break; - } - - if (i == num) { - if (num < wpa_s->num_multichan_concurrent && num > 0) { - wpa_printf(MSG_DEBUG, "P2P: Current operating channels are not available for P2P. Try to use another channel"); - *force_freq = 0; - } else if (num < wpa_s->num_multichan_concurrent) { - wpa_printf(MSG_DEBUG, "P2P: No current operating channels - try to use a new channel"); - *force_freq = 0; - } else { - wpa_printf(MSG_DEBUG, "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group"); - res = -2; - goto exit_free; + best_freq); + *force_freq = best_freq; } + } else if (num_unused > 0) { + wpa_printf(MSG_DEBUG, + "P2P: Current operating channels are not available for P2P. Try to use another channel"); + *force_freq = 0; + } else { + wpa_printf(MSG_DEBUG, + "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group"); + res = -2; + goto exit_free; } exit_ok: @@ -4962,8 +5019,8 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, int freq, int ht40, int vht, const struct p2p_channels *channels) { - int res, *freqs; - unsigned int pref_freq; + struct wpa_used_freq_data *freqs; + unsigned int pref_freq, cand_freq; unsigned int num, i; os_memset(params, 0, sizeof(*params)); @@ -5045,41 +5102,54 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, "known)", params->freq); } - freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); if (!freqs) return -1; - res = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, wpa_s->num_multichan_concurrent); - if (res < 0) { - os_free(freqs); - return -1; - } - num = res; - for (i = 0; i < num; i++) { - if (freq && freqs[i] == freq) - break; - if (!freq && freq_included(channels, freqs[i])) { - wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)", - freqs[i]); - params->freq = freqs[i]; - break; + 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; + } } - } - 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); - else + 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 if (num == 0) { - wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels"); - } else { - wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels"); + 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; + } + + 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"); + } } } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 8116f2915..4c2949d40 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -4652,12 +4652,30 @@ void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title, } +void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, + struct wpa_used_freq_data *freqs_data, + unsigned int len) +{ + unsigned int i; + + wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s", + len, title); + for (i = 0; i < len; i++) { + struct wpa_used_freq_data *cur = &freqs_data[i]; + wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X", + i, cur->freq, cur->flags); + } +} + + /* * Find the operating frequencies of any of the virtual interfaces that - * are using the same radio as the current interface. + * are using the same radio as the current interface, and in addition, get + * information about the interface types that are using the frequency. */ -int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, - int *freq_array, unsigned int len) +int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs_data, + unsigned int len) { struct wpa_supplicant *ifs; u8 bssid[ETH_ALEN]; @@ -4666,19 +4684,28 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Determining shared radio frequencies (max len %u)", len); - os_memset(freq_array, 0, sizeof(int) * len); + os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len); /* First add the frequency of the local interface */ if (wpa_s->current_ssid != NULL && wpa_s->assoc_freq != 0) { if (wpa_s->current_ssid->mode == WPAS_MODE_AP || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO) - freq_array[idx++] = wpa_s->current_ssid->frequency; + freqs_data[idx++].freq = wpa_s->current_ssid->frequency; else if (wpa_drv_get_bssid(wpa_s, bssid) == 0) - freq_array[idx++] = wpa_s->assoc_freq; + freqs_data[idx++].freq = wpa_s->assoc_freq; + + if (idx && wpa_s->current_ssid->mode == WPAS_MODE_INFRA) { + freqs_data[0].flags = wpa_s->current_ssid->p2p_group ? + WPA_FREQ_USED_BY_P2P_CLIENT : + WPA_FREQ_USED_BY_INFRA_STATION; + } } dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, radio_list) { + if (idx == len) + break; + if (wpa_s == ifs) continue; @@ -4695,13 +4722,45 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, /* Hold only distinct freqs */ for (i = 0; i < idx; i++) - if (freq_array[i] == freq) + if (freqs_data[i].freq == freq) break; if (i == idx) - freq_array[idx++] = freq; + freqs_data[idx++].freq = freq; + + if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { + freqs_data[i].flags = ifs->current_ssid->p2p_group ? + WPA_FREQ_USED_BY_P2P_CLIENT : + WPA_FREQ_USED_BY_INFRA_STATION; + } } - dump_freq_array(wpa_s, "completed iteration", freq_array, idx); + dump_freq_data(wpa_s, "completed iteration", freqs_data, idx); return idx; } + + +/* + * Find the operating frequencies of any of the virtual interfaces that + * are using the same radio as the current interface. + */ +int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, + int *freq_array, unsigned int len) +{ + struct wpa_used_freq_data *freqs_data; + int num, i; + + os_memset(freq_array, 0, sizeof(int) * len); + + freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data)); + if (!freqs_data) + return -1; + + num = get_shared_radio_freqs_data(wpa_s, freqs_data, len); + for (i = 0; i < num; i++) + freq_array[i] = freqs_data[i].freq; + + os_free(freqs_data); + + return num; +} diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 56434c464..b5cc49b9b 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -365,6 +365,14 @@ struct wpa_ssid_value { size_t ssid_len; }; +#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) +#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) + +struct wpa_used_freq_data { + int freq; + unsigned int flags; +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -989,6 +997,13 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); void dump_freq_array(struct wpa_supplicant *wpa_s, const char *title, int *freq_array, unsigned int len); +void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, + struct wpa_used_freq_data *freqs_data, + unsigned int len); + +int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs_data, + unsigned int len); int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, int *freq_array, unsigned int len);