/*
* 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
*
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;
}
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;
};
}
+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)
{
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;
if (flags & NL80211_FEATURE_DYNAMIC_SMPS)
capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC;
+
+ if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+ info->wmm_ac_supported = 1;
+
+ if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+ if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+ if (flags & NL80211_FEATURE_QUIET)
+ capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+ 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;
}
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]);
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;
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;
+ 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;
+ case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
+ drv->setband_vendor_cmd_avail = 1;
+ break;
+ }
}
wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
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);
}
{
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;
if (info->channel_switch_supported)
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;
}
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;
+}
+
+
+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_put_failure:
- nlmsg_free(msg);
+ 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;
}
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;
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;
drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
qca_nl80211_check_dfs_capa(drv);
+ qca_nl80211_get_features(drv);
return 0;
}
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,
*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;
}