Extend hw_mode to support any band for offloaded ACS case
[mech_eap.git] / src / drivers / driver_nl80211_capa.c
index 99c0027..e23c57e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211 - Capabilities
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
  *
@@ -42,15 +42,16 @@ static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
 
        msg = nlmsg_alloc();
        if (!msg)
-               goto nla_put_failure;
+               return 0;
+
+       if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+               nlmsg_free(msg);
+               return 0;
+       }
 
-       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;
 }
 
@@ -70,11 +71,14 @@ struct wiphy_info_data {
        unsigned int connect_supported:1;
        unsigned int p2p_go_supported:1;
        unsigned int p2p_client_supported:1;
+       unsigned int p2p_go_ctwindow_supported:1;
        unsigned int p2p_concurrent:1;
        unsigned int channel_switch_supported:1;
        unsigned int set_qos_map_supported:1;
        unsigned int have_low_prio_scan:1;
        unsigned int wmm_ac_supported:1;
+       unsigned int mac_addr_rand_scan_supported:1;
+       unsigned int mac_addr_rand_sched_scan_supported:1;
 };
 
 
@@ -331,6 +335,33 @@ static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
 }
 
 
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+                            enum nl80211_ext_feature_index ftidx)
+{
+       u8 ft_byte;
+
+       if ((int) ftidx / 8 >= ext_features_len)
+               return 0;
+
+       ft_byte = ext_features[ftidx / 8];
+       return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+                                        struct nlattr *tb)
+{
+       struct wpa_driver_capa *capa = info->capa;
+
+       if (tb == NULL)
+               return;
+
+       if (ext_feature_isset(nla_data(tb), nla_len(tb),
+                             NL80211_EXT_FEATURE_VHT_IBSS))
+               capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+}
+
+
 static void wiphy_info_feature_flags(struct wiphy_info_data *info,
                                     struct nlattr *tb)
 {
@@ -357,9 +388,23 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
        if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
                capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
 
+       if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+               capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+       }
+
+       if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+               info->p2p_go_ctwindow_supported = 1;
+
        if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
                info->have_low_prio_scan = 1;
 
+       if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+               info->mac_addr_rand_scan_supported = 1;
+
+       if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+               info->mac_addr_rand_sched_scan_supported = 1;
+
        if (flags & NL80211_FEATURE_STATIC_SMPS)
                capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC;
 
@@ -380,6 +425,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
 
        if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
                capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+       if (flags & NL80211_FEATURE_HT_IBSS)
+               capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
 }
 
 
@@ -488,6 +536,7 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                info->device_ap_sme = 1;
 
        wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+       wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
        wiphy_info_probe_resp_offload(capa,
                                      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
 
@@ -503,11 +552,11 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                nla_len(tb[NL80211_ATTR_EXT_CAPA]);
                }
                drv->extended_capa_mask =
-                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
                if (drv->extended_capa_mask) {
                        os_memcpy(drv->extended_capa_mask,
-                                 nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-                                 nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+                                 nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+                                 nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
                } else {
                        os_free(drv->extended_capa);
                        drv->extended_capa = NULL;
@@ -526,19 +575,25 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                continue;
                        }
                        vinfo = nla_data(nl);
-                       switch (vinfo->subcmd) {
-                       case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
-                               drv->roaming_vendor_cmd_avail = 1;
-                               break;
-                       case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
-                               drv->dfs_vendor_cmd_avail = 1;
-                               break;
-                       case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
-                               drv->key_mgmt_set_key_vendor_cmd_avail = 1;
-                               break;
-                       case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
-                               drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
-                               break;
+                       if (vinfo->vendor_id == OUI_QCA) {
+                               switch (vinfo->subcmd) {
+                               case QCA_NL80211_VENDOR_SUBCMD_TEST:
+                                       drv->vendor_cmd_test_avail = 1;
+                                       break;
+                               case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+                                       drv->roaming_vendor_cmd_avail = 1;
+                                       break;
+                               case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+                                       drv->dfs_vendor_cmd_avail = 1;
+                                       break;
+                               case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+                                       drv->get_features_vendor_cmd_avail = 1;
+                                       break;
+                               case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+                                       drv->capa.flags |=
+                                               WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+                                       break;
+                               }
                        }
 
                        wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
@@ -557,9 +612,6 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                continue;
                        }
                        vinfo = nla_data(nl);
-                       if (vinfo->subcmd ==
-                           QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH)
-                               drv->roam_auth_vendor_event_avail = 1;
                        wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
                                   vinfo->vendor_id, vinfo->subcmd);
                }
@@ -581,24 +633,20 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
 {
        u32 feat;
        struct nl_msg *msg;
+       int flags = 0;
 
        os_memset(info, 0, sizeof(*info));
        info->capa = &drv->capa;
        info->drv = drv;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
        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);
-       if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
-               goto nla_put_failure;
+               flags = NLM_F_DUMP;
+       msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+       if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
        if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
                return -1;
@@ -636,10 +684,12 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
                drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
        drv->capa.wmm_ac_supported = info->wmm_ac_supported;
 
+       drv->capa.mac_addr_rand_sched_scan_supported =
+               info->mac_addr_rand_sched_scan_supported;
+       drv->capa.mac_addr_rand_scan_supported =
+               info->mac_addr_rand_scan_supported;
+
        return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       return -1;
 }
 
 
@@ -681,25 +731,91 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
        if (!drv->dfs_vendor_cmd_avail)
                return;
 
-
-       msg = nlmsg_alloc();
-       if (!msg)
+       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_DFS_CAPABILITY)) {
+               nlmsg_free(msg);
                return;
-
-       nl80211_cmd(drv, msg, 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_DFS_CAPABILITY);
+       }
 
        ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
        if (!ret && dfs_capability)
                drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
-       msg = NULL;
+}
 
- nla_put_failure:
-       nlmsg_free(msg);
+
+struct features_info {
+       u8 *flags;
+       size_t flags_len;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct features_info *info = arg;
+       struct nlattr *nl_vend, *attr;
+
+       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) {
+               struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+               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_FEATURE_FLAGS];
+               if (attr) {
+                       info->flags = nla_data(attr);
+                       info->flags_len = nla_len(attr);
+               }
+       }
+
+       return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+                        struct features_info *info)
+{
+       size_t idx = feature / 8;
+
+       return (idx < info->flags_len) &&
+               (info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       struct features_info info;
+       int ret;
+
+       if (!drv->get_features_vendor_cmd_avail)
+               return;
+
+       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_GET_FEATURES)) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       os_memset(&info, 0, sizeof(info));
+       ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+       if (ret || !info.flags)
+               return;
+
+       if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+               drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+       if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+               drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
 }
 
 
@@ -716,7 +832,9 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
        drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+               WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
        drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
                WPA_DRIVER_AUTH_SHARED |
                WPA_DRIVER_AUTH_LEAP;
@@ -747,6 +865,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
        drv->device_ap_sme = info.device_ap_sme;
        drv->poll_command_supported = info.poll_command_supported;
        drv->data_tx_status = info.data_tx_status;
+       drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
        if (info.set_qos_map_supported)
                drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
        drv->have_low_prio_scan = info.have_low_prio_scan;
@@ -780,6 +899,7 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
                drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
        qca_nl80211_check_dfs_capa(drv);
+       qca_nl80211_get_features(drv);
 
        return 0;
 }
@@ -1416,6 +1536,7 @@ 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;
+       int nl_flags = 0;
        struct nl_msg *msg;
        struct phy_info_arg result = {
                .num_modes = num_modes,
@@ -1426,27 +1547,20 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
        *num_modes = 0;
        *flags = 0;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return NULL;
-
        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);
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+               nl_flags = NLM_F_DUMP;
+       if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+           nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+               nlmsg_free(msg);
+               return NULL;
+       }
 
        if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
                nl80211_set_regulatory_flags(drv, &result);
                return wpa_driver_nl80211_postprocess_modes(result.modes,
                                                            num_modes);
        }
-       msg = NULL;
- nla_put_failure:
-       nlmsg_free(msg);
+
        return NULL;
 }