]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
P2P: Use weighted preferred channel list for channel selection
authorSreeramya Soratkal <quic_ssramya@quicinc.com>
Thu, 5 Nov 2020 16:25:55 +0000 (21:55 +0530)
committerJouni Malinen <j@w1.fi>
Thu, 2 Jun 2022 14:09:10 +0000 (17:09 +0300)
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>
12 files changed:
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_build.c
src/p2p/p2p_go_neg.c
src/p2p/p2p_i.h
src/p2p/p2p_utils.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_supplicant_i.h

index 677b729d5d4afad0bca91da3b99a476febe5f2f3..f069b74e14c9dcc81190acfc5c70fce71dbbbe01 100644 (file)
@@ -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
index 0127a6be28ed484a038a0db98c8a9632e5d68ba3..60e44a1a9fb06396b82253cef7f45cd131868717 100644 (file)
@@ -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(&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,
                           "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, &param,
                                 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;
index 14eacf946ea85f1936957fd174c524da4e9540cf..a5e3c15c7ebac6df7e8cebf546a04f12d4825b59 100644 (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,
-                               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);
        }
 }
 
index f606fbb14a815392d98e7a8e37107efb11774d5a..768fc106e614ca57921c1c33ac8e2f06cfd1ac8b 100644 (file)
@@ -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 */
index a484fb0b0f123796e95071e1e134691d5067b7b1..e4f40fe8fcc0f26b4927df2fec20188e5379b390 100644 (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,
-                                  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);
index 303aa7dd2aefcb88a611cdced7040f53e4bdf9f6..e3d704b8eb82011ef9201349156b678e6185b4a0 100644 (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);
        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);
 }
 
 
index 59790de9c0643951e564904e49f29e9a30f34cc0..6573783fa69b29f194488f65074c6578afcf9f3a 100644 (file)
@@ -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, ...)
index a203606331e27566788921029138a924868d89bf..c1f0084b8d639eaa259f9be5244d512e866566d4 100644 (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;
 
        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;
+       }
+}
index 606c79bc2cc9f27fc8884942c59a2aa2c634ae0b..499e6d3dd5d890609489540795c5091f7847dd36 100644 (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,
                                                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;
index 237f4e08516fd237fdb7ebc18b3697e93ac55e6f..b0af1cd989142514478a7024a07b4d045d383cd5 100644 (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,
                                             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)
index a996b436b4297ab779edf15d9429500af641e5d8..6a23dd99862d4282b02a8478d28093355661ea76 100644 (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);
 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;
index ec0360e7056775a4acf79dfe4aeff931d9da4492..301d5d56a857eb6f783b11a7c154118e365f3f34 100644 (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,
                                                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);