nl80211: Add QCA vendor specific query of device/driver features

This commit introduces a QCA vendor command that allows interrogation of
the vendor-specific features supported by the device/driver. Currently
the only defined feature is the ability to offload key management.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
Chet Lanctot 2014-12-05 16:48:23 -08:00 committed by Jouni Malinen
parent d4913c585e
commit 15badebd47
6 changed files with 108 additions and 12 deletions

View file

@ -53,15 +53,22 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
* used to configure PMK to the driver even when not connected. This can
* be used to request offloading of key management operations.
* be used to request offloading of key management operations. Only used
* if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
*
* @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
* NL80211_CMD_ROAM event with optional attributes including information
* from offloaded key management operation. Uses
* enum qca_wlan_vendor_attr_roam_auth attributes.
* enum qca_wlan_vendor_attr_roam_auth attributes. Only used
* if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
*
* @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
* invoke the ACS function in device and pass selected channels to
* hostapd.
*
* @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
* supported by the driver. enum qca_wlan_vendor_features defines
* the possible features.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@ -107,6 +114,7 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
/* 53 - reserved for QCA */
QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
};
@ -124,6 +132,8 @@ enum qca_wlan_vendor_attr {
* by enum qca_roaming_policy. */
QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@ -170,4 +180,18 @@ enum qca_wlan_vendor_acs_hw_mode {
QCA_ACS_MODE_IEEE80211AD,
};
/**
* enum qca_wlan_vendor_features - Vendor device/driver feature flags
*
* @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
* management offload, a mechanism where the station's firmware
* does the exchange with the AP to establish the temporal keys
* after roaming, rather than having the user space wpa_supplicant do it.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
#endif /* QCA_VENDOR_H */

View file

@ -1075,6 +1075,8 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_MESH 0x0000000100000000ULL
/* Driver support ACS offload */
#define WPA_DRIVER_FLAGS_ACS_OFFLOAD 0x0000000200000000ULL
/* Driver supports key management offload */
#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD 0x0000000400000000ULL
u64 flags;
#define WPA_DRIVER_SMPS_MODE_STATIC 0x00000001

View file

@ -2347,7 +2347,7 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
struct nl_msg *msg;
int ret;
if (!drv->key_mgmt_set_key_vendor_cmd_avail)
if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
return 0;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
@ -2397,7 +2397,8 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
}
#endif /* CONFIG_TDLS */
if (alg == WPA_ALG_PMK && drv->key_mgmt_set_key_vendor_cmd_avail) {
if (alg == WPA_ALG_PMK &&
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
__func__);
ret = issue_key_mgmt_set_key(drv, key, key_len);

View file

@ -143,8 +143,7 @@ struct wpa_driver_nl80211_data {
unsigned int have_low_prio_scan:1;
unsigned int force_connect_cmd:1;
unsigned int addr_changed:1;
unsigned int key_mgmt_set_key_vendor_cmd_avail:1;
unsigned int roam_auth_vendor_event_avail:1;
unsigned int get_features_vendor_cmd_avail:1;
u64 remain_on_chan_cookie;
u64 send_action_cookie;

View file

@ -534,8 +534,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
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;
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;
@ -558,9 +558,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);
}
@ -689,6 +686,77 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
}
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 index = feature / 8;
return (index < info->flags_len) &&
(info->flags[index] & 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;
}
int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
{
struct wiphy_info_data info;
@ -766,6 +834,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;
}

View file

@ -1690,7 +1690,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
cmd, nl80211_command_to_string(cmd), bss->ifname);
if (cmd == NL80211_CMD_ROAM && drv->roam_auth_vendor_event_avail) {
if (cmd == NL80211_CMD_ROAM &&
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
/*
* Device will use roam+auth vendor event to indicate
* roaming, so ignore the regular roam event.