diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 677b729d5..f069b74e1 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -2602,6 +2602,24 @@ enum nested_attr { 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 * @@ -4446,14 +4464,17 @@ struct wpa_driver_ops { * @priv: Private driver interface data * @if_type: Interface type * @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 * * 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, - unsigned int *num, unsigned int *freq_list); + unsigned int *num, + struct weighted_pcl *freq_list); /** * set_prob_oper_freq - Indicate probable P2P operating channel diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 0127a6be2..60e44a1a9 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -11258,9 +11258,33 @@ static int nl80211_set_band(void *priv, u32 band_mask) struct nl80211_pcl { 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) { 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; enum qca_iface_type iface_type; 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; 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", iface_type); - attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST]; - if (!attr) { + attr = tb_vendor[ + 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(¶m->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, "nl80211: preferred_freq_list couldn't be found"); param->num = 0; 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; } @@ -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, enum wpa_driver_if_type if_type, unsigned int *num, - unsigned int *freq_list) + struct weighted_pcl *freq_list) { struct i802_bss *bss = priv; 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); - 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, ¶m, NULL, NULL); if (ret) { @@ -11390,8 +11459,10 @@ static int nl80211_get_pref_freq_list(void *priv, *num = param.num; for (i = 0; i < *num; i++) { - wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d", - i, freq_list[i]); + wpa_printf(MSG_DEBUG, + "nl80211: preferred_channel_list[%d]=%d[%d]:0x%x", + i, freq_list[i].freq, freq_list[i].weight, + freq_list[i].flag); } return 0; diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 14eacf946..a5e3c15c7 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -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, - const unsigned int *pref_freq_list, + const struct weighted_pcl *pref_freq_list, unsigned int size) { unsigned int i; @@ -5542,10 +5542,11 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, if (size > P2P_MAX_PREF_CHANNELS) size = P2P_MAX_PREF_CHANNELS; 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++) { - p2p->pref_freq_list[i] = pref_freq_list[i]; p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz", - i, p2p->pref_freq_list[i]); + i, p2p->pref_freq_list[i].freq); } } diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index f606fbb14..768fc106e 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -12,6 +12,8 @@ #include "common/ieee802_11_defs.h" #include "wps/wps.h" +struct weighted_pcl; + /* P2P ASP Setup Capability */ #define P2PS_SETUP_NONE 0 #define P2PS_SETUP_NEW BIT(0) @@ -1132,7 +1134,8 @@ struct p2p_config { * the driver specific to a particular interface type. */ 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 *ssid, size_t ssid_len); +bool p2p_pref_freq_allowed(const struct weighted_pcl *freq_list, bool go); + struct p2p_nfc_params { int sel; 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_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); void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, 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 is_p2p_allow_6ghz(struct p2p_data *p2p); 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 */ diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c index a484fb0b0..e4f40fe8f 100644 --- a/src/p2p/p2p_build.c +++ b/src/p2p/p2p_build.c @@ -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, - const unsigned int *preferred_freq_list, + const struct weighted_pcl *pref_freq_list, unsigned int size) { unsigned int i, count = 0; @@ -127,8 +127,9 @@ void p2p_buf_add_pref_channel_list(struct wpabuf *buf, * of the vendor IE size. */ for (i = 0; i < size; i++) { - if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, - &op_channel) == 0) + if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class, + &op_channel) == 0 && + !(pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) count++; } @@ -137,10 +138,11 @@ void p2p_buf_add_pref_channel_list(struct wpabuf *buf, wpabuf_put_be24(buf, OUI_QCA); wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST); for (i = 0; i < size; i++) { - if (p2p_freq_to_channel(preferred_freq_list[i], &op_class, - &op_channel) < 0) { + if (p2p_freq_to_channel(pref_freq_list[i].freq, &op_class, + &op_channel) < 0 || + (pref_freq_list[i].flag & WEIGHTED_PCL_EXCLUDE)) { wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz", - preferred_freq_list[i]); + pref_freq_list[i].freq); continue; } wpabuf_put_u8(buf, op_class); diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 303aa7dd2..e3d704b8e 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -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); is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); - p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->channels, - is_6ghz_capab); + if (p2p->num_pref_freq) { + 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_operating_channel(buf, p2p->cfg->country, 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; u16 pw_id; bool is_6ghz_capab; + struct p2p_channels pref_chanlist; 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_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) { p2p_buf_add_channel_list(buf, p2p->cfg->country, - &p2p->channels, false); + &pref_chanlist, false); } else if (peer->go_state == REMOTE_GO) { is_6ghz_capab = is_p2p_6ghz_capable(p2p) && p2p_is_peer_6ghz_capab(p2p, peer->info.p2p_device_addr); p2p_buf_add_channel_list(buf, p2p->cfg->country, - &p2p->channels, is_6ghz_capab); + &pref_chanlist, is_6ghz_capab); } else { struct p2p_channels res; is_6ghz_capab = is_p2p_6ghz_capable(p2p) && 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); p2p_buf_add_channel_list(buf, p2p->cfg->country, &res, 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, struct p2p_device *dev, struct p2p_message *msg, - unsigned freq_list[], unsigned int size) + const struct weighted_pcl freq_list[], + unsigned int size) { u8 op_class, op_channel; 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++) { /* 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, - &op_channel) < 0) - continue; /* cannot happen due to earlier check */ + &op_channel) < 0 || + !p2p_pref_freq_allowed(&freq_list[i], go)) + continue; for (j = 0; j < msg->channel_list_len; j++) { if (!msg->channel_list || 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, struct p2p_device *dev, struct p2p_message *msg, - unsigned freq_list[], unsigned int size) + const struct weighted_pcl freq_list[], + unsigned int size) { u8 op_class, op_channel; 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( msg->pref_freq_list[2 * j], msg->pref_freq_list[2 * j + 1]); - if (freq_list[i] != oper_freq) + if (freq_list[i].freq != oper_freq) continue; if (p2p_freq_to_channel(oper_freq, &op_class, &op_channel) < 0) continue; /* cannot happen */ + if (!p2p_pref_freq_allowed(&freq_list[i], go)) + break; p2p->op_reg_class = op_class; p2p->op_channel = op_channel; 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, struct p2p_device *dev, struct p2p_message *msg) { - unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; + unsigned int size; unsigned int i; u8 op_class, op_channel; 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. */ size = P2P_MAX_PREF_CHANNELS; - if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, - freq_list)) + if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, + &p2p->num_pref_freq, + p2p->pref_freq_list)) + return; + size = p2p->num_pref_freq; + if (!size) return; /* Filter out frequencies that are not acceptable for P2P use */ i = 0; while (i < size) { - if (p2p_freq_to_channel(freq_list[i], &op_class, - &op_channel) < 0 || + if (p2p_freq_to_channel(p2p->pref_freq_list[i].freq, + &op_class, &op_channel) < 0 || (!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, "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) - 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) * - sizeof(unsigned int)); + sizeof(struct weighted_pcl)); size--; continue; } @@ -710,7 +745,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, pos = txt; end = pos + sizeof(txt); 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)) break; pos += res; @@ -724,11 +760,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, * our preferred channel list. */ 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; } 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 */ p2p->op_reg_class = op_class; 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. */ 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 - 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); } diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 59790de9c..6573783fa 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -10,6 +10,7 @@ #define P2P_I_H #include "utils/list.h" +#include "drivers/driver.h" #include "p2p.h" #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1 @@ -542,7 +543,7 @@ struct p2p_data { 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; /* 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 all_attr); 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); /* 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); int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev, 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, ...) PRINTF_FORMAT(2, 3); void p2p_info(struct p2p_data *p2p, const char *fmt, ...) diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c index a20360633..c1f0084b8 100644 --- a/src/p2p/p2p_utils.c +++ b/src/p2p/p2p_utils.c @@ -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; 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", - pref_freq_list[i]); + pref_freq_list[i].freq); size--; os_memmove(&pref_freq_list[i], &pref_freq_list[i + 1], (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; } + + +/** + * 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; + } +} diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 606c79bc2..499e6d3dd 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -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, enum wpa_driver_if_type if_type, unsigned int *num, - unsigned int *freq_list) + struct weighted_pcl *freq_list) { char *pos = wpa_s->get_pref_freq_list_override; char *end; @@ -8147,7 +8147,8 @@ int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, pos++; end = os_strchr(pos, ' '); 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, ','); if (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( 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; enum wpa_driver_if_type iface_type; char *pos, *end; + struct weighted_pcl freq_list[100]; pos = buf; end = buf + buflen; @@ -8196,7 +8198,7 @@ static int wpas_ctrl_iface_get_pref_freq_list( for (i = 0; i < num; i++) { 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)) return -1; pos += ret; diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 237f4e085..b0af1cd98 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -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, enum wpa_driver_if_type if_type, unsigned int *num, - unsigned int *freq_list) + struct weighted_pcl *freq_list) { #ifdef CONFIG_TESTING_OPTIONS if (wpa_s->get_pref_freq_list_override) diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index a996b436b..6a23dd998 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -126,7 +126,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, const u8 *ssid, size_t ssid_len); static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, int *force_freq, int *pref_freq, int go, - unsigned int *pref_freq_list, + struct weighted_pcl *pref_freq_list, unsigned int *num_pref_freq); static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, 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_ssid *persistent_go; 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); @@ -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, unsigned int *len, - unsigned int *freq_list) + struct weighted_pcl *freq_list) { 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, int *force_freq, int *pref_freq, int go, - unsigned int *pref_freq_list, + struct weighted_pcl *pref_freq_list, unsigned int *num_pref_freq) { 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; while (i < *num_pref_freq && (!p2p_supported_freq(wpa_s->global->p2p, - pref_freq_list[i]) || - wpas_p2p_disallowed_freq(wpa_s->global, - pref_freq_list[i]))) { + pref_freq_list[i].freq) || + wpas_p2p_disallowed_freq( + wpa_s->global, + pref_freq_list[i].freq) || + !p2p_pref_freq_allowed(&pref_freq_list[i], + go))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", - i, pref_freq_list[i]); + i, pref_freq_list[i].freq); i++; } if (i != *num_pref_freq) { - best_freq = pref_freq_list[i]; + best_freq = pref_freq_list[i].freq; wpa_printf(MSG_DEBUG, "P2P: Using preferred_freq_list[%d]=%d", 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; const u8 *if_addr; 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) 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) { 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; 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; while (i < size && (!p2p_supported_freq(wpa_s->global->p2p, - pref_freq_list[i]) || - wpas_p2p_disallowed_freq(wpa_s->global, - pref_freq_list[i]))) { + pref_freq_list[i].freq) || + wpas_p2p_disallowed_freq( + wpa_s->global, + pref_freq_list[i].freq) || + !p2p_pref_freq_allowed(&pref_freq_list[i], + true))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", - i, pref_freq_list[i]); + i, pref_freq_list[i].freq); i++; } if (i != size) { - freq = pref_freq_list[i]; + freq = pref_freq_list[i].freq; wpa_printf(MSG_DEBUG, "P2P: Using preferred_freq_list[%d]=%d", i, freq); @@ -7562,7 +7570,8 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, int force_freq = 0; int res; 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)) return -1; @@ -7651,7 +7660,8 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, int persistent; int freq = 0, force_freq = 0, pref_freq = 0; 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_go_ht40 = 0; diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ec0360e70..301d5d56a 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -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, enum wpa_driver_if_type if_type, 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_sk_pfs_supported(struct wpa_supplicant *wpa_s);