aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-04-01 17:36:54 -0700
committerLinux Build Service Account <lnxbuild@localhost>2016-04-01 17:36:55 -0700
commit4c2da85e2ad93b710f51f7610a641aedd87a2b6f (patch)
treedc4e6d06816dff9493a301e6f546941888ab0616
parent030747493c71b091dafb4bdabddfd4538eb9f4f7 (diff)
parentf9262effc055345f900cb2d4c900160e2f0781a6 (diff)
downloadandroid_external_wpa_supplicant_8-4c2da85e2ad93b710f51f7610a641aedd87a2b6f.tar.gz
android_external_wpa_supplicant_8-4c2da85e2ad93b710f51f7610a641aedd87a2b6f.tar.bz2
android_external_wpa_supplicant_8-4c2da85e2ad93b710f51f7610a641aedd87a2b6f.zip
Promotion of wlan-service.lnx.1.0-00029.
CRs Change ID Subject -------------------------------------------------------------------------------------------------------------- 842468 I55ff714b3a213005f10a9e8959e78da71e2b26e3 P2P: Parse preferred frequency list extension 993995 Ie7a0e9027ac4cc85fd5b876fbb6321241b5ce800 P2P: Fix P2P configuration file name 842468 Ie2da6d2dd9dc107f5ff3bb4763608fa5374c7539 Add parsing of preferred frequency list element 944221 I6e009a6886dfa1984ef76a188a5fd473accf2740 nl80211: Add VHT 160 MHz channel flags 842468 I0657fe4e8ebf7cf51a2127450130ae6718a12d98 P2P: Add preferred frequency list extension to GO Neg Re 842468 Icd2cf14cbc5d1b781f12453ebcbf8ee147a3117e Define new registry for QCA vendor specific elements 924507 Ifb8cf34c7e58ce8fd5aef57b3d2e3b8fb1e733de P2P: Do not reply to GO Negotiation Request if peer is w 842468 I8135b44c91bdaa89fe80a3e4f802dadfbe557662 P2P: Expose driver preferred frequency list fetch to P2P 842468 I71de761d267f9d46ab6ae9cd35cc08daf5d2bd99 nl80211: Add means to query preferred channels 884126 Ie1e999e3dac746e29db0ad9a9fcec5abeff207bf wpa_supplicant: Fix a typo in wpa_scan_result_compar() 909284 I0a284ec156b742ee3aac6411ce2e93e7be85221a Add QCA vendor definitions for simultaneous offchannel 909284 Ia7f92909b4e09240f866fbb5aaf707943b65d627 Add QCA vendor definitions for vendor scan support 909284 Ib5d927950a7886347deeeda8fce92a343f127aaf Parallelize distinct radio work operations 842468 Iade4acbe723b7c1c36c886f305b476172535e850 nl80211: Add concurrency capabilities to driver status 887890 If62620b7d903dad077811b619940e349104d1412 P2P: Support driver preferred freq list for Autonomous G 842468 Idd39a8276daaf450b8d720a9fdf617f6427f5e48 P2P: Use preferred frequency list from the local driver 842468 Ia8a5ef25184788f836daec2e009eb5bc64108e5d Fix generating offloaded ACS channel list when hw_mode i 909284 I78be8d12a6f0ae1fef82a035a525929e2e15c972 nl80211: Support vendor scan together with normal scan 842468 I2192ac1fcac9253876fae3f54f10f5a89d0e3bac P2P: Do not omit known operating channel preference from 842468 Ida7c6bffffd7052e1c86dfb9c2be353f15ce26f2 Return error from wpa_drv_get_pref_freq_list() if no dri 944221 Ie7020e68f3442f2d20493488fde3f6f043de7fde P2P: Add support for VHT 80+80 MHz and 160 MHz 909284 I76dbc6b6f9d871b3703030f0e204eb10ccc90fa3 nl80211: Extract driver offchannel simultaneous capabili 842468 Ic3cbb824e1c8bac94839cde7819de75a03d75765 P2P: Inform driver of the operating channel following gr 887890 I2ba4cddfd03c741ee82ecbefe64617080e720d3e P2P: Process preferred frequency list as part of GO Neg 909284 Iaa5c47f5e5fa800afc8662314e0889af679a72ef nl80211: Driver interaction for QCA vendor scan 944221 Idbdf5b9a17f338cb19ea9e047cd3473be536732f IBSS/mesh: Add support for VHT80P80 configuration 876335 997716 I90dad050da7c279b54bbc12e0866c438f9ccaa16 Add QCA vendor attribute and event to indicate subnet ch 887890 I2fcf33980771e57e11539ee580cf4fe783b6719f P2P: Support driver preferred freq list for invitation c 944221 Ifb4dfec7e7f6fa4dac125260c488195562e2c170 P2P: Define operating classes for VHT 80+80 and 160 876335 997716 Idf29237e9a7a674de543a091f5edc4216b3b5a0e Extend QCA roam event with subnet change indication Change-Id: Ie35e76f3fdb76ec4e34fb1c888c4ab5fbe2b21ce CRs-Fixed: 909284, 993995, 924507, 944221, 842468, 997716, 887890, 876335, 884126
-rw-r--r--src/ap/ap_drv_ops.c59
-rw-r--r--src/common/ieee802_11_common.c15
-rw-r--r--src/common/ieee802_11_common.h2
-rw-r--r--src/common/qca-vendor.h89
-rw-r--r--src/common/wpa_ctrl.h13
-rw-r--r--src/drivers/driver.h66
-rw-r--r--src/drivers/driver_nl80211.c236
-rw-r--r--src/drivers/driver_nl80211.h13
-rw-r--r--src/drivers/driver_nl80211_capa.c68
-rw-r--r--src/drivers/driver_nl80211_event.c200
-rw-r--r--src/drivers/driver_nl80211_scan.c188
-rw-r--r--src/p2p/p2p.c17
-rw-r--r--src/p2p/p2p.h29
-rw-r--r--src/p2p/p2p_build.c39
-rw-r--r--src/p2p/p2p_go_neg.c217
-rw-r--r--src/p2p/p2p_i.h10
-rw-r--r--src/p2p/p2p_invitation.c15
-rw-r--r--src/p2p/p2p_parse.c3
-rw-r--r--wpa_supplicant/ap.c33
-rw-r--r--wpa_supplicant/config.c2
-rw-r--r--wpa_supplicant/config_file.c1
-rw-r--r--wpa_supplicant/config_ssid.h4
-rw-r--r--wpa_supplicant/ctrl_iface.c133
-rw-r--r--wpa_supplicant/dbus/dbus_new_handlers_p2p.c11
-rw-r--r--wpa_supplicant/driver_i.h19
-rw-r--r--wpa_supplicant/events.c35
-rw-r--r--wpa_supplicant/p2p_supplicant.c273
-rw-r--r--wpa_supplicant/p2p_supplicant.h19
-rw-r--r--wpa_supplicant/scan.c2
-rw-r--r--wpa_supplicant/wpa_cli.c12
-rw-r--r--wpa_supplicant/wpa_supplicant.c242
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h15
32 files changed, 1963 insertions, 117 deletions
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index e4176142..6cafcb74 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -743,6 +743,25 @@ int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
}
+static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+ struct hostapd_hw_modes *mode,
+ int acs_ch_list_all,
+ int **freq_list)
+{
+ int i;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+
+ if ((acs_ch_list_all ||
+ freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+ chan->chan)) &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ int_array_add_unique(freq_list, chan->freq);
+ }
+}
+
+
int hostapd_drv_do_acs(struct hostapd_data *hapd)
{
struct drv_acs_params params;
@@ -750,6 +769,7 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
u8 *channels = NULL;
unsigned int num_channels = 0;
struct hostapd_hw_modes *mode;
+ int *freq_list = NULL;
if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
return 0;
@@ -765,24 +785,35 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd)
acs_ch_list_all = 1;
mode = hapd->iface->current_mode;
- if (mode == NULL)
- return -1;
- channels = os_malloc(mode->num_channels);
- if (channels == NULL)
- return -1;
-
- for (i = 0; i < mode->num_channels; i++) {
- struct hostapd_channel_data *chan = &mode->channels[i];
- if (!acs_ch_list_all &&
- !freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
- chan->chan))
- continue;
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
- channels[num_channels++] = chan->chan;
+ if (mode) {
+ channels = os_malloc(mode->num_channels);
+ if (channels == NULL)
+ return -1;
+
+ for (i = 0; i < mode->num_channels; i++) {
+ struct hostapd_channel_data *chan = &mode->channels[i];
+ if (!acs_ch_list_all &&
+ !freq_range_list_includes(
+ &hapd->iface->conf->acs_ch_list,
+ chan->chan))
+ continue;
+ if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+ channels[num_channels++] = chan->chan;
+ int_array_add_unique(&freq_list, chan->freq);
+ }
+ }
+ } else {
+ for (i = 0; i < hapd->iface->num_hw_features; i++) {
+ mode = &hapd->iface->hw_features[i];
+ hostapd_get_hw_mode_any_channels(hapd, mode,
+ acs_ch_list_all,
+ &freq_list);
+ }
}
params.ch_list = channels;
params.ch_list_len = num_channels;
+ params.freq_list = freq_list;
params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index d55e2969..063de6a7 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
#include "common.h"
#include "defs.h"
#include "wpa_common.h"
+#include "qca-vendor.h"
#include "ieee802_11_defs.h"
#include "ieee802_11_common.h"
@@ -147,6 +148,20 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
}
break;
+ case OUI_QCA:
+ switch (pos[3]) {
+ case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
+ elems->pref_freq_list = pos;
+ elems->pref_freq_list_len = elen;
+ break;
+ default:
+ wpa_printf(MSG_EXCESSIVE,
+ "Unknown QCA information element ignored (type=%d len=%lu)",
+ pos[3], (unsigned long) elen);
+ return -1;
+ }
+ break;
+
default:
wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
"information element ignored (vendor OUI "
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 8d73980e..45cf121e 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -58,6 +58,7 @@ struct ieee802_11_elems {
const u8 *osen;
const u8 *ampe;
const u8 *mic;
+ const u8 *pref_freq_list;
u8 ssid_len;
u8 supp_rates_len;
@@ -86,6 +87,7 @@ struct ieee802_11_elems {
u8 osen_len;
u8 ampe_len;
u8 mic_len;
+ u8 pref_freq_list_len;
struct mb_ies_info mb_ies;
};
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2a6e2429..985143f5 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -156,6 +156,8 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+ QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
+ QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
};
@@ -205,6 +207,7 @@ enum qca_wlan_vendor_attr_roam_auth {
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -223,6 +226,7 @@ enum qca_wlan_vendor_attr_acs_offload {
QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+ QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ACS_MAX =
@@ -246,11 +250,14 @@ enum qca_wlan_vendor_acs_hw_mode {
* after roaming, rather than having the user space wpa_supplicant do it.
* @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
* band selection based on channel selection results.
+ * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
+ * simultaneous off-channel operations.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1,
+ QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -323,4 +330,86 @@ enum qca_set_band {
QCA_SETBAND_2G,
};
+/* IEEE 802.11 Vendor Specific elements */
+
+/**
+ * enum qca_vendor_element_id - QCA Vendor Specific element types
+ *
+ * These values are used to identify QCA Vendor Specific elements. The
+ * payload of the element starts with the three octet OUI (OUI_QCA) and
+ * is followed by a single octet type which is defined by this enum.
+ *
+ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
+ * This element can be used to specify preference order for supported
+ * channels. The channels in this list are in preference order (the first
+ * one has the highest preference) and are described as a pair of
+ * (global) Operating Class and Channel Number (each one octet) fields.
+ *
+ * This extends the standard P2P functionality by providing option to have
+ * more than one preferred operating channel. When this element is present,
+ * it replaces the preference indicated in the Operating Channel attribute.
+ * For supporting other implementations, the Operating Channel attribute is
+ * expected to be used with the highest preference channel. Similarly, all
+ * the channels included in this Preferred channel list element are
+ * expected to be included in the Channel List attribute.
+ *
+ * This vendor element may be included in GO Negotiation Request, P2P
+ * Invitation Request, and Provision Discovery Request frames.
+ */
+enum qca_vendor_element_id {
+ QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
+ * with frequencies to be scanned (in MHz)
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
+ * rates to be included
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
+ * at non CCK rate in 2GHz band
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
+ * driver for the specific scan request
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
+ * request decoded as in enum scan_status
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
+ * scan flag is set
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
+ * randomisation
+ */
+enum qca_wlan_vendor_attr_scan {
+ QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
+ QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
+ QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
+ QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
+ QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
+};
+
+/**
+ * enum scan_status - Specifies the valid values the vendor scan attribute
+ * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ *
+ * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
+ * new scan results
+ * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
+ */
+enum scan_status {
+ VENDOR_SCAN_STATUS_NEW_RESULTS,
+ VENDOR_SCAN_STATUS_ABORTED,
+ VENDOR_SCAN_STATUS_MAX,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 8738031c..d93e37b3 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -75,6 +75,19 @@ extern "C" {
/** Regulatory domain channel */
#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+/** IP subnet status change notification
+ *
+ * When using an offloaded roaming mechanism where driver/firmware takes care
+ * of roaming and IP subnet validation checks post-roaming, this event can
+ * indicate whether IP subnet has changed.
+ *
+ * The event has a status=<0/1/2> parameter where
+ * 0 = unknown
+ * 1 = IP subnet unchanged (can continue to use the old IP address)
+ * 2 = IP subnet changed (need to get a new IP address)
+ */
+#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
+
/** RSN IBSS 4-way handshakes completed with specified peer */
#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index f9847839..0b75b963 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -45,6 +45,15 @@
#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+#define HOSTAPD_CHAN_VHT_10_150 0x00100000
+#define HOSTAPD_CHAN_VHT_30_130 0x00200000
+#define HOSTAPD_CHAN_VHT_50_110 0x00400000
+#define HOSTAPD_CHAN_VHT_70_90 0x00800000
+#define HOSTAPD_CHAN_VHT_90_70 0x01000000
+#define HOSTAPD_CHAN_VHT_110_50 0x02000000
+#define HOSTAPD_CHAN_VHT_130_30 0x04000000
+#define HOSTAPD_CHAN_VHT_150_10 0x08000000
+
/**
* enum reg_change_initiator - Regulatory change initiator
*/
@@ -1214,6 +1223,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_VHT_IBSS 0x0000002000000000ULL
/** Driver supports automatic band selection */
#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY 0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS 0x0000008000000000ULL
u64 flags;
#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001
@@ -1297,6 +1308,13 @@ struct wpa_driver_capa {
*/
#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION 0x00000008
u32 rrm_flags;
+
+ /* Driver concurrency capabilities */
+ unsigned int conc_capab;
+ /* Maximum number of concurrent channels on 2.4 GHz */
+ unsigned int max_conc_chan_2_4;
+ /* Maximum number of concurrent channels on 5 GHz */
+ unsigned int max_conc_chan_5_0;
};
@@ -1397,6 +1415,16 @@ enum wpa_driver_if_type {
* WPA_IF_MESH - Mesh interface
*/
WPA_IF_MESH,
+
+ /*
+ * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+ */
+ WPA_IF_TDLS,
+
+ /*
+ * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+ */
+ WPA_IF_IBSS,
};
struct wpa_init_params {
@@ -1602,6 +1630,7 @@ struct drv_acs_params {
/* ACS channel list info */
unsigned int ch_list_len;
const u8 *ch_list;
+ const int *freq_list;
};
@@ -3401,6 +3430,32 @@ struct wpa_driver_ops {
* Returns 0 on success, -1 on failure
*/
int (*abort_scan)(void *priv);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @priv: Private driver interface data
+ * @if_type: Interface type
+ * @num: Number of channels
+ * @freq_list: Preferred channel frequency list encoded in MHz values
+ * 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.
+ */
+ int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+ unsigned int *num, unsigned int *freq_list);
+
+ /**
+ * set_prob_oper_freq - Indicate probable P2P operating channel
+ * @priv: Private driver interface data
+ * @freq: Channel frequency in MHz
+ * Returns 0 on success, -1 on failure
+ *
+ * This command can be used to inform the driver of the operating
+ * frequency that an ongoing P2P group formation is likely to come up
+ * on. Local device is assuming P2P Client role.
+ */
+ int (*set_prob_oper_freq)(void *priv, unsigned int freq);
};
@@ -4060,6 +4115,12 @@ union wpa_event_data {
* ptk_kek_len - The length of ptk_kek
*/
size_t ptk_kek_len;
+
+ /**
+ * subnet_status - The subnet status:
+ * 0 = unknown, 1 = unchanged, 2 = changed
+ */
+ u8 subnet_status;
} assoc_info;
/**
@@ -4343,6 +4404,9 @@ union wpa_event_data {
* @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
* SSID)
* @num_ssids: Number of entries in ssids array
+ * @external_scan: Whether the scan info is for an external scan
+ * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+ * 0 if the source of the scan event is a vendor scan
*/
struct scan_info {
int aborted;
@@ -4350,6 +4414,8 @@ union wpa_event_data {
size_t num_freqs;
struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
size_t num_ssids;
+ int external_scan;
+ int nl_scan_event;
} scan_info;
/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 55028868..ded7950f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5806,8 +5806,9 @@ static enum nl80211_iftype wpa_driver_nl80211_if_type(
return NL80211_IFTYPE_P2P_DEVICE;
case WPA_IF_MESH:
return NL80211_IFTYPE_MESH_POINT;
+ default:
+ return -1;
}
- return -1;
}
@@ -7259,6 +7260,17 @@ static int driver_nl80211_scan2(void *priv,
struct wpa_driver_scan_params *params)
{
struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /*
+ * Do a vendor specific scan if possible. If only_new_results is
+ * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+ * cannot be achieved through a vendor scan. The below condition may
+ * need to be modified if new scan flags are added in the future whose
+ * functionality can only be achieved through a normal scan.
+ */
+ if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+ return wpa_driver_nl80211_vendor_scan(bss, params);
return wpa_driver_nl80211_scan(bss, params);
}
@@ -7527,7 +7539,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
"capa.max_acl_mac_addrs=%u\n"
"capa.num_multichan_concurrent=%u\n"
"capa.mac_addr_rand_sched_scan_supported=%d\n"
- "capa.mac_addr_rand_scan_supported=%d\n",
+ "capa.mac_addr_rand_scan_supported=%d\n"
+ "capa.conc_capab=%u\n"
+ "capa.max_conc_chan_2_4=%u\n"
+ "capa.max_conc_chan_5_0=%u\n",
drv->capa.key_mgmt,
drv->capa.enc,
drv->capa.auth,
@@ -7543,7 +7558,10 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
drv->capa.max_acl_mac_addrs,
drv->capa.num_multichan_concurrent,
drv->capa.mac_addr_rand_sched_scan_supported,
- drv->capa.mac_addr_rand_scan_supported);
+ drv->capa.mac_addr_rand_scan_supported,
+ drv->capa.conc_capab,
+ drv->capa.max_conc_chan_2_4,
+ drv->capa.max_conc_chan_5_0);
if (os_snprintf_error(end - pos, res))
return pos - buf;
pos += res;
@@ -8424,6 +8442,26 @@ static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
}
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+ int i, len, ret;
+ u32 *freqs;
+
+ if (!freq_list)
+ return 0;
+ len = int_array_len(freq_list);
+ freqs = os_malloc(sizeof(u32) * len);
+ if (!freqs)
+ return -1;
+ for (i = 0; i < len; i++)
+ freqs[i] = freq_list[i];
+ ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+ sizeof(u32) * len, freqs);
+ os_free(freqs);
+ return ret;
+}
+
+
static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
{
struct i802_bss *bss = priv;
@@ -8453,7 +8491,8 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
params->ch_width) ||
(params->ch_list_len &&
nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
- params->ch_list))) {
+ params->ch_list)) ||
+ add_acs_freq_list(msg, params->freq_list)) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -8474,6 +8513,193 @@ static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
}
+struct nl80211_pcl {
+ unsigned int num;
+ unsigned int *freq_list;
+};
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nl80211_pcl *param = arg;
+ struct nlattr *nl_vend, *attr;
+ enum qca_iface_type iface_type;
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ unsigned int num, max_num;
+ u32 *freqs;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ if (!nl_vend)
+ return NL_SKIP;
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+ if (!attr) {
+ wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+ param->num = 0;
+ return NL_SKIP;
+ }
+
+ iface_type = (enum qca_iface_type) nla_get_u32(attr);
+ 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) {
+ 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;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+ enum wpa_driver_if_type if_type,
+ unsigned int *num,
+ unsigned int *freq_list)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ unsigned int i;
+ struct nlattr *params;
+ struct nl80211_pcl param;
+ enum qca_iface_type iface_type;
+
+ if (!drv->get_pref_freq_list)
+ return -1;
+
+ switch (if_type) {
+ case WPA_IF_STATION:
+ iface_type = QCA_IFACE_TYPE_STA;
+ break;
+ case WPA_IF_AP_BSS:
+ iface_type = QCA_IFACE_TYPE_AP;
+ break;
+ case WPA_IF_P2P_GO:
+ iface_type = QCA_IFACE_TYPE_P2P_GO;
+ break;
+ case WPA_IF_P2P_CLIENT:
+ iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+ break;
+ case WPA_IF_IBSS:
+ iface_type = QCA_IFACE_TYPE_IBSS;
+ break;
+ case WPA_IF_TDLS:
+ iface_type = QCA_IFACE_TYPE_TDLS;
+ break;
+ default:
+ return -1;
+ }
+
+ param.num = *num;
+ param.freq_list = freq_list;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+ iface_type)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+ ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in send_and_recv_msgs", __func__);
+ return ret;
+ }
+
+ *num = param.num;
+
+ for (i = 0; i < *num; i++) {
+ wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
+ i, freq_list[i]);
+ }
+
+ return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret;
+ struct nlattr *params;
+
+ if (!drv->set_prob_oper_freq)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set P2P probable operating freq %u for ifindex %d",
+ freq, bss->ifindex);
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+ !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+ QCA_IFACE_TYPE_P2P_CLIENT) ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+ freq)) {
+ wpa_printf(MSG_ERROR,
+ "%s: err in adding vendor_cmd and vendor_data",
+ __func__);
+ nlmsg_free(msg);
+ return -1;
+ }
+ nla_nest_end(msg, params);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+ __func__);
+ return ret;
+ }
+ nlmsg_free(msg);
+ return 0;
+}
+
+
static int nl80211_set_band(void *priv, enum set_band band)
{
struct i802_bss *bss = priv;
@@ -8630,4 +8856,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.del_tx_ts = nl80211_del_ts,
.do_acs = wpa_driver_do_acs,
.set_band = nl80211_set_band,
+ .get_pref_freq_list = nl80211_get_pref_freq_list,
+ .set_prob_oper_freq = nl80211_set_prob_oper_freq,
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 5e88a82d..e2df8b43 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -146,7 +146,11 @@ struct wpa_driver_nl80211_data {
unsigned int set_rekey_offload:1;
unsigned int p2p_go_ctwindow_supported:1;
unsigned int setband_vendor_cmd_avail:1;
+ unsigned int get_pref_freq_list:1;
+ unsigned int set_prob_oper_freq:1;
+ unsigned int scan_vendor_cmd_avail:1;
+ u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
u64 send_action_cookie;
#define MAX_SEND_ACTION_COOKIES 20
@@ -183,6 +187,13 @@ struct wpa_driver_nl80211_data {
int auth_wep_tx_keyidx;
int auth_local_state_change;
int auth_p2p;
+
+ /*
+ * Tells whether the last scan issued from wpa_supplicant was a normal
+ * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+ * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+ */
+ int last_scan_cmd;
};
struct nl_msg;
@@ -275,5 +286,7 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
int wpa_driver_nl80211_abort_scan(void *priv);
const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params);
#endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 4929cea2..7b23a8a9 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -589,6 +589,12 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
drv->get_features_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+ drv->get_pref_freq_list = 1;
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+ drv->set_prob_oper_freq = 1;
+ break;
case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
drv->capa.flags |=
WPA_DRIVER_FLAGS_ACS_OFFLOAD;
@@ -596,6 +602,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
drv->setband_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ drv->scan_vendor_cmd_avail = 1;
+ break;
}
}
@@ -751,6 +760,7 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
struct features_info {
u8 *flags;
size_t flags_len;
+ struct wpa_driver_capa *capa;
};
@@ -776,6 +786,19 @@ static int features_info_handler(struct nl_msg *msg, void *arg)
info->flags = nla_data(attr);
info->flags_len = nla_len(attr);
}
+ attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+ if (attr)
+ info->capa->conc_capab = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+ if (attr)
+ info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+ attr = tb_vendor[
+ QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+ if (attr)
+ info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
}
return NL_SKIP;
@@ -810,6 +833,7 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
}
os_memset(&info, 0, sizeof(info));
+ info.capa = &drv->capa;
ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
if (ret || !info.flags)
return;
@@ -819,6 +843,10 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+ &info))
+ drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
}
@@ -904,6 +932,16 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
qca_nl80211_check_dfs_capa(drv);
qca_nl80211_get_features(drv);
+ /*
+ * To enable offchannel simultaneous support in wpa_supplicant, the
+ * underlying driver needs to support the same along with offchannel TX.
+ * Offchannel TX support is needed since remain_on_channel and
+ * action_tx use some common data structures and hence cannot be
+ * scheduled simultaneously.
+ */
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+ drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+
return 0;
}
@@ -1360,7 +1398,7 @@ static void nl80211_reg_rule_sec(struct nlattr *tb[],
static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
- int end)
+ int end, int max_bw)
{
int c;
@@ -1377,6 +1415,32 @@ static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
if (chan->freq - 70 >= start && chan->freq + 10 <= end)
chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+
+ if (max_bw >= 160) {
+ if (chan->freq - 10 >= start && chan->freq + 150 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_10_150;
+
+ if (chan->freq - 30 >= start && chan->freq + 130 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_30_130;
+
+ if (chan->freq - 50 >= start && chan->freq + 110 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_50_110;
+
+ if (chan->freq - 70 >= start && chan->freq + 90 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_70_90;
+
+ if (chan->freq - 90 >= start && chan->freq + 70 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_90_70;
+
+ if (chan->freq - 110 >= start && chan->freq + 50 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_110_50;
+
+ if (chan->freq - 130 >= start && chan->freq + 30 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_130_30;
+
+ if (chan->freq - 150 >= start && chan->freq + 10 <= end)
+ chan->flag |= HOSTAPD_CHAN_VHT_150_10;
+ }
}
}
@@ -1407,7 +1471,7 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[],
if (!results->modes[m].vht_capab)
continue;
- nl80211_set_vht_mode(&results->modes[m], start, end);
+ nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
}
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7b0f721e..0070be89 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -268,7 +268,8 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
struct nlattr *authorized,
struct nlattr *key_replay_ctr,
struct nlattr *ptk_kck,
- struct nlattr *ptk_kek)
+ struct nlattr *ptk_kek,
+ struct nlattr *subnet_status)
{
union wpa_event_data event;
const u8 *ssid;
@@ -367,6 +368,17 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
}
+ if (subnet_status) {
+ /*
+ * At least for now, this is only available from
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+ * attribute has the same values 0, 1, 2 as are used in the
+ * variable here, so no mapping between different values are
+ * needed.
+ */
+ event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+ }
+
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
}
@@ -968,7 +980,7 @@ static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
- struct nlattr *tb[])
+ struct nlattr *tb[], int external_scan)
{
union wpa_event_data event;
struct nlattr *nl;
@@ -978,7 +990,7 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
int freqs[MAX_REPORT_FREQS];
int num_freqs = 0;
- if (drv->scan_for_auth) {
+ if (!external_scan && drv->scan_for_auth) {
drv->scan_for_auth = 0;
wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
"cfg80211 BSS entry");
@@ -989,6 +1001,8 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
os_memset(&event, 0, sizeof(event));
info = &event.scan_info;
info->aborted = aborted;
+ info->external_scan = external_scan;
+ info->nl_scan_event = 1;
if (tb[NL80211_ATTR_SCAN_SSIDS]) {
nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
@@ -1596,7 +1610,8 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
- tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
}
@@ -1686,6 +1701,138 @@ static void qca_nl80211_dfs_offload_radar_event(
}
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ union wpa_event_data event;
+ struct scan_info *info;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* External scan trigger event, ignore */
+ return;
+ }
+
+ /* Cookie match, own scan */
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->external_scan = 0;
+ info->nl_scan_event = 0;
+
+ drv->scan_state = SCAN_STARTED;
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+ int aborted, struct nlattr *tb[],
+ int external_scan)
+{
+ union wpa_event_data event;
+ struct nlattr *nl;
+ int rem;
+ struct scan_info *info;
+ int freqs[MAX_REPORT_FREQS];
+ int num_freqs = 0;
+
+ os_memset(&event, 0, sizeof(event));
+ info = &event.scan_info;
+ info->aborted = aborted;
+ info->external_scan = external_scan;
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) {
+ struct wpa_driver_scan_ssid *s =
+ &info->ssids[info->num_ssids];
+ s->ssid = nla_data(nl);
+ s->ssid_len = nla_len(nl);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Scan probed for SSID '%s'",
+ wpa_ssid_txt(s->ssid, s->ssid_len));
+ info->num_ssids++;
+ if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+ break;
+ }
+ }
+
+ if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+ char msg[200], *pos, *end;
+ int res;
+
+ pos = msg;
+ end = pos + sizeof(msg);
+ *pos = '\0';
+
+ nla_for_each_nested(nl,
+ tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES],
+ rem) {
+ freqs[num_freqs] = nla_get_u32(nl);
+ res = os_snprintf(pos, end - pos, " %d",
+ freqs[num_freqs]);
+ if (!os_snprintf_error(end - pos, res))
+ pos += res;
+ num_freqs++;
+ if (num_freqs == MAX_REPORT_FREQS - 1)
+ break;
+ }
+
+ info->freqs = freqs;
+ info->num_freqs = num_freqs;
+ wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+ msg);
+ }
+ wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+ u64 cookie = 0;
+ enum scan_status status;
+ int external_scan;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ return;
+
+ status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+ if (status >= VENDOR_SCAN_STATUS_MAX)
+ return; /* invalid status */
+
+ cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ if (cookie != drv->vendor_scan_cookie) {
+ /* Event from an external scan, get scan results */
+ external_scan = 1;
+ } else {
+ external_scan = 0;
+ if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+ drv->scan_state = SCAN_COMPLETED;
+ else
+ drv->scan_state = SCAN_ABORTED;
+
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+ drv->ctx);
+ drv->vendor_scan_cookie = 0;
+ drv->last_scan_cmd = 0;
+ }
+
+ send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+ external_scan);
+}
+
+
static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
u32 subcmd, u8 *data, size_t len)
{
@@ -1709,6 +1856,12 @@ static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
break;
+ case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+ qca_nl80211_scan_trigger_event(drv, data, len);
+ break;
+ case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+ qca_nl80211_scan_done_event(drv, data, len);
+ break;
default:
wpa_printf(MSG_DEBUG,
"nl80211: Ignore unsupported QCA vendor event %u",
@@ -1831,6 +1984,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
{
struct wpa_driver_nl80211_data *drv = bss->drv;
union wpa_event_data data;
+ int external_scan_event = 0;
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1883,28 +2037,38 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
case NL80211_CMD_NEW_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New scan results available");
- drv->scan_state = SCAN_COMPLETED;
drv->scan_complete_events = 1;
- eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
- drv->ctx);
- send_scan_event(drv, 0, tb);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ drv->scan_state = SCAN_COMPLETED;
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 0, tb, external_scan_event);
break;
case NL80211_CMD_SCHED_SCAN_RESULTS:
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: New sched scan results available");
drv->scan_state = SCHED_SCAN_RESULTS;
- send_scan_event(drv, 0, tb);
+ send_scan_event(drv, 0, tb, 0);
break;
case NL80211_CMD_SCAN_ABORTED:
wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
- drv->scan_state = SCAN_ABORTED;
- /*
- * Need to indicate that scan results are available in order
- * not to make wpa_supplicant stop its scanning.
- */
- eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
- drv->ctx);
- send_scan_event(drv, 1, tb);
+ if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+ drv->scan_state = SCAN_ABORTED;
+ /*
+ * Need to indicate that scan results are available in
+ * order not to make wpa_supplicant stop its scanning.
+ */
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = 0;
+ } else {
+ external_scan_event = 1;
+ }
+ send_scan_event(drv, 1, tb, external_scan_event);
break;
case NL80211_CMD_AUTHENTICATE:
case NL80211_CMD_ASSOCIATE:
@@ -1927,7 +2091,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
tb[NL80211_ATTR_RESP_IE],
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
break;
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 1c79f763..a8ce7ea8 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
#include "driver_nl80211.h"
@@ -294,6 +295,7 @@ int wpa_driver_nl80211_scan(struct i802_bss *bss,
eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
fail:
nlmsg_free(msg);
@@ -798,3 +800,189 @@ int wpa_driver_nl80211_abort_scan(void *priv)
return ret;
}
+
+static int scan_cookie_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ u64 *cookie = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+ nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+ nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+ if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+ *cookie = nla_get_u64(
+ tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+ }
+
+ return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+ struct wpa_driver_scan_params *params)
+{
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *attr;
+ size_t i;
+ u32 scan_flags = 0;
+ int ret = -1;
+ u64 cookie = 0;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+ drv->scan_for_auth = 0;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (attr == NULL)
+ goto fail;
+
+ if (params->num_ssids) {
+ struct nlattr *ssids;
+
+ ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS);
+ if (ssids == NULL)
+ goto fail;
+ for (i = 0; i < params->num_ssids; i++) {
+ wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+ params->ssids[i].ssid,
+ params->ssids[i].ssid_len);
+ if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+ params->ssids[i].ssid))
+ goto fail;
+ }
+ nla_nest_end(msg, ssids);
+ }
+
+ if (params->extra_ies) {
+ wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+ params->extra_ies, params->extra_ies_len);
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+ params->extra_ies_len, params->extra_ies))
+ goto fail;
+ }
+
+ if (params->freqs) {
+ struct nlattr *freqs;
+
+ freqs = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES);
+ if (freqs == NULL)
+ goto fail;
+ for (i = 0; params->freqs[i]; i++) {
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Scan frequency %u MHz",
+ params->freqs[i]);
+ if (nla_put_u32(msg, i + 1, params->freqs[i]))
+ goto fail;
+ }
+ nla_nest_end(msg, freqs);
+ }
+
+ os_free(drv->filter_ssids);
+ drv->filter_ssids = params->filter_ssids;
+ params->filter_ssids = NULL;
+ drv->num_filter_ssids = params->num_filter_ssids;
+
+ if (params->low_priority && drv->have_low_prio_scan) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+ scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+ }
+
+ if (params->mac_addr_rand) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+ scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+ if (params->mac_addr) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+ MAC2STR(params->mac_addr));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+ ETH_ALEN, params->mac_addr))
+ goto fail;
+ }
+
+ if (params->mac_addr_mask) {
+ wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+ MACSTR, MAC2STR(params->mac_addr_mask));
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+ ETH_ALEN, params->mac_addr_mask))
+ goto fail;
+ }
+ }
+
+ if (scan_flags &&
+ nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+ goto fail;
+
+ if (params->p2p_probe) {
+ struct nlattr *rates;
+
+ wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+ rates = nla_nest_start(msg,
+ QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES);
+ if (rates == NULL)
+ goto fail;
+
+ /*
+ * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+ * by masking out everything else apart from the OFDM rates 6,
+ * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+ * rates are left enabled.
+ */
+ if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+ "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+ goto fail;
+ nla_nest_end(msg, rates);
+
+ if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+ goto fail;
+ }
+
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+ msg = NULL;
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ goto fail;
+ }
+
+ drv->vendor_scan_cookie = cookie;
+ drv->scan_state = SCAN_REQUESTED;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+ ret, (long long unsigned int) cookie);
+ eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+ eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+ drv, drv->ctx);
+ drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+ nlmsg_free(msg);
+ return ret;
+}
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 99bd402a..fb4c0aec 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -5390,3 +5390,20 @@ void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
"Timeout on waiting peer to become ready for GO Negotiation");
p2p_go_neg_failed(p2p, -1);
}
+
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+ const unsigned int *pref_freq_list,
+ unsigned int size)
+{
+ unsigned int i;
+
+ if (size > P2P_MAX_PREF_CHANNELS)
+ size = P2P_MAX_PREF_CHANNELS;
+ p2p->num_pref_freq = size;
+ 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]);
+ }
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 67b8bdb6..9fa4aabf 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -23,9 +23,14 @@
#define P2P_MAX_QUERY_HASH 6
/**
+ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
+ */
+#define P2P_MAX_PREF_CHANNELS 100
+
+/**
* P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
*/
-#define P2P_MAX_REG_CLASSES 10
+#define P2P_MAX_REG_CLASSES 15
/**
* P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
@@ -93,6 +98,10 @@ struct p2p_go_neg_results {
int vht;
+ u8 max_oper_chwidth;
+
+ unsigned int vht_center_freq2;
+
/**
* ssid - SSID of the group
*/
@@ -1029,6 +1038,20 @@ struct p2p_config {
* P2PS_SETUP_* bitmap is used as the parameters and return value.
*/
u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+
+ /**
+ * get_pref_freq_list - Get preferred frequency list for an interface
+ * @ctx: Callback context from cb_ctx
+ * @go: Whether the use if for GO role
+ * @len: Length of freq_list in entries (both IN and OUT)
+ * @freq_list: Buffer for returning the preferred frequencies (MHz)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to query the preferred frequency list from
+ * the driver specific to a particular interface type.
+ */
+ int (*get_pref_freq_list)(void *ctx, int go,
+ unsigned int *len, unsigned int *freq_list);
};
@@ -2253,4 +2276,8 @@ int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
void p2p_service_flush_asp(struct p2p_data *p2p);
struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+ const unsigned int *pref_freq_list,
+ unsigned int size);
+
#endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index c733543c..793d28ba 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -10,6 +10,7 @@
#include "common.h"
#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
#include "wps/wps_i.h"
#include "p2p_i.h"
@@ -109,6 +110,44 @@ void p2p_buf_add_operating_channel(struct wpabuf *buf, const char *country,
}
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+ const u32 *preferred_freq_list,
+ unsigned int size)
+{
+ unsigned int i, count = 0;
+ u8 op_class, op_channel;
+
+ if (!size)
+ return;
+
+ /*
+ * First, determine the number of P2P supported channels in the
+ * pref_freq_list returned from driver. This is needed for calculations
+ * 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)
+ count++;
+ }
+
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + count * sizeof(u16));
+ 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) {
+ wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
+ preferred_freq_list[i]);
+ continue;
+ }
+ wpabuf_put_u8(buf, op_class);
+ wpabuf_put_u8(buf, op_channel);
+ }
+}
+
+
void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
struct p2p_channels *chan)
{
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 19f1daaf..3929979b 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -185,6 +185,9 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
p2p->op_reg_class, p2p->op_channel);
p2p_buf_update_ie_hdr(buf, len);
+ p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+ p2p->num_pref_freq);
+
/* WPS IE with Device Password ID attribute */
pw_id = p2p_wps_method_pw_id(peer->wps_method);
if (peer->oob_pw_id)
@@ -312,7 +315,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
group_capab);
p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
- if (peer && peer->go_state == REMOTE_GO) {
+ if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
p2p_dbg(p2p, "Omit Operating Channel attribute");
} else {
p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -381,7 +384,7 @@ void p2p_reselect_channel(struct p2p_data *p2p,
unsigned int i;
const int op_classes_5ghz[] = { 124, 125, 115, 0 };
const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
- const int op_classes_vht[] = { 128, 0 };
+ const int op_classes_vht[] = { 128, 129, 130, 0 };
if (p2p->own_freq_preference > 0 &&
p2p_freq_to_channel(p2p->own_freq_preference,
@@ -542,6 +545,195 @@ 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)
+{
+ u8 op_class, op_channel;
+ unsigned int oper_freq, i, j;
+ int found = 0;
+
+ p2p_dbg(p2p,
+ "Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
+
+ /*
+ * Search for a common channel in our preferred frequency list which is
+ * also supported by the peer device.
+ */
+ for (i = 0; i < size && !found; i++) {
+ /*
+ * Make sure that the common frequency is:
+ * 1. Supported by peer
+ * 2. Allowed for P2P use.
+ */
+ oper_freq = freq_list[i];
+ if (p2p_freq_to_channel(oper_freq, &op_class,
+ &op_channel) < 0) {
+ p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
+ continue;
+ }
+ if (!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,
+ "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+ oper_freq, op_class, op_channel);
+ break;
+ }
+ for (j = 0; j < msg->channel_list_len; j++) {
+
+ if (op_channel != msg->channel_list[j])
+ continue;
+
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ p2p_dbg(p2p,
+ "Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
+ oper_freq);
+ } else {
+ p2p_dbg(p2p,
+ "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
+ dev->oper_freq);
+ }
+}
+
+
+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)
+{
+ u8 op_class, op_channel;
+ unsigned int oper_freq, i, j;
+ int found = 0;
+
+ /*
+ * Peer device supports a Preferred Frequency List.
+ * Search for a common channel in the preferred frequency lists
+ * of both peer and local devices.
+ */
+ for (i = 0; i < size && !found; i++) {
+ for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
+ 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)
+ continue;
+
+ /*
+ * Make sure that the found frequency is:
+ * 1. Supported
+ * 2. Allowed for P2P use.
+ */
+ if (p2p_freq_to_channel(oper_freq, &op_class,
+ &op_channel) < 0) {
+ p2p_dbg(p2p, "Unsupported frequency %u MHz",
+ oper_freq);
+ continue;
+ }
+
+ if (!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,
+ "Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+ oper_freq, op_class, op_channel);
+ break;
+ }
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ p2p_dbg(p2p,
+ "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
+ oper_freq);
+ } else {
+ p2p_dbg(p2p,
+ "No common preferred channels found! Use: %d MHz for oper_channel",
+ dev->oper_freq);
+ }
+}
+
+
+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 i;
+ u8 op_class, op_channel;
+
+ /*
+ * Use the preferred channel list from the driver only if there is no
+ * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
+ * channel hardcoded in the configuration file.
+ */
+ if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
+ (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
+ return;
+
+ /* 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))
+ return;
+
+ /*
+ * Check if peer's preference of operating channel is in
+ * our preferred channel list.
+ */
+ for (i = 0; i < size; i++) {
+ if (freq_list[i] == (unsigned int) dev->oper_freq)
+ break;
+ }
+ if (i != size) {
+ /* Peer operating channel preference matches our preference */
+ if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
+ 0) {
+ p2p_dbg(p2p,
+ "Peer operating channel preference is unsupported frequency %u MHz",
+ freq_list[i]);
+ } else {
+ p2p->op_reg_class = op_class;
+ p2p->op_channel = op_channel;
+ os_memcpy(&p2p->channels, &p2p->cfg->channels,
+ sizeof(struct p2p_channels));
+ return;
+ }
+ }
+
+ p2p_dbg(p2p,
+ "Peer operating channel preference: %d MHz is not in our preferred channel list",
+ dev->oper_freq);
+
+ /*
+ Check if peer's preferred channel list is
+ * _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);
+ else
+ p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
+}
+
+
void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
const u8 *data, size_t len, int rx_freq)
{
@@ -709,6 +901,14 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
return;
}
+ if (dev->go_neg_req_sent &&
+ (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
+ p2p_dbg(p2p,
+ "Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent");
+ p2p_parse_free(&msg);
+ return;
+ }
+
go = p2p_go_det(p2p->go_intent, *msg.go_intent);
if (go < 0) {
p2p_dbg(p2p, "Incompatible GO Intent");
@@ -799,6 +999,12 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
dev->oper_freq);
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
if (msg.config_timeout) {
dev->go_timeout = msg.config_timeout[0];
dev->client_timeout = msg.config_timeout[1];
@@ -1150,6 +1356,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
goto fail;
+ /*
+ * Use the driver preferred frequency list extension if local device is
+ * GO.
+ */
+ if (go)
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
p2p_set_state(p2p, P2P_GO_NEG);
p2p_clear_timeout(p2p);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 78e52c93..4b01f86d 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -535,6 +535,9 @@ struct p2p_data {
u16 authorized_oob_dev_pw_id;
struct wpabuf **vendor_elem;
+
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+ unsigned int num_pref_freq;
};
/**
@@ -637,6 +640,9 @@ struct p2p_message {
const u8 *persistent_dev;
const u8 *persistent_ssid;
size_t persistent_ssid_len;
+
+ const u8 *pref_freq_list;
+ size_t pref_freq_list_len;
};
@@ -765,6 +771,8 @@ void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
const u8 *ssid, size_t ssid_len);
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 u32 *preferred_freq_list, u32 size);
/* p2p_sd.c */
struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -794,6 +802,8 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev);
u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
void p2p_reselect_channel(struct p2p_data *p2p,
struct p2p_channels *intersection);
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+ struct p2p_device *dev, struct p2p_message *msg);
/* p2p_pd.c */
void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index f5454f70..108e5b7f 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -85,6 +85,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
p2p_buf_add_device_info(buf, p2p, peer);
p2p_buf_update_ie_hdr(buf, len);
+ p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+ p2p->num_pref_freq);
+
#ifdef CONFIG_WIFI_DISPLAY
if (wfd_ie)
wpabuf_put_buf(buf, wfd_ie);
@@ -343,6 +346,12 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
p2p_reselect_channel(p2p, &intersection);
}
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, go, dev, &msg);
+
op_freq = p2p_channel_to_freq(p2p->op_reg_class,
p2p->op_channel);
if (op_freq < 0) {
@@ -534,6 +543,12 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
peer_oper_freq = 0;
}
+ /*
+ * Use the driver preferred frequency list extension if
+ * supported.
+ */
+ p2p_check_pref_chan(p2p, 0, dev, &msg);
+
p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
msg.group_bssid, channels, sa,
freq, peer_oper_freq);
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index afdef853..c902cad0 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -560,6 +560,9 @@ int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg)
}
#endif /* CONFIG_WIFI_DISPLAY */
+ msg->pref_freq_list = elems.pref_freq_list;
+ msg->pref_freq_list_len = elems.pref_freq_list_len;
+
return 0;
}
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index f3960c5e..e41fe30f 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -56,12 +56,32 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
if (!conf->secondary_channel)
goto no_vht;
- center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ switch (conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_80MHZ:
+ case VHT_CHANWIDTH_80P80MHZ:
+ center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+ break;
+ default:
+ /*
+ * conf->vht_oper_chwidth might not be set for non-P2P GO cases,
+ * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
+ * not supported.
+ */
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+ center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+ if (!center_chan) {
+ conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+ center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
+ channel);
+ }
+ break;
+ }
if (!center_chan)
goto no_vht;
- /* Use 80 MHz channel */
- conf->vht_oper_chwidth = 1;
conf->vht_oper_centr_freq_seg0_idx = center_chan;
return;
@@ -604,6 +624,13 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
return -1;
}
+ /* Use the maximum oper channel width if it's given. */
+ if (ssid->max_oper_chwidth)
+ conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+ ieee80211_freq_to_chan(ssid->vht_center_freq2,
+ &conf->vht_oper_centr_freq_seg1_idx);
+
os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
wpa_s->conf->wmm_ac_params,
sizeof(wpa_s->conf->wmm_ac_params));
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 44f6946d..7f13aab5 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1837,6 +1837,8 @@ static const struct parse_data ssid_fields[] = {
{ FUNC(auth_alg) },
{ FUNC(scan_freq) },
{ FUNC(freq_list) },
+ { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
+ VHT_CHANWIDTH_80P80MHZ) },
#ifdef IEEE8021X_EAPOL
{ FUNC(eap) },
{ STR_LENe(identity) },
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index aeea70c1..80831647 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -751,6 +751,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT(disabled);
INT(peerkey);
INT(mixed_cell);
+ INT(max_oper_chwidth);
#ifdef CONFIG_IEEE80211W
write_int(f, "ieee80211w", ssid->ieee80211w,
MGMT_FRAME_PROTECTION_DEFAULT);
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 1c63e519..2b1dd87b 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -447,6 +447,10 @@ struct wpa_ssid {
int vht;
+ u8 max_oper_chwidth;
+
+ unsigned int vht_center_freq2;
+
/**
* wpa_ptk_rekey - Maximum lifetime for PTK in seconds
*
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 22eed2a2..77865051 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -4781,6 +4781,30 @@ static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
}
+static int parse_freq(int chwidth, int freq2)
+{
+ if (freq2 < 0)
+ return -1;
+ if (freq2)
+ return VHT_CHANWIDTH_80P80MHZ;
+
+ switch (chwidth) {
+ case 0:
+ case 20:
+ case 40:
+ return VHT_CHANWIDTH_USE_HT;
+ case 80:
+ return VHT_CHANWIDTH_80MHZ;
+ case 160:
+ return VHT_CHANWIDTH_160MHZ;
+ default:
+ wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
+ chwidth);
+ return -1;
+ }
+}
+
+
static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
char *buf, size_t buflen)
{
@@ -4797,7 +4821,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
int go_intent = -1;
int freq = 0;
int pd;
- int ht40, vht;
+ int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
if (!wpa_s->global->p2p_init_wpa_s)
return -1;
@@ -4858,6 +4882,18 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
return -1;
}
+ pos2 = os_strstr(pos, " freq2=");
+ if (pos2)
+ freq2 = atoi(pos2 + 7);
+
+ pos2 = os_strstr(pos, " max_oper_chwidth=");
+ if (pos2)
+ chwidth = atoi(pos2 + 18);
+
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
if (os_strncmp(pos, "pin", 3) == 0) {
/* Request random PIN (to be displayed) and enable the PIN */
wps_method = WPS_PIN_DISPLAY;
@@ -4882,8 +4918,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, automatic, join,
- auth, go_intent, freq, persistent_id, pd,
- ht40, vht);
+ auth, go_intent, freq, freq2, persistent_id,
+ pd, ht40, vht, max_oper_chwidth);
if (new_pin == -2) {
os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
return 25;
@@ -5423,7 +5459,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
struct wpa_ssid *ssid;
u8 *_peer = NULL, peer[ETH_ALEN];
int freq = 0, pref_freq = 0;
- int ht40, vht;
+ int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
id = atoi(cmd);
pos = os_strstr(cmd, " peer=");
@@ -5461,8 +5497,20 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
vht;
- return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
- pref_freq);
+ pos = os_strstr(cmd, "freq2=");
+ if (pos)
+ freq2 = atoi(pos + 6);
+
+ pos = os_strstr(cmd, " max_oper_chwidth=");
+ if (pos)
+ chwidth = atoi(pos + 18);
+
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
+ return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
+ max_oper_chwidth, pref_freq);
}
@@ -5509,7 +5557,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
- int id, int freq, int ht40, int vht)
+ int id, int freq, int vht_center_freq2,
+ int ht40, int vht, int vht_chwidth)
{
struct wpa_ssid *ssid;
@@ -5521,8 +5570,9 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
return -1;
}
- return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
- NULL, 0, 0);
+ return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
+ vht_center_freq2, 0, ht40, vht,
+ vht_chwidth, NULL, 0, 0);
}
@@ -5531,11 +5581,14 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
int freq = 0, persistent = 0, group_id = -1;
int vht = wpa_s->conf->p2p_go_vht;
int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+ int max_oper_chwidth, chwidth = 0, freq2 = 0;
char *token, *context = NULL;
while ((token = str_token(cmd, " ", &context))) {
if (sscanf(token, "freq=%d", &freq) == 1 ||
- sscanf(token, "persistent=%d", &group_id) == 1) {
+ sscanf(token, "freq2=%d", &freq2) == 1 ||
+ sscanf(token, "persistent=%d", &group_id) == 1 ||
+ sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
continue;
} else if (os_strcmp(token, "ht40") == 0) {
ht40 = 1;
@@ -5552,11 +5605,17 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
}
}
+ max_oper_chwidth = parse_freq(chwidth, freq2);
+ if (max_oper_chwidth < 0)
+ return -1;
+
if (group_id >= 0)
return p2p_ctrl_group_add_persistent(wpa_s, group_id,
- freq, ht40, vht);
+ freq, freq2, ht40, vht,
+ max_oper_chwidth);
- return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+ return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
+ max_oper_chwidth);
}
@@ -6549,6 +6608,53 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
}
+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;
+ int ret;
+ enum wpa_driver_if_type iface_type;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ /* buf: "<interface_type>" */
+ if (os_strcmp(cmd, "STATION") == 0)
+ iface_type = WPA_IF_STATION;
+ else if (os_strcmp(cmd, "AP") == 0)
+ iface_type = WPA_IF_AP_BSS;
+ else if (os_strcmp(cmd, "P2P_GO") == 0)
+ iface_type = WPA_IF_P2P_GO;
+ else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+ iface_type = WPA_IF_P2P_CLIENT;
+ else if (os_strcmp(cmd, "IBSS") == 0)
+ iface_type = WPA_IF_IBSS;
+ else if (os_strcmp(cmd, "TDLS") == 0)
+ iface_type = WPA_IF_TDLS;
+ else
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+ iface_type, buf);
+
+ ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+ if (ret)
+ return -1;
+
+ for (i = 0; i < num; i++) {
+ ret = os_snprintf(pos, end - pos, "%s%u",
+ i > 0 ? "," : "", freq_list[i]);
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+
+ return pos - buf;
+}
+
+
static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
size_t buflen)
{
@@ -8647,6 +8753,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
reply_len = -1;
+ } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+ reply_len = wpas_ctrl_iface_get_pref_freq_list(
+ wpa_s, buf + 19, reply, reply_size);
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index ef07107d..f552905f 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -364,13 +364,14 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
goto inv_args;
if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
- NULL, 0, 0)) {
+ 0, 0, NULL, 0, 0)) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
goto out;
}
- } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
+ } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
+ 0))
goto inv_args;
out:
@@ -582,7 +583,7 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
persistent_group, 0, join, authorize_only,
- go_intent, freq, -1, 0, 0, 0);
+ go_intent, freq, 0, -1, 0, 0, 0, 0);
if (new_pin >= 0) {
char npin[9];
@@ -733,8 +734,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
if (ssid == NULL || ssid->disabled != 2)
goto err;
- if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
- 0) {
+ if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
+ 0) < 0) {
reply = wpas_dbus_error_unknown_error(
message,
"Failed to reinvoke a persistent group");
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d7a2676e..c739c720 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -900,4 +900,23 @@ static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
return wpa_s->driver->abort_scan(wpa_s->drv_priv);
}
+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)
+{
+ if (!wpa_s->driver->get_pref_freq_list)
+ return -1;
+ return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+ num, freq_list);
+}
+
+static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
+ unsigned int freq)
+{
+ if (!wpa_s->driver->set_prob_oper_freq)
+ return 0;
+ return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
+}
+
#endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 3a47ad96..a3623d3b 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1420,6 +1420,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
return -1;
if (!own_request)
return -1;
+ if (data && data->scan_info.external_scan)
+ return -1;
wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
"scanning again");
wpa_supplicant_req_new_scan(wpa_s, 1, 0);
@@ -1444,7 +1446,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
#endif /* CONFIG_NO_RANDOM_POOL */
if (own_request && wpa_s->scan_res_handler &&
- (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+ !(data && data->scan_info.external_scan)) {
void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res);
@@ -1465,9 +1467,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
}
wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
- wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+ wpa_s->own_scan_running,
+ data ? data->scan_info.external_scan : 0);
if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
- wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+ wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
+ own_request && !(data && data->scan_info.external_scan)) {
wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
wpa_s->manual_scan_id);
wpa_s->manual_scan_use_id = 0;
@@ -1478,7 +1482,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
wpas_notify_scan_done(wpa_s, 1);
- if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+ if (data && data->scan_info.external_scan) {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
wpa_scan_results_free(scan_res);
return 0;
@@ -1509,7 +1513,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
wpa_scan_results_free(scan_res);
- if (wpa_s->scan_work) {
+ if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
@@ -1519,7 +1523,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
scan_work_done:
wpa_scan_results_free(scan_res);
- if (wpa_s->scan_work) {
+ if (own_request && wpa_s->scan_work) {
struct wpa_radio_work *work = wpa_s->scan_work;
wpa_s->scan_work = NULL;
radio_work_done(work);
@@ -3357,6 +3361,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
wpa_supplicant_event_assoc(wpa_s, data);
if (data && data->assoc_info.authorized)
wpa_supplicant_event_assoc_auth(wpa_s, data);
+ if (data) {
+ wpa_msg(wpa_s, MSG_INFO,
+ WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
+ data->assoc_info.subnet_status);
+ }
break;
case EVENT_DISASSOC:
wpas_event_disassoc(wpa_s,
@@ -3371,10 +3380,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
break;
#ifndef CONFIG_NO_SCAN_PROCESSING
case EVENT_SCAN_STARTED:
- os_get_reltime(&wpa_s->scan_start_time);
- if (wpa_s->own_scan_requested) {
+ if (wpa_s->own_scan_requested ||
+ (data && !data->scan_info.external_scan)) {
struct os_reltime diff;
+ os_get_reltime(&wpa_s->scan_start_time);
os_reltime_sub(&wpa_s->scan_start_time,
&wpa_s->scan_trigger_time, &diff);
wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
@@ -3397,7 +3407,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
}
break;
case EVENT_SCAN_RESULTS:
- if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+ if (!(data && data->scan_info.external_scan) &&
+ os_reltime_initialized(&wpa_s->scan_start_time)) {
struct os_reltime now, diff;
os_get_reltime(&now);
os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
@@ -3408,8 +3419,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
}
if (wpa_supplicant_event_scan_results(wpa_s, data))
break; /* interface may have been removed */
- wpa_s->own_scan_running = 0;
- wpa_s->radio->external_scan_running = 0;
+ if (!(data && data->scan_info.external_scan))
+ wpa_s->own_scan_running = 0;
+ if (data && data->scan_info.nl_scan_event)
+ wpa_s->radio->external_scan_running = 0;
radio_work_check_next(wpa_s);
break;
#endif /* CONFIG_NO_SCAN_PROCESSING */
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 7e4b9134..4e9bc3b1 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1790,6 +1790,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
ssid->frequency = params->freq;
ssid->ht40 = params->ht40;
ssid->vht = params->vht;
+ ssid->max_oper_chwidth = params->max_oper_chwidth;
+ ssid->vht_center_freq2 = params->vht_center_freq2;
ssid->ssid = os_zalloc(params->ssid_len + 1);
if (ssid->ssid) {
os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -2081,10 +2083,17 @@ static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
return;
}
+ if (!res->role_go) {
+ /* Inform driver of the operating channel of GO. */
+ wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
+ }
+
if (wpa_s->p2p_go_ht40)
res->ht40 = 1;
if (wpa_s->p2p_go_vht)
res->vht = 1;
+ res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
+ res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
"freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -2800,7 +2809,7 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
if (s) {
int go = s->mode == WPAS_MODE_P2P_GO;
wpas_p2p_group_add_persistent(
- wpa_s, s, go, 0, op_freq, 0, 0, NULL,
+ wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL,
go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
1);
} else if (bssid) {
@@ -2985,7 +2994,9 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
ssid->mode == WPAS_MODE_P2P_GO,
wpa_s->p2p_persistent_go_freq,
freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth,
channels,
ssid->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
@@ -3125,7 +3136,7 @@ struct p2p_oper_class_map {
u8 min_chan;
u8 max_chan;
u8 inc;
- enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
+ enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
};
static const struct p2p_oper_class_map op_class[] = {
@@ -3151,6 +3162,8 @@ static const struct p2p_oper_class_map op_class[] = {
* removing invalid channels.
*/
{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
+ { HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80 },
+ { HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160 },
{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
{ -1, 0, 0, 0, 0, BW20 }
};
@@ -3217,6 +3230,75 @@ static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
}
+static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel)
+{
+ u8 center_channels[] = { 50, 114 };
+ unsigned int i;
+
+ if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+ /*
+ * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+ * so the center channel is 14 channels away from the start/end.
+ */
+ if (channel >= center_channels[i] - 14 &&
+ channel <= center_channels[i] + 14)
+ return center_channels[i];
+
+ return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode,
+ u8 channel, u8 bw)
+{
+ u8 center_chan;
+ int i, flags;
+ enum chan_allowed res, ret = ALLOWED;
+
+ center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+ if (!center_chan)
+ return NOT_ALLOWED;
+ /* VHT 160 MHz uses DFS channels in most countries. */
+
+ /* Check all the channels are available */
+ for (i = 0; i < 8; i++) {
+ int adj_chan = center_chan - 14 + i * 4;
+
+ res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+ if (res == NOT_ALLOWED)
+ return NOT_ALLOWED;
+
+ if (res == NO_IR)
+ ret = NO_IR;
+
+ if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
+ return NOT_ALLOWED;
+ if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+ return NOT_ALLOWED;
+ if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+ return NOT_ALLOWED;
+ if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+ return NOT_ALLOWED;
+ if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+ return NOT_ALLOWED;
+ if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+ return NOT_ALLOWED;
+ if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+ return NOT_ALLOWED;
+ if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+ return NOT_ALLOWED;
+ }
+
+ return ret;
+}
+
+
static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode,
u8 channel, u8 bw)
@@ -3235,6 +3317,8 @@ static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
} else if (bw == BW80) {
res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+ } else if (bw == BW160) {
+ res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
}
if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -3348,6 +3432,15 @@ int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
}
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel)
+{
+ if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160))
+ return 0;
+ return wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+}
+
+
static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
size_t buf_len)
{
@@ -3765,13 +3858,13 @@ static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
if (response_done && persistent_go) {
wpas_p2p_group_add_persistent(
wpa_s, persistent_go,
- 0, 0, 0, 0, 0, NULL,
+ 0, 0, 0, 0, 0, 0, 0, NULL,
persistent_go->mode ==
WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
0, 0);
} else if (response_done) {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, 0, 0, 0, 0, 0);
}
if (passwd_id == DEV_PW_P2PS_DEFAULT) {
@@ -3865,17 +3958,28 @@ static int wpas_prov_disc_resp_cb(void *ctx)
if (persistent_go) {
wpas_p2p_group_add_persistent(
- wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+ wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL,
persistent_go->mode == WPAS_MODE_P2P_GO ?
P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
} else {
- wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+ wpas_p2p_group_add(wpa_s, 1, 0, 0, 0, 0, 0);
}
return 1;
}
+static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
+ unsigned int *len,
+ unsigned int *freq_list)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
+ WPA_IF_P2P_CLIENT, len, freq_list);
+}
+
+
/**
* wpas_p2p_init - Initialize P2P module for %wpa_supplicant
* @global: Pointer to global data from wpa_supplicant_init()
@@ -3929,6 +4033,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
p2p.p2ps_group_capability = p2ps_group_capability;
+ p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -4374,10 +4479,12 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
wpa_s->p2p_persistent_group, 0, 0, 0,
wpa_s->p2p_go_intent,
wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth);
return;
}
@@ -4719,11 +4826,16 @@ 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)
+ int *force_freq, int *pref_freq, int go,
+ unsigned int *pref_freq_list,
+ unsigned int *num_pref_freq)
{
struct wpa_used_freq_data *freqs;
int res, best_freq, num_unused;
- unsigned int freq_in_use = 0, num, i;
+ unsigned int freq_in_use = 0, num, i, max_pref_freq;
+
+ max_pref_freq = *num_pref_freq;
+ *num_pref_freq = 0;
freqs = os_calloc(wpa_s->num_multichan_concurrent,
sizeof(struct wpa_used_freq_data));
@@ -4788,6 +4900,47 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+ if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
+ enum wpa_driver_if_type iface_type;
+
+ if (go)
+ iface_type = WPA_IF_P2P_GO;
+ else
+ iface_type = WPA_IF_P2P_CLIENT;
+
+ wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
+ best_freq, go);
+
+ res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
+ &max_pref_freq,
+ pref_freq_list);
+ if (!res && max_pref_freq > 0) {
+ *num_pref_freq = max_pref_freq;
+ i = 0;
+ while (wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]) &&
+ i < *num_pref_freq) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: preferred_freq_list[%d]=%d is disallowed",
+ i, pref_freq_list[i]);
+ i++;
+ }
+ if (i != *num_pref_freq) {
+ best_freq = pref_freq_list[i];
+ wpa_printf(MSG_DEBUG,
+ "P2P: Using preferred_freq_list[%d]=%d",
+ i, best_freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All driver preferred frequencies are disallowed for P2P use");
+ *num_pref_freq = 0;
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: No preferred frequency list available");
+ }
+ }
+
/* We have a candidate frequency to use */
if (best_freq > 0) {
if (*pref_freq == 0 && num_unused > 0) {
@@ -4831,12 +4984,15 @@ exit_free:
* initiating Group Owner negotiation
* @go_intent: GO Intent or -1 to use default
* @freq: Frequency for the group or 0 for auto-selection
+ * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
* @persistent_id: Persistent group credentials to use for forcing GO
* parameters or -1 to generate new values (SSID/passphrase)
* @pd: Whether to send Provision Discovery prior to GO Negotiation as an
* interoperability workaround when initiating group formation
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: Channel width supported by GO operating with VHT support
+ * (VHT_CHANWIDTH_*).
* Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
* failure, -2 on failure due to channel not currently available,
* -3 if forced channel is not supported
@@ -4844,14 +5000,16 @@ exit_free:
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join, int auth,
- int go_intent, int freq, int persistent_id, int pd,
- int ht40, int vht)
+ int go_intent, int freq, unsigned int vht_center_freq2,
+ int persistent_id, int pd, int ht40, int vht,
+ unsigned int vht_chwidth)
{
int force_freq = 0, pref_freq = 0;
int ret = 0, res;
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;
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -4884,6 +5042,8 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
wpa_s->p2p_pd_before_go_neg = !!pd;
wpa_s->p2p_go_ht40 = !!ht40;
wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
+ wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
if (pin)
os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -4928,13 +5088,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
return ret;
}
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- go_intent == 15);
+ go_intent == 15, pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s,
force_freq ? force_freq : pref_freq);
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
if (wpa_s->create_p2p_iface) {
@@ -5095,6 +5258,38 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
{
unsigned int r;
+ 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];
+ int res;
+
+ res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
+ &size, pref_freq_list);
+ if (!res && size > 0) {
+ i = 0;
+ while (wpas_p2p_disallowed_freq(wpa_s->global,
+ pref_freq_list[i]) &&
+ i < size) {
+ wpa_printf(MSG_DEBUG,
+ "P2P: preferred_freq_list[%d]=%d is disallowed",
+ i, pref_freq_list[i]);
+ i++;
+ }
+ if (i != size) {
+ freq = pref_freq_list[i];
+ wpa_printf(MSG_DEBUG,
+ "P2P: Using preferred_freq_list[%d]=%d",
+ i, freq);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: All driver preferred frequencies are disallowed for P2P use");
+ }
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "P2P: No preferred frequency list available");
+ }
+ }
+
if (freq == 2) {
wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
"band");
@@ -5230,7 +5425,8 @@ out:
static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
struct p2p_go_neg_results *params,
- int freq, int ht40, int vht,
+ int freq, int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
const struct p2p_channels *channels)
{
struct wpa_used_freq_data *freqs;
@@ -5241,6 +5437,8 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
params->role_go = 1;
params->ht40 = ht40;
params->vht = vht;
+ params->max_oper_chwidth = max_oper_chwidth;
+ params->vht_center_freq2 = vht_center_freq2;
if (freq) {
if (!freq_included(channels, freq)) {
wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -5400,15 +5598,18 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
* @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
* @persistent_group: Whether to create a persistent group
* @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
* @ht40: Start GO with 40 MHz channel width
* @vht: Start GO with VHT support
+ * @vht_chwidth: channel bandwidth for GO operating with VHT support
* Returns: 0 on success, -1 on failure
*
* This function creates a new P2P group with the local end as the Group Owner,
* i.e., without using Group Owner Negotiation.
*/
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40, int vht)
+ int freq, int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth)
{
struct p2p_go_neg_results params;
@@ -5426,7 +5627,8 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
if (freq < 0)
return -1;
- if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, NULL))
+ if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, NULL))
return -1;
if (params.freq &&
!p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
@@ -5517,8 +5719,10 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int force_freq, int neg_freq, int ht40,
- int vht, const struct p2p_channels *channels,
+ int force_freq, int neg_freq,
+ int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
+ const struct p2p_channels *channels,
int connection_timeout, int force_scan)
{
struct p2p_go_neg_results params;
@@ -5592,7 +5796,8 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
return -1;
}
- if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
+ if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+ ht40, vht, max_oper_chwidth, channels))
return -1;
params.role_go = 1;
@@ -6115,13 +6320,15 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
/* Invite to reinvoke a persistent group */
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int vht, int pref_freq)
+ int vht_center_freq2, int ht40, int vht, int max_chwidth,
+ int pref_freq)
{
enum p2p_invite_role role;
u8 *bssid = NULL;
int force_freq = 0;
int res;
int no_pref_freq_given = pref_freq == 0;
+ unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
wpa_s->global->p2p_invite_group = NULL;
if (peer_addr)
@@ -6131,6 +6338,9 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
wpa_s->p2p_persistent_go_freq = freq;
wpa_s->p2p_go_ht40 = !!ht40;
+ wpa_s->p2p_go_vht = !!vht;
+ wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
+ wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
if (ssid->mode == WPAS_MODE_P2P_GO) {
role = P2P_INVITE_ROLE_GO;
if (peer_addr == NULL) {
@@ -6155,10 +6365,13 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
}
wpa_s->pending_invite_ssid_id = ssid->id;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_GO);
+ role == P2P_INVITE_ROLE_GO,
+ pref_freq_list, &size);
if (res)
return res;
+ p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
@@ -6195,10 +6408,13 @@ 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;
wpa_s->p2p_persistent_go_freq = 0;
wpa_s->p2p_go_ht40 = 0;
wpa_s->p2p_go_vht = 0;
+ wpa_s->p2p_go_vht_center_freq2 = 0;
+ wpa_s->p2p_go_max_oper_chwidth = 0;
for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -6246,8 +6462,10 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
return -1;
+ size = P2P_MAX_PREF_CHANNELS;
res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
- role == P2P_INVITE_ROLE_ACTIVE_GO);
+ role == P2P_INVITE_ROLE_ACTIVE_GO,
+ pref_freq_list, &size);
if (res)
return res;
wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@@ -7130,10 +7348,12 @@ static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+ wpa_s->p2p_go_vht_center_freq2,
wpa_s->p2p_persistent_id,
wpa_s->p2p_pd_before_go_neg,
wpa_s->p2p_go_ht40,
- wpa_s->p2p_go_vht);
+ wpa_s->p2p_go_vht,
+ wpa_s->p2p_go_max_oper_chwidth);
return ret;
}
@@ -7667,7 +7887,8 @@ static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
- params->go_freq, -1, 0, 1, 1);
+ params->go_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
}
@@ -7743,7 +7964,8 @@ static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
"connection handover");
return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
}
@@ -7757,7 +7979,8 @@ static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
"connection handover");
res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
- forced_freq, -1, 0, 1, 1);
+ forced_freq, wpa_s->p2p_go_vht_center_freq2,
+ -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
if (res)
return res;
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 1851dabb..2b46ca06 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -26,16 +26,20 @@ struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
const char *pin, enum p2p_wps_method wps_method,
int persistent_group, int auto_join, int join,
- int auth, int go_intent, int freq, int persistent_id,
- int pd, int ht40, int vht);
+ int auth, int go_intent, int freq,
+ unsigned int vht_center_freq2, int persistent_id,
+ int pd, int ht40, int vht, unsigned int max_oper_chwidth);
int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
int freq, struct wpa_ssid *ssid);
int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
- int freq, int ht40, int vht);
+ int freq, int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth);
int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid, int addr_allocated,
- int force_freq, int neg_freq, int ht40,
- int vht, const struct p2p_channels *channels,
+ int force_freq, int neg_freq,
+ int vht_center_freq2, int ht40,
+ int vht, int max_oper_chwidth,
+ const struct p2p_channels *channels,
int connection_timeout, int force_scan);
struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
@@ -101,7 +105,8 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
- int ht40, int vht, int pref_freq);
+ int vht_center_freq2, int ht40, int vht,
+ int max_oper_chwidth, int pref_freq);
int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
const u8 *peer_addr, const u8 *go_dev_addr);
int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
@@ -130,6 +135,8 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel);
int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+ struct hostapd_hw_modes *mode, u8 channel);
unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
const u8 *p2p_dev_addr,
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 58b6561d..93eea4e6 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1685,7 +1685,7 @@ static int wpa_scan_result_compar(const void *a, const void *b)
snr_a_full = wa->snr;
snr_a = MIN(wa->snr, GREAT_SNR);
snr_b_full = wb->snr;
- snr_b = MIN(wa->snr, GREAT_SNR);
+ snr_b = MIN(wb->snr, GREAT_SNR);
} else {
/* Level is not in dBm, so we can't calculate
* SNR. Just use raw level (units unknown). */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index f9d9e06f..c507ddc8 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1543,7 +1543,7 @@ static const char *network_fields[] = {
"ssid", "scan_ssid", "bssid", "bssid_blacklist",
"bssid_whitelist", "psk", "proto", "key_mgmt",
"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
- "freq_list",
+ "freq_list", "max_oper_chwidth",
#ifdef IEEE8021X_EAPOL
"eap", "identity", "anonymous_identity", "password", "ca_cert",
"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -2818,6 +2818,13 @@ static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
}
+static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
+}
+
+
enum wpa_cli_cmd_flags {
cli_cmd_flag_none = 0x00,
cli_cmd_flag_sensitive = 0x01
@@ -3375,6 +3382,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
"<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
"mask=mac-address-mask] = scan MAC randomization"
},
+ { "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
+ cli_cmd_flag_none,
+ "<interface type> = retrieve preferred freq list for the specified interface type" },
{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
};
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index c57fe492..452f8dda 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1729,8 +1729,10 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
u8 channel;
int i, chan_idx, ht40 = -1, res, obss_scan = 1;
- unsigned int j;
+ unsigned int j, k;
struct hostapd_freq_params vht_freq;
+ int chwidth, seg0, seg1;
+ u32 vht_caps = 0;
freq->freq = ssid->frequency;
@@ -1916,12 +1918,45 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
return;
}
+ chwidth = VHT_CHANWIDTH_80MHZ;
+ seg0 = vht80[j] + 6;
+ seg1 = 0;
+
+ if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+ /* setup center_freq2, bandwidth */
+ for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+ /* Only accept 80 MHz segments separated by a gap */
+ if (j == k || abs(vht80[j] - vht80[k]) == 16)
+ continue;
+ for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+ struct hostapd_channel_data *chan;
+
+ chan = hw_get_channel_chan(mode, i, NULL);
+ if (!chan)
+ continue;
+
+ if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+ HOSTAPD_CHAN_NO_IR |
+ HOSTAPD_CHAN_RADAR))
+ continue;
+
+ /* Found a suitable second segment for 80+80 */
+ chwidth = VHT_CHANWIDTH_80P80MHZ;
+ vht_caps |=
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ seg1 = vht80[k] + 6;
+ }
+
+ if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+ break;
+ }
+ }
+
if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
freq->channel, freq->ht_enabled,
vht_freq.vht_enabled,
freq->sec_channel_offset,
- VHT_CHANWIDTH_80MHZ,
- vht80[j] + 6, 0, 0) != 0)
+ chwidth, seg0, seg1, vht_caps) != 0)
return;
*freq = vht_freq;
@@ -3858,6 +3893,56 @@ static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
}
+static enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+ if (freq < 3000)
+ return BAND_2_4_GHZ;
+ if (freq > 50000)
+ return BAND_60_GHZ;
+ return BAND_5_GHZ;
+}
+
+
+static unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s,
+ const int *freqs)
+{
+ int i;
+ unsigned int band = 0;
+
+ if (freqs) {
+ /* freqs are specified for the radio work */
+ for (i = 0; freqs[i]; i++)
+ band |= wpas_freq_to_band(freqs[i]);
+ } else {
+ /*
+ * freqs are not specified, implies all
+ * the supported freqs by HW
+ */
+ for (i = 0; i < wpa_s->hw.num_modes; i++) {
+ if (wpa_s->hw.modes[i].num_channels != 0) {
+ if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211B ||
+ wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211G)
+ band |= BAND_2_4_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211A)
+ band |= BAND_5_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211AD)
+ band |= BAND_60_GHZ;
+ else if (wpa_s->hw.modes[i].mode ==
+ HOSTAPD_MODE_IEEE80211ANY)
+ band = BAND_2_4_GHZ | BAND_5_GHZ |
+ BAND_60_GHZ;
+ }
+ }
+ }
+
+ return band;
+}
+
+
static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
const char *rn)
{
@@ -3910,11 +3995,103 @@ static void radio_work_free(struct wpa_radio_work *work)
}
#endif /* CONFIG_P2P */
+ if (work->started) {
+ work->wpa_s->radio->num_active_works--;
+ wpa_dbg(work->wpa_s, MSG_DEBUG,
+ "radio_work_free('%s'@%p: num_active_works --> %u",
+ work->type, work,
+ work->wpa_s->radio->num_active_works);
+ }
+
dl_list_del(&work->list);
os_free(work);
}
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+ struct wpa_radio_work *active_work = NULL;
+ struct wpa_radio_work *tmp;
+
+ /* Get the active work to know the type and band. */
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started) {
+ active_work = tmp;
+ break;
+ }
+ }
+
+ if (!active_work) {
+ /* No active work, start one */
+ radio->num_active_works = 0;
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+ list) {
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+ return tmp;
+ }
+ return NULL;
+ }
+
+ if (os_strcmp(active_work->type, "sme-connect") == 0 ||
+ os_strcmp(active_work->type, "connect") == 0) {
+ /*
+ * If the active work is either connect or sme-connect,
+ * do not parallelize them with other radio works.
+ */
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "Do not parallelize radio work with %s",
+ active_work->type);
+ return NULL;
+ }
+
+ dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+ if (tmp->started)
+ continue;
+
+ /*
+ * If connect or sme-connect are enqueued, parallelize only
+ * those operations ahead of them in the queue.
+ */
+ if (os_strcmp(tmp->type, "connect") == 0 ||
+ os_strcmp(tmp->type, "sme-connect") == 0)
+ break;
+
+ /*
+ * Check that the radio works are distinct and
+ * on different bands.
+ */
+ if (os_strcmp(active_work->type, tmp->type) != 0 &&
+ (active_work->bands != tmp->bands)) {
+ /*
+ * If a scan has to be scheduled through nl80211 scan
+ * interface and if an external scan is already running,
+ * do not schedule the scan since it is likely to get
+ * rejected by kernel.
+ */
+ if (os_strcmp(tmp->type, "scan") == 0 &&
+ radio->external_scan_running &&
+ (((struct wpa_driver_scan_params *)
+ tmp->ctx)->only_new_results ||
+ tmp->wpa_s->clear_driver_scan_cache))
+ continue;
+
+ wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+ "active_work:%s new_work:%s",
+ active_work->type, tmp->type);
+ return tmp;
+ }
+ }
+
+ /* Did not find a radio work to schedule in parallel. */
+ return NULL;
+}
+
+
static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_radio *radio = eloop_ctx;
@@ -3923,26 +4100,48 @@ static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
struct wpa_supplicant *wpa_s;
work = dl_list_first(&radio->work, struct wpa_radio_work, list);
- if (work == NULL)
+ if (work == NULL) {
+ radio->num_active_works = 0;
return;
-
- if (work->started)
- return; /* already started and still in progress */
+ }
wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
radio_list);
- if (wpa_s && wpa_s->radio->external_scan_running) {
- wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
- return;
+
+ if (!(wpa_s &&
+ wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+ if (work->started)
+ return; /* already started and still in progress */
+
+ if (wpa_s && wpa_s->radio->external_scan_running) {
+ wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+ return;
+ }
+ } else {
+ work = NULL;
+ if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+ /* get the work to schedule next */
+ work = radio_work_get_next_work(radio);
+ }
+ if (!work)
+ return;
}
+ wpa_s = work->wpa_s;
os_get_reltime(&now);
os_reltime_sub(&now, &work->time, &diff);
- wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Starting radio work '%s'@%p after %ld.%06ld second wait",
work->type, work, diff.sec, diff.usec);
work->started = 1;
work->time = now;
+ radio->num_active_works++;
+
work->cb(work, 0);
+
+ if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+ radio->num_active_works < MAX_ACTIVE_WORKS)
+ radio_work_check_next(wpa_s);
}
@@ -4050,6 +4249,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
void (*cb)(struct wpa_radio_work *work, int deinit),
void *ctx)
{
+ struct wpa_radio *radio = wpa_s->radio;
struct wpa_radio_work *work;
int was_empty;
@@ -4064,6 +4264,16 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
work->cb = cb;
work->ctx = ctx;
+ if (freq)
+ work->bands = wpas_freq_to_band(freq);
+ else if (os_strcmp(type, "scan") == 0 ||
+ os_strcmp(type, "p2p-scan") == 0)
+ work->bands = wpas_get_bands(wpa_s,
+ ((struct wpa_driver_scan_params *)
+ ctx)->freqs);
+ else
+ work->bands = wpas_get_bands(wpa_s, NULL);
+
was_empty = dl_list_empty(&wpa_s->radio->work);
if (next)
dl_list_add(&wpa_s->radio->work, &work->list);
@@ -4072,6 +4282,12 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
if (was_empty) {
wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
radio_work_check_next(wpa_s);
+ } else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+ && radio->num_active_works < MAX_ACTIVE_WORKS) {
+ wpa_dbg(wpa_s, MSG_DEBUG,
+ "Try to schedule a radio work (num_active_works=%u)",
+ radio->num_active_works);
+ radio_work_check_next(wpa_s);
}
return 0;
@@ -4825,9 +5041,11 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
if (params->override_ctrl_interface)
global->params.override_ctrl_interface =
os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_P2P
if (params->conf_p2p_dev)
global->params.conf_p2p_dev =
os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
wpa_debug_level = global->params.wpa_debug_level =
params->wpa_debug_level;
wpa_debug_show_keys = global->params.wpa_debug_show_keys =
@@ -4964,7 +5182,9 @@ void wpa_supplicant_deinit(struct wpa_global *global)
os_free(global->params.ctrl_interface_group);
os_free(global->params.override_driver);
os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_P2P
os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
os_free(global->p2p_disallow_freq.range);
os_free(global->p2p_go_avoid_freq.range);
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 3407a66b..df625706 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -225,7 +225,7 @@ struct wpa_params {
* This can also be %NULL. In such a case, if a P2P Device dedicated
* interfaces is created, the main configuration file will be used.
*/
- const char *conf_p2p_dev;
+ char *conf_p2p_dev;
#endif /* CONFIG_P2P */
};
@@ -300,10 +300,19 @@ struct wpa_radio {
char name[16]; /* from driver_ops get_radio_name() or empty if not
* available */
unsigned int external_scan_running:1;
+ unsigned int num_active_works;
struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
struct dl_list work; /* struct wpa_radio_work::list entries */
};
+#define MAX_ACTIVE_WORKS 2
+
+enum wpa_radio_work_band {
+ BAND_2_4_GHZ = BIT(0),
+ BAND_5_GHZ = BIT(1),
+ BAND_60_GHZ = BIT(2),
+};
+
/**
* struct wpa_radio_work - Radio work item
*/
@@ -316,6 +325,7 @@ struct wpa_radio_work {
void *ctx;
unsigned int started:1;
struct os_reltime time;
+ unsigned int bands;
};
int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
@@ -845,6 +855,9 @@ struct wpa_supplicant {
int *p2p_group_common_freqs;
unsigned int p2p_group_common_freqs_num;
u8 p2ps_join_addr[ETH_ALEN];
+
+ unsigned int p2p_go_max_oper_chwidth;
+ unsigned int p2p_go_vht_center_freq2;
#endif /* CONFIG_P2P */
struct wpa_ssid *bgscan_ssid;