diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 6bbc950b2..5bdf4733f 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -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) { diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index f8fa0616c..31d5cd120 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -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() diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 52162121a..230948db9 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -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. diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 9ca8f6387..5675d2e63 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -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 }, diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index 5b002a204..ac4ecfaf0 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -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 /** diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index b3dd40e06..cf5332014 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -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", diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 01b3e3a6c..874fe819d 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -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"); + } + } }