cfg80211: support FTM responder configuration/statistics
Allow userspace to enable fine timing measurement responder functionality with configurable lci/civic parameters in AP mode. This can be done at AP start or changing beacon parameters. A new EXT_FEATURE flag is introduced for drivers to advertise the capability. Also nl80211 API support for retrieving statistics is added. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Pradeep Kumar Chitrapu <pradeepc@codeaurora.org> [remove unused cfg80211_ftm_responder_params, clarify docs, move validation into policy] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:

committed by
Johannes Berg

parent
7057f2496c
commit
81e54d08d9
@@ -201,6 +201,15 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
|
||||
}
|
||||
|
||||
/* policy for the attributes */
|
||||
static const struct nla_policy
|
||||
nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = {
|
||||
[NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, },
|
||||
[NL80211_FTM_RESP_ATTR_LCI] = { .type = NLA_BINARY,
|
||||
.len = U8_MAX },
|
||||
[NL80211_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_BINARY,
|
||||
.len = U8_MAX },
|
||||
};
|
||||
|
||||
static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
|
||||
@@ -430,6 +439,11 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
|
||||
[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
|
||||
.len = NL80211_HE_MAX_CAPABILITY_LEN },
|
||||
|
||||
[NL80211_ATTR_FTM_RESPONDER] = {
|
||||
.type = NLA_NESTED,
|
||||
.validation_data = nl80211_ftm_responder_policy,
|
||||
},
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -3989,10 +4003,12 @@ static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nl80211_parse_beacon(struct nlattr *attrs[],
|
||||
static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev,
|
||||
struct nlattr *attrs[],
|
||||
struct cfg80211_beacon_data *bcn)
|
||||
{
|
||||
bool haveinfo = false;
|
||||
int err;
|
||||
|
||||
if (!is_valid_ie_attr(attrs[NL80211_ATTR_BEACON_TAIL]) ||
|
||||
!is_valid_ie_attr(attrs[NL80211_ATTR_IE]) ||
|
||||
@@ -4043,6 +4059,35 @@ static int nl80211_parse_beacon(struct nlattr *attrs[],
|
||||
bcn->probe_resp_len = nla_len(attrs[NL80211_ATTR_PROBE_RESP]);
|
||||
}
|
||||
|
||||
if (attrs[NL80211_ATTR_FTM_RESPONDER]) {
|
||||
struct nlattr *tb[NL80211_FTM_RESP_ATTR_MAX + 1];
|
||||
|
||||
err = nla_parse_nested(tb, NL80211_FTM_RESP_ATTR_MAX,
|
||||
attrs[NL80211_ATTR_FTM_RESPONDER],
|
||||
NULL, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tb[NL80211_FTM_RESP_ATTR_ENABLED] &&
|
||||
wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
|
||||
bcn->ftm_responder = 1;
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (tb[NL80211_FTM_RESP_ATTR_LCI]) {
|
||||
bcn->lci = nla_data(tb[NL80211_FTM_RESP_ATTR_LCI]);
|
||||
bcn->lci_len = nla_len(tb[NL80211_FTM_RESP_ATTR_LCI]);
|
||||
}
|
||||
|
||||
if (tb[NL80211_FTM_RESP_ATTR_CIVICLOC]) {
|
||||
bcn->civicloc = nla_data(tb[NL80211_FTM_RESP_ATTR_CIVICLOC]);
|
||||
bcn->civicloc_len = nla_len(tb[NL80211_FTM_RESP_ATTR_CIVICLOC]);
|
||||
}
|
||||
} else {
|
||||
bcn->ftm_responder = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4189,7 +4234,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
|
||||
!info->attrs[NL80211_ATTR_BEACON_HEAD])
|
||||
return -EINVAL;
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon);
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -4373,7 +4418,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!wdev->beacon_interval)
|
||||
return -EINVAL;
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms);
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -7935,7 +7980,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!need_new_beacon)
|
||||
goto skip_beacons;
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
||||
err = nl80211_parse_beacon(rdev, info->attrs, ¶ms.beacon_after);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -7945,7 +7990,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
|
||||
err = nl80211_parse_beacon(rdev, csa_attrs, ¶ms.beacon_csa);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -12984,6 +13029,76 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
||||
struct net_device *dev = info->user_ptr[1];
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct cfg80211_ftm_responder_stats ftm_stats = {};
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
struct nlattr *ftm_stats_attr;
|
||||
int err;
|
||||
|
||||
if (wdev->iftype != NL80211_IFTYPE_AP || !wdev->beacon_interval)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = rdev_get_ftm_responder_stats(rdev, dev, &ftm_stats);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!ftm_stats.filled)
|
||||
return -ENODATA;
|
||||
|
||||
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
|
||||
NL80211_CMD_GET_FTM_RESPONDER_STATS);
|
||||
if (!hdr)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
|
||||
goto nla_put_failure;
|
||||
|
||||
ftm_stats_attr = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER_STATS);
|
||||
if (!ftm_stats_attr)
|
||||
goto nla_put_failure;
|
||||
|
||||
#define SET_FTM(field, name, type) \
|
||||
do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
|
||||
nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name, \
|
||||
ftm_stats.field)) \
|
||||
goto nla_put_failure; } while (0)
|
||||
#define SET_FTM_U64(field, name) \
|
||||
do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \
|
||||
nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name, \
|
||||
ftm_stats.field, NL80211_FTM_STATS_PAD)) \
|
||||
goto nla_put_failure; } while (0)
|
||||
|
||||
SET_FTM(success_num, SUCCESS_NUM, u32);
|
||||
SET_FTM(partial_num, PARTIAL_NUM, u32);
|
||||
SET_FTM(failed_num, FAILED_NUM, u32);
|
||||
SET_FTM(asap_num, ASAP_NUM, u32);
|
||||
SET_FTM(non_asap_num, NON_ASAP_NUM, u32);
|
||||
SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC);
|
||||
SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32);
|
||||
SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32);
|
||||
SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32);
|
||||
#undef SET_FTM
|
||||
|
||||
nla_nest_end(msg, ftm_stats_attr);
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
return genlmsg_reply(msg, info);
|
||||
|
||||
nla_put_failure:
|
||||
nlmsg_free(msg);
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
#define NL80211_FLAG_NEED_WIPHY 0x01
|
||||
#define NL80211_FLAG_NEED_NETDEV 0x02
|
||||
#define NL80211_FLAG_NEED_RTNL 0x04
|
||||
@@ -13895,6 +14010,13 @@ static const struct genl_ops nl80211_ops[] = {
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS,
|
||||
.doit = nl80211_get_ftm_responder_stats,
|
||||
.policy = nl80211_policy,
|
||||
.internal_flags = NL80211_FLAG_NEED_NETDEV |
|
||||
NL80211_FLAG_NEED_RTNL,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family nl80211_fam __ro_after_init = {
|
||||
|
Reference in New Issue
Block a user