P2P: Add option to remove channels from GO use

The new p2p_no_go_freq frequency range list (comma-separated list of
min-max frequency ranges in MHz) can now be used to configure channels
on which the local device is not allowed to operate as a GO, but on
which that device can be a P2P Client. These channels are left in the
P2P Channel List in GO Negotiation to allow the peer device to select
one of the channels for the cases where the peer becomes the GO. The
local end will remove these channels from consideration if it becomes
the GO.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-10-22 19:45:46 +03:00 committed by Jouni Malinen
parent e7ecab4a3b
commit 556b30daca
10 changed files with 154 additions and 12 deletions

View File

@ -1550,8 +1550,15 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
}
}
p2p_channels_dump(p2p, "own channels", &p2p->channels);
p2p_channels_dump(p2p, "peer channels", &peer->channels);
p2p_channels_intersect(&p2p->channels, &peer->channels,
&intersection);
if (go) {
p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
p2p_channels_dump(p2p, "intersection after no-GO removal",
&intersection);
}
freqs = 0;
for (i = 0; i < intersection.reg_classes; i++) {
struct p2p_reg_class *c = &intersection.reg_class[i];
@ -2402,6 +2409,7 @@ void p2p_deinit(struct p2p_data *p2p)
wpabuf_free(p2p->sd_resp);
os_free(p2p->after_scan_tx);
p2p_remove_wps_vendor_extensions(p2p);
os_free(p2p->no_go_freq.range);
os_free(p2p);
}
@ -3933,6 +3941,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
}
int p2p_set_no_go_freq(struct p2p_data *p2p,
const struct wpa_freq_range_list *list)
{
struct wpa_freq_range *tmp;
if (list == NULL || list->num == 0) {
os_free(p2p->no_go_freq.range);
p2p->no_go_freq.range = NULL;
p2p->no_go_freq.num = 0;
return 0;
}
tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
if (tmp == NULL)
return -1;
os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
os_free(p2p->no_go_freq.range);
p2p->no_go_freq.range = tmp;
p2p->no_go_freq.num = list->num;
p2p_dbg(p2p, "Updated no GO chan list");
return 0;
}
int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
u8 *iface_addr)
{

View File

@ -1648,6 +1648,14 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels,
*/
int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
/**
* p2p_supported_freq_go - Check whether channel is supported for P2P GO operation
* @p2p: P2P module context from p2p_init()
* @freq: Channel frequency in MHz
* Returns: 0 if channel not usable for P2P, 1 if usable for P2P
*/
int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
/**
* p2p_get_pref_freq - Get channel from preferred channel list
* @p2p: P2P module context from p2p_init()
@ -1764,6 +1772,15 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
const struct p2p_channel *pref_chan);
/**
* p2p_set_no_go_freq - Set no GO channel ranges
* @p2p: P2P module context from p2p_init()
* @list: Channel ranges or %NULL to remove restriction
* Returns: 0 on success, -1 on failure
*/
int p2p_set_no_go_freq(struct p2p_data *p2p,
const struct wpa_freq_range_list *list);
/**
* p2p_in_progress - Check whether a P2P operation is progress
* @p2p: P2P module context from p2p_init()

View File

@ -479,6 +479,9 @@ static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
p2p_channels_dump(p2p, "peer channels", &dev->channels);
p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
p2p_channels_dump(p2p, "intersection", &intersection);
p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
p2p_channels_dump(p2p, "intersection after no-GO removal",
&intersection);
if (intersection.reg_classes == 0 ||
intersection.reg_class[0].channels == 0) {
*status = P2P_SC_FAIL_NO_COMMON_CHANNELS;

View File

@ -316,6 +316,8 @@ struct p2p_data {
*/
struct p2p_channels channels;
struct wpa_freq_range_list no_go_freq;
enum p2p_pending_action_state {
P2P_NO_PENDING_ACTION,
P2P_PENDING_GO_NEG_REQUEST,
@ -570,6 +572,8 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list);
int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
u8 channel);
void p2p_channels_dump(struct p2p_data *p2p, const char *title,

View File

@ -204,6 +204,42 @@ void p2p_channels_intersect(const struct p2p_channels *a,
}
void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list)
{
size_t o, c;
if (list == NULL)
return;
o = 0;
while (o < chan->reg_classes) {
struct p2p_reg_class *op = &chan->reg_class[o];
c = 0;
while (c < op->channels) {
int freq = p2p_channel_to_freq(op->reg_class,
op->channel[c]);
if (freq > 0 && freq_range_list_includes(list, freq)) {
op->channels--;
os_memmove(&op->channel[c],
&op->channel[c + 1],
op->channels - c);
} else
c++;
}
if (op->channels == 0) {
chan->reg_classes--;
os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1],
(chan->reg_classes - o) *
sizeof(struct p2p_reg_class));
} else
o++;
}
}
/**
* p2p_channels_includes - Check whether a channel is included in the list
* @channels: List of supported channels
@ -254,6 +290,17 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
}
int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq)
{
u8 op_reg_class, op_channel;
if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
return 0;
return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
op_channel) &&
!freq_range_list_includes(&p2p->no_go_freq, freq);
}
unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
const struct p2p_channels *channels)
{

View File

@ -1936,6 +1936,7 @@ void wpa_config_free(struct wpa_config *config)
os_free(config->p2p_ssid_postfix);
os_free(config->pssid);
os_free(config->p2p_pref_chan);
os_free(config->p2p_no_go_freq.range);
os_free(config->autoscan);
os_free(config->freq_list);
wpabuf_free(config->wps_nfc_dh_pubkey);
@ -3079,6 +3080,26 @@ fail:
wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
return -1;
}
static int wpa_config_process_p2p_no_go_freq(
const struct global_parse_data *data,
struct wpa_config *config, int line, const char *pos)
{
int ret;
ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
if (ret < 0) {
wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
return -1;
}
wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
config->p2p_no_go_freq.num);
return 0;
}
#endif /* CONFIG_P2P */
@ -3229,6 +3250,7 @@ static const struct global_parse_data global_fields[] = {
{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
{ INT(p2p_group_idle), 0 },
{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
{ FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
{ INT(p2p_go_ht40), 0 },
{ INT(p2p_disabled), 0 },
{ INT(p2p_no_group_iface), 0 },

View File

@ -605,6 +605,7 @@ struct wpa_config {
int p2p_intra_bss;
unsigned int num_p2p_pref_chan;
struct p2p_channel *p2p_pref_chan;
struct wpa_freq_range_list p2p_no_go_freq;
int p2p_ignore_shared_freq;
struct wpabuf *wps_vendor_ext_m1;

View File

@ -941,6 +941,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
}
fprintf(f, "\n");
}
if (config->p2p_no_go_freq.num) {
char *val = freq_range_list_str(&config->p2p_no_go_freq);
if (val) {
fprintf(f, "p2p_no_go_freq=%s\n", val);
os_free(val);
}
}
if (config->p2p_go_ht40)
fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
if (config->p2p_disabled)

View File

@ -3386,6 +3386,8 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
global->p2p, wpa_s->conf->wps_vendor_ext[i]);
}
p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
return 0;
}
@ -4313,8 +4315,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
"band");
if (wpa_s->best_24_freq > 0 &&
p2p_supported_freq(wpa_s->global->p2p,
wpa_s->best_24_freq)) {
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_24_freq)) {
freq = wpa_s->best_24_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
"channel: %d MHz", freq);
@ -4330,7 +4332,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
"band");
if (wpa_s->best_5_freq > 0 &&
p2p_supported_freq(wpa_s->global->p2p,
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_5_freq)) {
freq = wpa_s->best_5_freq;
wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
@ -4338,7 +4340,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
} else {
os_get_random((u8 *) &r, sizeof(r));
freq = 5180 + (r % 4) * 20;
if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_DEBUG, "P2P: Could not select "
"5 GHz channel for P2P group");
return -1;
@ -4348,7 +4350,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
}
}
if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
"(%u MHz) is not supported for P2P uses",
freq);
@ -4401,24 +4403,24 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
"frequency %d MHz", params->freq);
} else if (wpa_s->conf->p2p_oper_channel == 0 &&
wpa_s->best_overall_freq > 0 &&
p2p_supported_freq(wpa_s->global->p2p,
wpa_s->best_overall_freq) &&
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_overall_freq) &&
freq_included(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(wpa_s->global->p2p,
wpa_s->best_24_freq) &&
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_24_freq) &&
freq_included(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(wpa_s->global->p2p,
wpa_s->best_5_freq) &&
p2p_supported_freq_go(wpa_s->global->p2p,
wpa_s->best_5_freq) &&
freq_included(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 "
@ -4564,7 +4566,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, NULL))
return -1;
if (params.freq &&
!p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
"(%u MHz) is not supported for P2P uses",
params.freq);
@ -5605,6 +5607,11 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
"update failed");
}
if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
wpa_printf(MSG_ERROR, "P2P: No GO channel list "
"update failed");
}
}
}

View File

@ -615,6 +615,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
"p2p_oper_reg_class", "p2p_oper_channel",
"p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
"p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
"p2p_no_go_freq",
"p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
"p2p_ignore_shared_freq", "country", "bss_max_count",
"bss_expiration_age", "bss_expiration_scan_count",