From d58ba942c4b84b4ead8a51b89633f660f99399d7 Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Tue, 23 May 2023 17:40:04 +0200
Subject: [PATCH 6/6] ethtool: add phylink interface related commands

---
 ethtool.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)

diff --git a/ethtool.c b/ethtool.c
index 480b4f5..9b82d64 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5312,6 +5312,99 @@ static int do_get_shaper_params(struct cmd_context *ctx)
 	return 0;
 }
 
+static int do_list_phylink_iftypes(struct cmd_context *ctx)
+{
+	struct ethtool_gstrings *strings;
+	int i;
+
+	strings = get_stringset(ctx, ETH_SS_PHYLINK_IFTYPES, 0, 1);
+	if (!strings) {
+		perror("Cannot get strings");
+		return 74;
+	}
+
+	for (i = 0; i < strings->len; i++)
+		printf("%s\n",
+		       (const char *)strings->data + i * ETH_GSTRING_LEN);
+
+	return 0;
+}
+
+static const char *mode_str[] = {
+	"phy",
+	"fixed",
+	"in-band-autoneg",
+};
+
+static int do_get_phylink_iftype(struct cmd_context *ctx)
+{
+	struct ethtool_phylink_iftype p;
+	int rv;
+
+	memset(&p, 0, sizeof (p));
+	p.cmd = ETHTOOL_GPHYLINK_IFTYPE;
+
+	rv = send_ioctl(ctx, &p);
+	if (rv) {
+		perror("Cannot get phylink iftype");
+		return rv;
+	}
+
+	printf("phylink state:\n");
+	printf(" interface:  %s\n", p.iftype);
+	printf(" autoneg:    %s\n", p.autoneg_en ? "enabled" : "disabled");
+	printf(" link mode:  %s\n", mode_str[p.mode]);
+	return 0;
+}
+
+static int do_set_phylink_iftype(struct cmd_context *ctx)
+{
+	struct ethtool_phylink_iftype p;
+	int any_changed = 0;
+	int autoneg = 0;
+	char *type = NULL;
+	struct cmdline_info cmdline_pl[] = {
+		{ "type", CMDL_STR, &type, NULL },
+		{ "autoneg", CMDL_BOOL, &autoneg, NULL },
+	};
+	int err;
+
+	parse_generic_cmdline(ctx, &any_changed,
+			      cmdline_pl, ARRAY_SIZE(cmdline_pl));
+
+	memset(&p, 0, sizeof (p));
+	p.cmd = ETHTOOL_GPHYLINK_IFTYPE;
+
+	err = send_ioctl(ctx, &p);
+	if (err < 0) {
+		perror("Cannot get driver information");
+		return 72;
+	}
+
+	if (type) {
+		if (strlen(type) > sizeof (p.iftype) - 1) {
+			fprintf(stderr, "Invalid interface type\n");
+			exit_bad_args();
+		}
+		strcpy(p.iftype, type);
+	}
+
+	if (autoneg != -1)
+		p.autoneg_en = autoneg;
+
+	if (any_changed) {
+		p.cmd = ETHTOOL_SPHYLINK_IFTYPE;
+
+		err = send_ioctl(ctx, &p);
+		if (err < 0) {
+			perror("Cannot set interface type");
+			return 72;
+		}
+	}
+
+	return 0;
+}
+
 static const char *sfp_fsm_state_str[] = {
         [ETHTOOL_SFP_S_DOWN] = "down",
         [ETHTOOL_SFP_S_FAIL] = "fail",
@@ -5559,6 +5652,11 @@ static const struct option {
 	{ "--show-epon-param", 1, do_show_epon_param, "Show EPON params"},
 	{ "--set-tx-shaper-param", 1, do_set_shaper_params, "Set HW TX shaper params" },
 	{ "--get-tx-shaper-param", 1, do_get_shaper_params, "Get HW TX shaper params" },
+	{ "--list-phylink-iftypes", 1, do_list_phylink_iftypes, "List all phylink interface types" },
+	{ "--get-phylink-iftype", 1, do_get_phylink_iftype, "Get phylink current interface type" },
+	{ "--set-phylink-iftype", 1, do_set_phylink_iftype, "Set phylink current interface type",
+	  "              [ type TYPE ]\n"
+	  "              [ autoneg on|off ]]\n" },
 	{ "--show-sfp-state", 1, do_show_sfp_state, "Show SFP kernel state"},
 	{ "-Q|--per-queue", 1, do_perqueue, "Apply per-queue command."
 	  "The supported sub commands include --show-coalesce, --coalesce",
-- 
2.34.1

