P2P: Add option to allow additional client channels

The new p2p_add_cli_chan=1 configuration parameter can be used to
request passive-scan channels to be included in P2P channel lists for
cases where the local end may become the P2P client in a group. This
allows more options for the peer to use channels, e.g., if the local
device is not aware of its current location and has marked most channels
to require passive scanning.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Jouni Malinen 2013-10-22 21:00:49 +03:00 committed by Jouni Malinen
parent 556b30daca
commit 51e9f22809
10 changed files with 247 additions and 71 deletions

View file

@ -1140,19 +1140,21 @@ void p2p_stop_find(struct p2p_data *p2p)
static int p2p_prepare_channel_pref(struct p2p_data *p2p, static int p2p_prepare_channel_pref(struct p2p_data *p2p,
unsigned int force_freq, unsigned int force_freq,
unsigned int pref_freq) unsigned int pref_freq, int go)
{ {
u8 op_class, op_channel; u8 op_class, op_channel;
unsigned int freq = force_freq ? force_freq : pref_freq; unsigned int freq = force_freq ? force_freq : pref_freq;
p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u", p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
force_freq, pref_freq); force_freq, pref_freq, go);
if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) { if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
p2p_dbg(p2p, "Unsupported frequency %u MHz", freq); p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
return -1; return -1;
} }
if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) { if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) &&
(go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class,
op_channel))) {
p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P", p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P",
freq, op_class, op_channel); freq, op_class, op_channel);
return -1; return -1;
@ -1226,6 +1228,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
* @dev: Selected peer device * @dev: Selected peer device
* @force_freq: Forced frequency in MHz or 0 if not forced * @force_freq: Forced frequency in MHz or 0 if not forced
* @pref_freq: Preferred frequency in MHz or 0 if no preference * @pref_freq: Preferred frequency in MHz or 0 if no preference
* @go: Whether the local end will be forced to be GO
* Returns: 0 on success, -1 on failure (channel not supported for P2P) * Returns: 0 on success, -1 on failure (channel not supported for P2P)
* *
* This function is used to do initial operating channel selection for GO * This function is used to do initial operating channel selection for GO
@ -1234,16 +1237,25 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
* is available. * is available.
*/ */
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq) unsigned int force_freq, unsigned int pref_freq, int go)
{ {
p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u", p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
force_freq, pref_freq); force_freq, pref_freq, go);
if (force_freq || pref_freq) { if (force_freq || pref_freq) {
if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0) if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) <
0)
return -1; return -1;
} else { } else {
p2p_prepare_channel_best(p2p); p2p_prepare_channel_best(p2p);
} }
p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
if (go)
p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
else if (!force_freq)
p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
&p2p->channels);
p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s", p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
p2p->op_reg_class, p2p->op_channel, p2p->op_reg_class, p2p->op_channel,
force_freq ? " (forced)" : ""); force_freq ? " (forced)" : "");
@ -1299,7 +1311,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
return -1; return -1;
} }
if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
go_intent == 15) < 0)
return -1; return -1;
if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) { if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
@ -1409,7 +1422,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
return -1; return -1;
} }
if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
15) < 0)
return -1; return -1;
p2p->ssid_set = 0; p2p->ssid_set = 0;
@ -2373,6 +2387,10 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
p2p->go_timeout = 100; p2p->go_timeout = 100;
p2p->client_timeout = 20; p2p->client_timeout = 20;
p2p_dbg(p2p, "initialized");
p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
return p2p; return p2p;
} }
@ -4028,10 +4046,16 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
} }
void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan) void p2p_update_channel_list(struct p2p_data *p2p,
const struct p2p_channels *chan,
const struct p2p_channels *cli_chan)
{ {
p2p_dbg(p2p, "Update channel list"); p2p_dbg(p2p, "Update channel list");
os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels)); os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
os_memcpy(&p2p->cfg->cli_channels, cli_chan,
sizeof(struct p2p_channels));
p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
} }

View file

@ -286,6 +286,20 @@ struct p2p_config {
*/ */
struct p2p_channels channels; struct p2p_channels channels;
/**
* cli_channels - Additional client channels
*
* This list of channels (if any) will be used when advertising local
* channels during GO Negotiation or Invitation for the cases where the
* local end may become the client. This may allow the peer to become a
* GO on additional channels if it supports these options. The main use
* case for this is to include passive-scan channels on devices that may
* not know their current location and have configured most channels to
* not allow initiation of radition (i.e., another device needs to take
* master responsibilities).
*/
struct p2p_channels cli_channels;
/** /**
* num_pref_chan - Number of pref_chan entries * num_pref_chan - Number of pref_chan entries
*/ */
@ -1656,6 +1670,14 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
*/ */
int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq); int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
/**
* p2p_supported_freq_cli - Check whether channel is supported for P2P client 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_cli(struct p2p_data *p2p, unsigned int freq);
/** /**
* p2p_get_pref_freq - Get channel from preferred channel list * p2p_get_pref_freq - Get channel from preferred channel list
* @p2p: P2P module context from p2p_init() * @p2p: P2P module context from p2p_init()
@ -1665,7 +1687,9 @@ int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
unsigned int p2p_get_pref_freq(struct p2p_data *p2p, unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
const struct p2p_channels *channels); const struct p2p_channels *channels);
void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan); void p2p_update_channel_list(struct p2p_data *p2p,
const struct p2p_channels *chan,
const struct p2p_channels *cli_chan);
/** /**
* p2p_set_best_channels - Update best channel information * p2p_set_best_channels - Update best channel information

View file

@ -473,14 +473,16 @@ void p2p_reselect_channel(struct p2p_data *p2p,
static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
u8 *status) u8 *status)
{ {
struct p2p_channels intersection; struct p2p_channels tmp, intersection;
p2p_channels_dump(p2p, "own channels", &p2p->channels); p2p_channels_dump(p2p, "own channels", &p2p->channels);
p2p_channels_dump(p2p, "peer channels", &dev->channels); p2p_channels_dump(p2p, "peer channels", &dev->channels);
p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection); p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp);
p2p_channels_dump(p2p, "intersection", &intersection); p2p_channels_dump(p2p, "intersection", &tmp);
p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq); p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq);
p2p_channels_dump(p2p, "intersection after no-GO removal", p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp);
p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection);
p2p_channels_dump(p2p, "intersection with local channel list",
&intersection); &intersection);
if (intersection.reg_classes == 0 || if (intersection.reg_classes == 0 ||
intersection.reg_class[0].channels == 0) { intersection.reg_class[0].channels == 0) {

View file

@ -572,6 +572,9 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a, void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b, const struct p2p_channels *b,
struct p2p_channels *res); struct p2p_channels *res);
void p2p_channels_union(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res);
void p2p_channels_remove_freqs(struct p2p_channels *chan, void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list); const struct wpa_freq_range_list *list);
int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class, int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
@ -730,7 +733,8 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
size_t len, unsigned int wait_time); size_t len, unsigned int wait_time);
void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq); void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev, int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
unsigned int force_freq, unsigned int pref_freq); unsigned int force_freq, unsigned int pref_freq,
int go);
void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...) void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
PRINTF_FORMAT(2, 3); PRINTF_FORMAT(2, 3);
void p2p_info(struct p2p_data *p2p, const char *fmt, ...) void p2p_info(struct p2p_data *p2p, const char *fmt, ...)

View file

@ -548,7 +548,8 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
return -1; return -1;
} }
if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0) if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
role != P2P_INVITE_ROLE_CLIENT) < 0)
return -1; return -1;
if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq && if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq &&

View file

@ -204,6 +204,69 @@ void p2p_channels_intersect(const struct p2p_channels *a,
} }
static void p2p_op_class_union(struct p2p_reg_class *cl,
const struct p2p_reg_class *b_cl)
{
size_t i, j;
for (i = 0; i < b_cl->channels; i++) {
for (j = 0; j < cl->channels; j++) {
if (b_cl->channel[i] == cl->channel[j])
break;
}
if (j == cl->channels) {
if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS)
return;
cl->channel[cl->channels++] = b_cl->channel[i];
}
}
}
/**
* p2p_channels_union - Union of channel lists
* @a: First set of channels
* @b: Second set of channels
* @res: Data structure for returning the union of channels
*/
void p2p_channels_union(const struct p2p_channels *a,
const struct p2p_channels *b,
struct p2p_channels *res)
{
size_t i, j;
if (a != res)
os_memcpy(res, a, sizeof(*res));
for (i = 0; i < res->reg_classes; i++) {
struct p2p_reg_class *cl = &res->reg_class[i];
for (j = 0; j < b->reg_classes; j++) {
const struct p2p_reg_class *b_cl = &b->reg_class[j];
if (cl->reg_class != b_cl->reg_class)
continue;
p2p_op_class_union(cl, b_cl);
}
}
for (j = 0; j < b->reg_classes; j++) {
const struct p2p_reg_class *b_cl = &b->reg_class[j];
for (i = 0; i < res->reg_classes; i++) {
struct p2p_reg_class *cl = &res->reg_class[i];
if (cl->reg_class == b_cl->reg_class)
break;
}
if (i == res->reg_classes) {
if (res->reg_classes == P2P_MAX_REG_CLASSES)
return;
os_memcpy(&res->reg_class[res->reg_classes++],
b_cl, sizeof(struct p2p_reg_class));
}
}
}
void p2p_channels_remove_freqs(struct p2p_channels *chan, void p2p_channels_remove_freqs(struct p2p_channels *chan,
const struct wpa_freq_range_list *list) const struct wpa_freq_range_list *list)
{ {
@ -301,6 +364,18 @@ int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq)
} }
int p2p_supported_freq_cli(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) ||
p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class,
op_channel);
}
unsigned int p2p_get_pref_freq(struct p2p_data *p2p, unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
const struct p2p_channels *channels) const struct p2p_channels *channels)
{ {

View file

@ -3251,6 +3251,7 @@ static const struct global_parse_data global_fields[] = {
{ INT(p2p_group_idle), 0 }, { INT(p2p_group_idle), 0 },
{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
{ FUNC(p2p_no_go_freq), 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(p2p_go_ht40), 0 }, { INT(p2p_go_ht40), 0 },
{ INT(p2p_disabled), 0 }, { INT(p2p_disabled), 0 },
{ INT(p2p_no_group_iface), 0 }, { INT(p2p_no_group_iface), 0 },

View file

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

View file

@ -948,6 +948,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
os_free(val); os_free(val);
} }
} }
if (config->p2p_add_cli_chan)
fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
if (config->p2p_go_ht40) if (config->p2p_go_ht40)
fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
if (config->p2p_disabled) if (config->p2p_disabled)

View file

@ -2919,10 +2919,13 @@ static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
struct p2p_channels *chan) struct p2p_channels *chan,
struct p2p_channels *cli_chan)
{ {
int i, cla = 0; int i, cla = 0;
os_memset(cli_chan, 0, sizeof(*cli_chan));
wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
"band"); "band");
@ -2990,6 +2993,10 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
} }
enum chan_allowed {
NOT_ALLOWED, PASSIVE_ONLY, ALLOWED
};
static int has_channel(struct wpa_global *global, static int has_channel(struct wpa_global *global,
struct hostapd_hw_modes *mode, u8 chan, int *flags) struct hostapd_hw_modes *mode, u8 chan, int *flags)
{ {
@ -2999,21 +3006,25 @@ static int has_channel(struct wpa_global *global,
freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) + freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
chan * 5; chan * 5;
if (wpas_p2p_disallowed_freq(global, freq)) if (wpas_p2p_disallowed_freq(global, freq))
return 0; return NOT_ALLOWED;
for (i = 0; i < mode->num_channels; i++) { for (i = 0; i < mode->num_channels; i++) {
if (mode->channels[i].chan == chan) { if (mode->channels[i].chan == chan) {
if (flags) if (flags)
*flags = mode->channels[i].flag; *flags = mode->channels[i].flag;
return !(mode->channels[i].flag & if (mode->channels[i].flag &
(HOSTAPD_CHAN_DISABLED | (HOSTAPD_CHAN_DISABLED |
HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_RADAR))
HOSTAPD_CHAN_NO_IBSS | return NOT_ALLOWED;
HOSTAPD_CHAN_RADAR)); if (mode->channels[i].flag &
(HOSTAPD_CHAN_PASSIVE_SCAN |
HOSTAPD_CHAN_NO_IBSS))
return PASSIVE_ONLY;
return ALLOWED;
} }
} }
return 0; return NOT_ALLOWED;
} }
@ -3042,69 +3053,94 @@ static struct p2p_oper_class_map op_class[] = {
}; };
static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, struct hostapd_hw_modes *mode,
u8 channel, u8 bw) u8 channel, u8 bw)
{ {
int flag; int flag;
enum chan_allowed res, res2;
if (!has_channel(wpa_s->global, mode, channel, &flag)) res2 = res = has_channel(wpa_s->global, mode, channel, &flag);
return -1; if (bw == BW40MINUS) {
if (bw == BW40MINUS && if (!(flag & HOSTAPD_CHAN_HT40MINUS))
(!(flag & HOSTAPD_CHAN_HT40MINUS) || return NOT_ALLOWED;
!has_channel(wpa_s->global, mode, channel - 4, NULL))) res2 = has_channel(wpa_s->global, mode, channel - 4, NULL);
return 0; } else if (bw == BW40PLUS) {
if (bw == BW40PLUS && if (!(flag & HOSTAPD_CHAN_HT40PLUS))
(!(flag & HOSTAPD_CHAN_HT40PLUS) || return NOT_ALLOWED;
!has_channel(wpa_s->global, mode, channel + 4, NULL))) res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
return 0; }
return 1;
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
return NOT_ALLOWED;
if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY)
return PASSIVE_ONLY;
return res;
} }
static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
struct p2p_channels *chan) struct p2p_channels *chan,
struct p2p_channels *cli_chan)
{ {
struct hostapd_hw_modes *mode; struct hostapd_hw_modes *mode;
int cla, op; int cla, op, cli_cla;
if (wpa_s->hw.modes == NULL) { if (wpa_s->hw.modes == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching " wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
"of all supported channels; assume dualband " "of all supported channels; assume dualband "
"support"); "support");
return wpas_p2p_default_channels(wpa_s, chan); return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
} }
cla = 0; cla = cli_cla = 0;
for (op = 0; op_class[op].op_class; op++) { for (op = 0; op_class[op].op_class; op++) {
struct p2p_oper_class_map *o = &op_class[op]; struct p2p_oper_class_map *o = &op_class[op];
u8 ch; u8 ch;
struct p2p_reg_class *reg = NULL; struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
if (mode == NULL) if (mode == NULL)
continue; continue;
for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1) enum chan_allowed res;
continue; res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
if (res == ALLOWED) {
if (reg == NULL) { if (reg == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Add operating " wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
"class %u", o->op_class); o->op_class);
reg = &chan->reg_class[cla]; reg = &chan->reg_class[cla];
cla++; cla++;
reg->reg_class = o->op_class; reg->reg_class = o->op_class;
} }
reg->channel[reg->channels] = ch; reg->channel[reg->channels] = ch;
reg->channels++; reg->channels++;
} else if (res == PASSIVE_ONLY &&
wpa_s->conf->p2p_add_cli_chan) {
if (cli_reg == NULL) {
wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
o->op_class);
cli_reg = &cli_chan->reg_class[cli_cla];
cli_cla++;
cli_reg->reg_class = o->op_class;
}
cli_reg->channel[cli_reg->channels] = ch;
cli_reg->channels++;
}
} }
if (reg) { if (reg) {
wpa_hexdump(MSG_DEBUG, "P2P: Channels", wpa_hexdump(MSG_DEBUG, "P2P: Channels",
reg->channel, reg->channels); reg->channel, reg->channels);
} }
if (cli_reg) {
wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
cli_reg->channel, cli_reg->channels);
}
} }
chan->reg_classes = cla; chan->reg_classes = cla;
cli_chan->reg_classes = cli_cla;
return 0; return 0;
} }
@ -3113,7 +3149,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel) struct hostapd_hw_modes *mode, u8 channel)
{ {
int op, ret; int op;
enum chan_allowed ret;
for (op = 0; op_class[op].op_class; op++) { for (op = 0; op_class[op].op_class; op++) {
struct p2p_oper_class_map *o = &op_class[op]; struct p2p_oper_class_map *o = &op_class[op];
@ -3124,12 +3161,8 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
o->bw == BW20 || ch != channel) o->bw == BW20 || ch != channel)
continue; continue;
ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
if (ret < 0) if (ret == ALLOWED)
continue;
else if (ret > 0)
return (o->bw == BW40MINUS) ? -1 : 1; return (o->bw == BW40MINUS) ? -1 : 1;
else
return 0;
} }
} }
return 0; return 0;
@ -3343,7 +3376,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
} else } else
os_memcpy(p2p.country, "XX\x04", 3); os_memcpy(p2p.country, "XX\x04", 3);
if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) { if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) {
wpa_printf(MSG_ERROR, "P2P: Failed to configure supported " wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
"channel list"); "channel list");
return -1; return -1;
@ -3974,7 +4007,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
static int wpas_p2p_setup_freqs(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 *force_freq, int *pref_freq, int go)
{ {
int *freqs, res; int *freqs, res;
unsigned int freq_in_use = 0, num, i; unsigned int freq_in_use = 0, num, i;
@ -3990,7 +4023,12 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
freq, wpa_s->num_multichan_concurrent, num); freq, wpa_s->num_multichan_concurrent, num);
if (freq > 0) { if (freq > 0) {
if (!p2p_supported_freq(wpa_s->global->p2p, freq)) { int ret;
if (go)
ret = p2p_supported_freq(wpa_s->global->p2p, freq);
else
ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
if (!ret) {
wpa_printf(MSG_DEBUG, "P2P: The forced channel " wpa_printf(MSG_DEBUG, "P2P: The forced channel "
"(%u MHz) is not supported for P2P uses", "(%u MHz) is not supported for P2P uses",
freq); freq);
@ -4152,7 +4190,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
return ret; return ret;
} }
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq); res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
go_intent == 15);
if (res) if (res)
return res; return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq); wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@ -5166,7 +5205,8 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
} }
wpa_s->pending_invite_ssid_id = ssid->id; wpa_s->pending_invite_ssid_id = ssid->id;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq); res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
role == P2P_INVITE_ROLE_GO);
if (res) if (res)
return res; return res;
@ -5250,7 +5290,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1; return -1;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq); res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
role == P2P_INVITE_ROLE_ACTIVE_GO);
if (res) if (res)
return res; return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq); wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@ -5783,19 +5824,20 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
{ {
struct p2p_channels chan; struct p2p_channels chan, cli_chan;
if (wpa_s->global == NULL || wpa_s->global->p2p == NULL) if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
return; return;
os_memset(&chan, 0, sizeof(chan)); os_memset(&chan, 0, sizeof(chan));
if (wpas_p2p_setup_channels(wpa_s, &chan)) { os_memset(&cli_chan, 0, sizeof(cli_chan));
if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
wpa_printf(MSG_ERROR, "P2P: Failed to update supported " wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
"channel list"); "channel list");
return; return;
} }
p2p_update_channel_list(wpa_s->global->p2p, &chan); p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
} }