OSDN Git Service

Accumulative patch from commit f46fc73a3f8d0eeb1b43d17769464884f467ac47
authorDmitry Shmidt <dimitrysh@google.com>
Tue, 12 Mar 2013 19:44:17 +0000 (12:44 -0700)
committerDmitry Shmidt <dimitrysh@google.com>
Tue, 12 Mar 2013 19:44:17 +0000 (12:44 -0700)
P2P: Add a peer entry based on Association Request frame
P2P: Clear p2p_in_provisioning flag on group removal
Allow SME SA Query to be used by all drivers
Android: Sync makefile changes for HT/VHT overrides
wpa_supplicant: Support VHT capability overrides
wpa_supplicant: Parse int values in different bases and reject invalid
wpa_supplicant: Reschedule sched scan after network change
Remove unnecessary local variable
wpa_supplicant: Handle enabling of one or all networks equally
wpa_supplicant: Save prev_sched_ssid only if needed
wpa_supplicant: Do not allow too short sched scan
nl80211: Use helper function for phy_info_freqs()
nl80211: Split phy_info_band() into smaller helper functions
nl80211: Use helper function for phy_info_handler()
nl80211: Split wiphy_info_handler() into smaller helper functions
nl80211: Support splitting wiphy information in dumps
Synchronize with wireless-testing.git include/uapi/linux/nl80211.h
SAE: Add forgotten commit element validation step for FFC groups
SAE: Move commit element validation steps into single location

Change-Id: I369b6493f03714f6cfb4ed4a8c10ee6e6e71c1f8
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
19 files changed:
src/common/sae.c
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/nl80211_copy.h
src/p2p/p2p_group.c
wpa_supplicant/Android.mk
wpa_supplicant/Makefile
wpa_supplicant/android.config
wpa_supplicant/config.c
wpa_supplicant/config_ssid.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/dbus/dbus_new_handlers.c
wpa_supplicant/defconfig
wpa_supplicant/p2p_supplicant.c
wpa_supplicant/scan.c
wpa_supplicant/sme.c
wpa_supplicant/wpa_supplicant.c
wpa_supplicant/wpa_supplicant.conf
wpa_supplicant/wpa_supplicant_i.h

index 44ffcd0..bce60a3 100644 (file)
@@ -524,12 +524,6 @@ static int sae_derive_k_ecc(struct sae_data *sae, u8 *k)
        if (K == NULL)
                goto fail;
 
-       if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
-                                        sae->tmp->peer_commit_element_ecc)) {
-               wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
-               goto fail;
-       }
-
        /*
         * K = scalar-op(rand, (elem-op(scalar-op(peer-commit-scalar, PWE),
         *                                        PEER-COMMIT-ELEMENT)))
@@ -821,6 +815,12 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
        if (sae->tmp->peer_commit_element_ecc == NULL)
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
+       if (!crypto_ec_point_is_on_curve(sae->tmp->ec,
+                                        sae->tmp->peer_commit_element_ecc)) {
+               wpa_printf(MSG_DEBUG, "SAE: Peer element is not on curve");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
        return WLAN_STATUS_SUCCESS;
 }
 
@@ -828,6 +828,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
 static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
                                        const u8 *end)
 {
+       struct crypto_bignum *res;
+
        if (pos + sae->tmp->prime_len > end) {
                wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
                           "commit-element");
@@ -849,6 +851,18 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
+       /* scalar-op(r, ELEMENT) = 1 modulo p */
+       res = crypto_bignum_init();
+       if (res == NULL ||
+           crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
+                                 sae->tmp->order, sae->tmp->prime, res) < 0 ||
+           !crypto_bignum_is_one(res)) {
+               wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
+               crypto_bignum_deinit(res, 0);
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+       crypto_bignum_deinit(res, 0);
+
        return WLAN_STATUS_SUCCESS;
 }
 
index 5dee2e6..f12f4bc 100644 (file)
@@ -568,6 +568,19 @@ struct wpa_driver_associate_params {
         */
        const u8 *htcaps;       /* struct ieee80211_ht_capabilities * */
        const u8 *htcaps_mask;  /* struct ieee80211_ht_capabilities * */
+
+#ifdef CONFIG_VHT_OVERRIDES
+       /**
+        * disable_vht - Disable VHT for this connection
+        */
+       int disable_vht;
+
+       /**
+        * VHT capability overrides.
+        */
+       const struct ieee80211_vht_capabilities *vhtcaps;
+       const struct ieee80211_vht_capabilities *vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
 };
 
 enum hide_ssid {
index 759862e..3a35a6b 100644 (file)
@@ -2485,6 +2485,42 @@ nla_put_failure:
 }
 
 
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+       u32 *feat = arg;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
+               *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+       return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+       u32 feat = 0;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES);
+       if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+               return feat;
+
+       msg = NULL;
+nla_put_failure:
+       nlmsg_free(msg);
+       return 0;
+}
+
+
 struct wiphy_info_data {
        struct wpa_driver_capa *capa;
 
@@ -2493,6 +2529,12 @@ struct wiphy_info_data {
        unsigned int poll_command_supported:1;
        unsigned int data_tx_status:1;
        unsigned int monitor_supported:1;
+       unsigned int auth_supported:1;
+       unsigned int connect_supported:1;
+       unsigned int p2p_go_supported:1;
+       unsigned int p2p_client_supported:1;
+       unsigned int p2p_concurrent:1;
+       unsigned int p2p_multichan_concurrent:1;
 };
 
 
@@ -2513,15 +2555,42 @@ static unsigned int probe_resp_offload_support(int supp_protocols)
 }
 
 
-static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+                                        struct nlattr *tb)
 {
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct wiphy_info_data *info = arg;
-       int p2p_go_supported = 0, p2p_client_supported = 0;
-       int p2p_concurrent = 0, p2p_multichan_concurrent = 0;
-       int auth_supported = 0, connect_supported = 0;
-       struct wpa_driver_capa *capa = info->capa;
+       struct nlattr *nl_mode;
+       int i;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_mode, tb, i) {
+               switch (nla_type(nl_mode)) {
+               case NL80211_IFTYPE_AP:
+                       info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+                       break;
+               case NL80211_IFTYPE_P2P_GO:
+                       info->p2p_go_supported = 1;
+                       break;
+               case NL80211_IFTYPE_P2P_CLIENT:
+                       info->p2p_client_supported = 1;
+                       break;
+               case NL80211_IFTYPE_MONITOR:
+                       info->monitor_supported = 1;
+                       break;
+               }
+       }
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+                                        struct nlattr *nl_combi)
+{
+       struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+       struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+       struct nlattr *nl_limit, *nl_mode;
+       int err, rem_limit, rem_mode;
+       int combination_has_p2p = 0, combination_has_mgd = 0;
        static struct nla_policy
        iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
                [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
@@ -2534,6 +2603,164 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
        };
 
+       err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+                              nl_combi, iface_combination_policy);
+       if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+           !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+           !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+               return 0; /* broken combination */
+
+       nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+                           rem_limit) {
+               err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+                                      nl_limit, iface_limit_policy);
+               if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+                       return 0; /* broken combination */
+
+               nla_for_each_nested(nl_mode,
+                                   tb_limit[NL80211_IFACE_LIMIT_TYPES],
+                                   rem_mode) {
+                       int ift = nla_type(nl_mode);
+                       if (ift == NL80211_IFTYPE_P2P_GO ||
+                           ift == NL80211_IFTYPE_P2P_CLIENT)
+                               combination_has_p2p = 1;
+                       if (ift == NL80211_IFTYPE_STATION)
+                               combination_has_mgd = 1;
+               }
+               if (combination_has_p2p && combination_has_mgd)
+                       break;
+       }
+
+       if (combination_has_p2p && combination_has_mgd) {
+               info->p2p_concurrent = 1;
+               if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1)
+                       info->p2p_multichan_concurrent = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+                                 struct nlattr *tb)
+{
+       struct nlattr *nl_combi;
+       int rem_combi;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_combi, tb, rem_combi) {
+               if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+                       break;
+       }
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+                                struct nlattr *tb)
+{
+       struct nlattr *nl_cmd;
+       int i;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_cmd, tb, i) {
+               switch (nla_get_u32(nl_cmd)) {
+               case NL80211_CMD_AUTHENTICATE:
+                       info->auth_supported = 1;
+                       break;
+               case NL80211_CMD_CONNECT:
+                       info->connect_supported = 1;
+                       break;
+               case NL80211_CMD_START_SCHED_SCAN:
+                       info->capa->sched_scan_supported = 1;
+                       break;
+               case NL80211_CMD_PROBE_CLIENT:
+                       info->poll_command_supported = 1;
+                       break;
+               }
+       }
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+                              struct nlattr *tb)
+{
+       /* default to 5000 since early versions of mac80211 don't set it */
+       capa->max_remain_on_chan = 5000;
+
+       if (tb)
+               capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+                           struct nlattr *ext_setup)
+{
+       if (tdls == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+       capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+       if (ext_setup) {
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+               capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+       }
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+                                    struct nlattr *tb)
+{
+       u32 flags;
+       struct wpa_driver_capa *capa = info->capa;
+
+       if (tb == NULL)
+               return;
+
+       flags = nla_get_u32(tb);
+
+       if (flags & NL80211_FEATURE_SK_TX_STATUS)
+               info->data_tx_status = 1;
+
+       if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+               capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+       if (flags & NL80211_FEATURE_SAE)
+               capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+       if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
+               capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+                                         struct nlattr *tb)
+{
+       u32 protocols;
+
+       if (tb == NULL)
+               return;
+
+       protocols = nla_get_u32(tb);
+       wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+                  "mode");
+       capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+       capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wiphy_info_data *info = arg;
+       struct wpa_driver_capa *capa = info->capa;
+
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
 
@@ -2549,109 +2776,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                capa->max_match_sets =
                        nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
 
-       if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
-               struct nlattr *nl_mode;
-               int i;
-               nla_for_each_nested(nl_mode,
-                                   tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
-                       switch (nla_type(nl_mode)) {
-                       case NL80211_IFTYPE_AP:
-                               capa->flags |= WPA_DRIVER_FLAGS_AP;
-                               break;
-                       case NL80211_IFTYPE_P2P_GO:
-                               p2p_go_supported = 1;
-                               break;
-                       case NL80211_IFTYPE_P2P_CLIENT:
-                               p2p_client_supported = 1;
-                               break;
-                       case NL80211_IFTYPE_MONITOR:
-                               info->monitor_supported = 1;
-                               break;
-                       }
-               }
-       }
-
-       if (tb[NL80211_ATTR_INTERFACE_COMBINATIONS]) {
-               struct nlattr *nl_combi;
-               int rem_combi;
-
-               nla_for_each_nested(nl_combi,
-                                   tb[NL80211_ATTR_INTERFACE_COMBINATIONS],
-                                   rem_combi) {
-                       struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
-                       struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
-                       struct nlattr *nl_limit, *nl_mode;
-                       int err, rem_limit, rem_mode;
-                       int combination_has_p2p = 0, combination_has_mgd = 0;
-
-                       err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
-                                              nl_combi,
-                                              iface_combination_policy);
-                       if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
-                           !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
-                           !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
-                               goto broken_combination;
-
-                       nla_for_each_nested(nl_limit,
-                                           tb_comb[NL80211_IFACE_COMB_LIMITS],
-                                           rem_limit) {
-                               err = nla_parse_nested(tb_limit,
-                                                      MAX_NL80211_IFACE_LIMIT,
-                                                      nl_limit,
-                                                      iface_limit_policy);
-                               if (err ||
-                                   !tb_limit[NL80211_IFACE_LIMIT_TYPES])
-                                       goto broken_combination;
-
-                               nla_for_each_nested(
-                                       nl_mode,
-                                       tb_limit[NL80211_IFACE_LIMIT_TYPES],
-                                       rem_mode) {
-                                       int ift = nla_type(nl_mode);
-                                       if (ift == NL80211_IFTYPE_P2P_GO ||
-                                           ift == NL80211_IFTYPE_P2P_CLIENT)
-                                               combination_has_p2p = 1;
-                                       if (ift == NL80211_IFTYPE_STATION)
-                                               combination_has_mgd = 1;
-                               }
-                               if (combination_has_p2p && combination_has_mgd)
-                                       break;
-                       }
-
-                       if (combination_has_p2p && combination_has_mgd) {
-                               p2p_concurrent = 1;
-                               if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1)
-                                       p2p_multichan_concurrent = 1;
-                               break;
-                       }
-
-broken_combination:
-                       ;
-               }
-       }
-
-       if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
-               struct nlattr *nl_cmd;
-               int i;
-
-               nla_for_each_nested(nl_cmd,
-                                   tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
-                       switch (nla_get_u32(nl_cmd)) {
-                       case NL80211_CMD_AUTHENTICATE:
-                               auth_supported = 1;
-                               break;
-                       case NL80211_CMD_CONNECT:
-                               connect_supported = 1;
-                               break;
-                       case NL80211_CMD_START_SCHED_SCAN:
-                               capa->sched_scan_supported = 1;
-                               break;
-                       case NL80211_CMD_PROBE_CLIENT:
-                               info->poll_command_supported = 1;
-                               break;
-                       }
-               }
-       }
+       wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
+       wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
+       wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
 
        if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
                wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
@@ -2664,79 +2791,21 @@ broken_combination:
                capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
        }
 
-       /* default to 5000 since early versions of mac80211 don't set it */
-       capa->max_remain_on_chan = 5000;
+       wiphy_info_max_roc(capa,
+                          tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
 
        if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
                capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
 
-       if (tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION])
-               capa->max_remain_on_chan =
-                       nla_get_u32(tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
-
-       if (auth_supported)
-               capa->flags |= WPA_DRIVER_FLAGS_SME;
-       else if (!connect_supported) {
-               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
-                          "authentication/association or connect commands");
-               info->error = 1;
-       }
-
-       if (p2p_go_supported && p2p_client_supported)
-               capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
-       if (p2p_concurrent) {
-               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
-                          "interface (driver advertised support)");
-               capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
-               capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
-
-               if (p2p_multichan_concurrent) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
-                                  "concurrent (driver advertised support)");
-                       capa->flags |=
-                               WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT;
-               }
-       }
-
-       if (tb[NL80211_ATTR_TDLS_SUPPORT]) {
-               wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
-               capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
-
-               if (tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]) {
-                       wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
-                       capa->flags |=
-                               WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
-               }
-       }
+       wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+                       tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
 
        if (tb[NL80211_ATTR_DEVICE_AP_SME])
                info->device_ap_sme = 1;
 
-       if (tb[NL80211_ATTR_FEATURE_FLAGS]) {
-               u32 flags = nla_get_u32(tb[NL80211_ATTR_FEATURE_FLAGS]);
-
-               if (flags & NL80211_FEATURE_SK_TX_STATUS)
-                       info->data_tx_status = 1;
-
-               if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
-                       capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
-
-               if (flags & NL80211_FEATURE_SAE)
-                       capa->flags |= WPA_DRIVER_FLAGS_SAE;
-
-               if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
-                       capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
-       }
-
-       if (tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]) {
-               int protocols =
-                       nla_get_u32(tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
-               wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response "
-                          "offload in AP mode");
-               capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
-               capa->probe_resp_offloads =
-                       probe_resp_offload_support(protocols);
-       }
+       wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+       wiphy_info_probe_resp_offload(capa,
+                                     tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
 
        return NL_SKIP;
 }
@@ -2745,6 +2814,7 @@ broken_combination:
 static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
                                       struct wiphy_info_data *info)
 {
+       u32 feat;
        struct nl_msg *msg;
 
        os_memset(info, 0, sizeof(*info));
@@ -2754,13 +2824,40 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
        if (!msg)
                return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
+       feat = get_nl80211_protocol_features(drv);
+       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+               nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
+       else
+               nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
+       NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
 
-       if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0)
-               return 0;
-       msg = NULL;
+       if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+               return -1;
+
+       if (info->auth_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+       else if (!info->connect_supported) {
+               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+                          "authentication/association or connect commands");
+               info->error = 1;
+       }
+
+       if (info->p2p_go_supported && info->p2p_client_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+       if (info->p2p_concurrent) {
+               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+                          "interface (driver advertised support)");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+       }
+       if (info->p2p_multichan_concurrent) {
+               wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+                          "concurrent (driver advertised support)");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT;
+       }
+       return 0;
 nla_put_failure:
        nlmsg_free(msg);
        return -1;
@@ -4921,17 +5018,101 @@ static int wpa_driver_nl80211_authenticate_retry(
 struct phy_info_arg {
        u16 *num_modes;
        struct hostapd_hw_modes *modes;
+       int last_mode, last_chan_idx;
 };
 
-static int phy_info_handler(struct nl_msg *msg, void *arg)
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+                            struct nlattr *ampdu_factor,
+                            struct nlattr *ampdu_density,
+                            struct nlattr *mcs_set)
 {
-       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct phy_info_arg *phy_info = arg;
+       if (capa)
+               mode->ht_capab = nla_get_u16(capa);
 
-       struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+       if (ampdu_factor)
+               mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
 
-       struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+       if (ampdu_density)
+               mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+       if (mcs_set && nla_len(mcs_set) >= 16) {
+               u8 *mcs;
+               mcs = nla_data(mcs_set);
+               os_memcpy(mode->mcs_set, mcs, 16);
+       }
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+                             struct nlattr *capa,
+                             struct nlattr *mcs_set)
+{
+       if (capa)
+               mode->vht_capab = nla_get_u32(capa);
+
+       if (mcs_set && nla_len(mcs_set) >= 8) {
+               u8 *mcs;
+               mcs = nla_data(mcs_set);
+               os_memcpy(mode->vht_mcs_set, mcs, 8);
+       }
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+                         struct hostapd_channel_data *chan,
+                         struct nlattr *tb_freq[])
+{
+       chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+       chan->flag = 0;
+
+       /* mode is not set */
+       if (mode->mode >= NUM_HOSTAPD_MODES) {
+               /* crude heuristic */
+               if (chan->freq < 4000)
+                       mode->mode = HOSTAPD_MODE_IEEE80211B;
+               else if (chan->freq > 50000)
+                       mode->mode = HOSTAPD_MODE_IEEE80211AD;
+               else
+                       mode->mode = HOSTAPD_MODE_IEEE80211A;
+       }
+
+       switch (mode->mode) {
+       case HOSTAPD_MODE_IEEE80211AD:
+               chan->chan = (chan->freq - 56160) / 2160;
+               break;
+       case HOSTAPD_MODE_IEEE80211A:
+               chan->chan = chan->freq / 5 - 1000;
+               break;
+       case HOSTAPD_MODE_IEEE80211B:
+       case HOSTAPD_MODE_IEEE80211G:
+               if (chan->freq == 2484)
+                       chan->chan = 14;
+               else
+                       chan->chan = (chan->freq - 2407) / 5;
+               break;
+       default:
+               break;
+       }
+
+       if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+               chan->flag |= HOSTAPD_CHAN_DISABLED;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+               chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
+               chan->flag |= HOSTAPD_CHAN_NO_IBSS;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+               chan->flag |= HOSTAPD_CHAN_RADAR;
+
+       if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+           !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+               chan->max_tx_power = nla_get_u32(
+                       tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+                         struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
        static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
                [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
                [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
@@ -4940,192 +5121,167 @@ static int phy_info_handler(struct nl_msg *msg, void *arg)
                [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
                [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
        };
-
-       struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
-       static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
-               [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
-               [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
-       };
-
-       struct nlattr *nl_band;
+       int new_channels = 0;
+       struct hostapd_channel_data *channel;
+       struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
        struct nlattr *nl_freq;
-       struct nlattr *nl_rate;
-       int rem_band, rem_freq, rem_rate;
-       struct hostapd_hw_modes *mode;
-       int idx, mode_is_set;
+       int rem_freq, idx;
 
-       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (tb == NULL)
+               return NL_OK;
 
-       if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+       nla_for_each_nested(nl_freq, tb, rem_freq) {
+               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                       continue;
+               new_channels++;
+       }
+
+       channel = os_realloc_array(mode->channels,
+                                  mode->num_channels + new_channels,
+                                  sizeof(struct hostapd_channel_data));
+       if (!channel)
                return NL_SKIP;
 
-       nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
-               mode = os_realloc_array(phy_info->modes,
-                                       *phy_info->num_modes + 1,
-                                       sizeof(*mode));
-               if (!mode)
-                       return NL_SKIP;
-               phy_info->modes = mode;
+       mode->channels = channel;
+       mode->num_channels += new_channels;
 
-               mode_is_set = 0;
+       idx = phy_info->last_chan_idx;
 
-               mode = &phy_info->modes[*(phy_info->num_modes)];
-               memset(mode, 0, sizeof(*mode));
-               mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
-               *(phy_info->num_modes) += 1;
+       nla_for_each_nested(nl_freq, tb, rem_freq) {
+               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                       continue;
+               phy_info_freq(mode, &mode->channels[idx], tb_freq);
+               idx++;
+       }
+       phy_info->last_chan_idx = idx;
 
-               nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
-                         nla_len(nl_band), NULL);
+       return NL_OK;
+}
 
-               if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
-                       mode->ht_capab = nla_get_u16(
-                               tb_band[NL80211_BAND_ATTR_HT_CAPA]);
-               }
 
-               if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
-                       mode->a_mpdu_params |= nla_get_u8(
-                               tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) &
-                               0x03;
-               }
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+       static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+               [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+               [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
+               { .type = NLA_FLAG },
+       };
+       struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+       struct nlattr *nl_rate;
+       int rem_rate, idx;
 
-               if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
-                       mode->a_mpdu_params |= nla_get_u8(
-                               tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) <<
-                               2;
-               }
+       if (tb == NULL)
+               return NL_OK;
 
-               if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
-                   nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) {
-                       u8 *mcs;
-                       mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
-                       os_memcpy(mode->mcs_set, mcs, 16);
-               }
+       nla_for_each_nested(nl_rate, tb, rem_rate) {
+               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+                         nla_data(nl_rate), nla_len(nl_rate),
+                         rate_policy);
+               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                       continue;
+               mode->num_rates++;
+       }
 
-               if (tb_band[NL80211_BAND_ATTR_VHT_CAPA]) {
-                       mode->vht_capab = nla_get_u32(
-                               tb_band[NL80211_BAND_ATTR_VHT_CAPA]);
-               }
+       mode->rates = os_calloc(mode->num_rates, sizeof(int));
+       if (!mode->rates)
+               return NL_SKIP;
 
-               if (tb_band[NL80211_BAND_ATTR_VHT_MCS_SET] &&
-                   nla_len(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET])) {
-                       u8 *mcs;
-                       mcs = nla_data(tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
-                       os_memcpy(mode->vht_mcs_set, mcs, 8);
-               }
+       idx = 0;
 
-               nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-                       nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
-                                 nla_len(nl_freq), freq_policy);
-                       if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                               continue;
-                       mode->num_channels++;
-               }
+       nla_for_each_nested(nl_rate, tb, rem_rate) {
+               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+                         nla_data(nl_rate), nla_len(nl_rate),
+                         rate_policy);
+               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                       continue;
+               mode->rates[idx] = nla_get_u32(
+                       tb_rate[NL80211_BITRATE_ATTR_RATE]);
 
-               mode->channels = os_calloc(mode->num_channels,
-                                          sizeof(struct hostapd_channel_data));
-               if (!mode->channels)
-                       return NL_SKIP;
+               /* crude heuristic */
+               if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
+                   mode->rates[idx] > 200)
+                       mode->mode = HOSTAPD_MODE_IEEE80211G;
 
-               idx = 0;
-
-               nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
-                       nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
-                                 nla_len(nl_freq), freq_policy);
-                       if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                               continue;
-
-                       mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
-                       mode->channels[idx].flag = 0;
-
-                       if (!mode_is_set) {
-                               /* crude heuristic */
-                               if (mode->channels[idx].freq < 4000)
-                                       mode->mode = HOSTAPD_MODE_IEEE80211B;
-                               else if (mode->channels[idx].freq > 50000)
-                                       mode->mode = HOSTAPD_MODE_IEEE80211AD;
-                               else
-                                       mode->mode = HOSTAPD_MODE_IEEE80211A;
-                               mode_is_set = 1;
-                       }
+               idx++;
+       }
 
-                       switch (mode->mode) {
-                       case HOSTAPD_MODE_IEEE80211AD:
-                               mode->channels[idx].chan =
-                                       (mode->channels[idx].freq - 56160) /
-                                       2160;
-                               break;
-                       case HOSTAPD_MODE_IEEE80211A:
-                               mode->channels[idx].chan =
-                                       mode->channels[idx].freq / 5 - 1000;
-                               break;
-                       case HOSTAPD_MODE_IEEE80211B:
-                       case HOSTAPD_MODE_IEEE80211G:
-                               if (mode->channels[idx].freq == 2484)
-                                       mode->channels[idx].chan = 14;
-                               else
-                                       mode->channels[idx].chan =
-                                               (mode->channels[idx].freq -
-                                                2407) / 5;
-                               break;
-                       default:
-                               break;
-                       }
+       return NL_OK;
+}
 
-                       if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-                               mode->channels[idx].flag |=
-                                       HOSTAPD_CHAN_DISABLED;
-                       if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
-                               mode->channels[idx].flag |=
-                                       HOSTAPD_CHAN_PASSIVE_SCAN;
-                       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
-                               mode->channels[idx].flag |=
-                                       HOSTAPD_CHAN_NO_IBSS;
-                       if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
-                               mode->channels[idx].flag |=
-                                       HOSTAPD_CHAN_RADAR;
-
-                       if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
-                           !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-                               mode->channels[idx].max_tx_power =
-                                       nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
-
-                       idx++;
-               }
 
-               nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
-                       nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
-                                 nla_len(nl_rate), rate_policy);
-                       if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-                               continue;
-                       mode->num_rates++;
-               }
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+       struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+       struct hostapd_hw_modes *mode;
+       int ret;
 
-               mode->rates = os_calloc(mode->num_rates, sizeof(int));
-               if (!mode->rates)
+       if (phy_info->last_mode != nl_band->nla_type) {
+               mode = os_realloc_array(phy_info->modes,
+                                       *phy_info->num_modes + 1,
+                                       sizeof(*mode));
+               if (!mode)
                        return NL_SKIP;
+               phy_info->modes = mode;
 
-               idx = 0;
+               mode = &phy_info->modes[*(phy_info->num_modes)];
+               os_memset(mode, 0, sizeof(*mode));
+               mode->mode = NUM_HOSTAPD_MODES;
+               mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
+               *(phy_info->num_modes) += 1;
+               phy_info->last_mode = nl_band->nla_type;
+               phy_info->last_chan_idx = 0;
+       } else
+               mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+       nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+                 nla_len(nl_band), NULL);
+
+       phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+                        tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+       phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+                         tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+       ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
+       if (ret != NL_OK)
+               return ret;
+       ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+       if (ret != NL_OK)
+               return ret;
 
-               nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
-                       nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
-                                 nla_len(nl_rate), rate_policy);
-                       if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-                               continue;
-                       mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+       return NL_OK;
+}
 
-                       /* crude heuristic */
-                       if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
-                           mode->rates[idx] > 200)
-                               mode->mode = HOSTAPD_MODE_IEEE80211G;
 
-                       idx++;
-               }
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct phy_info_arg *phy_info = arg;
+       struct nlattr *nl_band;
+       int rem_band;
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+               return NL_SKIP;
+
+       nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
+       {
+               int res = phy_info_band(phy_info, nl_band);
+               if (res != NL_OK)
+                       return res;
        }
 
        return NL_SKIP;
 }
 
+
 static struct hostapd_hw_modes *
 wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes)
 {
@@ -5350,12 +5506,14 @@ static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
 static struct hostapd_hw_modes *
 wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
 {
+       u32 feat;
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        struct phy_info_arg result = {
                .num_modes = num_modes,
                .modes = NULL,
+               .last_mode = -1,
        };
 
        *num_modes = 0;
@@ -5365,8 +5523,13 @@ wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
        if (!msg)
                return NULL;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
+       feat = get_nl80211_protocol_features(drv);
+       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+               nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
+       else
+               nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
 
+       NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
        NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 
        if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
@@ -7043,6 +7206,20 @@ skip_auth_type:
                        params->htcaps_mask);
        }
 
+#ifdef CONFIG_VHT_OVERRIDES
+       if (params->disable_vht) {
+               wpa_printf(MSG_DEBUG, "  * VHT disabled");
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
+       }
+
+       if (params->vhtcaps && params->vhtcaps_mask) {
+               int sz = sizeof(struct ieee80211_vht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
+               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+                       params->vhtcaps_mask);
+       }
+#endif /* CONFIG_VHT_OVERRIDES */
+
        ret = nl80211_set_conn_keys(params, msg);
        if (ret)
                goto nla_put_failure;
@@ -7229,6 +7406,20 @@ static int wpa_driver_nl80211_associate(
                        params->htcaps_mask);
        }
 
+#ifdef CONFIG_VHT_OVERRIDES
+       if (params->disable_vht) {
+               wpa_printf(MSG_DEBUG, "  * VHT disabled");
+               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
+       }
+
+       if (params->vhtcaps && params->vhtcaps_mask) {
+               int sz = sizeof(struct ieee80211_vht_capabilities);
+               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
+               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+                       params->vhtcaps_mask);
+       }
+#endif /* CONFIG_VHT_OVERRIDES */
+
        if (params->p2p)
                wpa_printf(MSG_DEBUG, "  * P2P group");
 
index c46bb01..79da871 100644 (file)
  * The station is still assumed to belong to the AP interface it was added
  * to.
  *
- * TODO: need more info?
+ * Station handling varies per interface type and depending on the driver's
+ * capabilities.
+ *
+ * For drivers supporting TDLS with external setup (WIPHY_FLAG_SUPPORTS_TDLS
+ * and WIPHY_FLAG_TDLS_EXTERNAL_SETUP), the station lifetime is as follows:
+ *  - a setup station entry is added, not yet authorized, without any rate
+ *    or capability information, this just exists to avoid race conditions
+ *  - when the TDLS setup is done, a single NL80211_CMD_SET_STATION is valid
+ *    to add rate and capability information to the station and at the same
+ *    time mark it authorized.
+ *  - %NL80211_TDLS_ENABLE_LINK is then used
+ *  - after this, the only valid operation is to remove it by tearing down
+ *    the TDLS link (%NL80211_TDLS_DISABLE_LINK)
+ *
+ * TODO: need more info for other interface types
  */
 
 /**
  * @NL80211_CMD_NEW_PEER_CANDIDATE: Notification on the reception of a
  *      beacon or probe response from a compatible mesh peer.  This is only
  *      sent while no station information (sta_info) exists for the new peer
- *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH is set.  On
- *      reception of this notification, userspace may decide to create a new
- *      station (@NL80211_CMD_NEW_STATION).  To stop this notification from
+ *      candidate and when @NL80211_MESH_SETUP_USERSPACE_AUTH,
+ *      @NL80211_MESH_SETUP_USERSPACE_AMPE, or
+ *      @NL80211_MESH_SETUP_USERSPACE_MPM is set.  On reception of this
+ *      notification, userspace may decide to create a new station
+ *      (@NL80211_CMD_NEW_STATION).  To stop this notification from
  *      reoccurring, the userspace authentication daemon may want to create the
  *      new station with the AUTHENTICATED flag unset and maybe change it later
  *      depending on the authentication result.
  *     %NL80211_ATTR_RADAR_EVENT is used to inform about the type of the
  *     event.
  *
+ * @NL80211_CMD_GET_PROTOCOL_FEATURES: Get global nl80211 protocol features,
+ *     i.e. features for the nl80211 protocol rather than device features.
+ *     Returns the features in the %NL80211_ATTR_PROTOCOL_FEATURES bitmap.
+ *
+ * @NL80211_CMD_UPDATE_FT_IES: Pass down the most up-to-date Fast Transition
+ *     Information Element to the WLAN driver
+ *
+ * @NL80211_CMD_FT_EVENT: Send a Fast transition event from the WLAN driver
+ *     to the supplicant. This will carry the target AP's MAC address along
+ *     with the relevant Information Elements. This event is used to report
+ *     received FT IEs (MDIE, FTIE, RSN IE, TIE, RICIE).
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -765,6 +793,11 @@ enum nl80211_commands {
 
        NL80211_CMD_RADAR_DETECT,
 
+       NL80211_CMD_GET_PROTOCOL_FEATURES,
+
+       NL80211_CMD_UPDATE_FT_IES,
+       NL80211_CMD_FT_EVENT,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -884,7 +917,8 @@ enum nl80211_commands {
  *     consisting of a nested array.
  *
  * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
- * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_STA_PLINK_ACTION: action to perform on the mesh peer link
+ *     (see &enum nl80211_plink_action).
  * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
  * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
  *     info given for %NL80211_CMD_GET_MPATH, nested attribute described at
@@ -1167,10 +1201,10 @@ enum nl80211_commands {
  * @NL80211_ATTR_SUPPORT_MESH_AUTH: Currently, this means the underlying driver
  *     allows auth frames in a mesh to be passed to userspace for processing via
  *     the @NL80211_MESH_SETUP_USERSPACE_AUTH flag.
- * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as
- *     defined in &enum nl80211_plink_state. Used when userspace is
- *     driving the peer link management state machine.
- *     @NL80211_MESH_SETUP_USERSPACE_AMPE must be enabled.
+ * @NL80211_ATTR_STA_PLINK_STATE: The state of a mesh peer link as defined in
+ *     &enum nl80211_plink_state. Used when userspace is driving the peer link
+ *     management state machine.  @NL80211_MESH_SETUP_USERSPACE_AMPE or
+ *     @NL80211_MESH_SETUP_USERSPACE_MPM must be enabled.
  *
  * @NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: indicates, as part of the wiphy
  *     capabilities, the supported WoWLAN triggers
@@ -1368,6 +1402,18 @@ enum nl80211_commands {
  *     advertised to the driver, e.g., to enable TDLS off channel operations
  *     and PU-APSD.
  *
+ * @NL80211_ATTR_PROTOCOL_FEATURES: global nl80211 feature flags, see
+ *     &enum nl80211_protocol_features, the attribute is a u32.
+ *
+ * @NL80211_ATTR_SPLIT_WIPHY_DUMP: flag attribute, userspace supports
+ *     receiving the data for a single wiphy split across multiple
+ *     messages, given with wiphy dump message
+ *
+ * @NL80211_ATTR_MDID: Mobility Domain Identifier
+ *
+ * @NL80211_ATTR_IE_RIC: Resource Information Container Information
+ *     Element
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1654,6 +1700,15 @@ enum nl80211_attrs {
        NL80211_ATTR_STA_CAPABILITY,
        NL80211_ATTR_STA_EXT_CAPABILITY,
 
+       NL80211_ATTR_PROTOCOL_FEATURES,
+       NL80211_ATTR_SPLIT_WIPHY_DUMP,
+
+       NL80211_ATTR_DISABLE_VHT,
+       NL80211_ATTR_VHT_CAPABILITY_MASK,
+
+       NL80211_ATTR_MDID,
+       NL80211_ATTR_IE_RIC,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -2412,8 +2467,10 @@ enum nl80211_mesh_power_mode {
  * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
  *     point.
  *
- * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
- *     open peer links when we detect compatible mesh peers.
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically open
+ *     peer links when we detect compatible mesh peers. Disabled if
+ *     @NL80211_MESH_SETUP_USERSPACE_MPM or @NL80211_MESH_SETUP_USERSPACE_AMPE are
+ *     set.
  *
  * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
  *     containing a PREQ that an MP can send to a particular destination (path
@@ -2559,6 +2616,9 @@ enum nl80211_meshconf_params {
  *     vendor specific synchronization method or disable it to use the default
  *     neighbor offset synchronization
  *
+ * @NL80211_MESH_SETUP_USERSPACE_MPM: Enable this option if userspace will
+ *     implement an MPM which handles peer allocation and state.
+ *
  * @NL80211_MESH_SETUP_ATTR_MAX: highest possible mesh setup attribute number
  *
  * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
@@ -2571,6 +2631,7 @@ enum nl80211_mesh_setup_params {
        NL80211_MESH_SETUP_USERSPACE_AUTH,
        NL80211_MESH_SETUP_USERSPACE_AMPE,
        NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC,
+       NL80211_MESH_SETUP_USERSPACE_MPM,
 
        /* keep last */
        __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
@@ -3307,6 +3368,23 @@ enum nl80211_plink_state {
        MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
 };
 
+/**
+ * enum nl80211_plink_action - actions to perform in mesh peers
+ *
+ * @NL80211_PLINK_ACTION_NO_ACTION: perform no action
+ * @NL80211_PLINK_ACTION_OPEN: start mesh peer link establishment
+ * @NL80211_PLINK_ACTION_BLOCK: block traffic from this mesh peer
+ * @NUM_NL80211_PLINK_ACTIONS: number of possible actions
+ */
+enum plink_actions {
+       NL80211_PLINK_ACTION_NO_ACTION,
+       NL80211_PLINK_ACTION_OPEN,
+       NL80211_PLINK_ACTION_BLOCK,
+
+       NUM_NL80211_PLINK_ACTIONS,
+};
+
+
 #define NL80211_KCK_LEN                        16
 #define NL80211_KEK_LEN                        16
 #define NL80211_REPLAY_CTR_LEN         8
@@ -3456,6 +3534,10 @@ enum nl80211_ap_sme_features {
  *     stations the authenticated/associated bits have to be set in the mask.
  * @NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: cfg80211 advertises channel limits
  *     (HT40, VHT 80/160 MHz) if this flag is set
+ * @NL80211_FEATURE_USERSPACE_MPM: This driver supports a userspace Mesh
+ *     Peering Management entity which may be implemented by registering for
+ *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
+ *     still generated by the driver.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3474,6 +3556,7 @@ enum nl80211_feature_flags {
        /* bit 13 is reserved */
        NL80211_FEATURE_ADVERTISE_CHAN_LIMITS           = 1 << 14,
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
+       NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
 };
 
 /**
@@ -3587,4 +3670,16 @@ enum nl80211_dfs_state {
        NL80211_DFS_AVAILABLE,
 };
 
+/**
+ * enum enum nl80211_protocol_features - nl80211 protocol features
+ * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
+ *     wiphy dumps (if requested by the application with the attribute
+ *     %NL80211_ATTR_SPLIT_WIPHY_DUMP. Also supported is filtering the
+ *     wiphy dump by %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFINDEX or
+ *     %NL80211_ATTR_WDEV.
+ */
+enum nl80211_protocol_features {
+       NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP =     1 << 0,
+};
+
 #endif /* __LINUX_NL80211_H */
index 9559e44..edb8d72 100644 (file)
@@ -564,6 +564,8 @@ int p2p_group_notif_assoc(struct p2p_group *group, const u8 *addr,
        if (group == NULL)
                return -1;
 
+       p2p_add_device(group->p2p, addr, 0, NULL, 0, ie, len, 0);
+
        m = os_zalloc(sizeof(*m));
        if (m == NULL)
                return -1;
index 4332d82..c0553f7 100644 (file)
@@ -147,6 +147,10 @@ ifdef CONFIG_HT_OVERRIDES
 L_CFLAGS += -DCONFIG_HT_OVERRIDES
 endif
 
+ifdef CONFIG_VHT_OVERRIDES
+L_CFLAGS += -DCONFIG_VHT_OVERRIDES
+endif
+
 ifndef CONFIG_BACKEND
 CONFIG_BACKEND=file
 endif
index da2abfc..0634219 100644 (file)
@@ -126,6 +126,10 @@ ifdef CONFIG_HT_OVERRIDES
 CFLAGS += -DCONFIG_HT_OVERRIDES
 endif
 
+ifdef CONFIG_VHT_OVERRIDES
+CFLAGS += -DCONFIG_VHT_OVERRIDES
+endif
+
 ifndef CONFIG_BACKEND
 CONFIG_BACKEND=file
 endif
index 58d0c43..36f0e80 100644 (file)
@@ -224,6 +224,9 @@ CONFIG_SMARTCARD=y
 # Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
 #CONFIG_HT_OVERRIDES=y
 
+# Support VHT overrides (disable VHT, mask MCS rates, etc.)
+#CONFIG_VHT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
index 91d82ae..6860765 100644 (file)
@@ -178,10 +178,17 @@ static int wpa_config_parse_int(const struct parse_data *data,
                                struct wpa_ssid *ssid,
                                int line, const char *value)
 {
-       int *dst;
+       int val, *dst;
+       char *end;
 
        dst = (int *) (((u8 *) ssid) + (long) data->param1);
-       *dst = atoi(value);
+       val = strtol(value, &end, 0);
+       if (*end) {
+               wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+                          line, value);
+               return -1;
+       }
+       *dst = val;
        wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
        if (data->param3 && *dst < (long) data->param3) {
@@ -1543,6 +1550,27 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(ampdu_density, -1, 7) },
        { STR(ht_mcs) },
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       { INT_RANGE(disable_vht, 0, 1) },
+       { INT(vht_capa) },
+       { INT(vht_capa_mask) },
+       { INT_RANGE(vht_rx_mcs_nss_1, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_2, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_3, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_4, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_5, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_6, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_7, -1, 3) },
+       { INT_RANGE(vht_rx_mcs_nss_8, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_1, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_2, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_3, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_4, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_5, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_6, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_7, -1, 3) },
+       { INT_RANGE(vht_tx_mcs_nss_8, -1, 3) },
+#endif /* CONFIG_VHT_OVERRIDES */
        { INT(ap_max_inactivity) },
        { INT(dtim_period) },
        { INT(beacon_int) },
@@ -1946,6 +1974,24 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
        ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       ssid->vht_rx_mcs_nss_1 = -1;
+       ssid->vht_rx_mcs_nss_2 = -1;
+       ssid->vht_rx_mcs_nss_3 = -1;
+       ssid->vht_rx_mcs_nss_4 = -1;
+       ssid->vht_rx_mcs_nss_5 = -1;
+       ssid->vht_rx_mcs_nss_6 = -1;
+       ssid->vht_rx_mcs_nss_7 = -1;
+       ssid->vht_rx_mcs_nss_8 = -1;
+       ssid->vht_tx_mcs_nss_1 = -1;
+       ssid->vht_tx_mcs_nss_2 = -1;
+       ssid->vht_tx_mcs_nss_3 = -1;
+       ssid->vht_tx_mcs_nss_4 = -1;
+       ssid->vht_tx_mcs_nss_5 = -1;
+       ssid->vht_tx_mcs_nss_6 = -1;
+       ssid->vht_tx_mcs_nss_7 = -1;
+       ssid->vht_tx_mcs_nss_8 = -1;
+#endif /* CONFIG_VHT_OVERRIDES */
        ssid->proactive_key_caching = -1;
 #ifdef CONFIG_IEEE80211W
        ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
@@ -2590,9 +2636,18 @@ static int wpa_global_config_parse_int(const struct global_parse_data *data,
                                       struct wpa_config *config, int line,
                                       const char *pos)
 {
-       int *dst;
+       int val, *dst;
+       char *end;
+
        dst = (int *) (((u8 *) config) + (long) data->param1);
-       *dst = atoi(pos);
+       val = strtol(pos, &end, 0);
+       if (*end) {
+               wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"",
+                          line, pos);
+               return -1;
+       }
+       *dst = val;
+
        wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
 
        if (data->param2 && *dst < (long) data->param2) {
index e225d3f..baa28b3 100644 (file)
@@ -541,6 +541,35 @@ struct wpa_ssid {
        char *ht_mcs;
 #endif /* CONFIG_HT_OVERRIDES */
 
+#ifdef CONFIG_VHT_OVERRIDES
+       /**
+        * disable_vht - Disable VHT (IEEE 802.11ac) for this network
+        *
+        * By default, use it if it is available, but this can be configured
+        * to 1 to have it disabled.
+        */
+       int disable_vht;
+
+       /**
+        * vht_capa - VHT capabilities to use
+        */
+       unsigned int vht_capa;
+
+       /**
+        * vht_capa_mask - mask for VHT capabilities
+        */
+       unsigned int vht_capa_mask;
+
+       int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2,
+           vht_rx_mcs_nss_3, vht_rx_mcs_nss_4,
+           vht_rx_mcs_nss_5, vht_rx_mcs_nss_6,
+           vht_rx_mcs_nss_7, vht_rx_mcs_nss_8;
+       int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2,
+           vht_tx_mcs_nss_3, vht_tx_mcs_nss_4,
+           vht_tx_mcs_nss_5, vht_tx_mcs_nss_6,
+           vht_tx_mcs_nss_7, vht_tx_mcs_nss_8;
+#endif /* CONFIG_VHT_OVERRIDES */
+
        /**
         * ap_max_inactivity - Timeout in seconds to detect STA's inactivity
         *
index dec002f..d636a67 100644 (file)
@@ -2235,10 +2235,14 @@ static int wpa_supplicant_ctrl_iface_remove_network(
 {
        int id;
        struct wpa_ssid *ssid;
+       int was_disabled;
 
        /* cmd: "<network id>" or "all" */
        if (os_strcmp(cmd, "all") == 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
+               if (wpa_s->sched_scanning)
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+
                eapol_sm_invalidate_cached_session(wpa_s->eapol);
                if (wpa_s->current_ssid) {
 #ifdef CONFIG_SME
@@ -2291,12 +2295,21 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
 
+       was_disabled = ssid->disabled;
+
        if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
                           "network id=%d", id);
                return -1;
        }
 
+       if (!was_disabled && wpa_s->sched_scanning) {
+               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
+                          "network from filters");
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+
        return 0;
 }
 
index 5e06932..8088755 100644 (file)
@@ -1479,6 +1479,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
        char *iface = NULL, *net_id = NULL;
        int id;
        struct wpa_ssid *ssid;
+       int was_disabled;
 
        dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
                              DBUS_TYPE_INVALID);
@@ -1505,6 +1506,8 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
                goto out;
        }
 
+       was_disabled = ssid->disabled;
+
        wpas_notify_network_removed(wpa_s, ssid);
 
        if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
@@ -1520,6 +1523,13 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
        if (ssid == wpa_s->current_ssid)
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
+       else if (!was_disabled && wpa_s->sched_scanning) {
+               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
+                          "network from filters");
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+
 
 out:
        os_free(iface);
@@ -1559,6 +1569,9 @@ static void remove_network(void *arg, struct wpa_ssid *ssid)
 DBusMessage * wpas_dbus_handler_remove_all_networks(
        DBusMessage *message, struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->sched_scanning)
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+
        /* NB: could check for failure and return an error */
        wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s);
        return NULL;
index 711b407..e867bae 100644 (file)
@@ -225,6 +225,9 @@ CONFIG_SMARTCARD=y
 # Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
 #CONFIG_HT_OVERRIDES=y
 
+# Support VHT overrides (disable VHT, mask MCS rates, etc.)
+#CONFIG_VHT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
index 0b21700..db7cd6e 100644 (file)
@@ -340,9 +340,11 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
        if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
        if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-                                wpa_s->parent, NULL) > 0)
+                                wpa_s->parent, NULL) > 0) {
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
                           "timeout");
+               wpa_s->p2p_in_provisioning = 0;
+       }
 
        if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
                wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
index 91a436a..54afd01 100644 (file)
@@ -1134,8 +1134,16 @@ scan:
                wpa_s->first_sched_scan = 0;
                wpa_s->sched_scan_timeout /= 2;
                wpa_s->sched_scan_interval *= 2;
+               if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
+                       wpa_s->sched_scan_interval = 10;
+                       wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
+               }
        }
 
+       /* If there is no more ssids, start next time from the beginning */
+       if (!ssid)
+               wpa_s->prev_sched_ssid = NULL;
+
        return 0;
 }
 
index 30f9779..4c78161 100644 (file)
@@ -632,6 +632,10 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        struct ieee80211_ht_capabilities htcaps;
        struct ieee80211_ht_capabilities htcaps_mask;
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       struct ieee80211_vht_capabilities vhtcaps;
+       struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
 
        os_memset(&params, 0, sizeof(params));
        params.bssid = bssid;
@@ -653,6 +657,13 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.htcaps_mask = (u8 *) &htcaps_mask;
        wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+       os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+       params.vhtcaps = &vhtcaps;
+       params.vhtcaps_mask = &vhtcaps_mask;
+       wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
 #ifdef CONFIG_IEEE80211R
        if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
                params.wpa_ie = wpa_s->sme.ft_ies;
@@ -1258,8 +1269,6 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
 {
        struct wpa_ssid *ssid;
 
-       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
-               return;
        if (wpa_s->wpa_state != WPA_COMPLETED)
                return;
        ssid = wpa_s->current_ssid;
index 9ef932a..7d50e4d 100644 (file)
@@ -1747,6 +1747,24 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
        wpa_supplicant_clear_connection(wpa_s, addr);
 }
 
+static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid)
+{
+       if (!ssid || !ssid->disabled || ssid->disabled == 2)
+               return;
+
+       ssid->disabled = 0;
+       wpas_clear_temp_disabled(wpa_s, ssid, 1);
+       wpas_notify_network_enabled_changed(wpa_s, ssid);
+
+       /*
+        * Try to reassociate since there is no current configuration and a new
+        * network was made available.
+        */
+       if (!wpa_s->current_ssid)
+               wpa_s->reassociate = 1;
+}
+
 
 /**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
@@ -1758,48 +1776,20 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
                                   struct wpa_ssid *ssid)
 {
-       struct wpa_ssid *other_ssid;
-       int was_disabled;
-
        if (ssid == NULL) {
-               for (other_ssid = wpa_s->conf->ssid; other_ssid;
-                    other_ssid = other_ssid->next) {
-                       if (other_ssid->disabled == 2)
-                               continue; /* do not change persistent P2P group
-                                          * data */
-                       if (other_ssid == wpa_s->current_ssid &&
-                           other_ssid->disabled)
-                               wpa_s->reassociate = 1;
-
-                       was_disabled = other_ssid->disabled;
-
-                       other_ssid->disabled = 0;
-                       if (was_disabled)
-                               wpas_clear_temp_disabled(wpa_s, other_ssid, 0);
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+                       wpa_supplicant_enable_one_network(wpa_s, ssid);
+       } else
+               wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-                       if (was_disabled != other_ssid->disabled)
-                               wpas_notify_network_enabled_changed(
-                                       wpa_s, other_ssid);
-               }
-               if (wpa_s->reassociate)
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
-       } else if (ssid->disabled && ssid->disabled != 2) {
-               if (wpa_s->current_ssid == NULL) {
-                       /*
-                        * Try to reassociate since there is no current
-                        * configuration and a new network was made available.
-                        */
-                       wpa_s->reassociate = 1;
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       if (wpa_s->reassociate) {
+               if (wpa_s->sched_scanning) {
+                       wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
+                                  "new network to scan filters");
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
                }
 
-               was_disabled = ssid->disabled;
-
-               ssid->disabled = 0;
-               wpas_clear_temp_disabled(wpa_s, ssid, 1);
-
-               if (was_disabled != ssid->disabled)
-                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
        }
 }
 
@@ -1818,6 +1808,9 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
        int was_disabled;
 
        if (ssid == NULL) {
+               if (wpa_s->sched_scanning)
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+
                for (other_ssid = wpa_s->conf->ssid; other_ssid;
                     other_ssid = other_ssid->next) {
                        was_disabled = other_ssid->disabled;
@@ -1843,8 +1836,15 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
 
                ssid->disabled = 1;
 
-               if (was_disabled != ssid->disabled)
+               if (was_disabled != ssid->disabled) {
                        wpas_notify_network_enabled_changed(wpa_s, ssid);
+                       if (wpa_s->sched_scanning) {
+                               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan "
+                                          "to remove network from filters");
+                               wpa_supplicant_cancel_sched_scan(wpa_s);
+                               wpa_supplicant_req_scan(wpa_s, 0, 0);
+                       }
+               }
        }
 }
 
@@ -2646,6 +2646,54 @@ void wpa_supplicant_apply_ht_overrides(
 #endif /* CONFIG_HT_OVERRIDES */
 
 
+#ifdef CONFIG_VHT_OVERRIDES
+void wpa_supplicant_apply_vht_overrides(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       struct wpa_driver_associate_params *params)
+{
+       struct ieee80211_vht_capabilities *vhtcaps;
+       struct ieee80211_vht_capabilities *vhtcaps_mask;
+
+       if (!ssid)
+               return;
+
+       params->disable_vht = ssid->disable_vht;
+
+       vhtcaps = (void *) params->vhtcaps;
+       vhtcaps_mask = (void *) params->vhtcaps_mask;
+
+       if (!vhtcaps || !vhtcaps_mask)
+               return;
+
+       vhtcaps->vht_capabilities_info = ssid->vht_capa;
+       vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+
+#define OVERRIDE_MCS(i)                                                        \
+       if (ssid->vht_tx_mcs_nss_ ##i >= 0) {                           \
+               vhtcaps_mask->vht_supported_mcs_set.tx_map |=           \
+                       3 << 2 * (i - 1);                               \
+               vhtcaps->vht_supported_mcs_set.tx_map |=                \
+                       ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);       \
+       }                                                               \
+       if (ssid->vht_rx_mcs_nss_ ##i >= 0) {                           \
+               vhtcaps_mask->vht_supported_mcs_set.rx_map |=           \
+                       3 << 2 * (i - 1);                               \
+               vhtcaps->vht_supported_mcs_set.rx_map |=                \
+                       ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);       \
+       }
+
+       OVERRIDE_MCS(1);
+       OVERRIDE_MCS(2);
+       OVERRIDE_MCS(3);
+       OVERRIDE_MCS(4);
+       OVERRIDE_MCS(5);
+       OVERRIDE_MCS(6);
+       OVERRIDE_MCS(7);
+       OVERRIDE_MCS(8);
+}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+
 static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
 {
 #ifdef PCSC_FUNCS
index c9deb4b..1ded4e1 100644 (file)
@@ -869,6 +869,20 @@ fast_reauth=1
 # -1 = Do not make any changes.
 # 0-3 = Set AMPDU density (aka factor) to specified value.
 
+# disable_vht: Whether VHT should be disabled.
+# 0 = VHT enabled (if AP supports it)
+# 1 = VHT disabled
+#
+# vht_capa: VHT capabilities to set in the override
+# vht_capa_mask: mask of VHT capabilities
+#
+# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8
+# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8
+#  0: MCS 0-7
+#  1: MCS 0-8
+#  2: MCS 0-9
+#  3: not supported
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
index 0f51f8e..4ec15c1 100644 (file)
@@ -681,6 +681,9 @@ struct wpa_supplicant {
 void wpa_supplicant_apply_ht_overrides(
        struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
        struct wpa_driver_associate_params *params);
+void wpa_supplicant_apply_vht_overrides(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       struct wpa_driver_associate_params *params);
 
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);