P2P: Use weighted preferred channel list for channel selection

Previously, the driver could optionally (using QCA vendor command)
provide a preferred channel list to wpa_supplicant for channel selection
during the GO negotiation. Channel selection process can be more
efficient with the information of weights and flags of the preferred
channel list that can be provided by the driver. Use a weighted
preferred channel list provided by the driver for channel selection
during GO negotiation if such a list is available.

Signed-off-by: Sreeramya Soratkal <quic_ssramya@quicinc.com>
This commit is contained in:
Sreeramya Soratkal 2020-11-05 21:55:55 +05:30 committed by Jouni Malinen
parent f2c5c8d38f
commit 4383528e01
12 changed files with 322 additions and 88 deletions

View file

@ -2602,6 +2602,24 @@ enum nested_attr {
NESTED_ATTR_UNSPECIFIED = 2, NESTED_ATTR_UNSPECIFIED = 2,
}; };
/* Preferred channel list information */
/* GO role */
#define WEIGHTED_PCL_GO BIT(0)
/* P2P Client role */
#define WEIGHTED_PCL_CLI BIT(1)
/* Must be considered for operating channel */
#define WEIGHTED_PCL_MUST_CONSIDER BIT(2)
/* Should be excluded in GO negotiation */
#define WEIGHTED_PCL_EXCLUDE BIT(3)
/* Preferred channel list with weight */
struct weighted_pcl {
u32 freq; /* MHz */
u8 weight;
u32 flag; /* bitmap for WEIGHTED_PCL_* */
};
/** /**
* struct wpa_driver_ops - Driver interface API definition * struct wpa_driver_ops - Driver interface API definition
* *
@ -4446,14 +4464,17 @@ struct wpa_driver_ops {
* @priv: Private driver interface data * @priv: Private driver interface data
* @if_type: Interface type * @if_type: Interface type
* @num: Number of channels * @num: Number of channels
* @freq_list: Preferred channel frequency list encoded in MHz values * @freq_list: Weighted preferred channel list
* Returns 0 on success, -1 on failure * Returns 0 on success, -1 on failure
* *
* This command can be used to query the preferred frequency list from * This command can be used to query the preferred frequency list from
* the driver specific to a particular interface type. * the driver specific to a particular interface type. The freq_list
* array needs to have room for *num entries. *num will be updated to
* indicate the number of entries fetched from the driver.
*/ */
int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type, int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *freq_list); unsigned int *num,
struct weighted_pcl *freq_list);
/** /**
* set_prob_oper_freq - Indicate probable P2P operating channel * set_prob_oper_freq - Indicate probable P2P operating channel

View file

@ -11258,9 +11258,33 @@ static int nl80211_set_band(void *priv, u32 band_mask)
struct nl80211_pcl { struct nl80211_pcl {
unsigned int num; unsigned int num;
unsigned int *freq_list; struct weighted_pcl *freq_list;
}; };
static void get_pcl_attr_values(struct weighted_pcl *wpcl, struct nlattr *nl[])
{
if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ])
wpcl->freq = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FREQ]);
if (nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT])
wpcl->weight = nla_get_u8(nl[QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT]);
if (nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]) {
u32 flags = nla_get_u32(nl[QCA_WLAN_VENDOR_ATTR_PCL_FLAG]);
wpcl->flag = 0;
if (flags & BIT(0))
wpcl->flag |= WEIGHTED_PCL_GO;
if (flags & BIT(1))
wpcl->flag |= WEIGHTED_PCL_CLI;
if (flags & BIT(2))
wpcl->flag |= WEIGHTED_PCL_MUST_CONSIDER;
if (flags & BIT(3))
wpcl->flag |= WEIGHTED_PCL_EXCLUDE;
} else {
wpcl->flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
}
}
static int preferred_freq_info_handler(struct nl_msg *msg, void *arg) static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
{ {
struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct nlattr *tb[NL80211_ATTR_MAX + 1];
@ -11269,6 +11293,7 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
struct nlattr *nl_vend, *attr; struct nlattr *nl_vend, *attr;
enum qca_iface_type iface_type; enum qca_iface_type iface_type;
struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
struct nlattr *nl_pcl[QCA_WLAN_VENDOR_ATTR_PCL_MAX + 1];
unsigned int num, max_num; unsigned int num, max_num;
u32 *freqs; u32 *freqs;
@ -11294,26 +11319,69 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d", wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
iface_type); iface_type);
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; attr = tb_vendor[
if (!attr) { QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL];
if (attr) {
int rem;
struct nlattr *wpcl = attr;
unsigned int i;
num = 0;
nla_for_each_nested(attr, wpcl, rem) {
if (num == param->num)
break; /* not enough room for all entries */
if (nla_parse(nl_pcl, QCA_WLAN_VENDOR_ATTR_PCL_MAX,
nla_data(attr), nla_len(attr), NULL)) {
wpa_printf(MSG_ERROR,
"nl80211: Failed to parse PCL info");
param->num = 0;
return NL_SKIP;
}
get_pcl_attr_values(&param->freq_list[num], nl_pcl);
num++;
}
param->num = num;
/* Sort frequencies based on their weight */
for (i = 0; i < num; i++) {
unsigned int j;
for (j = i + 1; j < num; j++) {
if (param->freq_list[i].weight <
param->freq_list[j].weight) {
struct weighted_pcl tmp;
tmp = param->freq_list[i];
param->freq_list[i] =
param->freq_list[j];
param->freq_list[j] = tmp;
}
}
}
} else if (tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]) {
wpa_printf(MSG_DEBUG,
"nl80211: Driver does not provide weighted PCL; use the non-weighted variant");
attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
/*
* param->num has the maximum number of entries for which there
* is room in the freq_list provided by the caller.
*/
freqs = nla_data(attr);
max_num = nla_len(attr) / sizeof(u32);
if (max_num > param->num)
max_num = param->num;
for (num = 0; num < max_num; num++) {
param->freq_list[num].freq = freqs[num];
param->freq_list[num].flag =
WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
}
param->num = num;
} else {
wpa_printf(MSG_ERROR, wpa_printf(MSG_ERROR,
"nl80211: preferred_freq_list couldn't be found"); "nl80211: preferred_freq_list couldn't be found");
param->num = 0; param->num = 0;
return NL_SKIP; return NL_SKIP;
} }
/*
* param->num has the maximum number of entries for which there
* is room in the freq_list provided by the caller.
*/
freqs = nla_data(attr);
max_num = nla_len(attr) / sizeof(u32);
if (max_num > param->num)
max_num = param->num;
for (num = 0; num < max_num; num++)
param->freq_list[num] = freqs[num];
param->num = num;
return NL_SKIP; return NL_SKIP;
} }
@ -11321,7 +11389,7 @@ static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
static int nl80211_get_pref_freq_list(void *priv, static int nl80211_get_pref_freq_list(void *priv,
enum wpa_driver_if_type if_type, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *num,
unsigned int *freq_list) struct weighted_pcl *freq_list)
{ {
struct i802_bss *bss = priv; struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv; struct wpa_driver_nl80211_data *drv = bss->drv;
@ -11378,7 +11446,8 @@ static int nl80211_get_pref_freq_list(void *priv,
} }
nla_nest_end(msg, params); nla_nest_end(msg, params);
os_memset(freq_list, 0, *num * sizeof(freq_list[0])); if (freq_list)
os_memset(freq_list, 0, *num * sizeof(struct weighted_pcl));
ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param, ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param,
NULL, NULL); NULL, NULL);
if (ret) { if (ret) {
@ -11390,8 +11459,10 @@ static int nl80211_get_pref_freq_list(void *priv,
*num = param.num; *num = param.num;
for (i = 0; i < *num; i++) { for (i = 0; i < *num; i++) {
wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", wpa_printf(MSG_DEBUG,
i, freq_list[i]); "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x",
i, freq_list[i].freq, freq_list[i].weight,
freq_list[i].flag);
} }
return 0; return 0;

View file

@ -5534,7 +5534,7 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
void p2p_set_own_pref_freq_list(struct p2p_data *p2p, void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
const unsigned int *pref_freq_list, const struct weighted_pcl *pref_freq_list,
unsigned int size) unsigned int size)
{ {
unsigned int i; unsigned int i;
@ -5542,10 +5542,11 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
if (size > P2P_MAX_PREF_CHANNELS) if (size > P2P_MAX_PREF_CHANNELS)
size = P2P_MAX_PREF_CHANNELS; size = P2P_MAX_PREF_CHANNELS;
p2p->num_pref_freq = size; p2p->num_pref_freq = size;
os_memcpy(p2p->pref_freq_list, pref_freq_list,
size * sizeof(struct weighted_pcl));
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
p2p->pref_freq_list[i] = pref_freq_list[i];
p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
i, p2p->pref_freq_list[i]); i, p2p->pref_freq_list[i].freq);
} }
} }

View file

@ -12,6 +12,8 @@
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#include "wps/wps.h" #include "wps/wps.h"
struct weighted_pcl;
/* P2P ASP Setup Capability */ /* P2P ASP Setup Capability */
#define P2PS_SETUP_NONE 0 #define P2PS_SETUP_NONE 0
#define P2PS_SETUP_NEW BIT(0) #define P2PS_SETUP_NEW BIT(0)
@ -1132,7 +1134,8 @@ struct p2p_config {
* the driver specific to a particular interface type. * the driver specific to a particular interface type.
*/ */
int (*get_pref_freq_list)(void *ctx, int go, int (*get_pref_freq_list)(void *ctx, int go,
unsigned int *len, unsigned int *freq_list); unsigned int *len,
struct weighted_pcl *freq_list);
}; };
@ -2338,6 +2341,8 @@ struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
const u8 *go_dev_addr, const u8 *go_dev_addr,
const u8 *ssid, size_t ssid_len); const u8 *ssid, size_t ssid_len);
bool p2p_pref_freq_allowed(const struct weighted_pcl *freq_list, bool go);
struct p2p_nfc_params { struct p2p_nfc_params {
int sel; int sel;
const u8 *wsc_attr; const u8 *wsc_attr;
@ -2397,7 +2402,7 @@ struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
void p2p_expire_peers(struct p2p_data *p2p); void p2p_expire_peers(struct p2p_data *p2p);
void p2p_set_own_pref_freq_list(struct p2p_data *p2p, void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
const unsigned int *pref_freq_list, const struct weighted_pcl *pref_freq_list,
unsigned int size); unsigned int size);
void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
u8 chan); u8 chan);
@ -2422,6 +2427,6 @@ bool p2p_peer_wfd_enabled(struct p2p_data *p2p, const u8 *peer_addr);
bool p2p_wfd_enabled(struct p2p_data *p2p); bool p2p_wfd_enabled(struct p2p_data *p2p);
bool is_p2p_allow_6ghz(struct p2p_data *p2p); bool is_p2p_allow_6ghz(struct p2p_data *p2p);
void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value); void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value);
int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size); int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size);
#endif /* P2P_H */ #endif /* P2P_H */

View file

@ -112,7 +112,7 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
void p2p_buf_add_pref_channel_list(struct wpabuf *buf, void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
const unsigned int *preferred_freq_list, const struct weighted_pcl *pref_freq_list,
unsigned int size) unsigned int size)
{ {
unsigned int i, count = 0; unsigned int i, count = 0;
@ -127,8 +127,9 @@ void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
* of the vendor IE size. * of the vendor IE size.
*/ */
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
&op_channel) == 0) &op_channel) == 0 &&
!(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE))
count++; count++;
} }
@ -137,10 +138,11 @@ void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
wpabuf_put_be24(buf, OUI_QCA); wpabuf_put_be24(buf, OUI_QCA);
wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class,
&op_channel) < 0) { &op_channel) < 0 ||
(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) {
wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
preferred_freq_list[i]); pref_freq_list[i].freq);
continue; continue;
} }
wpabuf_put_u8(buf, op_class); wpabuf_put_u8(buf, op_class);

View file

@ -182,8 +182,20 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
p2p_buf_add_intended_addr(buf, p2p->intended_addr); p2p_buf_add_intended_addr(buf, p2p->intended_addr);
is_6ghz_capab = is_p2p_6ghz_capable(p2p) && is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels, if (p2p->num_pref_freq) {
is_6ghz_capab); bool go = p2p->go_intent == 15;
struct p2p_channels pref_chanlist;
p2p_pref_channel_filter(&p2p->channels, p2p->pref_freq_list,
p2p->num_pref_freq, &pref_chanlist, go);
p2p_channels_dump(p2p, "channel list after filtering",
&pref_chanlist);
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&pref_chanlist, is_6ghz_capab);
} else {
p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels, is_6ghz_capab);
}
p2p_buf_add_device_info(buf, p2p, peer); p2p_buf_add_device_info(buf, p2p, peer);
p2p_buf_add_operating_channel(buf, p2p->cfg->country, p2p_buf_add_operating_channel(buf, p2p->cfg->country,
p2p->op_reg_class, p2p->op_channel); p2p->op_reg_class, p2p->op_channel);
@ -283,6 +295,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
size_t extra = 0; size_t extra = 0;
u16 pw_id; u16 pw_id;
bool is_6ghz_capab; bool is_6ghz_capab;
struct p2p_channels pref_chanlist;
p2p_dbg(p2p, "Building GO Negotiation Response"); p2p_dbg(p2p, "Building GO Negotiation Response");
@ -333,20 +346,32 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
p2p->op_channel); p2p->op_channel);
} }
p2p_buf_add_intended_addr(buf, p2p->intended_addr); p2p_buf_add_intended_addr(buf, p2p->intended_addr);
if (p2p->num_pref_freq) {
bool go = (peer && peer->go_state == LOCAL_GO) ||
p2p->go_intent == 15;
p2p_pref_channel_filter(&p2p->channels, p2p->pref_freq_list,
p2p->num_pref_freq, &pref_chanlist, go);
p2p_channels_dump(p2p, "channel list after filtering",
&pref_chanlist);
} else {
p2p_copy_channels(&pref_chanlist, &p2p->channels,
p2p->allow_6ghz);
}
if (status || peer == NULL) { if (status || peer == NULL) {
p2p_buf_add_channel_list(buf, p2p->cfg->country, p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels, false); &pref_chanlist, false);
} else if (peer->go_state == REMOTE_GO) { } else if (peer->go_state == REMOTE_GO) {
is_6ghz_capab = is_p2p_6ghz_capable(p2p) && is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
p2p_buf_add_channel_list(buf, p2p->cfg->country, p2p_buf_add_channel_list(buf, p2p->cfg->country,
&p2p->channels, is_6ghz_capab); &pref_chanlist, is_6ghz_capab);
} else { } else {
struct p2p_channels res; struct p2p_channels res;
is_6ghz_capab = is_p2p_6ghz_capable(p2p) && is_6ghz_capab = is_p2p_6ghz_capable(p2p) &&
p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr);
p2p_channels_intersect(&p2p->channels, &peer->channels, p2p_channels_intersect(&pref_chanlist, &peer->channels,
&res); &res);
p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, p2p_buf_add_channel_list(buf, p2p->cfg->country, &res,
is_6ghz_capab); is_6ghz_capab);
@ -573,7 +598,8 @@ int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
struct p2p_device *dev, struct p2p_device *dev,
struct p2p_message *msg, struct p2p_message *msg,
unsigned freq_list[], unsigned int size) const struct weighted_pcl freq_list[],
unsigned int size)
{ {
u8 op_class, op_channel; u8 op_class, op_channel;
unsigned int oper_freq = 0, i, j; unsigned int oper_freq = 0, i, j;
@ -588,10 +614,11 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
*/ */
for (i = 0; i < size && !found; i++) { for (i = 0; i < size && !found; i++) {
/* Make sure that the common frequency is supported by peer. */ /* Make sure that the common frequency is supported by peer. */
oper_freq = freq_list[i]; oper_freq = freq_list[i].freq;
if (p2p_freq_to_channel(oper_freq, &op_class, if (p2p_freq_to_channel(oper_freq, &op_class,
&op_channel) < 0) &op_channel) < 0 ||
continue; /* cannot happen due to earlier check */ !p2p_pref_freq_allowed(&freq_list[i], go))
continue;
for (j = 0; j < msg->channel_list_len; j++) { for (j = 0; j < msg->channel_list_len; j++) {
if (!msg->channel_list || if (!msg->channel_list ||
op_channel != msg->channel_list[j]) op_channel != msg->channel_list[j])
@ -620,7 +647,8 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
struct p2p_device *dev, struct p2p_device *dev,
struct p2p_message *msg, struct p2p_message *msg,
unsigned freq_list[], unsigned int size) const struct weighted_pcl freq_list[],
unsigned int size)
{ {
u8 op_class, op_channel; u8 op_class, op_channel;
unsigned int oper_freq = 0, i, j; unsigned int oper_freq = 0, i, j;
@ -636,11 +664,13 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
oper_freq = p2p_channel_to_freq( oper_freq = p2p_channel_to_freq(
msg->pref_freq_list[2 * j], msg->pref_freq_list[2 * j],
msg->pref_freq_list[2 * j + 1]); msg->pref_freq_list[2 * j + 1]);
if (freq_list[i] != oper_freq) if (freq_list[i].freq != oper_freq)
continue; continue;
if (p2p_freq_to_channel(oper_freq, &op_class, if (p2p_freq_to_channel(oper_freq, &op_class,
&op_channel) < 0) &op_channel) < 0)
continue; /* cannot happen */ continue; /* cannot happen */
if (!p2p_pref_freq_allowed(&freq_list[i], go))
break;
p2p->op_reg_class = op_class; p2p->op_reg_class = op_class;
p2p->op_channel = op_channel; p2p->op_channel = op_channel;
os_memcpy(&p2p->channels, &p2p->cfg->channels, os_memcpy(&p2p->channels, &p2p->cfg->channels,
@ -663,7 +693,7 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
void p2p_check_pref_chan(struct p2p_data *p2p, int go, void p2p_check_pref_chan(struct p2p_data *p2p, int go,
struct p2p_device *dev, struct p2p_message *msg) struct p2p_device *dev, struct p2p_message *msg)
{ {
unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; unsigned int size;
unsigned int i; unsigned int i;
u8 op_class, op_channel; u8 op_class, op_channel;
char txt[100], *pos, *end; char txt[100], *pos, *end;
@ -680,25 +710,30 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
/* Obtain our preferred frequency list from driver based on P2P role. */ /* Obtain our preferred frequency list from driver based on P2P role. */
size = P2P_MAX_PREF_CHANNELS; size = P2P_MAX_PREF_CHANNELS;
if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go,
freq_list)) &p2p->num_pref_freq,
p2p->pref_freq_list))
return;
size = p2p->num_pref_freq;
if (!size)
return; return;
/* Filter out frequencies that are not acceptable for P2P use */ /* Filter out frequencies that are not acceptable for P2P use */
i = 0; i = 0;
while (i < size) { while (i < size) {
if (p2p_freq_to_channel(freq_list[i], &op_class, if (p2p_freq_to_channel(p2p->pref_freq_list[i].freq,
&op_channel) < 0 || &op_class, &op_channel) < 0 ||
(!p2p_channels_includes(&p2p->cfg->channels, (!p2p_channels_includes(&p2p->cfg->channels,
op_class, op_channel) && op_class, op_channel) &&
(go || !p2p_channels_includes(&p2p->cfg->cli_channels, (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
op_class, op_channel)))) { op_class, op_channel)))) {
p2p_dbg(p2p, p2p_dbg(p2p,
"Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
freq_list[i], go); p2p->pref_freq_list[i].freq, go);
if (size - i - 1 > 0) if (size - i - 1 > 0)
os_memmove(&freq_list[i], &freq_list[i + 1], os_memmove(&p2p->pref_freq_list[i],
&p2p->pref_freq_list[i + 1],
(size - i - 1) * (size - i - 1) *
sizeof(unsigned int)); sizeof(struct weighted_pcl));
size--; size--;
continue; continue;
} }
@ -710,7 +745,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
pos = txt; pos = txt;
end = pos + sizeof(txt); end = pos + sizeof(txt);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
res = os_snprintf(pos, end - pos, " %u", freq_list[i]); res = os_snprintf(pos, end - pos, " %u",
p2p->pref_freq_list[i].freq);
if (os_snprintf_error(end - pos, res)) if (os_snprintf_error(end - pos, res))
break; break;
pos += res; pos += res;
@ -724,11 +760,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
* our preferred channel list. * our preferred channel list.
*/ */
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (freq_list[i] == (unsigned int) dev->oper_freq) if (p2p->pref_freq_list[i].freq ==
(unsigned int) dev->oper_freq &&
p2p_pref_freq_allowed(&p2p->pref_freq_list[i], go))
break; break;
} }
if (i != size && if (i != size &&
p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) { p2p_freq_to_channel(p2p->pref_freq_list[i].freq, &op_class,
&op_channel) == 0) {
/* Peer operating channel preference matches our preference */ /* Peer operating channel preference matches our preference */
p2p->op_reg_class = op_class; p2p->op_reg_class = op_class;
p2p->op_channel = op_channel; p2p->op_channel = op_channel;
@ -746,9 +785,11 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go,
* _not_ included in the GO Negotiation Request or Invitation Request. * _not_ included in the GO Negotiation Request or Invitation Request.
*/ */
if (msg->pref_freq_list_len == 0) if (msg->pref_freq_list_len == 0)
p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size); p2p_check_pref_chan_no_recv(p2p, go, dev, msg,
p2p->pref_freq_list, size);
else else
p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size); p2p_check_pref_chan_recv(p2p, go, dev, msg,
p2p->pref_freq_list, size);
} }

View file

@ -10,6 +10,7 @@
#define P2P_I_H #define P2P_I_H
#include "utils/list.h" #include "utils/list.h"
#include "drivers/driver.h"
#include "p2p.h" #include "p2p.h"
#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
@ -542,7 +543,7 @@ struct p2p_data {
struct wpabuf **vendor_elem; struct wpabuf **vendor_elem;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int num_pref_freq; unsigned int num_pref_freq;
/* Override option for preferred operating channel in GO Negotiation */ /* Override option for preferred operating channel in GO Negotiation */
@ -789,7 +790,7 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id, int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
int all_attr); int all_attr);
void p2p_buf_add_pref_channel_list(struct wpabuf *buf, void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
const unsigned int *preferred_freq_list, const struct weighted_pcl *pref_freq_list,
unsigned int size); unsigned int size);
/* p2p_sd.c */ /* p2p_sd.c */
@ -891,6 +892,10 @@ int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx); void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
u8 *status); u8 *status);
void p2p_pref_channel_filter(const struct p2p_channels *a,
const struct weighted_pcl *freq_list,
unsigned int num_channels,
struct p2p_channels *res, bool 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

@ -519,14 +519,14 @@ void p2p_copy_channels(struct p2p_channels *dst,
} }
int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size) int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size)
{ {
int i; int i;
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
if (is_6ghz_freq(pref_freq_list[i])) { if (is_6ghz_freq(pref_freq_list[i].freq)) {
wpa_printf(MSG_DEBUG, "P2P: Remove 6 GHz channel %d", wpa_printf(MSG_DEBUG, "P2P: Remove 6 GHz channel %d",
pref_freq_list[i]); pref_freq_list[i].freq);
size--; size--;
os_memmove(&pref_freq_list[i], &pref_freq_list[i + 1], os_memmove(&pref_freq_list[i], &pref_freq_list[i + 1],
(size - i) * sizeof(pref_freq_list[0])); (size - i) * sizeof(pref_freq_list[0]));
@ -535,3 +535,79 @@ int p2p_remove_6ghz_channels(unsigned int *pref_freq_list, int size)
} }
return i; return i;
} }
/**
* p2p_pref_freq_allowed - Based on the flags set, check if the preferred
* frequency is allowed
* @freq_list: Weighted preferred channel list
* @go: Whether the local device is the group owner
* Returns: Whether the preferred frequency is allowed
*/
bool p2p_pref_freq_allowed(const struct weighted_pcl *freq_list, bool go)
{
if (freq_list->flag & WEIGHTED_PCL_EXCLUDE)
return false;
if (!(freq_list->flag & WEIGHTED_PCL_CLI) && !go)
return false;
if (!(freq_list->flag & WEIGHTED_PCL_GO) && go)
return false;
return true;
}
static int p2p_check_pref_channel(int channel, u8 op_class,
const struct weighted_pcl *freq_list,
unsigned int num_channels, bool go)
{
unsigned int i;
/* If the channel is present in the preferred channel list, check if it
* has appropriate flags for the role.
*/
for (i = 0; i < num_channels; i++) {
if (p2p_channel_to_freq(op_class, channel) !=
(int) freq_list[i].freq)
continue;
if (!p2p_pref_freq_allowed(&freq_list[i], go))
return -1;
break;
}
return 0;
}
void p2p_pref_channel_filter(const struct p2p_channels *p2p_chan,
const struct weighted_pcl *freq_list,
unsigned int num_channels,
struct p2p_channels *res, bool go)
{
size_t i, j;
os_memset(res, 0, sizeof(*res));
for (i = 0; i < p2p_chan->reg_classes; i++) {
const struct p2p_reg_class *reg = &p2p_chan->reg_class[i];
struct p2p_reg_class *res_reg = &res->reg_class[i];
if (num_channels > 0) {
for (j = 0; j < reg->channels; j++) {
if (p2p_check_pref_channel(reg->channel[j],
reg->reg_class,
freq_list,
num_channels,
go) < 0)
continue;
res_reg->channel[res_reg->channels++] =
reg->channel[j];
}
}
if (res_reg->channels == 0)
continue;
res->reg_classes++;
res_reg->reg_class = reg->reg_class;
}
}

View file

@ -8123,7 +8123,7 @@ static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s,
int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type if_type, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *num,
unsigned int *freq_list) struct weighted_pcl *freq_list)
{ {
char *pos = wpa_s->get_pref_freq_list_override; char *pos = wpa_s->get_pref_freq_list_override;
char *end; char *end;
@ -8147,7 +8147,8 @@ int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
pos++; pos++;
end = os_strchr(pos, ' '); end = os_strchr(pos, ' ');
while (pos && (!end || pos < end) && count < *num) { while (pos && (!end || pos < end) && count < *num) {
freq_list[count++] = atoi(pos); freq_list[count].freq = atoi(pos);
freq_list[count++].flag = WEIGHTED_PCL_GO | WEIGHTED_PCL_CLI;
pos = os_strchr(pos, ','); pos = os_strchr(pos, ',');
if (pos) if (pos)
pos++; pos++;
@ -8162,10 +8163,11 @@ int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
static int wpas_ctrl_iface_get_pref_freq_list( static int wpas_ctrl_iface_get_pref_freq_list(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{ {
unsigned int freq_list[100], num = 100, i; unsigned int num = 100, i;
int ret; int ret;
enum wpa_driver_if_type iface_type; enum wpa_driver_if_type iface_type;
char *pos, *end; char *pos, *end;
struct weighted_pcl freq_list[100];
pos = buf; pos = buf;
end = buf + buflen; end = buf + buflen;
@ -8196,7 +8198,7 @@ static int wpas_ctrl_iface_get_pref_freq_list(
for (i = 0; i < num; i++) { for (i = 0; i < num; i++) {
ret = os_snprintf(pos, end - pos, "%s%u", ret = os_snprintf(pos, end - pos, "%s%u",
i > 0 ? "," : "", freq_list[i]); i > 0 ? "," : "", freq_list[i].freq);
if (os_snprintf_error(end - pos, ret)) if (os_snprintf_error(end - pos, ret))
return -1; return -1;
pos += ret; pos += ret;

View file

@ -964,7 +964,7 @@ static inline int wpa_drv_setband(struct wpa_supplicant *wpa_s,
static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s, static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type if_type, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *num,
unsigned int *freq_list) struct weighted_pcl *freq_list)
{ {
#ifdef CONFIG_TESTING_OPTIONS #ifdef CONFIG_TESTING_OPTIONS
if (wpa_s->get_pref_freq_list_override) if (wpa_s->get_pref_freq_list_override)

View file

@ -126,7 +126,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len); const u8 *ssid, size_t ssid_len);
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 go, int *force_freq, int *pref_freq, int go,
unsigned int *pref_freq_list, struct weighted_pcl *pref_freq_list,
unsigned int *num_pref_freq); unsigned int *num_pref_freq);
static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
const u8 *ssid, size_t ssid_len); const u8 *ssid, size_t ssid_len);
@ -702,7 +702,8 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
struct wpa_supplicant *go_wpa_s, *cli_wpa_s; struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
struct wpa_ssid *persistent_go; struct wpa_ssid *persistent_go;
int p2p_no_group_iface; int p2p_no_group_iface;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int size;
wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
@ -4701,7 +4702,7 @@ static int wpas_prov_disc_resp_cb(void *ctx)
static int wpas_p2p_get_pref_freq_list(void *ctx, int go, static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
unsigned int *len, unsigned int *len,
unsigned int *freq_list) struct weighted_pcl *freq_list)
{ {
struct wpa_supplicant *wpa_s = ctx; struct wpa_supplicant *wpa_s = ctx;
@ -5679,7 +5680,7 @@ 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, static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
int *force_freq, int *pref_freq, int go, int *force_freq, int *pref_freq, int go,
unsigned int *pref_freq_list, struct weighted_pcl *pref_freq_list,
unsigned int *num_pref_freq) unsigned int *num_pref_freq)
{ {
struct wpa_used_freq_data *freqs; struct wpa_used_freq_data *freqs;
@ -5776,16 +5777,19 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
i = 0; i = 0;
while (i < *num_pref_freq && while (i < *num_pref_freq &&
(!p2p_supported_freq(wpa_s->global->p2p, (!p2p_supported_freq(wpa_s->global->p2p,
pref_freq_list[i]) || pref_freq_list[i].freq) ||
wpas_p2p_disallowed_freq(wpa_s->global, wpas_p2p_disallowed_freq(
pref_freq_list[i]))) { wpa_s->global,
pref_freq_list[i].freq) ||
!p2p_pref_freq_allowed(&pref_freq_list[i],
go))) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed", "P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]); i, pref_freq_list[i].freq);
i++; i++;
} }
if (i != *num_pref_freq) { if (i != *num_pref_freq) {
best_freq = pref_freq_list[i]; best_freq = pref_freq_list[i].freq;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"P2P: Using preferred_freq_list[%d]=%d", "P2P: Using preferred_freq_list[%d]=%d",
i, best_freq); i, best_freq);
@ -5907,7 +5911,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
enum wpa_driver_if_type iftype; enum wpa_driver_if_type iftype;
const u8 *if_addr; const u8 *if_addr;
struct wpa_ssid *ssid = NULL; struct wpa_ssid *ssid = NULL;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int size;
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;
@ -6192,7 +6197,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
if (!wpa_s->conf->num_p2p_pref_chan && !freq) { if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
unsigned int i, size = P2P_MAX_PREF_CHANNELS; unsigned int i, size = P2P_MAX_PREF_CHANNELS;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
int res; int res;
res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO, res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
@ -6204,16 +6209,19 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
i = 0; i = 0;
while (i < size && while (i < size &&
(!p2p_supported_freq(wpa_s->global->p2p, (!p2p_supported_freq(wpa_s->global->p2p,
pref_freq_list[i]) || pref_freq_list[i].freq) ||
wpas_p2p_disallowed_freq(wpa_s->global, wpas_p2p_disallowed_freq(
pref_freq_list[i]))) { wpa_s->global,
pref_freq_list[i].freq) ||
!p2p_pref_freq_allowed(&pref_freq_list[i],
true))) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"P2P: preferred_freq_list[%d]=%d is disallowed", "P2P: preferred_freq_list[%d]=%d is disallowed",
i, pref_freq_list[i]); i, pref_freq_list[i].freq);
i++; i++;
} }
if (i != size) { if (i != size) {
freq = pref_freq_list[i]; freq = pref_freq_list[i].freq;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"P2P: Using preferred_freq_list[%d]=%d", "P2P: Using preferred_freq_list[%d]=%d",
i, freq); i, freq);
@ -7562,7 +7570,8 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
int force_freq = 0; int force_freq = 0;
int res; int res;
int no_pref_freq_given = pref_freq == 0; int no_pref_freq_given = pref_freq == 0;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int size;
if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq)) if (wpas_p2p_check_6ghz(wpa_s, NULL, allow_6ghz, freq))
return -1; return -1;
@ -7651,7 +7660,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
int persistent; int persistent;
int freq = 0, force_freq = 0, pref_freq = 0; int freq = 0, force_freq = 0, pref_freq = 0;
int res; int res;
unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size; struct weighted_pcl pref_freq_list[P2P_MAX_PREF_CHANNELS];
unsigned int size;
wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0; wpa_s->p2p_go_ht40 = 0;

View file

@ -1854,7 +1854,7 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
enum wpa_driver_if_type if_type, enum wpa_driver_if_type if_type,
unsigned int *num, unsigned int *num,
unsigned int *freq_list); struct weighted_pcl *freq_list);
int wpa_is_fils_supported(struct wpa_supplicant *wpa_s); int wpa_is_fils_supported(struct wpa_supplicant *wpa_s);
int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s); int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s);