diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-04-01 17:36:54 -0700 |
|---|---|---|
| committer | Linux Build Service Account <lnxbuild@localhost> | 2016-04-01 17:36:55 -0700 |
| commit | 4c2da85e2ad93b710f51f7610a641aedd87a2b6f (patch) | |
| tree | dc4e6d06816dff9493a301e6f546941888ab0616 | |
| parent | 030747493c71b091dafb4bdabddfd4538eb9f4f7 (diff) | |
| parent | f9262effc055345f900cb2d4c900160e2f0781a6 (diff) | |
| download | android_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
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, ¶m); + 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, ¶ms, freq, ht40, vht, NULL)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 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, ¶ms, freq, ht40, vht, channels)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, 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; |
