Netdev List
 help / color / mirror / Atom feed
From: Shubham Das <shubham.das@intel.com>
To: netdev@vger.kernel.org, andrew@lunn.ch,
	maxime.chevallier@bootlin.com, alexander.duyck@gmail.com,
	lee@trager.us
Cc: siddaraju.dh@intel.com, balaji.chintalapalle@intel.com,
	dasshubhamk@gmail.com, magnus.k.lindberg@ericsson.com,
	jonas.wirandi@ericsson.com, Shubham Das <shubham.das@intel.com>
Subject: [RFC ethtool 1/2] ethtool: Add interface capability get support
Date: Wed, 30 Sep 2026 08:21:50 -0500	[thread overview]
Message-ID: <20260930132151.932792-5-shubham.das@intel.com> (raw)
In-Reply-To: <20260930132151.932792-1-shubham.das@intel.com>

Add the --interface-capabilities command to display the interface
block topology and per-block support for loopback, PRBS, BERT, and
error injection.

Each block reports:
  - id: unique block identifier for per-block commands
  - component: MAC, PHY, or MODULE
  - sublayer: RS, PCS, FEC, PMA, PMD, or NONE
  - instance: distinguishes multiple instances of the same component
    (for example, internal vs external PHY, host vs port MAC)
  - name: driver-chosen label (for example, host-pma, ext-phy-pma)
  - depth: ordering within the same (component, sublayer) tuple
  - lanes: number of SerDes lanes at this block
  - loopback_supported: supported loopback direction bitmask
  - supported_tx_patterns: patterns the block can generate
  - supported_rx_patterns: patterns the block can check
  - error_inject_supported: block supports bit error injection
  - bert_supported: block supports BERT (Bit Error Ratio Test)

Example:
  ethtool --interface-capabilities eth1

Signed-off-by: Shubham Das <shubham.das@intel.com>
---
 Makefile.am                            |   1 +
 ethtool.c                              |   5 +
 netlink/extapi.h                       |   2 +
 netlink/intf_caps.c                    | 230 +++++++++++++++++++++++++
 uapi/linux/ethtool_netlink_generated.h |  73 ++++++++
 5 files changed, 311 insertions(+)
 create mode 100644 netlink/intf_caps.c

diff --git a/Makefile.am b/Makefile.am
index db22be4..67f1be4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ ethtool_SOURCES += \
 		  netlink/pse-pd.c \
 		  netlink/phy.c \
 		  netlink/tsconfig.c \
+		  netlink/intf_caps.c \
 		  netlink/mse.c \
 		  uapi/linux/const.h uapi/linux/ethtool.h \
 		  uapi/linux/ethtool_netlink.h \
diff --git a/ethtool.c b/ethtool.c
index 30a1d97..bb36754 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6514,6 +6514,11 @@ static const struct option args[] = {
 		.nlfunc	= nl_gmse,
 		.help	= "Show Mean Square Error (MSE) diagnostics",
 	},
+	{
+		.opts	= "--interface-capabilities",
+		.nlfunc	= nl_get_intf_caps,
+		.help	= "Show interface block capabilities",
+	},
 	{
 		.opts	= "-h|--help",
 		.no_dev	= true,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index e10af62..696f829 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -67,6 +67,7 @@ void nl_monitor_usage(void);
 int nl_get_eeprom_page(struct cmd_context *ctx,
 		       struct ethtool_module_eeprom *request);
 
+int nl_get_intf_caps(struct cmd_context *ctx);
 #else /* ETHTOOL_ENABLE_NETLINK */
 
 static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused,
@@ -142,6 +143,7 @@ nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
 #define nl_flash_module_fw	NULL
 #define nl_get_phy		NULL
 #define nl_gmse			NULL
+#define nl_get_intf_caps	NULL
 
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
diff --git a/netlink/intf_caps.c b/netlink/intf_caps.c
new file mode 100644
index 0000000..a78d605
--- /dev/null
+++ b/netlink/intf_caps.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * intf_caps.c - netlink implementation of interface capabilities
+ *
+ * Implementation of "ethtool --interface-capabilities <dev>"
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+
+static const char *component_to_str(uint32_t component)
+{
+	switch (component) {
+	case INTF_COMPONENT_MAC:	return "MAC";
+	case INTF_COMPONENT_PHY:	return "PHY";
+	case INTF_COMPONENT_MODULE:	return "MODULE";
+	default:			return "UNKNOWN";
+	}
+}
+
+static const char *sublayer_to_str(uint32_t sublayer)
+{
+	switch (sublayer) {
+	case INTF_SUBLAYER_NONE:	return "(none)";
+	case INTF_SUBLAYER_RS:		return "RS";
+	case INTF_SUBLAYER_PCS:		return "PCS";
+	case INTF_SUBLAYER_FEC:		return "FEC";
+	case INTF_SUBLAYER_PMA:		return "PMA";
+	case INTF_SUBLAYER_PMD:		return "PMD";
+	default:			return "UNKNOWN";
+	}
+}
+
+static const char * const pattern_names[] = {
+	[PHY_TEST_PATTERN_OFF]		= "off",
+	[PHY_TEST_PATTERN_PRBS7]	= "prbs7",
+	[PHY_TEST_PATTERN_PRBS9]	= "prbs9",
+	[PHY_TEST_PATTERN_PRBS11]	= "prbs11",
+	[PHY_TEST_PATTERN_PRBS13]	= "prbs13",
+	[PHY_TEST_PATTERN_PRBS15]	= "prbs15",
+	[PHY_TEST_PATTERN_PRBS23]	= "prbs23",
+	[PHY_TEST_PATTERN_PRBS31]	= "prbs31",
+	[PHY_TEST_PATTERN_SSPRQ]		= "ssprq",
+	[PHY_TEST_PATTERN_PRBS13Q]		= "prbs13q",
+	[PHY_TEST_PATTERN_PRBS31Q]		= "prbs31q",
+	[PHY_TEST_PATTERN_SQUARE_NRZ]		= "square-nrz",
+	[PHY_TEST_PATTERN_SQUARE_PAM4]		= "square-pam4",
+	[PHY_TEST_PATTERN_TX_LINEARITY]		= "tx-linearity",
+	[PHY_TEST_PATTERN_SCRAMBLED_IDLE]	= "scrambled-idle",
+	[PHY_TEST_PATTERN_K28_5]		= "k28.5",
+	[PHY_TEST_PATTERN_K28_7]		= "k28.7",
+};
+
+static void print_pattern_bitmask(const char *label, u32 bitmask)
+{
+	bool first = true;
+	unsigned int i;
+
+	printf("    %s:", label);
+	if (!bitmask) {
+		printf(" (none)\n");
+		return;
+	}
+
+	for (i = 1; i < ARRAY_SIZE(pattern_names); i++) {
+		if (bitmask & (1U << i)) {
+			const char *name = pattern_names[i];
+
+			printf("%s %s", first ? "" : ",",
+			       name ? name : "???");
+			first = false;
+		}
+	}
+	printf("\n");
+}
+
+static void print_loopback_supported(u32 loopback)
+{
+	printf("    Supported loopback:");
+	if (!loopback) {
+		printf(" (none)\n");
+		return;
+	}
+	if (loopback & LOOPBACK_SUPPORT_LOCAL)
+		printf(" local");
+	if (loopback & LOOPBACK_SUPPORT_REMOTE)
+		printf(" remote");
+	printf("\n");
+}
+
+static int parse_intf_block(const struct nlattr *block_attr)
+{
+	const struct nlattr *tb[ETHTOOL_A_INTF_BLOCK_MAX + 1] = {};
+	struct nlattr *attr;
+
+	mnl_attr_for_each_nested(attr, block_attr) {
+		u16 type = mnl_attr_get_type(attr);
+
+		if (type <= ETHTOOL_A_INTF_BLOCK_MAX)
+			tb[type] = attr;
+	}
+
+	/* Block ID */
+	u32 id = tb[ETHTOOL_A_INTF_BLOCK_ID]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_ID]) : 0;
+	u32 component = tb[ETHTOOL_A_INTF_BLOCK_COMPONENT]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_COMPONENT]) : 0;
+	u32 sublayer = tb[ETHTOOL_A_INTF_BLOCK_SUBLAYER]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_SUBLAYER]) : 0;
+	u32 instance = tb[ETHTOOL_A_INTF_BLOCK_INSTANCE]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_INSTANCE]) : 0;
+	const char *name = tb[ETHTOOL_A_INTF_BLOCK_NAME]
+		? mnl_attr_get_str(tb[ETHTOOL_A_INTF_BLOCK_NAME]) : "";
+	u8 depth = tb[ETHTOOL_A_INTF_BLOCK_DEPTH]
+		? mnl_attr_get_u8(tb[ETHTOOL_A_INTF_BLOCK_DEPTH]) : 0;
+	u32 lanes = tb[ETHTOOL_A_INTF_BLOCK_LANES]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_LANES]) : 0;
+	u32 loopback = 0;
+	u32 tx_pat = tb[ETHTOOL_A_INTF_BLOCK_TX_PATTERNS]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_TX_PATTERNS]) : 0;
+	u32 rx_pat = tb[ETHTOOL_A_INTF_BLOCK_RX_PATTERNS]
+		? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_RX_PATTERNS]) : 0;
+	bool bert = !!tb[ETHTOOL_A_INTF_BLOCK_BERT];
+	bool inject = !!tb[ETHTOOL_A_INTF_BLOCK_ERROR_INJECT];
+
+	if (tb[ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED]) {
+		const struct nlattr *attr =
+			tb[ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED];
+
+		loopback = mnl_attr_get_u32(attr);
+	}
+
+	printf("  Block %u: %s\n", id, name);
+	printf("    component: %s\n", component_to_str(component));
+	printf("    instance: %u\n", instance);
+	printf("    sublayer: %s\n", sublayer_to_str(sublayer));
+	printf("    depth: %u\n", depth);
+	printf("    lanes: %u\n", lanes);
+
+	print_loopback_supported(loopback);
+	print_pattern_bitmask("Supported TX patterns", tx_pat);
+	print_pattern_bitmask("Supported RX patterns", rx_pat);
+
+	printf("    Supported tests:");
+	if (!bert && !inject) {
+		printf(" (none)\n");
+	} else {
+		if (bert)
+			printf(" bert");
+		if (inject)
+			printf(" error-inject");
+		printf("\n");
+	}
+	printf("\n");
+
+	return 0;
+}
+
+static int intf_caps_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+	const struct nlattr *tb[ETHTOOL_A_INTF_CAPS_MAX + 1] = {};
+	struct nl_context *nlctx = data;
+	DECLARE_ATTR_TB_INFO(tb);
+	struct nlattr *attr;
+	bool silent;
+	int err_ret;
+	int ret;
+
+	silent = nlctx->is_dump;
+	err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+	if (ret < 0)
+		return err_ret;
+
+	nlctx->devname = get_dev_name(tb[ETHTOOL_A_INTF_CAPS_HEADER]);
+	if (!dev_ok(nlctx))
+		return err_ret;
+
+	if (silent)
+		print_nl();
+
+	printf("Interface capabilities for %s:\n\n", nlctx->devname);
+
+	if (tb[ETHTOOL_A_INTF_CAPS_BLOCKS]) {
+		mnl_attr_for_each_nested(attr, tb[ETHTOOL_A_INTF_CAPS_BLOCKS]) {
+			parse_intf_block(attr);
+		}
+	}
+
+	return MNL_CB_OK;
+}
+
+int nl_get_intf_caps(struct cmd_context *ctx)
+{
+	struct nl_context *nlctx = ctx->nlctx;
+	struct nl_socket *nlsk = nlctx->ethnl_socket;
+	struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+	int ret;
+
+	if (netlink_cmd_check(ctx, ETHTOOL_MSG_INTF_CAPS_GET, true))
+		return -EOPNOTSUPP;
+
+	if (ctx->argc > 0) {
+		fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+			*ctx->argp);
+		return 1;
+	}
+
+	ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_INTF_CAPS_GET,
+		       NLM_F_REQUEST | NLM_F_ACK);
+	if (ret < 0)
+		return ret;
+
+	if (ethnla_fill_header(msgbuff, ETHTOOL_A_INTF_CAPS_HEADER,
+			       ctx->devname, 0))
+		return -EMSGSIZE;
+
+	ret = nlsock_sendmsg(nlsk, NULL);
+	if (ret < 0)
+		return ret;
+
+	return nlsock_process_reply(nlsk, intf_caps_reply_cb, nlctx);
+}
diff --git a/uapi/linux/ethtool_netlink_generated.h b/uapi/linux/ethtool_netlink_generated.h
index 572c5f9..7b9860a 100644
--- a/uapi/linux/ethtool_netlink_generated.h
+++ b/uapi/linux/ethtool_netlink_generated.h
@@ -840,6 +840,77 @@ enum {
 	ETHTOOL_A_MSE_MAX = (__ETHTOOL_A_MSE_CNT - 1)
 };
 
+enum intf_component {
+	INTF_COMPONENT_MAC,
+	INTF_COMPONENT_PHY,
+	INTF_COMPONENT_MODULE,
+	__INTF_COMPONENT_COUNT,
+	INTF_COMPONENT_MAX = (__INTF_COMPONENT_COUNT - 1)
+};
+
+enum intf_sublayer {
+	INTF_SUBLAYER_NONE,
+	INTF_SUBLAYER_RS,
+	INTF_SUBLAYER_PCS,
+	INTF_SUBLAYER_FEC,
+	INTF_SUBLAYER_PMA,
+	INTF_SUBLAYER_PMD,
+	__INTF_SUBLAYER_COUNT,
+	INTF_SUBLAYER_MAX = (__INTF_SUBLAYER_COUNT - 1)
+};
+
+enum {
+	ETHTOOL_A_INTF_BLOCK_ID,
+	ETHTOOL_A_INTF_BLOCK_COMPONENT,
+	ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+	ETHTOOL_A_INTF_BLOCK_INSTANCE,
+	ETHTOOL_A_INTF_BLOCK_NAME,
+	ETHTOOL_A_INTF_BLOCK_DEPTH,
+	ETHTOOL_A_INTF_BLOCK_LANES,
+	ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+	ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+	ETHTOOL_A_INTF_BLOCK_ERROR_INJECT,
+	ETHTOOL_A_INTF_BLOCK_BERT,
+	__ETHTOOL_A_INTF_BLOCK_CNT,
+	ETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)
+};
+
+enum {
+	ETHTOOL_A_INTF_CAPS_UNSPEC,
+	ETHTOOL_A_INTF_CAPS_HEADER,
+	ETHTOOL_A_INTF_CAPS_BLOCKS,
+	__ETHTOOL_A_INTF_CAPS_CNT,
+	ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
+};
+
+#define LOOPBACK_SUPPORT_LOCAL         1U
+#define LOOPBACK_SUPPORT_REMOTE        2U
+
+enum phy_test_pattern {
+	PHY_TEST_PATTERN_OFF,
+	PHY_TEST_PATTERN_PRBS7,
+	PHY_TEST_PATTERN_PRBS9,
+	PHY_TEST_PATTERN_PRBS11,
+	PHY_TEST_PATTERN_PRBS13,
+	PHY_TEST_PATTERN_PRBS15,
+	PHY_TEST_PATTERN_PRBS23,
+	PHY_TEST_PATTERN_PRBS31,
+	PHY_TEST_PATTERN_SSPRQ,
+	PHY_TEST_PATTERN_PRBS13Q,
+	PHY_TEST_PATTERN_PRBS31Q,
+	PHY_TEST_PATTERN_SQUARE_NRZ,
+	PHY_TEST_PATTERN_SQUARE_PAM4,
+	PHY_TEST_PATTERN_TX_LINEARITY,
+	PHY_TEST_PATTERN_SCRAMBLED_IDLE,
+	PHY_TEST_PATTERN_K28_5,
+	PHY_TEST_PATTERN_K28_7,
+	__PHY_TEST_PATTERN_COUNT,
+	PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
+};
+
+#define LOOPBACK_SUPPORT_LOCAL         1U
+#define LOOPBACK_SUPPORT_REMOTE        2U
 enum {
 	ETHTOOL_MSG_USER_NONE = 0,
 	ETHTOOL_MSG_STRSET_GET = 1,
@@ -893,6 +964,7 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_ACT,
 	ETHTOOL_MSG_RSS_DELETE_ACT,
 	ETHTOOL_MSG_MSE_GET,
+	ETHTOOL_MSG_INTF_CAPS_GET,
 
 	__ETHTOOL_MSG_USER_CNT,
 	ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -954,6 +1026,7 @@ enum {
 	ETHTOOL_MSG_RSS_CREATE_NTF,
 	ETHTOOL_MSG_RSS_DELETE_NTF,
 	ETHTOOL_MSG_MSE_GET_REPLY,
+	ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
 
 	__ETHTOOL_MSG_KERNEL_CNT,
 	ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
-- 
2.25.1


  parent reply	other threads:[~2026-09-30 13:22 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
2026-09-30 13:53   ` Andrew Lunn
2026-10-05  4:10     ` Das, Shubham
2026-09-30 13:21 ` [RFC net-next 2/2] ethtool: add PHY test framework Shubham Das
2026-09-30 13:21 ` [RFC ethtool 0/2] add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` Shubham Das [this message]
2026-09-30 13:21 ` [RFC ethtool 2/2] ethtool: Add PHY test and BERT support Shubham Das
2026-09-30 13:58 ` [RFC net-next 0/2] ethtool: add interface caps and PHY tests Andrew Lunn
2026-10-05  4:13   ` Das, Shubham
2026-10-05 13:43     ` Andrew Lunn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260930132151.932792-5-shubham.das@intel.com \
    --to=shubham.das@intel.com \
    --cc=alexander.duyck@gmail.com \
    --cc=andrew@lunn.ch \
    --cc=balaji.chintalapalle@intel.com \
    --cc=dasshubhamk@gmail.com \
    --cc=jonas.wirandi@ericsson.com \
    --cc=lee@trager.us \
    --cc=magnus.k.lindberg@ericsson.com \
    --cc=maxime.chevallier@bootlin.com \
    --cc=netdev@vger.kernel.org \
    --cc=siddaraju.dh@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox