@@ -369,6 +369,14 @@ ethtool \- query or control network driver and hardware settings
.BN bank
.BN i2c
.HP
+.B ethtool \-M|\-\-change\-module\-eeprom
+.I devname
+.BN offset \ N
+.BN page \ N
+.BN bank \ N
+.BN i2c \ N
+.BN value \ N
+.HP
.B ethtool \-\-show\-priv\-flags
.I devname
.HP
@@ -1188,6 +1196,27 @@ and
.I length
parameters are treated relatively to EEPROM page boundaries.
.TP
+.B \-M \-\-change\-module\-eeprom
+Changes a single byte in the module EEPROM at the specified address to a
+specific value.
+.RS 4
+.TP
+.BI offset \ N
+Specifies the offset within a page to write to.
+.TP
+.BI page \ N
+Specifies the page number to write to.
+.TP
+.BI bank \ N
+Specifies the bank number to write to. If not specified, kernel assumes bank 0.
+.TP
+.BI i2c \ N
+Specifies the I2C address of the page to write to.
+.TP
+.BI value \ N
+Specifies the value to write.
+.RE
+.TP
.B \-\-show\-priv\-flags
Queries the specified network device for its private flags. The
names and meanings of private flags (if any) are defined by each
@@ -5907,6 +5907,16 @@ static const struct option args[] = {
" [ bank N ]\n"
" [ i2c N ]\n"
},
+ {
+ .opts = "-M|--change-module-eeprom",
+ .nlfunc = nl_setmodule,
+ .help = "Change byte in device module EEPROM",
+ .xhelp = " [ offset N ]\n"
+ " [ page N ]\n"
+ " [ bank N ]\n"
+ " [ i2c N ]\n"
+ " [ value N ]\n"
+ },
{
.opts = "--show-eee",
.func = do_geee,
@@ -408,6 +408,7 @@ const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_FEC_SET, fec),
NLMSG_DESC(ETHTOOL_MSG_STATS_GET, stats),
NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET, module_eeprom),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_SET, module_eeprom),
};
const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
@@ -447,6 +448,7 @@ const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_FEC_NTF, fec),
NLMSG_DESC(ETHTOOL_MSG_STATS_GET_REPLY, stats),
NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY, module_eeprom),
+ NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_NTF, module_eeprom),
};
const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
@@ -45,6 +45,7 @@ bool nl_gstats_chk(struct cmd_context *ctx);
int nl_gstats(struct cmd_context *ctx);
int nl_monitor(struct cmd_context *ctx);
int nl_getmodule(struct cmd_context *ctx);
+int nl_setmodule(struct cmd_context *ctx);
void nl_monitor_usage(void);
@@ -99,6 +100,7 @@ static inline void nl_monitor_usage(void)
#define nl_gstats_chk NULL
#define nl_gstats NULL
#define nl_getmodule NULL
+#define nl_setmodule NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
@@ -1,7 +1,7 @@
/*
- * module-eeprom.c - netlink implementation of module eeprom get command
+ * module-eeprom.c - netlink implementation of module eeprom commands
*
- * ethtool -m <dev>
+ * Implementation of "ethtool -m <dev>" and "ethtool -M <dev> ..."
*/
#include <errno.h>
@@ -18,6 +18,8 @@
#include "netlink.h"
#include "parser.h"
+/* MODULE_EEPROM_GET */
+
#define ETH_I2C_ADDRESS_LOW 0x50
#define ETH_I2C_ADDRESS_HIGH 0x51
#define ETH_I2C_MAX_ADDRESS 0x7F
@@ -340,6 +342,43 @@ static void decoder_print(void)
}
}
+int module_eeprom_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_MAX + 1] = {};
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nl_context *nlctx = data;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump || nlctx->is_monitor;
+ 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;
+ if (!tb[ETHTOOL_A_MODULE_EEPROM_OFFSET] ||
+ !tb[ETHTOOL_A_MODULE_EEPROM_LENGTH] ||
+ !tb[ETHTOOL_A_MODULE_EEPROM_PAGE] ||
+ !tb[ETHTOOL_A_MODULE_EEPROM_BANK] ||
+ !tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS])
+ return err_ret;
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_FEC_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ putchar('\n');
+ printf("Module EEPROM write for %s:\n", nlctx->devname);
+ show_u32(tb[ETHTOOL_A_MODULE_EEPROM_OFFSET], "offset: ");
+ show_u32(tb[ETHTOOL_A_MODULE_EEPROM_LENGTH], "length: ");
+ printf("page: %u\n", mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_PAGE]));
+ printf("bank: %u\n", mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_BANK]));
+ printf("i2c address: 0x%x\n",
+ mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS]));
+
+ return MNL_CB_OK;
+}
+
int nl_getmodule(struct cmd_context *ctx)
{
struct ethtool_module_eeprom request = {0};
@@ -414,3 +453,85 @@ cleanup:
cache_free();
return ret;
}
+
+/* MODULE_EEPROM_SET */
+
+static const struct param_parser setmodule_params[] = {
+ {
+ .arg = "offset",
+ .type = ETHTOOL_A_MODULE_EEPROM_OFFSET,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "page",
+ .type = ETHTOOL_A_MODULE_EEPROM_PAGE,
+ .handler = nl_parse_direct_u8,
+ .min_argc = 1,
+ },
+ {
+ .arg = "bank",
+ .type = ETHTOOL_A_MODULE_EEPROM_BANK,
+ .handler = nl_parse_direct_u8,
+ .min_argc = 1,
+ },
+ {
+ .arg = "i2c",
+ .type = ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
+ .handler = nl_parse_direct_u8,
+ .min_argc = 1,
+ },
+ {
+ .arg = "value",
+ .type = ETHTOOL_A_MODULE_EEPROM_DATA,
+ .handler = nl_parse_direct_u8,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_setmodule(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_SET, false))
+ return -EOPNOTSUPP;
+ if (!ctx->argc) {
+ fprintf(stderr, "ethtool (-M): parameters missing\n");
+ return 1;
+ }
+
+ nlctx->cmd = "-M";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MODULE_EEPROM_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return 2;
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_MODULE_EEPROM_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nl_parser(nlctx, setmodule_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return 1;
+
+ if (ethnla_put_u32(msgbuff, ETHTOOL_A_MODULE_EEPROM_LENGTH, 1))
+ return -EMSGSIZE;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return 83;
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret == 0)
+ return 0;
+ else
+ return nlctx->exit_code ?: 83;
+}
@@ -71,6 +71,10 @@ static struct {
.cmd = ETHTOOL_MSG_FEC_NTF,
.cb = fec_reply_cb,
},
+ {
+ .cmd = ETHTOOL_MSG_MODULE_EEPROM_NTF,
+ .cb = module_eeprom_ntf_cb,
+ },
};
static void clear_filter(struct nl_context *nlctx)
@@ -91,6 +91,7 @@ int cable_test_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
int cable_test_tdr_reply_cb(const struct nlmsghdr *nlhdr, void *data);
int cable_test_tdr_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
int fec_reply_cb(const struct nlmsghdr *nlhdr, void *data);
+int module_eeprom_ntf_cb(const struct nlmsghdr *nlhdr, void *data);
/* dump helpers */