P2P: Add option to remove channels from GO use
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 22 Oct 2013 16:45:46 +0000 (19:45 +0300)
committerJouni Malinen <j@w1.fi>
Sat, 26 Oct 2013 14:49:10 +0000 (17:49 +0300)
The new p2p_no_go_freq frequency range list (comma-separated list of
min-max frequency ranges in MHz) can now be used to configure channels
on which the local device is not allowed to operate as a GO, but on
which that device can be a P2P Client. These channels are left in the
P2P Channel List in GO Negotiation to allow the peer device to select
one of the channels for the cases where the peer becomes the GO. The
local end will remove these channels from consideration if it becomes
the GO.

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

src/p2p/p2p.c
src/p2p/p2p.h
src/p2p/p2p_go_neg.c
src/p2p/p2p_i.h
src/p2p/p2p_utils.c
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/wpa_cli.c

index aa1f291..21d3c7e 100644 (file)
@@ -1550,8 +1550,15 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
                }
        }
 
+       p2p_channels_dump(p2p, "own channels", &p2p->channels);
+       p2p_channels_dump(p2p, "peer channels", &peer->channels);
        p2p_channels_intersect(&p2p->channels, &peer->channels,
                               &intersection);
+       if (go) {
+               p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
+               p2p_channels_dump(p2p, "intersection after no-GO removal",
+                                 &intersection);
+       }
        freqs = 0;
        for (i = 0; i < intersection.reg_classes; i++) {
                struct p2p_reg_class *c = &intersection.reg_class[i];
@@ -2402,6 +2409,7 @@ void p2p_deinit(struct p2p_data *p2p)
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
        p2p_remove_wps_vendor_extensions(p2p);
+       os_free(p2p->no_go_freq.range);
        os_free(p2p);
 }
 
@@ -3933,6 +3941,31 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
 }
 
 
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+                      const struct wpa_freq_range_list *list)
+{
+       struct wpa_freq_range *tmp;
+
+       if (list == NULL || list->num == 0) {
+               os_free(p2p->no_go_freq.range);
+               p2p->no_go_freq.range = NULL;
+               p2p->no_go_freq.num = 0;
+               return 0;
+       }
+
+       tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
+       if (tmp == NULL)
+               return -1;
+       os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
+       os_free(p2p->no_go_freq.range);
+       p2p->no_go_freq.range = tmp;
+       p2p->no_go_freq.num = list->num;
+       p2p_dbg(p2p, "Updated no GO chan list");
+
+       return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr)
 {
index 9b6921e..db7ca9d 100644 (file)
@@ -1649,6 +1649,14 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels,
 int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
 
 /**
+ * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
+
+/**
  * p2p_get_pref_freq - Get channel from preferred channel list
  * @p2p: P2P module context from p2p_init()
  * @channels: List of channels
@@ -1765,6 +1773,15 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
                      const struct p2p_channel *pref_chan);
 
 /**
+ * p2p_set_no_go_freq - Set no GO channel ranges
+ * @p2p: P2P module context from p2p_init()
+ * @list: Channel ranges or %NULL to remove restriction
+ * Returns: 0 on success, -1 on failure
+ */
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+                      const struct wpa_freq_range_list *list);
+
+/**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
  * Returns: 0 if P2P module is idle or 1 if an operation is in progress
index 9e9d2ee..1c0aeb0 100644 (file)
@@ -479,6 +479,9 @@ static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
        p2p_channels_dump(p2p, "peer channels", &dev->channels);
        p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
        p2p_channels_dump(p2p, "intersection", &intersection);
+       p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
+       p2p_channels_dump(p2p, "intersection after no-GO removal",
+                         &intersection);
        if (intersection.reg_classes == 0 ||
            intersection.reg_class[0].channels == 0) {
                *status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
index 3631abd..e5075ae 100644 (file)
@@ -316,6 +316,8 @@ struct p2p_data {
         */
        struct p2p_channels channels;
 
+       struct wpa_freq_range_list no_go_freq;
+
        enum p2p_pending_action_state {
                P2P_NO_PENDING_ACTION,
                P2P_PENDING_GO_NEG_REQUEST,
@@ -570,6 +572,8 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
 void p2p_channels_intersect(const struct p2p_channels *a,
                            const struct p2p_channels *b,
                            struct p2p_channels *res);
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+                              const struct wpa_freq_range_list *list);
 int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
                          u8 channel);
 void p2p_channels_dump(struct p2p_data *p2p, const char *title,
index 8489b53..b7bbfcf 100644 (file)
@@ -204,6 +204,42 @@ void p2p_channels_intersect(const struct p2p_channels *a,
 }
 
 
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+                              const struct wpa_freq_range_list *list)
+{
+       size_t o, c;
+
+       if (list == NULL)
+               return;
+
+       o = 0;
+       while (o < chan->reg_classes) {
+               struct p2p_reg_class *op = &chan->reg_class[o];
+
+               c = 0;
+               while (c < op->channels) {
+                       int freq = p2p_channel_to_freq(op->reg_class,
+                                                      op->channel[c]);
+                       if (freq > 0 && freq_range_list_includes(list, freq)) {
+                               op->channels--;
+                               os_memmove(&op->channel[c],
+                                          &op->channel[c + 1],
+                                          op->channels - c);
+                       } else
+                               c++;
+               }
+
+               if (op->channels == 0) {
+                       chan->reg_classes--;
+                       os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1],
+                                  (chan->reg_classes - o) *
+                                  sizeof(struct p2p_reg_class));
+               } else
+                       o++;
+       }
+}
+
+
 /**
  * p2p_channels_includes - Check whether a channel is included in the list
  * @channels: List of supported channels
@@ -254,6 +290,17 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
 }
 
 
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq)
+{
+       u8 op_reg_class, op_channel;
+       if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
+               return 0;
+       return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                    op_channel) &&
+               !freq_range_list_includes(&p2p->no_go_freq, freq);
+}
+
+
 unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
                               const struct p2p_channels *channels)
 {
index 66ee30a..d9f2bc2 100644 (file)
@@ -1936,6 +1936,7 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->p2p_ssid_postfix);
        os_free(config->pssid);
        os_free(config->p2p_pref_chan);
+       os_free(config->p2p_no_go_freq.range);
        os_free(config->autoscan);
        os_free(config->freq_list);
        wpabuf_free(config->wps_nfc_dh_pubkey);
@@ -3079,6 +3080,26 @@ fail:
        wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
        return -1;
 }
+
+
+static int wpa_config_process_p2p_no_go_freq(
+       const struct global_parse_data *data,
+       struct wpa_config *config, int line, const char *pos)
+{
+       int ret;
+
+       ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
+                  config->p2p_no_go_freq.num);
+
+       return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -3229,6 +3250,7 @@ static const struct global_parse_data global_fields[] = {
        { 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 },
+       { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
        { INT(p2p_go_ht40), 0 },
        { INT(p2p_disabled), 0 },
        { INT(p2p_no_group_iface), 0 },
index 2e558fd..de8ba9e 100644 (file)
@@ -605,6 +605,7 @@ struct wpa_config {
        int p2p_intra_bss;
        unsigned int num_p2p_pref_chan;
        struct p2p_channel *p2p_pref_chan;
+       struct wpa_freq_range_list p2p_no_go_freq;
        int p2p_ignore_shared_freq;
 
        struct wpabuf *wps_vendor_ext_m1;
index b8fff70..75b3022 100644 (file)
@@ -941,6 +941,13 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                }
                fprintf(f, "\n");
        }
+       if (config->p2p_no_go_freq.num) {
+               char *val = freq_range_list_str(&config->p2p_no_go_freq);
+               if (val) {
+                       fprintf(f, "p2p_no_go_freq=%s\n", val);
+                       os_free(val);
+               }
+       }
        if (config->p2p_go_ht40)
                fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
        if (config->p2p_disabled)
index 817232b..8e7a45b 100644 (file)
@@ -3386,6 +3386,8 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
                        global->p2p, wpa_s->conf->wps_vendor_ext[i]);
        }
 
+       p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
+
        return 0;
 }
 
@@ -4313,8 +4315,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
                           "band");
                if (wpa_s->best_24_freq > 0 &&
-                   p2p_supported_freq(wpa_s->global->p2p,
-                                      wpa_s->best_24_freq)) {
+                   p2p_supported_freq_go(wpa_s->global->p2p,
+                                         wpa_s->best_24_freq)) {
                        freq = wpa_s->best_24_freq;
                        wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
                                   "channel: %d MHz", freq);
@@ -4330,7 +4332,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
                           "band");
                if (wpa_s->best_5_freq > 0 &&
-                   p2p_supported_freq(wpa_s->global->p2p,
+                   p2p_supported_freq_go(wpa_s->global->p2p,
                                       wpa_s->best_5_freq)) {
                        freq = wpa_s->best_5_freq;
                        wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
@@ -4338,7 +4340,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                } else {
                        os_get_random((u8 *) &r, sizeof(r));
                        freq = 5180 + (r % 4) * 20;
-                       if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+                       if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
                                wpa_printf(MSG_DEBUG, "P2P: Could not select "
                                           "5 GHz channel for P2P group");
                                return -1;
@@ -4348,7 +4350,7 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                }
        }
 
-       if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+       if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
                wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
                           "(%u MHz) is not supported for P2P uses",
                           freq);
@@ -4401,24 +4403,24 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                           "frequency %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_overall_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_overall_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_overall_freq) &&
                   freq_included(channels, wpa_s->best_overall_freq)) {
                params->freq = wpa_s->best_overall_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
                           "channel %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_24_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_24_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_24_freq) &&
                   freq_included(channels, wpa_s->best_24_freq)) {
                params->freq = wpa_s->best_24_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
                           "channel %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_5_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_5_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_5_freq) &&
                   freq_included(channels, wpa_s->best_5_freq)) {
                params->freq = wpa_s->best_5_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
@@ -4564,7 +4566,7 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
        if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, NULL))
                return -1;
        if (params.freq &&
-           !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
+           !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
                wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
                           "(%u MHz) is not supported for P2P uses",
                           params.freq);
@@ -5605,6 +5607,11 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                        wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
                                   "update failed");
                }
+
+               if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: No GO channel list "
+                                  "update failed");
+               }
        }
 }
 
index d793819..9268446 100644 (file)
@@ -615,6 +615,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
                "p2p_oper_reg_class", "p2p_oper_channel",
                "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
                "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
+               "p2p_no_go_freq",
                "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
                "p2p_ignore_shared_freq", "country", "bss_max_count",
                "bss_expiration_age", "bss_expiration_scan_count",