Commit 92d13386 authored by Jaewan Kim's avatar Jaewan Kim Committed by Johannes Berg

mac80211_hwsim: add PMSR capability support

PMSR (a.k.a. peer measurement) is generalized measurement between two
Wi-Fi devices. And currently FTM (a.k.a. fine time measurement or flight
time measurement) is the one and only measurement. FTM is measured by
RTT (a.k.a. round trip time) of packets between two Wi-Fi devices.

Add necessary functionality to allow mac80211_hwsim to be configured with
PMSR capability. The capability is mandatory to accept incoming PMSR
request because nl80211_pmsr_start() ignores incoming the request without
the PMSR capability.

In detail, add new mac80211_hwsim attribute HWSIM_ATTR_PMSR_SUPPORT.
HWSIM_ATTR_PMSR_SUPPORT is used to set PMSR capability when creating a new
radio. To send extra capability details, HWSIM_ATTR_PMSR_SUPPORT can have
nested PMSR capability attributes defined in the nl80211.h. Data format is
the same as cfg80211_pmsr_capabilities.

If HWSIM_ATTR_PMSR_SUPPORT is specified, mac80211_hwsim builds
cfg80211_pmsr_capabilities and sets wiphy.pmsr_capa.
Signed-off-by: default avatarJaewan Kim <jaewan@google.com>
Link: https://lore.kernel.org/r/20230322131637.2633968-2-jaewan@google.comSigned-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 68b9bea2
...@@ -719,6 +719,9 @@ struct mac80211_hwsim_data { ...@@ -719,6 +719,9 @@ struct mac80211_hwsim_data {
/* RSSI in rx status of the receiver */ /* RSSI in rx status of the receiver */
int rx_rssi; int rx_rssi;
/* only used when pmsr capability is supplied */
struct cfg80211_pmsr_capabilities pmsr_capa;
struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS]; struct mac80211_hwsim_link_data link_data[IEEE80211_MLD_MAX_NUM_LINKS];
}; };
...@@ -760,6 +763,34 @@ static const struct genl_multicast_group hwsim_mcgrps[] = { ...@@ -760,6 +763,34 @@ static const struct genl_multicast_group hwsim_mcgrps[] = {
/* MAC80211_HWSIM netlink policy */ /* MAC80211_HWSIM netlink policy */
static const struct nla_policy
hwsim_ftm_capa_policy[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1] = {
[NL80211_PMSR_FTM_CAPA_ATTR_ASAP] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES] = { .type = NLA_U32 },
[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS] = { .type = NLA_U32 },
[NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT] = NLA_POLICY_MAX(NLA_U8, 15),
[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST] = NLA_POLICY_MAX(NLA_U8, 31),
[NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG },
[NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG },
};
static const struct nla_policy
hwsim_pmsr_capa_type_policy[NL80211_PMSR_TYPE_MAX + 1] = {
[NL80211_PMSR_TYPE_FTM] = NLA_POLICY_NESTED(hwsim_ftm_capa_policy),
};
static const struct nla_policy
hwsim_pmsr_capa_policy[NL80211_PMSR_ATTR_MAX + 1] = {
[NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_U32 },
[NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_FLAG },
[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_FLAG },
[NL80211_PMSR_ATTR_TYPE_CAPA] = NLA_POLICY_NESTED(hwsim_pmsr_capa_type_policy),
[NL80211_PMSR_ATTR_PEERS] = { .type = NLA_REJECT }, // only for request.
};
static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_ADDR_RECEIVER] = NLA_POLICY_ETH_ADDR_COMPAT, [HWSIM_ATTR_ADDR_RECEIVER] = NLA_POLICY_ETH_ADDR_COMPAT,
[HWSIM_ATTR_ADDR_TRANSMITTER] = NLA_POLICY_ETH_ADDR_COMPAT, [HWSIM_ATTR_ADDR_TRANSMITTER] = NLA_POLICY_ETH_ADDR_COMPAT,
...@@ -788,6 +819,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { ...@@ -788,6 +819,7 @@ static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
[HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 }, [HWSIM_ATTR_IFTYPE_SUPPORT] = { .type = NLA_U32 },
[HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY }, [HWSIM_ATTR_CIPHER_SUPPORT] = { .type = NLA_BINARY },
[HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, [HWSIM_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG },
[HWSIM_ATTR_PMSR_SUPPORT] = NLA_POLICY_NESTED(hwsim_pmsr_capa_policy),
}; };
#if IS_REACHABLE(CONFIG_VIRTIO) #if IS_REACHABLE(CONFIG_VIRTIO)
...@@ -3217,6 +3249,7 @@ struct hwsim_new_radio_params { ...@@ -3217,6 +3249,7 @@ struct hwsim_new_radio_params {
u32 *ciphers; u32 *ciphers;
u8 n_ciphers; u8 n_ciphers;
bool mlo; bool mlo;
const struct cfg80211_pmsr_capabilities *pmsr_capa;
}; };
static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb, static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
...@@ -4479,6 +4512,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ...@@ -4479,6 +4512,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS);
wiphy_ext_feature_set(hw->wiphy, wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); NL80211_EXT_FEATURE_BEACON_RATE_LEGACY);
wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER);
wiphy_ext_feature_set(hw->wiphy, wiphy_ext_feature_set(hw->wiphy,
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT);
...@@ -4643,6 +4677,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ...@@ -4643,6 +4677,11 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
data->debugfs, data->debugfs,
data, &hwsim_simulate_radar); data, &hwsim_simulate_radar);
if (param->pmsr_capa) {
data->pmsr_capa = *param->pmsr_capa;
hw->wiphy->pmsr_capa = &data->pmsr_capa;
}
spin_lock_bh(&hwsim_radio_lock); spin_lock_bh(&hwsim_radio_lock);
err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht, err = rhashtable_insert_fast(&hwsim_radios_rht, &data->rht,
hwsim_rht_params); hwsim_rht_params);
...@@ -4752,6 +4791,7 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb, ...@@ -4752,6 +4791,7 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb,
param.regd = data->regd; param.regd = data->regd;
param.channels = data->channels; param.channels = data->channels;
param.hwname = wiphy_name(data->hw->wiphy); param.hwname = wiphy_name(data->hw->wiphy);
param.pmsr_capa = &data->pmsr_capa;
res = append_radio_msg(skb, data->idx, &param); res = append_radio_msg(skb, data->idx, &param);
if (res < 0) if (res < 0)
...@@ -5090,6 +5130,79 @@ static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers) ...@@ -5090,6 +5130,79 @@ static bool hwsim_known_ciphers(const u32 *ciphers, int n_ciphers)
return true; return true;
} }
static int parse_ftm_capa(const struct nlattr *ftm_capa, struct cfg80211_pmsr_capabilities *out,
struct genl_info *info)
{
struct nlattr *tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX + 1];
int ret;
ret = nla_parse_nested(tb, NL80211_PMSR_FTM_CAPA_ATTR_MAX, ftm_capa, hwsim_ftm_capa_policy,
NULL);
if (ret) {
NL_SET_ERR_MSG_ATTR(info->extack, ftm_capa, "malformed FTM capability");
return -EINVAL;
}
out->ftm.supported = 1;
if (tb[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES])
out->ftm.preambles = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES]);
if (tb[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS])
out->ftm.bandwidths = nla_get_u32(tb[NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS]);
if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT])
out->ftm.max_bursts_exponent =
nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT]);
if (tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST])
out->ftm.max_ftms_per_burst =
nla_get_u8(tb[NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST]);
out->ftm.asap = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_ASAP];
out->ftm.non_asap = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP];
out->ftm.request_lci = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI];
out->ftm.request_civicloc = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC];
out->ftm.trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED];
out->ftm.non_trigger_based = !!tb[NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED];
return 0;
}
static int parse_pmsr_capa(const struct nlattr *pmsr_capa, struct cfg80211_pmsr_capabilities *out,
struct genl_info *info)
{
struct nlattr *tb[NL80211_PMSR_ATTR_MAX + 1];
struct nlattr *nla;
int size;
int ret;
ret = nla_parse_nested(tb, NL80211_PMSR_ATTR_MAX, pmsr_capa, hwsim_pmsr_capa_policy, NULL);
if (ret) {
NL_SET_ERR_MSG_ATTR(info->extack, pmsr_capa, "malformed PMSR capability");
return -EINVAL;
}
if (tb[NL80211_PMSR_ATTR_MAX_PEERS])
out->max_peers = nla_get_u32(tb[NL80211_PMSR_ATTR_MAX_PEERS]);
out->report_ap_tsf = !!tb[NL80211_PMSR_ATTR_REPORT_AP_TSF];
out->randomize_mac_addr = !!tb[NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR];
if (!tb[NL80211_PMSR_ATTR_TYPE_CAPA]) {
NL_SET_ERR_MSG_ATTR(info->extack, tb[NL80211_PMSR_ATTR_TYPE_CAPA],
"malformed PMSR type");
return -EINVAL;
}
nla_for_each_nested(nla, tb[NL80211_PMSR_ATTR_TYPE_CAPA], size) {
switch (nla_type(nla)) {
case NL80211_PMSR_TYPE_FTM:
parse_ftm_capa(nla, out, info);
break;
default:
NL_SET_ERR_MSG_ATTR(info->extack, nla, "unsupported measurement type");
return -EINVAL;
}
}
return 0;
}
static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
{ {
struct hwsim_new_radio_params param = { 0 }; struct hwsim_new_radio_params param = { 0 };
...@@ -5210,8 +5323,25 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info) ...@@ -5210,8 +5323,25 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
param.hwname = hwname; param.hwname = hwname;
} }
if (info->attrs[HWSIM_ATTR_PMSR_SUPPORT]) {
struct cfg80211_pmsr_capabilities *pmsr_capa;
pmsr_capa = kmalloc(sizeof(*pmsr_capa), GFP_KERNEL);
if (!pmsr_capa) {
ret = -ENOMEM;
goto out_free;
}
ret = parse_pmsr_capa(info->attrs[HWSIM_ATTR_PMSR_SUPPORT], pmsr_capa, info);
if (ret)
goto out_free;
param.pmsr_capa = pmsr_capa;
}
ret = mac80211_hwsim_new_radio(info, &param); ret = mac80211_hwsim_new_radio(info, &param);
out_free:
kfree(hwname); kfree(hwname);
kfree(param.pmsr_capa);
return ret; return ret;
} }
......
...@@ -142,6 +142,8 @@ enum { ...@@ -142,6 +142,8 @@ enum {
* @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types * @HWSIM_ATTR_CIPHER_SUPPORT: u32 array of supported cipher types
* @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for * @HWSIM_ATTR_MLO_SUPPORT: claim MLO support (exact parameters TBD) for
* the new radio * the new radio
* @HWSIM_ATTR_PMSR_SUPPORT: nested attribute used with %HWSIM_CMD_CREATE_RADIO
* to provide peer measurement capabilities. (nl80211_peer_measurement_attrs)
* @__HWSIM_ATTR_MAX: enum limit * @__HWSIM_ATTR_MAX: enum limit
*/ */
...@@ -173,6 +175,7 @@ enum { ...@@ -173,6 +175,7 @@ enum {
HWSIM_ATTR_IFTYPE_SUPPORT, HWSIM_ATTR_IFTYPE_SUPPORT,
HWSIM_ATTR_CIPHER_SUPPORT, HWSIM_ATTR_CIPHER_SUPPORT,
HWSIM_ATTR_MLO_SUPPORT, HWSIM_ATTR_MLO_SUPPORT,
HWSIM_ATTR_PMSR_SUPPORT,
__HWSIM_ATTR_MAX, __HWSIM_ATTR_MAX,
}; };
#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment