P2P: Allow P2P listen being offloaded to the driver/firmware
[mech_eap.git] / src / drivers / driver_nl80211_capa.c
index 1b5752f..1134886 100644 (file)
@@ -352,13 +352,20 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
                                         struct nlattr *tb)
 {
        struct wpa_driver_capa *capa = info->capa;
+       u8 *ext_features;
+       int len;
 
        if (tb == NULL)
                return;
 
-       if (ext_feature_isset(nla_data(tb), nla_len(tb),
-                             NL80211_EXT_FEATURE_VHT_IBSS))
+       ext_features = nla_data(tb);
+       len = nla_len(tb);
+
+       if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
                capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+
+       if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
+               capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
 }
 
 
@@ -428,6 +435,9 @@ static void wiphy_info_feature_flags(struct wiphy_info_data *info,
 
        if (flags & NL80211_FEATURE_HT_IBSS)
                capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+       if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+               capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
 }
 
 
@@ -476,6 +486,74 @@ static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
 }
 
 
+static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
+                                     struct nlattr *tb)
+{
+       int rem = 0, i;
+       struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
+
+       if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+               return;
+
+       nla_for_each_nested(attr, tb, rem) {
+               unsigned int len;
+               struct drv_nl80211_ext_capa *capa;
+
+               nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
+                         nla_len(attr), NULL);
+
+               if (!tb1[NL80211_ATTR_IFTYPE] ||
+                   !tb1[NL80211_ATTR_EXT_CAPA] ||
+                   !tb1[NL80211_ATTR_EXT_CAPA_MASK])
+                       continue;
+
+               capa = &drv->iface_ext_capa[drv->num_iface_ext_capa];
+               capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Driver-advertised extended capabilities for interface type %s",
+                          nl80211_iftype_str(capa->iftype));
+
+               len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
+               capa->ext_capa = os_malloc(len);
+               if (!capa->ext_capa)
+                       goto err;
+
+               os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+                         len);
+               capa->ext_capa_len = len;
+               wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
+                           capa->ext_capa, capa->ext_capa_len);
+
+               len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
+               capa->ext_capa_mask = os_malloc(len);
+               if (!capa->ext_capa_mask)
+                       goto err;
+
+               os_memcpy(capa->ext_capa_mask,
+                         nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
+               wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
+                           capa->ext_capa_mask, capa->ext_capa_len);
+
+               drv->num_iface_ext_capa++;
+               if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+                       break;
+       }
+
+       return;
+
+err:
+       /* Cleanup allocated memory on error */
+       for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
+               os_free(drv->iface_ext_capa[i].ext_capa);
+               drv->iface_ext_capa[i].ext_capa = NULL;
+               os_free(drv->iface_ext_capa[i].ext_capa_mask);
+               drv->iface_ext_capa[i].ext_capa_mask = NULL;
+               drv->iface_ext_capa[i].ext_capa_len = 0;
+       }
+       drv->num_iface_ext_capa = 0;
+}
+
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -487,6 +565,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
 
+       if (tb[NL80211_ATTR_WIPHY])
+               drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
        if (tb[NL80211_ATTR_WIPHY_NAME])
                os_strlcpy(drv->phyname,
                           nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
@@ -499,6 +580,19 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                capa->max_sched_scan_ssids =
                        nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
 
+       if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+           tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+           tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+               capa->max_sched_scan_plans =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+               capa->max_sched_scan_plan_interval =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+               capa->max_sched_scan_plan_iterations =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+       }
+
        if (tb[NL80211_ATTR_MAX_MATCH_SETS])
                capa->max_match_sets =
                        nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
@@ -550,6 +644,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                                  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
                        drv->extended_capa_len =
                                nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+                       wpa_hexdump(MSG_DEBUG,
+                                   "nl80211: Driver-advertised extended capabilities (default)",
+                                   drv->extended_capa, drv->extended_capa_len);
                }
                drv->extended_capa_mask =
                        os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
@@ -557,6 +654,10 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                        os_memcpy(drv->extended_capa_mask,
                                  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
                                  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+                       wpa_hexdump(MSG_DEBUG,
+                                   "nl80211: Driver-advertised extended capabilities mask (default)",
+                                   drv->extended_capa_mask,
+                                   drv->extended_capa_len);
                } else {
                        os_free(drv->extended_capa);
                        drv->extended_capa = NULL;
@@ -564,6 +665,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+
        if (tb[NL80211_ATTR_VENDOR_DATA]) {
                struct nlattr *nl;
                int rem;
@@ -711,6 +814,12 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
                        drv->capa.max_csa_counters = 1;
        }
 
+       if (!drv->capa.max_sched_scan_plans) {
+               drv->capa.max_sched_scan_plans = 1;
+               drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+               drv->capa.max_sched_scan_plan_iterations = 0;
+       }
+
        return 0;
 }
 
@@ -795,8 +904,12 @@ static int features_info_handler(struct nl_msg *msg, void *arg)
 
                attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
                if (attr) {
-                       info->flags = nla_data(attr);
-                       info->flags_len = nla_len(attr);
+                       int len = nla_len(attr);
+                       info->flags = os_malloc(len);
+                       if (info->flags != NULL) {
+                               os_memcpy(info->flags, nla_data(attr), len);
+                               info->flags_len = len;
+                       }
                }
                attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
                if (attr)
@@ -859,6 +972,9 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
        if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
                          &info))
                drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+       if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
+       os_free(info.flags);
 }
 
 #endif /* CONFIG_DRIVER_NL80211_QCA */