Add phy capability ctrl/dump vendor command]

[Description]
Add vendor command phy capability ctrl/dump.

[Release-log]
N/A

Change-Id: I8bcb9e27b91d54344ef3dfa921de0bc34cedd68b
diff --git a/feed/mt76-vendor/src/CMakeLists.txt b/feed/mt76-vendor/src/CMakeLists.txt
index f5e1d51..dd6ef76 100644
--- a/feed/mt76-vendor/src/CMakeLists.txt
+++ b/feed/mt76-vendor/src/CMakeLists.txt
@@ -3,7 +3,7 @@
 PROJECT(mt76-vendor C)
 ADD_DEFINITIONS(-Os -Wall --std=gnu99 -g3)
 
-ADD_EXECUTABLE(mt76-vendor main.c csi.c amnt.c capi.c hemu.c)
+ADD_EXECUTABLE(mt76-vendor main.c csi.c amnt.c capi.c hemu.c phy_capa.c)
 TARGET_LINK_LIBRARIES(mt76-vendor nl-tiny)
 
 SET(CMAKE_INSTALL_PREFIX /usr)
diff --git a/feed/mt76-vendor/src/main.c b/feed/mt76-vendor/src/main.c
index 64e8b57..f308d9c 100644
--- a/feed/mt76-vendor/src/main.c
+++ b/feed/mt76-vendor/src/main.c
@@ -33,6 +33,8 @@
 		"set ap_wireless cert=<enable>",
 
 		"set hemu onoff=<val> (bitmap- UL MU-MIMO(bit3), DL MU-MIMO(bit2), UL OFDMA(bit1), DL OFDMA(bit0))",
+
+		"dump phy_capa",
 	};
 	int i;
 
@@ -68,6 +70,8 @@
 			ret = mt76_csi_dump(if_idx, argc, argv);
 		else if (!strncmp(subcmd, "amnt", 4))
 			ret = mt76_amnt_dump(if_idx, argc, argv);
+		else if (!strncmp(subcmd, "phy_capa", 4))
+			ret = mt76_phy_capa_dump(if_idx, argc, argv);
 	} else if (!strncmp(cmd, "set", 3)) {
 		if (!strncmp(subcmd, "csi", 3))
 			ret = mt76_csi_set(if_idx, argc, argv);
diff --git a/feed/mt76-vendor/src/mt76-vendor.h b/feed/mt76-vendor/src/mt76-vendor.h
index c72a0bb..2d39cb4 100644
--- a/feed/mt76-vendor/src/mt76-vendor.h
+++ b/feed/mt76-vendor/src/mt76-vendor.h
@@ -39,6 +39,7 @@
 	MTK_NL80211_VENDOR_SUBCMD_RFEATURE_CTRL = 0xc3,
 	MTK_NL80211_VENDOR_SUBCMD_WIRELESS_CTRL = 0xc4,
 	MTK_NL80211_VENDOR_SUBCMD_HEMU_CTRL = 0xc5,
+	MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL = 0xc6,
 };
 
 enum mtk_vendor_attr_csi_ctrl {
@@ -173,6 +174,30 @@
 		NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL - 1
 };
 
+enum mtk_vendor_attr_phy_capa_ctrl {
+	MTK_VENDOR_ATTR_PHY_CAPA_CTRL_UNSPEC,
+
+	MTK_VENDOR_ATTR_PHY_CAPA_CTRL_SET,
+	MTK_VENDOR_ATTR_PHY_CAPA_CTRL_DUMP,
+
+	/* keep last */
+	NUM_MTK_VENDOR_ATTRS_PHY_CAPA_CTRL,
+	MTK_VENDOR_ATTR_PHY_CAPA_CTRL_MAX =
+		NUM_MTK_VENDOR_ATTRS_PHY_CAPA_CTRL - 1
+};
+
+enum mtk_vendor_attr_phy_capa_dump {
+	MTK_VENDOR_ATTR_PHY_CAPA_DUMP_UNSPEC,
+
+	MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_BSS,
+	MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_STA,
+
+	/* keep last */
+	NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP,
+	MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX =
+		NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP - 1
+};
+
 #define CSI_MAX_COUNT 256
 #define ETH_ALEN 6
 
@@ -211,4 +236,6 @@
 int mt76_ap_wireless_set(int idx, int argc, char **argv);
 
 int mt76_hemu_onoff_set(int idx, int argc, char **argv);
+
+int mt76_phy_capa_dump(int idx, int argc, char **argv);
 #endif
diff --git a/feed/mt76-vendor/src/phy_capa.c b/feed/mt76-vendor/src/phy_capa.c
new file mode 100644
index 0000000..32c9504
--- /dev/null
+++ b/feed/mt76-vendor/src/phy_capa.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2021 Mediatek Inc. */
+#define _GNU_SOURCE
+
+#include "mt76-vendor.h"
+
+static struct nla_policy
+phy_capa_ctrl_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+	[MTK_VENDOR_ATTR_PHY_CAPA_CTRL_SET] = {.type = NLA_NESTED },
+	[MTK_VENDOR_ATTR_PHY_CAPA_CTRL_DUMP] = {.type = NLA_NESTED },
+};
+
+static struct nla_policy
+phy_capa_dump_policy[NUM_MTK_VENDOR_ATTRS_RFEATURE_CTRL] = {
+	[MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_BSS] = {.type = NLA_U16 },
+	[MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_STA] = {.type = NLA_U16 },
+};
+
+static int mt76_phy_capa_dump_cb(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP];
+	struct nlattr *tb_dump[NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP];
+	struct nlattr *attr;
+	int max_bss, max_sta;
+
+	attr = unl_find_attr(&unl, msg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr) {
+		fprintf(stderr, "Testdata attribute not found\n");
+		return NL_SKIP;
+	}
+
+	nla_parse_nested(tb, MTK_VENDOR_ATTR_PHY_CAPA_CTRL_MAX,
+			 attr, phy_capa_ctrl_policy);
+
+	if (!tb[MTK_VENDOR_ATTR_PHY_CAPA_CTRL_DUMP])
+		return NL_SKIP;
+
+	nla_parse_nested(tb_dump, NUM_MTK_VENDOR_ATTRS_PHY_CAPA_DUMP,
+			 tb[MTK_VENDOR_ATTR_PHY_CAPA_CTRL_DUMP], phy_capa_dump_policy);
+
+	max_bss = nla_get_u16(tb_dump[MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_BSS]);
+	max_sta = nla_get_u16(tb_dump[MTK_VENDOR_ATTR_PHY_CAPA_DUMP_MAX_SUPPORTED_STA]);
+
+	printf("[vendor] Max Supported BSS=%d "
+		" Max Supported STA=%d\n", __func__, max_bss, max_sta);
+
+	return 0;
+}
+
+int mt76_phy_capa_dump(int idx, int argc, char **argv)
+{
+	struct nl_msg *msg;
+	void *data;
+	int ret = -EINVAL;
+
+	if (unl_genl_init(&unl, "nl80211") < 0) {
+		fprintf(stderr, "Failed to connect to nl80211\n");
+		return 2;
+	}
+
+	msg = unl_genl_msg(&unl, NL80211_CMD_VENDOR, true);
+
+	if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, idx) ||
+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, MTK_NL80211_VENDOR_ID) ||
+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, MTK_NL80211_VENDOR_SUBCMD_PHY_CAPA_CTRL))
+		return false;
+
+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA | NLA_F_NESTED);
+	if (!data)
+		goto out;
+
+	nla_nest_end(msg, data);
+
+	ret = unl_genl_request(&unl, msg, mt76_phy_capa_dump_cb, NULL);
+	if (ret)
+		fprintf(stderr, "nl80211 call failed: %s\n", strerror(-ret));
+
+out:
+	unl_free(&unl);
+
+	return ret;
+}