]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
P2P: Add support for preferred channel list
authorJouni Malinen <jouni@qca.qualcomm.com>
Thu, 29 Mar 2012 18:28:34 +0000 (21:28 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 29 Mar 2012 18:28:34 +0000 (21:28 +0300)
p2p_pref_chan configuration parameter can now be used to set the
list of preferred channel for P2P GO Negotiation. This will be used
in the priority order if the peer does not support the channel we
are trying to use as the GO (configured operating channel or the
best 2.4 GHz/5 GHz channel) for the case where a forced channel is
not used.

p2p_pref_chan=<op class:channel>,...

For example:
p2p_pref_chan=81:1,81:2,81:3,81:4,81:5,81:6

This would configure 2.4 GHz channels 1-6 as the preferred ones with
channel 1 the most preferred option.

These configuration parameters can be set in wpa_supplicant.conf and
dynamically updated with "wpa_cli set <param> <value>".

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>

src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_go_neg.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/p2p_supplicant.c

index 6bbc950b2a305a588a8166d71f0799f0237f5765..5bdf4733fcbb69325d1bcac42dd9f8bf6a15f45b 100644 (file)
@@ -2212,6 +2212,16 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
                p2p->cfg->model_number = os_strdup(cfg->model_number);
        if (cfg->serial_number)
                p2p->cfg->serial_number = os_strdup(cfg->serial_number);
+       if (cfg->pref_chan) {
+               p2p->cfg->pref_chan = os_malloc(cfg->num_pref_chan *
+                                               sizeof(struct p2p_channel));
+               if (p2p->cfg->pref_chan) {
+                       os_memcpy(p2p->cfg->pref_chan, cfg->pref_chan,
+                                 cfg->num_pref_chan *
+                                 sizeof(struct p2p_channel));
+               } else
+                       p2p->cfg->num_pref_chan = 0;
+       }
 
        p2p->min_disc_int = 1;
        p2p->max_disc_int = 3;
@@ -2246,6 +2256,7 @@ void p2p_deinit(struct p2p_data *p2p)
        os_free(p2p->cfg->model_name);
        os_free(p2p->cfg->model_number);
        os_free(p2p->cfg->serial_number);
+       os_free(p2p->cfg->pref_chan);
        os_free(p2p->groups);
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
@@ -3701,6 +3712,28 @@ int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
 }
 
 
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+                     const struct p2p_channel *pref_chan)
+{
+       struct p2p_channel *n;
+
+       if (pref_chan) {
+               n = os_malloc(num_pref_chan * sizeof(struct p2p_channel));
+               if (n == NULL)
+                       return -1;
+               os_memcpy(n, pref_chan,
+                         num_pref_chan * sizeof(struct p2p_channel));
+       } else
+               n = NULL;
+
+       os_free(p2p->cfg->pref_chan);
+       p2p->cfg->pref_chan = n;
+       p2p->cfg->num_pref_chan = num_pref_chan;
+
+       return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr)
 {
index f8fa0616ce06c041473ee6484120438b82ed32da..31d5cd1200dd2712a0b15e73db1e9fe4dd238545 100644 (file)
@@ -218,6 +218,11 @@ enum p2p_prov_disc_status {
        P2P_PROV_DISC_REJECTED,
 };
 
+struct p2p_channel {
+       u8 op_class;
+       u8 chan;
+};
+
 /**
  * struct p2p_config - P2P configuration
  *
@@ -264,6 +269,16 @@ struct p2p_config {
         */
        struct p2p_channels channels;
 
+       /**
+        * num_pref_chan - Number of pref_chan entries
+        */
+       unsigned int num_pref_chan;
+
+       /**
+        * pref_chan - Preferred channels for GO Negotiation
+        */
+       struct p2p_channel *pref_chan;
+
        /**
         * pri_dev_type - Primary Device Type (see WPS)
         */
@@ -1608,6 +1623,16 @@ int p2p_add_wps_vendor_extension(struct p2p_data *p2p,
 int p2p_set_oper_channel(struct p2p_data *p2p, u8 op_reg_class, u8 op_channel,
                         int cfg_op_channel);
 
+/**
+ * p2p_set_pref_chan - Set P2P preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @num_pref_chan: Number of entries in pref_chan list
+ * @pref_chan: Preferred channels or %NULL to remove preferences
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
+                     const struct p2p_channel *pref_chan);
+
 /**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
index 52162121af4821c2e9af3e5ff9d04a626eba660a..230948db9d31f3eb90e78487cf48aa5a964557c1 100644 (file)
@@ -296,6 +296,7 @@ static void p2p_reselect_channel(struct p2p_data *p2p,
        struct p2p_reg_class *cl;
        int freq;
        u8 op_reg_class, op_channel;
+       unsigned int i;
 
        wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Selected operating "
                "channel (reg_class %u channel %u) not acceptable to the "
@@ -328,6 +329,21 @@ static void p2p_reselect_channel(struct p2p_data *p2p,
                return;
        }
 
+       /* Select channel with highest preference if the peer supports it */
+       for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+               if (p2p_channels_includes(intersection,
+                                         p2p->cfg->pref_chan[i].op_class,
+                                         p2p->cfg->pref_chan[i].chan)) {
+                       p2p->op_reg_class = p2p->cfg->pref_chan[i].op_class;
+                       p2p->op_channel = p2p->cfg->pref_chan[i].chan;
+                       wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG, "P2P: Pick "
+                               "highest preferred chnnel (op_class %u "
+                               "channel %u) from intersection",
+                               p2p->op_reg_class, p2p->op_channel);
+                       return;
+               }
+       }
+
        /*
         * Fall back to whatever is included in the channel intersection since
         * no better options seems to be available.
index 9ca8f6387c2255f85234b01f5663a3df9d705832..5675d2e6398fc2a532fe7c48f2f1c6ca75c4a207 100644 (file)
@@ -13,6 +13,7 @@
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
+#include "p2p/p2p.h"
 #include "config.h"
 
 
@@ -1849,6 +1850,7 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->config_methods);
        os_free(config->p2p_ssid_postfix);
        os_free(config->pssid);
+       os_free(config->p2p_pref_chan);
        os_free(config);
 }
 
@@ -2692,6 +2694,55 @@ static int wpa_config_process_sec_device_type(
        config->num_sec_device_types++;
        return 0;
 }
+
+
+static int wpa_config_process_p2p_pref_chan(
+       const struct global_parse_data *data,
+       struct wpa_config *config, int line, const char *pos)
+{
+       struct p2p_channel *pref = NULL, *n;
+       unsigned int num = 0;
+       const char *pos2;
+       u8 op_class, chan;
+
+       /* format: class:chan,class:chan,... */
+
+       while (*pos) {
+               op_class = atoi(pos);
+               pos2 = os_strchr(pos, ':');
+               if (pos2 == NULL)
+                       goto fail;
+               pos2++;
+               chan = atoi(pos2);
+
+               n = os_realloc(pref, (num + 1) * sizeof(struct p2p_channel));
+               if (n == NULL)
+                       goto fail;
+               pref = n;
+               pref[num].op_class = op_class;
+               pref[num].chan = chan;
+               num++;
+
+               pos = os_strchr(pos2, ',');
+               if (pos == NULL)
+                       break;
+               pos++;
+       }
+
+       os_free(config->p2p_pref_chan);
+       config->p2p_pref_chan = pref;
+       config->num_p2p_pref_chan = num;
+       wpa_hexdump(MSG_DEBUG, "P2P: Preferred class/channel pairs",
+                   (u8 *) config->p2p_pref_chan,
+                   config->num_p2p_pref_chan * sizeof(struct p2p_channel));
+
+       return 0;
+
+fail:
+       os_free(pref);
+       wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
+       return -1;
+}
 #endif /* CONFIG_P2P */
 
 
@@ -2768,6 +2819,7 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(persistent_reconnect, 0, 1), 0 },
        { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
        { INT(p2p_group_idle), 0 },
+       { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
 #endif /* CONFIG_P2P */
        { FUNC(country), CFG_CHANGED_COUNTRY },
        { INT(bss_max_count), 0 },
index 5b002a204f14717b99d88f2d7eff1f3d19e263ff..ac4ecfaf02920025c5af6904e3f90e0457f855c9 100644 (file)
@@ -164,6 +164,7 @@ struct wpa_cred {
 #define CFG_CHANGED_VENDOR_EXTENSION BIT(10)
 #define CFG_CHANGED_P2P_LISTEN_CHANNEL BIT(11)
 #define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12)
+#define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -503,6 +504,8 @@ struct wpa_config {
        char *p2p_ssid_postfix;
        int persistent_reconnect;
        int p2p_intra_bss;
+       unsigned int num_p2p_pref_chan;
+       struct p2p_channel *p2p_pref_chan;
 
 #define MAX_WPS_VENDOR_EXT 10
        /**
index b3dd40e06afe72f081fe701d507b379b9c335334..cf5332014ff0cc8132075e008d3d7b17c0692455 100644 (file)
@@ -16,6 +16,7 @@
 #include "config.h"
 #include "base64.h"
 #include "uuid.h"
+#include "p2p/p2p.h"
 
 
 /**
@@ -790,6 +791,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
        if (config->p2p_group_idle)
                fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+       if (config->p2p_pref_chan) {
+               unsigned int i;
+               fprintf(f, "p2p_pref_chan=");
+               for (i = 0; i < config->num_p2p_pref_chan; i++) {
+                       fprintf(f, "%s%u:%u", i > 0 ? "," : "",
+                               config->p2p_pref_chan[i].op_class,
+                               config->p2p_pref_chan[i].chan);
+               }
+               fprintf(f, "\n");
+       }
 #endif /* CONFIG_P2P */
        if (config->country[0] && config->country[1]) {
                fprintf(f, "country=%c%c\n",
index 01b3e3a6c4c9aab6aeaabba1e473b21c9805c8eb..874fe819d1bdeacd0406837f115dc0df43b5f2f8 100644 (file)
@@ -4151,6 +4151,14 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                        wpa_printf(MSG_ERROR, "P2P: Own oper channel update "
                                   "failed: %d", ret);
        }
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PREF_CHAN) {
+               if (p2p_set_pref_chan(p2p, wpa_s->conf->num_p2p_pref_chan,
+                                     wpa_s->conf->p2p_pref_chan) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
+                                  "update failed");
+               }
+       }
 }