P2P: Collect and use extended data on used frequencies

When the number of frequencies supported by the kernel is bigger than
one, and there is a need to pick a frequency for a new flow such as P2P
GO Negotiation or P2P Invitation, the flow should be able to pick the
best frequency among all the frequencies currently used by the device.

In order to prioritize between the currently used frequencies, add
the ability to collect additional data about each used frequency
(if the frequency is used by a station interface or P2P Client)
and when needed select the best frequency, where:

1. Infrastructure interfaces have highest priority
2. P2P Client interfaces have higher priority over AP/GO
   interfaces.

The rational is that the frequency of an AP/GO can change while
that of a station interface cannot.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
This commit is contained in:
Ilan Peer 2014-05-19 10:05:35 +03:00 committed by Jouni Malinen
parent b278f323ed
commit a0c90bb073
3 changed files with 234 additions and 90 deletions

View File

@ -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");
}
}
}

View File

@ -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;
}

View File

@ -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);