[PATCH 50/71] nl80211: Parse NAN PHY capabilities as reported by the kernel
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Wed Apr 1 15:01:59 PDT 2026
From: Ilan Peer <ilan.peer at intel.com>
Parse the NAN PHY capabilities (HT/VHT/HE capabilities) reported
by the kernel and store them as part of the driver reported
NAN capabilities.
While at it, move the NAN capabilities parsing to a separate
function.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/drivers/driver_nl80211_capa.c | 224 +++++++++++++++++++++++-------
1 file changed, 173 insertions(+), 51 deletions(-)
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index bd673f60f0..ba694ef16a 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -87,6 +87,7 @@ struct wiphy_info_data {
unsigned int nan_supported:1;
unsigned int nan_ndp_supported:1;
+ unsigned int nan_phy_capabilities_valid:1;
};
@@ -976,6 +977,166 @@ static void wiphy_info_mbssid(struct wpa_driver_capa *cap, struct nlattr *attr)
}
+#ifdef CONFIG_NAN
+
+static void wiphy_info_nan_capa_handler(struct wpa_driver_capa *capa,
+ struct wiphy_info_data *info,
+ struct nlattr *attr)
+{
+ static struct nla_policy
+ nan_capa_policy[NL80211_NAN_CAPABILITIES_MAX + 1] = {
+ [NL80211_NAN_CAPA_CONFIGURABLE_SYNC] = { .type = NLA_FLAG },
+ [NL80211_NAN_CAPA_USERSPACE_DE] = { .type = NLA_FLAG },
+ };
+ struct nlattr *tb_nan_capa[NL80211_NAN_CAPABILITIES_MAX + 1];
+ static struct nla_policy
+ nan_capa_phy_policy[NL80211_NAN_PHY_CAP_ATTR_MAX + 1] = {
+ [NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] = { .type = NLA_U16 },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] = {
+ .type = NLA_U8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY] = {
+ .type = NLA_U8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 16, .maxlen = 16,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA] = { .type = NLA_U32 },
+ [NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 8, .maxlen = 8,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_MAC] = {
+ .type = NLA_BINARY, .minlen = 6, .maxlen = 6
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_PHY] = {
+ .type = NLA_BINARY, .minlen = 11, .maxlen = 11
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] = {
+ .type = NLA_BINARY, .minlen = 12, .maxlen = 12,
+ },
+ [NL80211_NAN_PHY_CAP_ATTR_HE_PPE] = {
+ .type = NLA_BINARY, .maxlen = 255,
+ },
+ };
+ struct nlattr *tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_MAX + 1];
+
+ if (!attr)
+ return;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Parsing NAN capabilities");
+
+ if (nla_parse_nested(tb_nan_capa,
+ NL80211_NAN_CAPABILITIES_MAX,
+ attr,
+ nan_capa_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse NAN capabilities");
+ return;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_CONFIGURABLE_SYNC]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN sync offload supported");
+ capa->nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_USERSPACE_DE]) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN user space DE is supported");
+ capa->nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE;
+ }
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME])
+ capa->nan_capa.max_channel_switch_time =
+ nla_get_u16(tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS])
+ capa->nan_capa.num_antennas =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_OP_MODE])
+ capa->nan_capa.op_modes =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_OP_MODE]);
+
+ if (tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES])
+ capa->nan_capa.dev_capa =
+ nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES]);
+
+ if (!tb_nan_capa[NL80211_NAN_CAPA_PHY])
+ return;
+
+ if (nla_parse_nested(tb_nan_phy_capa,
+ NL80211_NAN_PHY_CAP_ATTR_MAX,
+ tb_nan_capa[NL80211_NAN_CAPA_PHY],
+ nan_capa_phy_policy)) {
+ wpa_printf(MSG_DEBUG, "nl80211: Failed to parse NAN PHY capabilities");
+ return;
+ }
+
+ /* HT Capabilities */
+ if (!tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY])
+ return;
+
+ capa->nan_capa.ht_capab = nla_get_u16(
+ tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_CAPA]);
+ capa->nan_capa.ht_ampdu_params = nla_get_u8(
+ tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_FACTOR]) & 0x3;
+ capa->nan_capa.ht_ampdu_params |= (nla_get_u8(
+ tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_AMPDU_DENSITY]) & 0x7) << 2;
+ os_memcpy(capa->nan_capa.ht_mcs_set,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HT_MCS_SET]));
+
+ info->nan_phy_capabilities_valid = 1;
+
+ /* VHT Capabilities */
+ if (tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET] &&
+ tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]) {
+ capa->nan_capa.vht_capab = nla_get_u32(
+ tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_VHT_CAPA]);
+ os_memcpy(capa->nan_capa.vht_mcs_set,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_VHT_MCS_SET]));
+
+ capa->nan_capa.vht_valid = true;
+ }
+
+ /* HE Capabilities */
+ if (!tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MAC] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PHY] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET] ||
+ !tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PPE])
+ goto out;
+
+ os_memcpy(capa->nan_capa.he_capab.mac_cap,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MAC]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MAC]));
+ os_memcpy(capa->nan_capa.he_capab.phy_cap,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PHY]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PHY]));
+
+ os_memcpy(capa->nan_capa.he_capab.mcs,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_MCS_SET]));
+ os_memcpy(capa->nan_capa.he_capab.ppet,
+ nla_data(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]),
+ nla_len(tb_nan_phy_capa[NL80211_NAN_PHY_CAP_ATTR_HE_PPE]));
+
+ capa->nan_capa.he_valid = true;
+out:
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN PHY capabilities vht_valid=%u, he_valid=%u",
+ capa->nan_capa.vht_valid, capa->nan_capa.he_valid);
+}
+
+#endif /* CONFIG_NAN */
+
+
static int wiphy_info_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1240,54 +1401,8 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
WPA_DRIVER_FLAGS_NAN_SUPPORT_DUAL_BAND;
}
- if (tb[NL80211_ATTR_NAN_CAPABILITIES]) {
- static struct nla_policy nan_capa_policy[
- NL80211_NAN_CAPABILITIES_MAX + 1] = {
- [NL80211_NAN_CAPA_CONFIGURABLE_SYNC] =
- { .type = NLA_FLAG },
- [NL80211_NAN_CAPA_USERSPACE_DE] = { .type = NLA_FLAG },
- };
- struct nlattr *tb_nan_capa[NL80211_NAN_CAPABILITIES_MAX + 1];
-
- if (nla_parse_nested(tb_nan_capa,
- NL80211_NAN_CAPABILITIES_MAX,
- tb[NL80211_ATTR_NAN_CAPABILITIES],
- nan_capa_policy)) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Failed to parse NAN capabilities");
- return NL_SKIP;
- }
-
- if (tb_nan_capa[NL80211_NAN_CAPA_CONFIGURABLE_SYNC]) {
- wpa_printf(MSG_DEBUG,
- "nl80211: NAN sync offload supported");
- capa->nan_capa.drv_flags |=
- WPA_DRIVER_FLAGS_NAN_SUPPORT_SYNC_CONFIG;
- }
-
- if (tb_nan_capa[NL80211_NAN_CAPA_USERSPACE_DE]) {
- wpa_printf(MSG_DEBUG,
- "nl80211: NAN user space DE is supported");
- capa->nan_capa.drv_flags |=
- WPA_DRIVER_FLAGS_NAN_SUPPORT_USERSPACE_DE;
- }
-
- if (tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME])
- capa->nan_capa.max_channel_switch_time =
- nla_get_u16(tb_nan_capa[NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME]);
-
- if (tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS])
- capa->nan_capa.num_antennas =
- nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_NUM_ANTENNAS]);
-
- if (tb_nan_capa[NL80211_NAN_CAPA_OP_MODE])
- capa->nan_capa.op_modes =
- nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_OP_MODE]);
-
- if (tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES])
- capa->nan_capa.dev_capa =
- nla_get_u8(tb_nan_capa[NL80211_NAN_CAPA_CAPABILITIES]);
- }
+ wiphy_info_nan_capa_handler(capa, info,
+ tb[NL80211_ATTR_NAN_CAPABILITIES]);
#endif /* CONFIG_NAN */
return NL_SKIP;
@@ -1377,10 +1492,17 @@ static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
#ifdef CONFIG_NAN
if (info->nan_supported) {
- wpa_printf(MSG_DEBUG, "nl80211: NAN supported");
+ if (info->nan_ndp_supported) {
+ if (info->nan_phy_capabilities_valid) {
+ wpa_printf(MSG_DEBUG, "nl80211: NAN supported");
+ drv->capa.nan_capa.drv_flags |=
+ WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP;
- if (info->nan_ndp_supported)
- drv->capa.nan_capa.drv_flags |= WPA_DRIVER_FLAGS_NAN_SUPPORT_NDP;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: NAN NDP supported but NAN PHY capabilities not valid");
+ }
+ }
/* TODO: Currently support only a single radio */
drv->capa.nan_capa.num_radios = 1;
--
2.53.0
More information about the Hostap
mailing list