All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH ethtool v3 0/4] Extend module EEPROM API
@ 2021-06-08 10:32 Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 1/4] ethtool: Add netlink handler for getmodule (-m) Moshe Shemesh
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-08 10:32 UTC (permalink / raw)
  To: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger, netdev
  Cc: Vladyslav Tarasiuk, Moshe Shemesh

Ethtool supports module EEPROM dumps via the `ethtool -m <dev>` command.
But in current state its functionality is limited - offset and length
parameters, which are used to specify a linear desired region of EEPROM
data to dump, is not enough, considering emergence of complex module
EEPROM layouts such as CMIS.

Moreover, CMIS extends the amount of pages that may be accessible by
introducing another parameter for page addressing - banks. Besides,
currently module EEPROM is represented as a chunk of concatenated pages,
where lower 128 bytes of all pages, except page 00h, are omitted. Offset
and length are used to address parts of this fake linear memory. But in
practice drivers, which implement get_module_info() and
get_module_eeprom() ethtool ops still calculate page number and set I2C
address on their own.

This series adds support in `ethtool -m` of dumping an arbitrary page
specified by page number, bank number and I2C address. Implement netlink
handler for `ethtool -m` in order to make such requests to the kernel
and extend CLI by adding corresponding parameters.
New command line format:
 ethtool -m <dev> [hex on|off] [raw on|off] [offset N] [length N] [page N] [bank N] [i2c N]

Netlink infrastructure works on per-page basis and allows dumps of a
single page at once. But in case user requests human-readable output,
which currently may require more than one page, userspace can make such
additional calls to kernel on demand and place pages in a linked list.
It allows to get pages from cache on demand and pass them to refactored
SFF decoders.

Change Log:
v2 -> v3:
- Removed spec version from CMIS identifiers by changing 'CMIS4' and 'cmis4' to 'CMIS' and 'cmis' respectively.

v1 -> v2:
- Changed offset defines to specification values.
- Added default offset value (128) if page number is specified.
- Fixed return values.
- Removed page_available()


Vladyslav Tarasiuk (4):
  ethtool: Add netlink handler for getmodule (-m)
  ethtool: Refactor human-readable module EEPROM output for new API
  ethtool: Rename QSFP-DD identifiers to use CMIS
  ethtool: Update manpages to reflect changes to getmodule (-m) command

 Makefile.am             |   3 +-
 cmis.c                  | 359 +++++++++++++++++++++++++++++++++++++++++
 cmis.h                  | 128 +++++++++++++++
 ethtool.8.in            |  14 ++
 ethtool.c               |   4 +
 internal.h              |  12 ++
 list.h                  |  34 ++++
 netlink/desc-ethtool.c  |  13 ++
 netlink/extapi.h        |   2 +
 netlink/module-eeprom.c | 416 ++++++++++++++++++++++++++++++++++++++++++++++++
 qsfp-dd.c               | 333 --------------------------------------
 qsfp-dd.h               | 125 ---------------
 qsfp.c                  | 130 ++++++++-------
 qsfp.h                  |  51 +++---
 sff-common.c            |   3 +
 sff-common.h            |   3 +-
 16 files changed, 1090 insertions(+), 540 deletions(-)
 create mode 100644 cmis.c
 create mode 100644 cmis.h
 create mode 100644 list.h
 create mode 100644 netlink/module-eeprom.c
 delete mode 100644 qsfp-dd.c
 delete mode 100644 qsfp-dd.h

-- 
1.8.2.3


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH ethtool v3 1/4] ethtool: Add netlink handler for getmodule (-m)
  2021-06-08 10:32 [PATCH ethtool v3 0/4] Extend module EEPROM API Moshe Shemesh
@ 2021-06-08 10:32 ` Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 2/4] ethtool: Refactor human-readable module EEPROM output for new API Moshe Shemesh
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-08 10:32 UTC (permalink / raw)
  To: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger, netdev
  Cc: Vladyslav Tarasiuk, Moshe Shemesh

From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

Implement "ethtool -m <dev>" subcommand using netlink and extend the
interface for new module EEPROM standards. Currently, ethtool supports
module EEPROM dumps of continuous memory regions, which are specified
using a pair of parameters - offset and length. But due to emergence of
new standards such as CMIS 4.0, which further extends possible
addressed memory, this approach shows its limitations.

Extend command line interface in order to support dumps of arbitrary
pages including CMIS specific banked pages:
 ethtool -m <dev> [page N] [bank N] [i2c N]

Command example:
 # ethtool -m eth2 page 1 offset 0x80 length 0x20

 Offset          Values
 ------          ------
 0x0080:         11 00 23 80 00 00 00 00 00 00 00 08 ff 00 00 00
 0x0090:         00 00 01 a0 4d 65 6c 6c 61 6e 6f 78 20 20 20 20

Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
 Makefile.am             |   1 +
 ethtool.c               |   4 +
 internal.h              |  10 ++
 list.h                  |  34 ++++
 netlink/desc-ethtool.c  |  13 ++
 netlink/extapi.h        |   2 +
 netlink/module-eeprom.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 468 insertions(+)
 create mode 100644 list.h
 create mode 100644 netlink/module-eeprom.c

diff --git a/Makefile.am b/Makefile.am
index 75c2456..6abd2b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,6 +38,7 @@ ethtool_SOURCES += \
 		  netlink/eee.c netlink/tsinfo.c netlink/fec.c \
 		  netlink/stats.c \
 		  netlink/desc-ethtool.c netlink/desc-genlctrl.c \
+		  netlink/module-eeprom.c \
 		  netlink/desc-rtnl.c netlink/cable_test.c netlink/tunnels.c \
 		  uapi/linux/ethtool_netlink.h \
 		  uapi/linux/netlink.h uapi/linux/genetlink.h \
diff --git a/ethtool.c b/ethtool.c
index 8ed5a33..33a0a49 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -5897,11 +5897,15 @@ static const struct option args[] = {
 	{
 		.opts	= "-m|--dump-module-eeprom|--module-info",
 		.func	= do_getmodule,
+		.nlfunc = nl_getmodule,
 		.help	= "Query/Decode Module EEPROM information and optical diagnostics if available",
 		.xhelp	= "		[ raw on|off ]\n"
 			  "		[ hex on|off ]\n"
 			  "		[ offset N ]\n"
 			  "		[ length N ]\n"
+			  "		[ page N ]\n"
+			  "		[ bank N ]\n"
+			  "		[ i2c N ]\n"
 	},
 	{
 		.opts	= "--show-eee",
diff --git a/internal.h b/internal.h
index 27da8ea..2affebe 100644
--- a/internal.h
+++ b/internal.h
@@ -216,6 +216,16 @@ static inline int ethtool_link_mode_set_bit(unsigned int nr, u32 *mask)
 	return 0;
 }
 
+/* Struct for managing module EEPROM pages */
+struct ethtool_module_eeprom {
+	u32	offset;
+	u32	length;
+	u8	page;
+	u8	bank;
+	u8	i2c_address;
+	u8	*data;
+};
+
 /* Context for sub-commands */
 struct cmd_context {
 	const char *devname;	/* net device name */
diff --git a/list.h b/list.h
new file mode 100644
index 0000000..aa97fdd
--- /dev/null
+++ b/list.h
@@ -0,0 +1,34 @@
+#ifndef ETHTOOL_LIST_H__
+#define ETHTOOL_LIST_H__
+
+#include <unistd.h>
+
+/* Generic list utilities */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	head->next->prev = new;
+	new->next = head->next;
+	new->prev = head;
+	head->next = new;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+	entry->next->prev = entry->prev;
+	entry->prev->next = entry->next;
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+#endif
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
index 8ea7c53..d6fc4e2 100644
--- a/netlink/desc-ethtool.c
+++ b/netlink/desc-ethtool.c
@@ -363,6 +363,17 @@ static const struct pretty_nla_desc __stats_desc[] = {
 	NLATTR_DESC_NESTED(ETHTOOL_A_STATS_GRP, stats_grp),
 };
 
+const struct pretty_nla_desc __module_eeprom_desc[] = {
+	NLATTR_DESC_INVALID(ETHTOOL_A_MODULE_EEPROM_UNSPEC),
+	NLATTR_DESC_NESTED(ETHTOOL_A_MODULE_EEPROM_HEADER, header),
+	NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_OFFSET),
+	NLATTR_DESC_U32(ETHTOOL_A_MODULE_EEPROM_LENGTH),
+	NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_PAGE),
+	NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_BANK),
+	NLATTR_DESC_U8(ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS),
+	NLATTR_DESC_BINARY(ETHTOOL_A_MODULE_EEPROM_DATA)
+};
+
 const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
 	NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE),
 	NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset),
@@ -396,6 +407,7 @@ const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
 	NLMSG_DESC(ETHTOOL_MSG_FEC_GET, fec),
 	NLMSG_DESC(ETHTOOL_MSG_FEC_SET, fec),
 	NLMSG_DESC(ETHTOOL_MSG_STATS_GET, stats),
+	NLMSG_DESC(ETHTOOL_MSG_MODULE_EEPROM_GET, module_eeprom),
 };
 
 const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
@@ -434,6 +446,7 @@ const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
 	NLMSG_DESC(ETHTOOL_MSG_FEC_GET_REPLY, fec),
 	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),
 };
 
 const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 7015907..91bf02b 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -44,6 +44,7 @@ int nl_sfec(struct cmd_context *ctx);
 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);
 
 void nl_monitor_usage(void);
 
@@ -97,6 +98,7 @@ static inline void nl_monitor_usage(void)
 #define nl_sfec			NULL
 #define nl_gstats_chk		NULL
 #define nl_gstats		NULL
+#define nl_getmodule		NULL
 
 #endif /* ETHTOOL_ENABLE_NETLINK */
 
diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
new file mode 100644
index 0000000..16fe09e
--- /dev/null
+++ b/netlink/module-eeprom.c
@@ -0,0 +1,404 @@
+/*
+ * module-eeprom.c - netlink implementation of module eeprom get command
+ *
+ * ethtool -m <dev>
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "../sff-common.h"
+#include "../qsfp.h"
+#include "../qsfp-dd.h"
+#include "../internal.h"
+#include "../common.h"
+#include "../list.h"
+#include "netlink.h"
+#include "parser.h"
+
+#define ETH_I2C_ADDRESS_LOW	0x50
+#define ETH_I2C_ADDRESS_HIGH	0x51
+#define ETH_I2C_MAX_ADDRESS	0x7F
+
+static struct cmd_params
+{
+	u8 dump_hex;
+	u8 dump_raw;
+	u32 offset;
+	u32 length;
+	u32 page;
+	u32 bank;
+	u32 i2c_address;
+} getmodule_cmd_params;
+
+static const struct param_parser getmodule_params[] = {
+	{
+		.arg		= "hex",
+		.handler	= nl_parse_u8bool,
+		.dest_offset	= offsetof(struct cmd_params, dump_hex),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "raw",
+		.handler	= nl_parse_u8bool,
+		.dest_offset	= offsetof(struct cmd_params, dump_raw),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "offset",
+		.handler	= nl_parse_direct_u32,
+		.dest_offset	= offsetof(struct cmd_params, offset),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "length",
+		.handler	= nl_parse_direct_u32,
+		.dest_offset	= offsetof(struct cmd_params, length),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "page",
+		.handler	= nl_parse_direct_u32,
+		.dest_offset	= offsetof(struct cmd_params, page),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "bank",
+		.handler	= nl_parse_direct_u32,
+		.dest_offset	= offsetof(struct cmd_params, bank),
+		.min_argc	= 1,
+	},
+	{
+		.arg		= "i2c",
+		.handler	= nl_parse_direct_u32,
+		.dest_offset	= offsetof(struct cmd_params, i2c_address),
+		.min_argc	= 1,
+	},
+	{}
+};
+
+struct page_entry {
+	struct list_head link;
+	struct ethtool_module_eeprom *page;
+};
+
+static struct list_head page_list = LIST_HEAD_INIT(page_list);
+
+static int cache_add(struct ethtool_module_eeprom *page)
+{
+	struct page_entry *list_element;
+
+	if (!page)
+		return -1;
+	list_element = malloc(sizeof(*list_element));
+	if (!list_element)
+		return -ENOMEM;
+	list_element->page = page;
+
+	list_add(&list_element->link, &page_list);
+	return 0;
+}
+
+static void page_free(struct ethtool_module_eeprom *page)
+{
+	free(page->data);
+	free(page);
+}
+
+static void cache_del(struct ethtool_module_eeprom *page)
+{
+	struct ethtool_module_eeprom *entry;
+	struct list_head *head, *next;
+
+	list_for_each_safe(head, next, &page_list) {
+		entry = ((struct page_entry *)head)->page;
+		if (entry == page) {
+			list_del(head);
+			free(head);
+			page_free(entry);
+			break;
+		}
+	}
+}
+
+static void cache_free(void)
+{
+	struct ethtool_module_eeprom *entry;
+	struct list_head *head, *next;
+
+	list_for_each_safe(head, next, &page_list) {
+		entry = ((struct page_entry *)head)->page;
+		list_del(head);
+		free(head);
+		page_free(entry);
+	}
+}
+
+static struct ethtool_module_eeprom *page_join(struct ethtool_module_eeprom *page_a,
+					       struct ethtool_module_eeprom *page_b)
+{
+	struct ethtool_module_eeprom *joined_page;
+	u32 total_length;
+
+	if (!page_a || !page_b ||
+	    page_a->page != page_b->page ||
+	    page_a->bank != page_b->bank ||
+	    page_a->i2c_address != page_b->i2c_address)
+		return NULL;
+
+	total_length = page_a->length + page_b->length;
+	joined_page = calloc(1, sizeof(*joined_page));
+	joined_page->data = calloc(1, total_length);
+	joined_page->page = page_a->page;
+	joined_page->bank = page_a->bank;
+	joined_page->length = total_length;
+	joined_page->i2c_address = page_a->i2c_address;
+
+	if (page_a->offset < page_b->offset) {
+		memcpy(joined_page->data, page_a->data, page_a->length);
+		memcpy(joined_page->data + page_a->length, page_b->data, page_b->length);
+		joined_page->offset = page_a->offset;
+	} else {
+		memcpy(joined_page->data, page_b->data, page_b->length);
+		memcpy(joined_page->data + page_b->length, page_a->data, page_a->length);
+		joined_page->offset = page_b->offset;
+	}
+
+	return joined_page;
+}
+
+static struct ethtool_module_eeprom *cache_get(u32 page, u32 bank, u8 i2c_address)
+{
+	struct ethtool_module_eeprom *entry;
+	struct list_head *head, *next;
+
+	list_for_each_safe(head, next, &page_list) {
+		entry = ((struct page_entry *)head)->page;
+		if (entry->page == page && entry->bank == bank &&
+		    entry->i2c_address == i2c_address)
+			return entry;
+	}
+
+	return NULL;
+}
+
+static int getmodule_page_fetch_reply_cb(const struct nlmsghdr *nlhdr,
+					 void *data)
+{
+	const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
+	DECLARE_ATTR_TB_INFO(tb);
+	struct ethtool_module_eeprom *lower_page;
+	struct ethtool_module_eeprom *response;
+	struct ethtool_module_eeprom *request;
+	struct ethtool_module_eeprom *joined;
+	u8 *eeprom_data;
+	int ret;
+
+	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+	if (ret < 0)
+		return ret;
+
+	if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) {
+		fprintf(stderr, "Malformed netlink message (getmodule)\n");
+		return MNL_CB_ERROR;
+	}
+
+	response = calloc(1, sizeof(*response));
+	if (!response)
+		return -ENOMEM;
+
+	request = (struct ethtool_module_eeprom *)data;
+	response->offset = request->offset;
+	response->page = request->page;
+	response->bank = request->bank;
+	response->i2c_address = request->i2c_address;
+	response->length = mnl_attr_get_payload_len(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
+	eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
+
+	response->data = malloc(response->length);
+	if (!response->data) {
+		free(response);
+		return -ENOMEM;
+	}
+	memcpy(response->data, eeprom_data, response->length);
+
+	if (!request->page) {
+		lower_page = cache_get(request->page, request->bank, response->i2c_address);
+		if (lower_page) {
+			joined = page_join(lower_page, response);
+			page_free(response);
+			cache_del(lower_page);
+			return cache_add(joined);
+		}
+	}
+
+	return cache_add(response);
+}
+
+static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eeprom *request)
+{
+	struct nl_socket *nlsock = nlctx->ethnl_socket;
+	struct nl_msg_buff *msg = &nlsock->msgbuff;
+	struct ethtool_module_eeprom *page;
+	int ret;
+
+	if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
+		return -EINVAL;
+
+	/* Satisfy request right away, if region is already in cache */
+	page = cache_get(request->page, request->bank, request->i2c_address);
+	if (page && page->offset <= request->offset &&
+	    page->offset + page->length >= request->offset + request->length) {
+		return 0;
+	}
+
+	ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
+				      ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
+	if (ret < 0)
+		return ret;
+
+	if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, request->length) ||
+	    ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, request->offset) ||
+	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, request->page) ||
+	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, request->bank) ||
+	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, request->i2c_address))
+		return -EMSGSIZE;
+
+	ret = nlsock_sendmsg(nlsock, NULL);
+	if (ret < 0)
+		return ret;
+	ret = nlsock_process_reply(nlsock, getmodule_page_fetch_reply_cb, (void *)request);
+	if (ret < 0)
+		return ret;
+
+	return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL);
+}
+
+static int decoder_prefetch(struct nl_context *nlctx)
+{
+	struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+	struct ethtool_module_eeprom request = {0};
+	u8 module_id = page_zero_lower->data[0];
+	int err = 0;
+
+	/* Fetch rest of page 00 */
+	request.i2c_address = ETH_I2C_ADDRESS_LOW;
+	request.offset = 128;
+	request.length = 128;
+	err = page_fetch(nlctx, &request);
+	if (err)
+		return err;
+
+	switch (module_id) {
+	case SFF8024_ID_QSFP:
+	case SFF8024_ID_QSFP28:
+	case SFF8024_ID_QSFP_PLUS:
+		memset(&request, 0, sizeof(request));
+		request.i2c_address = ETH_I2C_ADDRESS_LOW;
+		request.offset = 128;
+		request.length = 128;
+		request.page = 3;
+		break;
+	case SFF8024_ID_QSFP_DD:
+		memset(&request, 0, sizeof(request));
+		request.i2c_address = ETH_I2C_ADDRESS_LOW;
+		request.offset = 128;
+		request.length = 128;
+		request.page = 1;
+		break;
+	}
+
+	return page_fetch(nlctx, &request);
+}
+
+static void decoder_print(void)
+{
+	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
+
+	switch (module_id) {
+	case SFF8024_ID_SFP:
+		sff8079_show_all(page_zero->data);
+		break;
+	default:
+		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
+		break;
+	}
+}
+
+int nl_getmodule(struct cmd_context *ctx)
+{
+	struct ethtool_module_eeprom request = {0};
+	struct ethtool_module_eeprom *reply_page;
+	struct nl_context *nlctx = ctx->nlctx;
+	u32 dump_length;
+	u8 *eeprom_data;
+	int ret;
+
+	if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false))
+		return -EOPNOTSUPP;
+
+	nlctx->cmd = "-m";
+	nlctx->argp = ctx->argp;
+	nlctx->argc = ctx->argc;
+	nlctx->devname = ctx->devname;
+	ret = nl_parser(nlctx, getmodule_params, &getmodule_cmd_params, PARSER_GROUP_NONE, NULL);
+	if (ret < 0)
+		return ret;
+
+	if (getmodule_cmd_params.dump_hex && getmodule_cmd_params.dump_raw) {
+		fprintf(stderr, "Hex and raw dump cannot be specified together\n");
+		return -EINVAL;
+	}
+
+	request.i2c_address = ETH_I2C_ADDRESS_LOW;
+	request.length = 128;
+	ret = page_fetch(nlctx, &request);
+	if (ret)
+		goto cleanup;
+
+#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+	if (getmodule_cmd_params.page || getmodule_cmd_params.bank ||
+	    getmodule_cmd_params.offset || getmodule_cmd_params.length)
+#endif
+		getmodule_cmd_params.dump_hex = true;
+
+	request.offset = getmodule_cmd_params.offset;
+	request.length = getmodule_cmd_params.length ?: 128;
+	request.page = getmodule_cmd_params.page;
+	request.bank = getmodule_cmd_params.bank;
+	request.i2c_address = getmodule_cmd_params.i2c_address ?: ETH_I2C_ADDRESS_LOW;
+
+	if (request.page && !request.offset)
+		request.offset = 128;
+
+	if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) {
+		ret = page_fetch(nlctx, &request);
+		if (ret < 0)
+			goto cleanup;
+		reply_page = cache_get(request.page, request.bank, request.i2c_address);
+		if (!reply_page) {
+			ret = -EINVAL;
+			goto cleanup;
+		}
+
+		eeprom_data = reply_page->data + (request.offset - reply_page->offset);
+		dump_length = reply_page->length < request.length ? reply_page->length
+								  : request.length;
+		if (getmodule_cmd_params.dump_raw)
+			fwrite(eeprom_data, 1, request.length, stdout);
+		else
+			dump_hex(stdout, eeprom_data, dump_length, request.offset);
+	} else {
+		ret = decoder_prefetch(nlctx);
+		if (ret)
+			goto cleanup;
+		decoder_print();
+	}
+
+cleanup:
+	cache_free();
+	return ret;
+}
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH ethtool v3 2/4] ethtool: Refactor human-readable module EEPROM output for new API
  2021-06-08 10:32 [PATCH ethtool v3 0/4] Extend module EEPROM API Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 1/4] ethtool: Add netlink handler for getmodule (-m) Moshe Shemesh
@ 2021-06-08 10:32 ` Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 4/4] ethtool: Update manpages to reflect changes to getmodule (-m) command Moshe Shemesh
  3 siblings, 0 replies; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-08 10:32 UTC (permalink / raw)
  To: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger, netdev
  Cc: Vladyslav Tarasiuk, Moshe Shemesh

From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

Reuse existing SFF8636 and QSFP-DD infrastructures to implement
EEPROM decoders, which work with paged memory. Add support for
human-readable output for QSFP, QSFP28, QSFP Plus, QSFP-DD and DSFP
transreceivers from netlink 'ethtool -m' handler.

Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
 internal.h              |   2 +
 netlink/module-eeprom.c |  12 +++++
 qsfp-dd.c               |  44 +++++++++++++----
 qsfp-dd.h               |  29 ++++++-----
 qsfp.c                  | 128 +++++++++++++++++++++++++++---------------------
 qsfp.h                  |  51 ++++++++++---------
 sff-common.c            |   3 ++
 sff-common.h            |   3 +-
 8 files changed, 170 insertions(+), 102 deletions(-)

diff --git a/internal.h b/internal.h
index 2affebe..33e619b 100644
--- a/internal.h
+++ b/internal.h
@@ -391,6 +391,8 @@ void sff8472_show_all(const __u8 *id);
 
 /* QSFP Optics diagnostics */
 void sff8636_show_all(const __u8 *id, __u32 eeprom_len);
+void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero,
+			    const struct ethtool_module_eeprom *page_three);
 
 /* FUJITSU Extended Socket network device */
 int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
index 16fe09e..664f5c6 100644
--- a/netlink/module-eeprom.c
+++ b/netlink/module-eeprom.c
@@ -302,6 +302,7 @@ static int decoder_prefetch(struct nl_context *nlctx)
 		request.page = 3;
 		break;
 	case SFF8024_ID_QSFP_DD:
+	case SFF8024_ID_DSFP:
 		memset(&request, 0, sizeof(request));
 		request.i2c_address = ETH_I2C_ADDRESS_LOW;
 		request.offset = 128;
@@ -315,13 +316,24 @@ static int decoder_prefetch(struct nl_context *nlctx)
 
 static void decoder_print(void)
 {
+	struct ethtool_module_eeprom *page_three = cache_get(3, 0, ETH_I2C_ADDRESS_LOW);
 	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+	struct ethtool_module_eeprom *page_one = cache_get(1, 0, ETH_I2C_ADDRESS_LOW);
 	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
 
 	switch (module_id) {
 	case SFF8024_ID_SFP:
 		sff8079_show_all(page_zero->data);
 		break;
+	case SFF8024_ID_QSFP:
+	case SFF8024_ID_QSFP28:
+	case SFF8024_ID_QSFP_PLUS:
+		sff8636_show_all_paged(page_zero, page_three);
+		break;
+	case SFF8024_ID_QSFP_DD:
+	case SFF8024_ID_DSFP:
+		cmis_show_all(page_zero, page_one);
+		break;
 	default:
 		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
 		break;
diff --git a/qsfp-dd.c b/qsfp-dd.c
index 900bbb5..55adbcb 100644
--- a/qsfp-dd.c
+++ b/qsfp-dd.c
@@ -274,6 +274,20 @@ static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id)
 		  OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
 }
 
+static void qsfp_dd_show_link_len_from_page(const __u8 *page_one_data)
+{
+	qsfp_dd_print_smf_cbl_len(page_one_data);
+	sff_show_value_with_unit(page_one_data, QSFP_DD_OM5_LEN_OFFSET,
+				 "Length (OM5)", 2, "m");
+	sff_show_value_with_unit(page_one_data, QSFP_DD_OM4_LEN_OFFSET,
+				 "Length (OM4)", 2, "m");
+	sff_show_value_with_unit(page_one_data, QSFP_DD_OM3_LEN_OFFSET,
+				 "Length (OM3 50/125um)", 2, "m");
+	sff_show_value_with_unit(page_one_data, QSFP_DD_OM2_LEN_OFFSET,
+				 "Length (OM2 50/125um)", 1, "m");
+}
+
+
 /**
  * Print relevant info about the maximum supported fiber media length
  * for each type of fiber media at the maximum module-supported bit rate.
@@ -283,15 +297,7 @@ static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id)
  */
 static void qsfp_dd_show_link_len(const __u8 *id)
 {
-	qsfp_dd_print_smf_cbl_len(id);
-	sff_show_value_with_unit(id, QSFP_DD_OM5_LEN_OFFSET,
-				 "Length (OM5)", 2, "m");
-	sff_show_value_with_unit(id, QSFP_DD_OM4_LEN_OFFSET,
-				 "Length (OM4)", 2, "m");
-	sff_show_value_with_unit(id, QSFP_DD_OM3_LEN_OFFSET,
-				 "Length (OM3 50/125um)", 2, "m");
-	sff_show_value_with_unit(id, QSFP_DD_OM2_LEN_OFFSET,
-				 "Length (OM2 50/125um)", 1, "m");
+	qsfp_dd_show_link_len_from_page(id + PAG01H_UPPER_OFFSET);
 }
 
 /**
@@ -331,3 +337,23 @@ void qsfp_dd_show_all(const __u8 *id)
 	qsfp_dd_show_vendor_info(id);
 	qsfp_dd_show_rev_compliance(id);
 }
+
+void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
+		   const struct ethtool_module_eeprom *page_one)
+{
+	const __u8 *page_zero_data = page_zero->data;
+
+	qsfp_dd_show_identifier(page_zero_data);
+	qsfp_dd_show_power_info(page_zero_data);
+	qsfp_dd_show_connector(page_zero_data);
+	qsfp_dd_show_cbl_asm_len(page_zero_data);
+	qsfp_dd_show_sig_integrity(page_zero_data);
+	qsfp_dd_show_mit_compliance(page_zero_data);
+	qsfp_dd_show_mod_lvl_monitors(page_zero_data);
+
+	if (page_one)
+		qsfp_dd_show_link_len_from_page(page_one->data);
+
+	qsfp_dd_show_vendor_info(page_zero_data);
+	qsfp_dd_show_rev_compliance(page_zero_data);
+}
diff --git a/qsfp-dd.h b/qsfp-dd.h
index f589c4e..51f92fa 100644
--- a/qsfp-dd.h
+++ b/qsfp-dd.h
@@ -96,30 +96,33 @@
 /*-----------------------------------------------------------------------
  * Upper Memory Page 0x01: contains advertising fields that define properties
  * that are unique to active modules and cable assemblies.
- * RealOffset = 1 * 0x80 + LocalOffset
+ * GlobalOffset = 2 * 0x80 + LocalOffset
  */
-#define PAG01H_UPPER_OFFSET			(0x01 * 0x80)
+#define PAG01H_UPPER_OFFSET			(0x02 * 0x80)
 
 /* Supported Link Length (Page 1) */
-#define QSFP_DD_SMF_LEN_OFFSET			(PAG01H_UPPER_OFFSET + 0x84)
-#define QSFP_DD_OM5_LEN_OFFSET			(PAG01H_UPPER_OFFSET + 0x85)
-#define QSFP_DD_OM4_LEN_OFFSET			(PAG01H_UPPER_OFFSET + 0x86)
-#define QSFP_DD_OM3_LEN_OFFSET			(PAG01H_UPPER_OFFSET + 0x87)
-#define QSFP_DD_OM2_LEN_OFFSET			(PAG01H_UPPER_OFFSET + 0x88)
+#define QSFP_DD_SMF_LEN_OFFSET			0x84
+#define QSFP_DD_OM5_LEN_OFFSET			0x85
+#define QSFP_DD_OM4_LEN_OFFSET			0x86
+#define QSFP_DD_OM3_LEN_OFFSET			0x87
+#define QSFP_DD_OM2_LEN_OFFSET			0x88
 
 /* Wavelength (Page 1) */
-#define QSFP_DD_NOM_WAVELENGTH_MSB		(PAG01H_UPPER_OFFSET + 0x8A)
-#define QSFP_DD_NOM_WAVELENGTH_LSB		(PAG01H_UPPER_OFFSET + 0x8B)
-#define QSFP_DD_WAVELENGTH_TOL_MSB		(PAG01H_UPPER_OFFSET + 0x8C)
-#define QSFP_DD_WAVELENGTH_TOL_LSB		(PAG01H_UPPER_OFFSET + 0x8D)
+#define QSFP_DD_NOM_WAVELENGTH_MSB		0x8A
+#define QSFP_DD_NOM_WAVELENGTH_LSB		0x8B
+#define QSFP_DD_WAVELENGTH_TOL_MSB		0x8C
+#define QSFP_DD_WAVELENGTH_TOL_LSB		0x8D
 
 /* Signal integrity controls */
-#define QSFP_DD_SIG_INTEG_TX_OFFSET		(PAG01H_UPPER_OFFSET + 0xA1)
-#define QSFP_DD_SIG_INTEG_RX_OFFSET		(PAG01H_UPPER_OFFSET + 0xA2)
+#define QSFP_DD_SIG_INTEG_TX_OFFSET		0xA1
+#define QSFP_DD_SIG_INTEG_RX_OFFSET		0xA2
 
 #define YESNO(x) (((x) != 0) ? "Yes" : "No")
 #define ONOFF(x) (((x) != 0) ? "On" : "Off")
 
 void qsfp_dd_show_all(const __u8 *id);
 
+void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
+		   const struct ethtool_module_eeprom *page_one);
+
 #endif /* QSFP_DD_H__ */
diff --git a/qsfp.c b/qsfp.c
index 3c1a300..211c2df 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -672,38 +672,39 @@ static void sff8636_show_revision_compliance(const __u8 *id)
  * Second byte are 1/256th of degree, which are added to the dec part.
  */
 #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1])
 
-static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd)
+static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff_diags *sd)
 {
 	int i = 0;
 
 	/* Monitoring Thresholds for Alarms and Warnings */
-	sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF8636_VCC_CURR);
-	sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF8636_VCC_HALRM);
-	sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF8636_VCC_LALRM);
-	sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF8636_VCC_HWARN);
-	sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF8636_VCC_LWARN);
+	sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR);
+	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HALRM);
+	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LALRM);
+	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HWARN);
+	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LWARN);
 
 	sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR);
-	sd->sfp_temp[HALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HALRM);
-	sd->sfp_temp[LALRM] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LALRM);
-	sd->sfp_temp[HWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_HWARN);
-	sd->sfp_temp[LWARN] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_LWARN);
+	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HALRM);
+	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LALRM);
+	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HWARN);
+	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LWARN);
 
-	sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_HALRM);
-	sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF8636_TX_BIAS_LALRM);
-	sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_HWARN);
-	sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF8636_TX_BIAS_LWARN);
+	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HALRM);
+	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LALRM);
+	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HWARN);
+	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LWARN);
 
-	sd->tx_power[HALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_HALRM);
-	sd->tx_power[LALRM] = OFFSET_TO_U16(SFF8636_TX_PWR_LALRM);
-	sd->tx_power[HWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_HWARN);
-	sd->tx_power[LWARN] = OFFSET_TO_U16(SFF8636_TX_PWR_LWARN);
+	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HALRM);
+	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LALRM);
+	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HWARN);
+	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LWARN);
 
-	sd->rx_power[HALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_HALRM);
-	sd->rx_power[LALRM] = OFFSET_TO_U16(SFF8636_RX_PWR_LALRM);
-	sd->rx_power[HWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_HWARN);
-	sd->rx_power[LWARN] = OFFSET_TO_U16(SFF8636_RX_PWR_LWARN);
+	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HALRM);
+	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LALRM);
+	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HWARN);
+	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LWARN);
 
 
 	/* Channel Specific Data */
@@ -740,7 +741,7 @@ static void sff8636_dom_parse(const __u8 *id, struct sff_diags *sd)
 
 }
 
-static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
+static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eeprom_len)
 {
 	struct sff_diags sd = {0};
 	char *rx_power_string = NULL;
@@ -767,7 +768,7 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
 	sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] &
 						SFF8636_RX_PWR_TYPE_MASK;
 
-	sff8636_dom_parse(id, &sd);
+	sff8636_dom_parse(id, page_three, &sd);
 
 	PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
 	PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
@@ -818,6 +819,42 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
 	}
 }
 
+
+static void sff6836_show_page_zero(const __u8 *id)
+{
+	sff8636_show_ext_identifier(id);
+	sff8636_show_connector(id);
+	sff8636_show_transceiver(id);
+	sff8636_show_encoding(id);
+	sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET,
+			"BR, Nominal", 100, "Mbps");
+	sff8636_show_rate_identifier(id);
+	sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET,
+		     "Length (SMF,km)", 1, "km");
+	sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET,
+			"Length (OM3 50um)", 2, "m");
+	sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET,
+			"Length (OM2 50um)", 1, "m");
+	sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET,
+		     "Length (OM1 62.5um)", 1, "m");
+	sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET,
+		     "Length (Copper or Active cable)", 1, "m");
+	sff8636_show_wavelength_or_copper_compliance(id);
+	sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET,
+		       SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
+	sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
+	sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET,
+		       SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
+	sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
+		       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
+	sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
+		       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
+	sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
+		       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+	sff8636_show_revision_compliance(id);
+
+}
+
 void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 {
 	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
@@ -829,36 +866,17 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 	if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) {
-		sff8636_show_ext_identifier(id);
-		sff8636_show_connector(id);
-		sff8636_show_transceiver(id);
-		sff8636_show_encoding(id);
-		sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET,
-				"BR, Nominal", 100, "Mbps");
-		sff8636_show_rate_identifier(id);
-		sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET,
-			     "Length (SMF,km)", 1, "km");
-		sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET,
-				"Length (OM3 50um)", 2, "m");
-		sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET,
-				"Length (OM2 50um)", 1, "m");
-		sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET,
-			     "Length (OM1 62.5um)", 1, "m");
-		sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET,
-			     "Length (Copper or Active cable)", 1, "m");
-		sff8636_show_wavelength_or_copper_compliance(id);
-		sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET,
-			       SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
-		sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
-		sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET,
-			       SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
-		sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
-			       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
-		sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
-			       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
-		sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
-			       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
-		sff8636_show_revision_compliance(id);
-		sff8636_show_dom(id, eeprom_len);
+		sff6836_show_page_zero(id);
+		sff8636_show_dom(id, id + SFF8636_PAGE03H_OFFSET, eeprom_len);
 	}
 }
+
+void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero,
+			    const struct ethtool_module_eeprom *page_three)
+{
+	sff8636_show_identifier(page_zero->data);
+	sff6836_show_page_zero(page_zero->data);
+	if (page_three)
+		sff8636_show_dom(page_zero->data, page_three->data - 0x80,
+				 ETH_MODULE_SFF_8636_MAX_LEN);
+}
diff --git a/qsfp.h b/qsfp.h
index 9636b0c..1d8f24b 100644
--- a/qsfp.h
+++ b/qsfp.h
@@ -592,32 +592,35 @@
  * Offset - Page Num(3) * PageSize(0x80) + Page offset
  */
 
+/* 3 * 128 + Lower page 00h(128) */
+#define SFF8636_PAGE03H_OFFSET (128 * 4)
+
 /* Module Thresholds (48 Bytes) 128-175 */
 /* MSB at low address, LSB at high address */
-#define	SFF8636_TEMP_HALRM		0x200
-#define	SFF8636_TEMP_LALRM		0x202
-#define	SFF8636_TEMP_HWARN		0x204
-#define	SFF8636_TEMP_LWARN		0x206
-
-#define	SFF8636_VCC_HALRM		0x210
-#define	SFF8636_VCC_LALRM		0x212
-#define	SFF8636_VCC_HWARN		0x214
-#define	SFF8636_VCC_LWARN		0x216
-
-#define	SFF8636_RX_PWR_HALRM		0x230
-#define	SFF8636_RX_PWR_LALRM		0x232
-#define	SFF8636_RX_PWR_HWARN		0x234
-#define	SFF8636_RX_PWR_LWARN		0x236
-
-#define	SFF8636_TX_BIAS_HALRM		0x238
-#define	SFF8636_TX_BIAS_LALRM		0x23A
-#define	SFF8636_TX_BIAS_HWARN		0x23C
-#define	SFF8636_TX_BIAS_LWARN		0x23E
-
-#define	SFF8636_TX_PWR_HALRM		0x240
-#define	SFF8636_TX_PWR_LALRM		0x242
-#define	SFF8636_TX_PWR_HWARN		0x244
-#define	SFF8636_TX_PWR_LWARN		0x246
+#define	SFF8636_TEMP_HALRM	0x80
+#define	SFF8636_TEMP_LALRM	0x82
+#define	SFF8636_TEMP_HWARN	0x84
+#define	SFF8636_TEMP_LWARN	0x86
+
+#define	SFF8636_VCC_HALRM	0x90
+#define	SFF8636_VCC_LALRM	0x92
+#define	SFF8636_VCC_HWARN	0x94
+#define	SFF8636_VCC_LWARN	0x96
+
+#define	SFF8636_RX_PWR_HALRM	0xB0
+#define	SFF8636_RX_PWR_LALRM	0xB2
+#define	SFF8636_RX_PWR_HWARN	0xB4
+#define	SFF8636_RX_PWR_LWARN	0xB6
+
+#define	SFF8636_TX_BIAS_HALRM	0xB8
+#define	SFF8636_TX_BIAS_LALRM	0xBA
+#define	SFF8636_TX_BIAS_HWARN	0xBC
+#define	SFF8636_TX_BIAS_LWARN	0xBE
+
+#define	SFF8636_TX_PWR_HALRM	0xC0
+#define	SFF8636_TX_PWR_LALRM	0xC2
+#define	SFF8636_TX_PWR_HWARN	0xC4
+#define	SFF8636_TX_PWR_LWARN	0xC6
 
 #define	ETH_MODULE_SFF_8636_MAX_LEN	640
 #define	ETH_MODULE_SFF_8436_MAX_LEN	640
diff --git a/sff-common.c b/sff-common.c
index 5285645..2815951 100644
--- a/sff-common.c
+++ b/sff-common.c
@@ -139,6 +139,9 @@ void sff8024_show_identifier(const __u8 *id, int id_offset)
 	case SFF8024_ID_QSFP_DD:
 		printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n");
 		break;
+	case SFF8024_ID_DSFP:
+		printf(" (DSFP Dual Small Form Factor Pluggable Transceiver)\n");
+		break;
 	default:
 		printf(" (reserved or unknown)\n");
 		break;
diff --git a/sff-common.h b/sff-common.h
index cfb5d0e..2183f41 100644
--- a/sff-common.h
+++ b/sff-common.h
@@ -62,7 +62,8 @@
 #define  SFF8024_ID_CDFP_S3				0x16
 #define  SFF8024_ID_MICRO_QSFP			0x17
 #define  SFF8024_ID_QSFP_DD				0x18
-#define  SFF8024_ID_LAST				SFF8024_ID_QSFP_DD
+#define  SFF8024_ID_DSFP				0x1B
+#define  SFF8024_ID_LAST				SFF8024_ID_DSFP
 #define  SFF8024_ID_UNALLOCATED_LAST	0x7F
 #define  SFF8024_ID_VENDOR_START		0x80
 #define  SFF8024_ID_VENDOR_LAST			0xFF
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS
  2021-06-08 10:32 [PATCH ethtool v3 0/4] Extend module EEPROM API Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 1/4] ethtool: Add netlink handler for getmodule (-m) Moshe Shemesh
  2021-06-08 10:32 ` [PATCH ethtool v3 2/4] ethtool: Refactor human-readable module EEPROM output for new API Moshe Shemesh
@ 2021-06-08 10:32 ` Moshe Shemesh
  2021-06-13 14:20   ` Ido Schimmel
  2021-06-08 10:32 ` [PATCH ethtool v3 4/4] ethtool: Update manpages to reflect changes to getmodule (-m) command Moshe Shemesh
  3 siblings, 1 reply; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-08 10:32 UTC (permalink / raw)
  To: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger, netdev
  Cc: Vladyslav Tarasiuk, Moshe Shemesh

From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

QSFP-DD and DSFP EEPROM layout complies to CMIS specification. As DSFP
support is added, there are currently two standards, which share the
same infrastructure. Rename QSFP_DD and qsfp_dd occurrences to use CMIS
or cmis respectively to make function names generic for any module
compliant to CMIS 4.0 or future CMIS versions.

Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
 Makefile.am             |   2 +-
 cmis.c                  | 359 ++++++++++++++++++++++++++++++++++++++++++++++++
 cmis.h                  | 128 +++++++++++++++++
 netlink/module-eeprom.c |   2 +-
 qsfp-dd.c               | 359 ------------------------------------------------
 qsfp-dd.h               | 128 -----------------
 qsfp.c                  |   2 +-
 7 files changed, 490 insertions(+), 490 deletions(-)
 create mode 100644 cmis.c
 create mode 100644 cmis.h
 delete mode 100644 qsfp-dd.c
 delete mode 100644 qsfp-dd.h

diff --git a/Makefile.am b/Makefile.am
index 6abd2b7..73233fb 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,7 +17,7 @@ ethtool_SOURCES += \
 		  smsc911x.c at76c50x-usb.c sfc.c stmmac.c	\
 		  sff-common.c sff-common.h sfpid.c sfpdiag.c	\
 		  ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \
-		  igc.c qsfp-dd.c qsfp-dd.h bnxt.c
+		  igc.c cmis.c cmis.h bnxt.c
 endif
 
 if ENABLE_BASH_COMPLETION
diff --git a/cmis.c b/cmis.c
new file mode 100644
index 0000000..361b721
--- /dev/null
+++ b/cmis.c
@@ -0,0 +1,359 @@
+/**
+ * Description:
+ *
+ * This module adds QSFP-DD support to ethtool. The changes are similar to
+ * the ones already existing in qsfp.c, but customized to use the memory
+ * addresses and logic as defined in the specification's document.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include "internal.h"
+#include "sff-common.h"
+#include "cmis.h"
+
+static void cmis_show_identifier(const __u8 *id)
+{
+	sff8024_show_identifier(id, CMIS_ID_OFFSET);
+}
+
+static void cmis_show_connector(const __u8 *id)
+{
+	sff8024_show_connector(id, CMIS_CTOR_OFFSET);
+}
+
+static void cmis_show_oui(const __u8 *id)
+{
+	sff8024_show_oui(id, CMIS_VENDOR_OUI_OFFSET);
+}
+
+/**
+ * Print the revision compliance. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
+ * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
+ */
+static void cmis_show_rev_compliance(const __u8 *id)
+{
+	__u8 rev = id[CMIS_REV_COMPLIANCE_OFFSET];
+	int major = (rev >> 4) & 0x0F;
+	int minor = rev & 0x0F;
+
+	printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
+}
+
+/**
+ * Print information about the device's power consumption.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
+ * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
+ */
+static void cmis_show_power_info(const __u8 *id)
+{
+	float max_power = 0.0f;
+	__u8 base_power = 0;
+	__u8 power_class;
+
+	/* Get the power class (first 3 most significat bytes) */
+	power_class = (id[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07;
+
+	/* Get the base power in multiples of 0.25W */
+	base_power = id[CMIS_PWR_MAX_POWER_OFFSET];
+	max_power = base_power * 0.25f;
+
+	printf("\t%-41s : %d\n", "Power class", power_class + 1);
+	printf("\t%-41s : %.02fW\n", "Max power", max_power);
+}
+
+/**
+ * Print the cable assembly length, for both passive copper and active
+ * optical or electrical cables. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
+ * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
+ */
+static void cmis_show_cbl_asm_len(const __u8 *id)
+{
+	static const char *fn = "Cable assembly length";
+	float mul = 1.0f;
+	float val = 0.0f;
+
+	/* Check if max length */
+	if (id[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) {
+		printf("\t%-41s : > 6.3km\n", fn);
+		return;
+	}
+
+	/* Get the multiplier from the first two bits */
+	switch (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+	case CMIS_MULTIPLIER_00:
+		mul = 0.1f;
+		break;
+	case CMIS_MULTIPLIER_01:
+		mul = 1.0f;
+		break;
+	case CMIS_MULTIPLIER_10:
+		mul = 10.0f;
+		break;
+	case CMIS_MULTIPLIER_11:
+		mul = 100.0f;
+		break;
+	default:
+		break;
+	}
+
+	/* Get base value from first 6 bits and multiply by mul */
+	val = (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+	val = (float)val * mul;
+	printf("\t%-41s : %0.2fm\n", fn, val);
+}
+
+/**
+ * Print the length for SMF fiber. The base length (bits 5-0) must be
+ * multiplied with the SMF length multiplier (bits 7-6) to obtain the
+ * correct value. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
+ */
+static void cmis_print_smf_cbl_len(const __u8 *id)
+{
+	static const char *fn = "Length (SMF)";
+	float mul = 1.0f;
+	float val = 0.0f;
+
+	/* Get the multiplier from the first two bits */
+	switch (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+	case CMIS_MULTIPLIER_00:
+		mul = 0.1f;
+		break;
+	case CMIS_MULTIPLIER_01:
+		mul = 1.0f;
+		break;
+	default:
+		break;
+	}
+
+	/* Get base value from first 6 bits and multiply by mul */
+	val = (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+	val = (float)val * mul;
+	printf("\t%-41s : %0.2fkm\n", fn, val);
+}
+
+/**
+ * Print relevant signal integrity control properties. Relevant documents:
+ * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
+ * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
+ */
+static void cmis_show_sig_integrity(const __u8 *id)
+{
+	/* CDR Bypass control: 2nd bit from each byte */
+	printf("\t%-41s : ", "Tx CDR bypass control");
+	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x02));
+
+	printf("\t%-41s : ", "Rx CDR bypass control");
+	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x02));
+
+	/* CDR Implementation: 1st bit from each byte */
+	printf("\t%-41s : ", "Tx CDR");
+	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x01));
+
+	printf("\t%-41s : ", "Rx CDR");
+	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x01));
+}
+
+/**
+ * Print relevant media interface technology info. Relevant documents:
+ * [1] CMIS Rev. 3
+ * --> pag. 61, section 1.7.3.14, Table 36
+ * --> pag. 64, section 1.7.4.3, 1.7.4.4
+ * [2] CMIS Rev. 4
+ * --> pag. 97, section 8.3.14, Table 8-24
+ * --> pag. 98, section 8.4, Table 8-25
+ * --> page 100, section 8.4.3, 8.4.4
+ */
+static void cmis_show_mit_compliance(const __u8 *id)
+{
+	static const char *cc = " (Copper cable,";
+
+	printf("\t%-41s : 0x%02x", "Transmitter technology",
+	       id[CMIS_MEDIA_INTF_TECH_OFFSET]);
+
+	switch (id[CMIS_MEDIA_INTF_TECH_OFFSET]) {
+	case CMIS_850_VCSEL:
+		printf(" (850 nm VCSEL)\n");
+		break;
+	case CMIS_1310_VCSEL:
+		printf(" (1310 nm VCSEL)\n");
+		break;
+	case CMIS_1550_VCSEL:
+		printf(" (1550 nm VCSEL)\n");
+		break;
+	case CMIS_1310_FP:
+		printf(" (1310 nm FP)\n");
+		break;
+	case CMIS_1310_DFB:
+		printf(" (1310 nm DFB)\n");
+		break;
+	case CMIS_1550_DFB:
+		printf(" (1550 nm DFB)\n");
+		break;
+	case CMIS_1310_EML:
+		printf(" (1310 nm EML)\n");
+		break;
+	case CMIS_1550_EML:
+		printf(" (1550 nm EML)\n");
+		break;
+	case CMIS_OTHERS:
+		printf(" (Others/Undefined)\n");
+		break;
+	case CMIS_1490_DFB:
+		printf(" (1490 nm DFB)\n");
+		break;
+	case CMIS_COPPER_UNEQUAL:
+		printf("%s unequalized)\n", cc);
+		break;
+	case CMIS_COPPER_PASS_EQUAL:
+		printf("%s passive equalized)\n", cc);
+		break;
+	case CMIS_COPPER_NF_EQUAL:
+		printf("%s near and far end limiting active equalizers)\n", cc);
+		break;
+	case CMIS_COPPER_F_EQUAL:
+		printf("%s far end limiting active equalizers)\n", cc);
+		break;
+	case CMIS_COPPER_N_EQUAL:
+		printf("%s near end limiting active equalizers)\n", cc);
+		break;
+	case CMIS_COPPER_LINEAR_EQUAL:
+		printf("%s linear active equalizers)\n", cc);
+		break;
+	}
+
+	if (id[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) {
+		printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
+		       id[CMIS_COPPER_ATT_5GHZ]);
+		printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
+		       id[CMIS_COPPER_ATT_7GHZ]);
+		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+		       id[CMIS_COPPER_ATT_12P9GHZ]);
+		printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
+		       id[CMIS_COPPER_ATT_25P8GHZ]);
+	} else {
+		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+		       (((id[CMIS_NOM_WAVELENGTH_MSB] << 8) |
+				id[CMIS_NOM_WAVELENGTH_LSB]) * 0.05));
+		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+		       (((id[CMIS_WAVELENGTH_TOL_MSB] << 8) |
+		       id[CMIS_WAVELENGTH_TOL_LSB]) * 0.005));
+	}
+}
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte is a multiple of 1/256th of a degree, which is added to
+ * the dec part.
+ */
+#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+
+/**
+ * Print relevant module level monitoring values. Relevant documents:
+ * [1] CMIS Rev. 3:
+ * --> pag. 50, section 1.7.2.4, Table 22
+ *
+ * [2] CMIS Rev. 4:
+ * --> pag. 84, section 8.2.4, Table 8-6
+ */
+static void cmis_show_mod_lvl_monitors(const __u8 *id)
+{
+	PRINT_TEMP("Module temperature",
+		   OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET));
+	PRINT_VCC("Module voltage",
+		  OFFSET_TO_U16(CMIS_CURR_CURR_OFFSET));
+}
+
+static void cmis_show_link_len_from_page(const __u8 *page_one_data)
+{
+	cmis_print_smf_cbl_len(page_one_data);
+	sff_show_value_with_unit(page_one_data, CMIS_OM5_LEN_OFFSET,
+				 "Length (OM5)", 2, "m");
+	sff_show_value_with_unit(page_one_data, CMIS_OM4_LEN_OFFSET,
+				 "Length (OM4)", 2, "m");
+	sff_show_value_with_unit(page_one_data, CMIS_OM3_LEN_OFFSET,
+				 "Length (OM3 50/125um)", 2, "m");
+	sff_show_value_with_unit(page_one_data, CMIS_OM2_LEN_OFFSET,
+				 "Length (OM2 50/125um)", 1, "m");
+}
+
+
+/**
+ * Print relevant info about the maximum supported fiber media length
+ * for each type of fiber media at the maximum module-supported bit rate.
+ * Relevant documents:
+ * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
+ * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
+ */
+static void cmis_show_link_len(const __u8 *id)
+{
+	cmis_show_link_len_from_page(id + PAG01H_UPPER_OFFSET);
+}
+
+/**
+ * Show relevant information about the vendor. Relevant documents:
+ * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
+ * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
+ */
+static void cmis_show_vendor_info(const __u8 *id)
+{
+	sff_show_ascii(id, CMIS_VENDOR_NAME_START_OFFSET,
+		       CMIS_VENDOR_NAME_END_OFFSET, "Vendor name");
+	cmis_show_oui(id);
+	sff_show_ascii(id, CMIS_VENDOR_PN_START_OFFSET,
+		       CMIS_VENDOR_PN_END_OFFSET, "Vendor PN");
+	sff_show_ascii(id, CMIS_VENDOR_REV_START_OFFSET,
+		       CMIS_VENDOR_REV_END_OFFSET, "Vendor rev");
+	sff_show_ascii(id, CMIS_VENDOR_SN_START_OFFSET,
+		       CMIS_VENDOR_SN_END_OFFSET, "Vendor SN");
+	sff_show_ascii(id, CMIS_DATE_YEAR_OFFSET,
+		       CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+
+	if (id[CMIS_CLEI_PRESENT_BYTE] & CMIS_CLEI_PRESENT_MASK)
+		sff_show_ascii(id, CMIS_CLEI_START_OFFSET,
+			       CMIS_CLEI_END_OFFSET, "CLEI code");
+}
+
+void qsfp_dd_show_all(const __u8 *id)
+{
+	cmis_show_identifier(id);
+	cmis_show_power_info(id);
+	cmis_show_connector(id);
+	cmis_show_cbl_asm_len(id);
+	cmis_show_sig_integrity(id);
+	cmis_show_mit_compliance(id);
+	cmis_show_mod_lvl_monitors(id);
+	cmis_show_link_len(id);
+	cmis_show_vendor_info(id);
+	cmis_show_rev_compliance(id);
+}
+
+void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
+		   const struct ethtool_module_eeprom *page_one)
+{
+	const __u8 *page_zero_data = page_zero->data;
+
+	cmis_show_identifier(page_zero_data);
+	cmis_show_power_info(page_zero_data);
+	cmis_show_connector(page_zero_data);
+	cmis_show_cbl_asm_len(page_zero_data);
+	cmis_show_sig_integrity(page_zero_data);
+	cmis_show_mit_compliance(page_zero_data);
+	cmis_show_mod_lvl_monitors(page_zero_data);
+
+	if (page_one)
+		cmis_show_link_len_from_page(page_one->data - 0x80);
+
+	cmis_show_vendor_info(page_zero_data);
+	cmis_show_rev_compliance(page_zero_data);
+}
diff --git a/cmis.h b/cmis.h
new file mode 100644
index 0000000..5b7ac38
--- /dev/null
+++ b/cmis.h
@@ -0,0 +1,128 @@
+#ifndef CMIS_H__
+#define CMIS_H__
+
+/* Identifier and revision compliance (Page 0) */
+#define CMIS_ID_OFFSET				0x00
+#define CMIS_REV_COMPLIANCE_OFFSET		0x01
+
+#define CMIS_MODULE_TYPE_OFFSET			0x55
+#define CMIS_MT_MMF				0x01
+#define CMIS_MT_SMF				0x02
+
+/* Module-Level Monitors (Page 0) */
+#define CMIS_CURR_TEMP_OFFSET			0x0E
+#define CMIS_CURR_CURR_OFFSET			0x10
+
+#define CMIS_CTOR_OFFSET			0xCB
+
+/* Vendor related information (Page 0) */
+#define CMIS_VENDOR_NAME_START_OFFSET		0x81
+#define CMIS_VENDOR_NAME_END_OFFSET		0x90
+
+#define CMIS_VENDOR_OUI_OFFSET			0x91
+
+#define CMIS_VENDOR_PN_START_OFFSET		0x94
+#define CMIS_VENDOR_PN_END_OFFSET		0xA3
+
+#define CMIS_VENDOR_REV_START_OFFSET		0xA4
+#define CMIS_VENDOR_REV_END_OFFSET		0xA5
+
+#define CMIS_VENDOR_SN_START_OFFSET		0xA6
+#define CMIS_VENDOR_SN_END_OFFSET		0xB5
+
+#define CMIS_DATE_YEAR_OFFSET			0xB6
+#define CMIS_DATE_VENDOR_LOT_OFFSET		0xBC
+
+/* CLEI Code (Page 0) */
+#define CMIS_CLEI_PRESENT_BYTE			0x02
+#define CMIS_CLEI_PRESENT_MASK			0x20
+#define CMIS_CLEI_START_OFFSET			0xBE
+#define CMIS_CLEI_END_OFFSET			0xC7
+
+/* Cable assembly length */
+#define CMIS_CBL_ASM_LEN_OFFSET			0xCA
+#define CMIS_6300M_MAX_LEN			0xFF
+
+/* Cable length with multiplier */
+#define CMIS_MULTIPLIER_00			0x00
+#define CMIS_MULTIPLIER_01			0x40
+#define CMIS_MULTIPLIER_10			0x80
+#define CMIS_MULTIPLIER_11			0xC0
+#define CMIS_LEN_MUL_MASK			0xC0
+#define CMIS_LEN_VAL_MASK			0x3F
+
+/* Module power characteristics */
+#define CMIS_PWR_CLASS_OFFSET			0xC8
+#define CMIS_PWR_MAX_POWER_OFFSET		0xC9
+#define CMIS_PWR_CLASS_MASK			0xE0
+#define CMIS_PWR_CLASS_1			0x00
+#define CMIS_PWR_CLASS_2			0x01
+#define CMIS_PWR_CLASS_3			0x02
+#define CMIS_PWR_CLASS_4			0x03
+#define CMIS_PWR_CLASS_5			0x04
+#define CMIS_PWR_CLASS_6			0x05
+#define CMIS_PWR_CLASS_7			0x06
+#define CMIS_PWR_CLASS_8			0x07
+
+/* Copper cable attenuation */
+#define CMIS_COPPER_ATT_5GHZ			0xCC
+#define CMIS_COPPER_ATT_7GHZ			0xCD
+#define CMIS_COPPER_ATT_12P9GHZ			0xCE
+#define CMIS_COPPER_ATT_25P8GHZ			0xCF
+
+/* Cable assembly lane */
+#define CMIS_CABLE_ASM_NEAR_END_OFFSET		0xD2
+#define CMIS_CABLE_ASM_FAR_END_OFFSET		0xD3
+
+/* Media interface technology */
+#define CMIS_MEDIA_INTF_TECH_OFFSET		0xD4
+#define CMIS_850_VCSEL				0x00
+#define CMIS_1310_VCSEL				0x01
+#define CMIS_1550_VCSEL				0x02
+#define CMIS_1310_FP				0x03
+#define CMIS_1310_DFB				0x04
+#define CMIS_1550_DFB				0x05
+#define CMIS_1310_EML				0x06
+#define CMIS_1550_EML				0x07
+#define CMIS_OTHERS				0x08
+#define CMIS_1490_DFB				0x09
+#define CMIS_COPPER_UNEQUAL			0x0A
+#define CMIS_COPPER_PASS_EQUAL			0x0B
+#define CMIS_COPPER_NF_EQUAL			0x0C
+#define CMIS_COPPER_F_EQUAL			0x0D
+#define CMIS_COPPER_N_EQUAL			0x0E
+#define CMIS_COPPER_LINEAR_EQUAL		0x0F
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x01: contains advertising fields that define properties
+ * that are unique to active modules and cable assemblies.
+ * GlobalOffset = 2 * 0x80 + LocalOffset
+ */
+#define PAG01H_UPPER_OFFSET			(0x02 * 0x80)
+
+/* Supported Link Length (Page 1) */
+#define CMIS_SMF_LEN_OFFSET			0x84
+#define CMIS_OM5_LEN_OFFSET			0x85
+#define CMIS_OM4_LEN_OFFSET			0x86
+#define CMIS_OM3_LEN_OFFSET			0x87
+#define CMIS_OM2_LEN_OFFSET			0x88
+
+/* Wavelength (Page 1) */
+#define CMIS_NOM_WAVELENGTH_MSB			0x8A
+#define CMIS_NOM_WAVELENGTH_LSB			0x8B
+#define CMIS_WAVELENGTH_TOL_MSB			0x8C
+#define CMIS_WAVELENGTH_TOL_LSB			0x8D
+
+/* Signal integrity controls */
+#define CMIS_SIG_INTEG_TX_OFFSET		0xA1
+#define CMIS_SIG_INTEG_RX_OFFSET		0xA2
+
+#define YESNO(x) (((x) != 0) ? "Yes" : "No")
+#define ONOFF(x) (((x) != 0) ? "On" : "Off")
+
+void qsfp_dd_show_all(const __u8 *id);
+
+void cmis4_show_all(const struct ethtool_module_eeprom *page_zero,
+		    const struct ethtool_module_eeprom *page_one);
+
+#endif /* CMIS_H__ */
diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
index 664f5c6..355d1de 100644
--- a/netlink/module-eeprom.c
+++ b/netlink/module-eeprom.c
@@ -11,7 +11,7 @@
 
 #include "../sff-common.h"
 #include "../qsfp.h"
-#include "../qsfp-dd.h"
+#include "../cmis.h"
 #include "../internal.h"
 #include "../common.h"
 #include "../list.h"
diff --git a/qsfp-dd.c b/qsfp-dd.c
deleted file mode 100644
index 55adbcb..0000000
--- a/qsfp-dd.c
+++ /dev/null
@@ -1,359 +0,0 @@
-/**
- * Description:
- *
- * This module adds QSFP-DD support to ethtool. The changes are similar to
- * the ones already existing in qsfp.c, but customized to use the memory
- * addresses and logic as defined in the specification's document.
- *
- */
-
-#include <stdio.h>
-#include <math.h>
-#include "internal.h"
-#include "sff-common.h"
-#include "qsfp-dd.h"
-
-static void qsfp_dd_show_identifier(const __u8 *id)
-{
-	sff8024_show_identifier(id, QSFP_DD_ID_OFFSET);
-}
-
-static void qsfp_dd_show_connector(const __u8 *id)
-{
-	sff8024_show_connector(id, QSFP_DD_CTOR_OFFSET);
-}
-
-static void qsfp_dd_show_oui(const __u8 *id)
-{
-	sff8024_show_oui(id, QSFP_DD_VENDOR_OUI_OFFSET);
-}
-
-/**
- * Print the revision compliance. Relevant documents:
- * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
- * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
- */
-static void qsfp_dd_show_rev_compliance(const __u8 *id)
-{
-	__u8 rev = id[QSFP_DD_REV_COMPLIANCE_OFFSET];
-	int major = (rev >> 4) & 0x0F;
-	int minor = rev & 0x0F;
-
-	printf("\t%-41s : Rev. %d.%d\n", "Revision compliance", major, minor);
-}
-
-/**
- * Print information about the device's power consumption.
- * Relevant documents:
- * [1] CMIS Rev. 3, pag. 59, section 1.7.3.9, Table 30
- * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
- * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
- */
-static void qsfp_dd_show_power_info(const __u8 *id)
-{
-	float max_power = 0.0f;
-	__u8 base_power = 0;
-	__u8 power_class;
-
-	/* Get the power class (first 3 most significat bytes) */
-	power_class = (id[QSFP_DD_PWR_CLASS_OFFSET] >> 5) & 0x07;
-
-	/* Get the base power in multiples of 0.25W */
-	base_power = id[QSFP_DD_PWR_MAX_POWER_OFFSET];
-	max_power = base_power * 0.25f;
-
-	printf("\t%-41s : %d\n", "Power class", power_class + 1);
-	printf("\t%-41s : %.02fW\n", "Max power", max_power);
-}
-
-/**
- * Print the cable assembly length, for both passive copper and active
- * optical or electrical cables. The base length (bits 5-0) must be
- * multiplied with the SMF length multiplier (bits 7-6) to obtain the
- * correct value. Relevant documents:
- * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
- * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
- */
-static void qsfp_dd_show_cbl_asm_len(const __u8 *id)
-{
-	static const char *fn = "Cable assembly length";
-	float mul = 1.0f;
-	float val = 0.0f;
-
-	/* Check if max length */
-	if (id[QSFP_DD_CBL_ASM_LEN_OFFSET] == QSFP_DD_6300M_MAX_LEN) {
-		printf("\t%-41s : > 6.3km\n", fn);
-		return;
-	}
-
-	/* Get the multiplier from the first two bits */
-	switch (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
-	case QSFP_DD_MULTIPLIER_00:
-		mul = 0.1f;
-		break;
-	case QSFP_DD_MULTIPLIER_01:
-		mul = 1.0f;
-		break;
-	case QSFP_DD_MULTIPLIER_10:
-		mul = 10.0f;
-		break;
-	case QSFP_DD_MULTIPLIER_11:
-		mul = 100.0f;
-		break;
-	default:
-		break;
-	}
-
-	/* Get base value from first 6 bits and multiply by mul */
-	val = (id[QSFP_DD_CBL_ASM_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
-	val = (float)val * mul;
-	printf("\t%-41s : %0.2fm\n", fn, val);
-}
-
-/**
- * Print the length for SMF fiber. The base length (bits 5-0) must be
- * multiplied with the SMF length multiplier (bits 7-6) to obtain the
- * correct value. Relevant documents:
- * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
- * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
- */
-static void qsfp_dd_print_smf_cbl_len(const __u8 *id)
-{
-	static const char *fn = "Length (SMF)";
-	float mul = 1.0f;
-	float val = 0.0f;
-
-	/* Get the multiplier from the first two bits */
-	switch (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_MUL_MASK) {
-	case QSFP_DD_MULTIPLIER_00:
-		mul = 0.1f;
-		break;
-	case QSFP_DD_MULTIPLIER_01:
-		mul = 1.0f;
-		break;
-	default:
-		break;
-	}
-
-	/* Get base value from first 6 bits and multiply by mul */
-	val = (id[QSFP_DD_SMF_LEN_OFFSET] & QSFP_DD_LEN_VAL_MASK);
-	val = (float)val * mul;
-	printf("\t%-41s : %0.2fkm\n", fn, val);
-}
-
-/**
- * Print relevant signal integrity control properties. Relevant documents:
- * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
- * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
- */
-static void qsfp_dd_show_sig_integrity(const __u8 *id)
-{
-	/* CDR Bypass control: 2nd bit from each byte */
-	printf("\t%-41s : ", "Tx CDR bypass control");
-	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x02));
-
-	printf("\t%-41s : ", "Rx CDR bypass control");
-	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x02));
-
-	/* CDR Implementation: 1st bit from each byte */
-	printf("\t%-41s : ", "Tx CDR");
-	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_TX_OFFSET] & 0x01));
-
-	printf("\t%-41s : ", "Rx CDR");
-	printf("%s\n", YESNO(id[QSFP_DD_SIG_INTEG_RX_OFFSET] & 0x01));
-}
-
-/**
- * Print relevant media interface technology info. Relevant documents:
- * [1] CMIS Rev. 3
- * --> pag. 61, section 1.7.3.14, Table 36
- * --> pag. 64, section 1.7.4.3, 1.7.4.4
- * [2] CMIS Rev. 4
- * --> pag. 97, section 8.3.14, Table 8-24
- * --> pag. 98, section 8.4, Table 8-25
- * --> page 100, section 8.4.3, 8.4.4
- */
-static void qsfp_dd_show_mit_compliance(const __u8 *id)
-{
-	static const char *cc = " (Copper cable,";
-
-	printf("\t%-41s : 0x%02x", "Transmitter technology",
-	       id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]);
-
-	switch (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET]) {
-	case QSFP_DD_850_VCSEL:
-		printf(" (850 nm VCSEL)\n");
-		break;
-	case QSFP_DD_1310_VCSEL:
-		printf(" (1310 nm VCSEL)\n");
-		break;
-	case QSFP_DD_1550_VCSEL:
-		printf(" (1550 nm VCSEL)\n");
-		break;
-	case QSFP_DD_1310_FP:
-		printf(" (1310 nm FP)\n");
-		break;
-	case QSFP_DD_1310_DFB:
-		printf(" (1310 nm DFB)\n");
-		break;
-	case QSFP_DD_1550_DFB:
-		printf(" (1550 nm DFB)\n");
-		break;
-	case QSFP_DD_1310_EML:
-		printf(" (1310 nm EML)\n");
-		break;
-	case QSFP_DD_1550_EML:
-		printf(" (1550 nm EML)\n");
-		break;
-	case QSFP_DD_OTHERS:
-		printf(" (Others/Undefined)\n");
-		break;
-	case QSFP_DD_1490_DFB:
-		printf(" (1490 nm DFB)\n");
-		break;
-	case QSFP_DD_COPPER_UNEQUAL:
-		printf("%s unequalized)\n", cc);
-		break;
-	case QSFP_DD_COPPER_PASS_EQUAL:
-		printf("%s passive equalized)\n", cc);
-		break;
-	case QSFP_DD_COPPER_NF_EQUAL:
-		printf("%s near and far end limiting active equalizers)\n", cc);
-		break;
-	case QSFP_DD_COPPER_F_EQUAL:
-		printf("%s far end limiting active equalizers)\n", cc);
-		break;
-	case QSFP_DD_COPPER_N_EQUAL:
-		printf("%s near end limiting active equalizers)\n", cc);
-		break;
-	case QSFP_DD_COPPER_LINEAR_EQUAL:
-		printf("%s linear active equalizers)\n", cc);
-		break;
-	}
-
-	if (id[QSFP_DD_MEDIA_INTF_TECH_OFFSET] >= QSFP_DD_COPPER_UNEQUAL) {
-		printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
-		       id[QSFP_DD_COPPER_ATT_5GHZ]);
-		printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
-		       id[QSFP_DD_COPPER_ATT_7GHZ]);
-		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
-		       id[QSFP_DD_COPPER_ATT_12P9GHZ]);
-		printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
-		       id[QSFP_DD_COPPER_ATT_25P8GHZ]);
-	} else {
-		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
-		       (((id[QSFP_DD_NOM_WAVELENGTH_MSB] << 8) |
-				id[QSFP_DD_NOM_WAVELENGTH_LSB]) * 0.05));
-		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
-		       (((id[QSFP_DD_WAVELENGTH_TOL_MSB] << 8) |
-		       id[QSFP_DD_WAVELENGTH_TOL_LSB]) * 0.005));
-	}
-}
-
-/*
- * 2-byte internal temperature conversions:
- * First byte is a signed 8-bit integer, which is the temp decimal part
- * Second byte is a multiple of 1/256th of a degree, which is added to
- * the dec part.
- */
-#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
-
-/**
- * Print relevant module level monitoring values. Relevant documents:
- * [1] CMIS Rev. 3:
- * --> pag. 50, section 1.7.2.4, Table 22
- *
- * [2] CMIS Rev. 4:
- * --> pag. 84, section 8.2.4, Table 8-6
- */
-static void qsfp_dd_show_mod_lvl_monitors(const __u8 *id)
-{
-	PRINT_TEMP("Module temperature",
-		   OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET));
-	PRINT_VCC("Module voltage",
-		  OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
-}
-
-static void qsfp_dd_show_link_len_from_page(const __u8 *page_one_data)
-{
-	qsfp_dd_print_smf_cbl_len(page_one_data);
-	sff_show_value_with_unit(page_one_data, QSFP_DD_OM5_LEN_OFFSET,
-				 "Length (OM5)", 2, "m");
-	sff_show_value_with_unit(page_one_data, QSFP_DD_OM4_LEN_OFFSET,
-				 "Length (OM4)", 2, "m");
-	sff_show_value_with_unit(page_one_data, QSFP_DD_OM3_LEN_OFFSET,
-				 "Length (OM3 50/125um)", 2, "m");
-	sff_show_value_with_unit(page_one_data, QSFP_DD_OM2_LEN_OFFSET,
-				 "Length (OM2 50/125um)", 1, "m");
-}
-
-
-/**
- * Print relevant info about the maximum supported fiber media length
- * for each type of fiber media at the maximum module-supported bit rate.
- * Relevant documents:
- * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
- * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
- */
-static void qsfp_dd_show_link_len(const __u8 *id)
-{
-	qsfp_dd_show_link_len_from_page(id + PAG01H_UPPER_OFFSET);
-}
-
-/**
- * Show relevant information about the vendor. Relevant documents:
- * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
- * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
- */
-static void qsfp_dd_show_vendor_info(const __u8 *id)
-{
-	sff_show_ascii(id, QSFP_DD_VENDOR_NAME_START_OFFSET,
-		       QSFP_DD_VENDOR_NAME_END_OFFSET, "Vendor name");
-	qsfp_dd_show_oui(id);
-	sff_show_ascii(id, QSFP_DD_VENDOR_PN_START_OFFSET,
-		       QSFP_DD_VENDOR_PN_END_OFFSET, "Vendor PN");
-	sff_show_ascii(id, QSFP_DD_VENDOR_REV_START_OFFSET,
-		       QSFP_DD_VENDOR_REV_END_OFFSET, "Vendor rev");
-	sff_show_ascii(id, QSFP_DD_VENDOR_SN_START_OFFSET,
-		       QSFP_DD_VENDOR_SN_END_OFFSET, "Vendor SN");
-	sff_show_ascii(id, QSFP_DD_DATE_YEAR_OFFSET,
-		       QSFP_DD_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
-
-	if (id[QSFP_DD_CLEI_PRESENT_BYTE] & QSFP_DD_CLEI_PRESENT_MASK)
-		sff_show_ascii(id, QSFP_DD_CLEI_START_OFFSET,
-			       QSFP_DD_CLEI_END_OFFSET, "CLEI code");
-}
-
-void qsfp_dd_show_all(const __u8 *id)
-{
-	qsfp_dd_show_identifier(id);
-	qsfp_dd_show_power_info(id);
-	qsfp_dd_show_connector(id);
-	qsfp_dd_show_cbl_asm_len(id);
-	qsfp_dd_show_sig_integrity(id);
-	qsfp_dd_show_mit_compliance(id);
-	qsfp_dd_show_mod_lvl_monitors(id);
-	qsfp_dd_show_link_len(id);
-	qsfp_dd_show_vendor_info(id);
-	qsfp_dd_show_rev_compliance(id);
-}
-
-void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
-		   const struct ethtool_module_eeprom *page_one)
-{
-	const __u8 *page_zero_data = page_zero->data;
-
-	qsfp_dd_show_identifier(page_zero_data);
-	qsfp_dd_show_power_info(page_zero_data);
-	qsfp_dd_show_connector(page_zero_data);
-	qsfp_dd_show_cbl_asm_len(page_zero_data);
-	qsfp_dd_show_sig_integrity(page_zero_data);
-	qsfp_dd_show_mit_compliance(page_zero_data);
-	qsfp_dd_show_mod_lvl_monitors(page_zero_data);
-
-	if (page_one)
-		qsfp_dd_show_link_len_from_page(page_one->data);
-
-	qsfp_dd_show_vendor_info(page_zero_data);
-	qsfp_dd_show_rev_compliance(page_zero_data);
-}
diff --git a/qsfp-dd.h b/qsfp-dd.h
deleted file mode 100644
index 51f92fa..0000000
--- a/qsfp-dd.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#ifndef QSFP_DD_H__
-#define QSFP_DD_H__
-
-/* Identifier and revision compliance (Page 0) */
-#define QSFP_DD_ID_OFFSET			0x00
-#define QSFP_DD_REV_COMPLIANCE_OFFSET		0x01
-
-#define QSFP_DD_MODULE_TYPE_OFFSET		0x55
-#define QSFP_DD_MT_MMF				0x01
-#define QSFP_DD_MT_SMF				0x02
-
-/* Module-Level Monitors (Page 0) */
-#define QSFP_DD_CURR_TEMP_OFFSET		0x0E
-#define QSFP_DD_CURR_CURR_OFFSET		0x10
-
-#define QSFP_DD_CTOR_OFFSET			0xCB
-
-/* Vendor related information (Page 0) */
-#define QSFP_DD_VENDOR_NAME_START_OFFSET	0x81
-#define QSFP_DD_VENDOR_NAME_END_OFFSET		0x90
-
-#define QSFP_DD_VENDOR_OUI_OFFSET		0x91
-
-#define QSFP_DD_VENDOR_PN_START_OFFSET		0x94
-#define QSFP_DD_VENDOR_PN_END_OFFSET		0xA3
-
-#define QSFP_DD_VENDOR_REV_START_OFFSET		0xA4
-#define QSFP_DD_VENDOR_REV_END_OFFSET		0xA5
-
-#define QSFP_DD_VENDOR_SN_START_OFFSET		0xA6
-#define QSFP_DD_VENDOR_SN_END_OFFSET		0xB5
-
-#define QSFP_DD_DATE_YEAR_OFFSET		0xB6
-#define QSFP_DD_DATE_VENDOR_LOT_OFFSET		0xBC
-
-/* CLEI Code (Page 0) */
-#define QSFP_DD_CLEI_PRESENT_BYTE		0x02
-#define QSFP_DD_CLEI_PRESENT_MASK		0x20
-#define QSFP_DD_CLEI_START_OFFSET		0xBE
-#define QSFP_DD_CLEI_END_OFFSET			0xC7
-
-/* Cable assembly length */
-#define QSFP_DD_CBL_ASM_LEN_OFFSET		0xCA
-#define QSFP_DD_6300M_MAX_LEN			0xFF
-
-/* Cable length with multiplier */
-#define QSFP_DD_MULTIPLIER_00			0x00
-#define QSFP_DD_MULTIPLIER_01			0x40
-#define QSFP_DD_MULTIPLIER_10			0x80
-#define QSFP_DD_MULTIPLIER_11			0xC0
-#define QSFP_DD_LEN_MUL_MASK			0xC0
-#define QSFP_DD_LEN_VAL_MASK			0x3F
-
-/* Module power characteristics */
-#define QSFP_DD_PWR_CLASS_OFFSET		0xC8
-#define QSFP_DD_PWR_MAX_POWER_OFFSET		0xC9
-#define QSFP_DD_PWR_CLASS_MASK			0xE0
-#define QSFP_DD_PWR_CLASS_1			0x00
-#define QSFP_DD_PWR_CLASS_2			0x01
-#define QSFP_DD_PWR_CLASS_3			0x02
-#define QSFP_DD_PWR_CLASS_4			0x03
-#define QSFP_DD_PWR_CLASS_5			0x04
-#define QSFP_DD_PWR_CLASS_6			0x05
-#define QSFP_DD_PWR_CLASS_7			0x06
-#define QSFP_DD_PWR_CLASS_8			0x07
-
-/* Copper cable attenuation */
-#define QSFP_DD_COPPER_ATT_5GHZ			0xCC
-#define QSFP_DD_COPPER_ATT_7GHZ			0xCD
-#define QSFP_DD_COPPER_ATT_12P9GHZ		0xCE
-#define QSFP_DD_COPPER_ATT_25P8GHZ		0xCF
-
-/* Cable assembly lane */
-#define QSFP_DD_CABLE_ASM_NEAR_END_OFFSET	0xD2
-#define QSFP_DD_CABLE_ASM_FAR_END_OFFSET	0xD3
-
-/* Media interface technology */
-#define QSFP_DD_MEDIA_INTF_TECH_OFFSET		0xD4
-#define QSFP_DD_850_VCSEL			0x00
-#define QSFP_DD_1310_VCSEL			0x01
-#define QSFP_DD_1550_VCSEL			0x02
-#define QSFP_DD_1310_FP				0x03
-#define QSFP_DD_1310_DFB			0x04
-#define QSFP_DD_1550_DFB			0x05
-#define QSFP_DD_1310_EML			0x06
-#define QSFP_DD_1550_EML			0x07
-#define QSFP_DD_OTHERS				0x08
-#define QSFP_DD_1490_DFB			0x09
-#define QSFP_DD_COPPER_UNEQUAL			0x0A
-#define QSFP_DD_COPPER_PASS_EQUAL		0x0B
-#define QSFP_DD_COPPER_NF_EQUAL			0x0C
-#define QSFP_DD_COPPER_F_EQUAL			0x0D
-#define QSFP_DD_COPPER_N_EQUAL			0x0E
-#define QSFP_DD_COPPER_LINEAR_EQUAL		0x0F
-
-/*-----------------------------------------------------------------------
- * Upper Memory Page 0x01: contains advertising fields that define properties
- * that are unique to active modules and cable assemblies.
- * GlobalOffset = 2 * 0x80 + LocalOffset
- */
-#define PAG01H_UPPER_OFFSET			(0x02 * 0x80)
-
-/* Supported Link Length (Page 1) */
-#define QSFP_DD_SMF_LEN_OFFSET			0x84
-#define QSFP_DD_OM5_LEN_OFFSET			0x85
-#define QSFP_DD_OM4_LEN_OFFSET			0x86
-#define QSFP_DD_OM3_LEN_OFFSET			0x87
-#define QSFP_DD_OM2_LEN_OFFSET			0x88
-
-/* Wavelength (Page 1) */
-#define QSFP_DD_NOM_WAVELENGTH_MSB		0x8A
-#define QSFP_DD_NOM_WAVELENGTH_LSB		0x8B
-#define QSFP_DD_WAVELENGTH_TOL_MSB		0x8C
-#define QSFP_DD_WAVELENGTH_TOL_LSB		0x8D
-
-/* Signal integrity controls */
-#define QSFP_DD_SIG_INTEG_TX_OFFSET		0xA1
-#define QSFP_DD_SIG_INTEG_RX_OFFSET		0xA2
-
-#define YESNO(x) (((x) != 0) ? "Yes" : "No")
-#define ONOFF(x) (((x) != 0) ? "On" : "Off")
-
-void qsfp_dd_show_all(const __u8 *id);
-
-void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
-		   const struct ethtool_module_eeprom *page_one);
-
-#endif /* QSFP_DD_H__ */
diff --git a/qsfp.c b/qsfp.c
index 211c2df..644fe14 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -58,7 +58,7 @@
 #include "internal.h"
 #include "sff-common.h"
 #include "qsfp.h"
-#include "qsfp-dd.h"
+#include "cmis.h"
 
 #define MAX_DESC_SIZE	42
 
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH ethtool v3 4/4] ethtool: Update manpages to reflect changes to getmodule (-m) command
  2021-06-08 10:32 [PATCH ethtool v3 0/4] Extend module EEPROM API Moshe Shemesh
                   ` (2 preceding siblings ...)
  2021-06-08 10:32 ` [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS Moshe Shemesh
@ 2021-06-08 10:32 ` Moshe Shemesh
  3 siblings, 0 replies; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-08 10:32 UTC (permalink / raw)
  To: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger, netdev
  Cc: Vladyslav Tarasiuk, Moshe Shemesh

From: Vladyslav Tarasiuk <vladyslavt@nvidia.com>

Add page, bank and i2c parameters and mention change in offset and
length treatment if either one of new parameters is specified by the
user.

Signed-off-by: Vladyslav Tarasiuk <vladyslavt@nvidia.com>
Reviewed-by: Moshe Shemesh <moshe@nvidia.com>
---
 ethtool.8.in | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/ethtool.8.in b/ethtool.8.in
index 5afde0d..115322a 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -365,6 +365,9 @@ ethtool \- query or control network driver and hardware settings
 .B2 hex on off
 .BN offset
 .BN length
+.BN page
+.BN bank
+.BN i2c
 .HP
 .B ethtool \-\-show\-priv\-flags
 .I devname
@@ -1173,6 +1176,17 @@ Changes the number of multi-purpose channels.
 Retrieves and if possible decodes the EEPROM from plugin modules, e.g SFP+, QSFP.
 If the driver and module support it, the optical diagnostic information is also
 read and decoded.
+When either one of
+.I page,
+.I bank
+or
+.I i2c
+parameters is specified, dumps only of a single page or its portion is
+allowed. In such a case
+.I offset
+and
+.I length
+parameters are treated relatively to EEPROM page boundaries.
 .TP
 .B \-\-show\-priv\-flags
 Queries the specified network device for its private flags.  The
-- 
1.8.2.3


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS
  2021-06-08 10:32 ` [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS Moshe Shemesh
@ 2021-06-13 14:20   ` Ido Schimmel
  2021-06-15 16:12     ` Moshe Shemesh
  0 siblings, 1 reply; 7+ messages in thread
From: Ido Schimmel @ 2021-06-13 14:20 UTC (permalink / raw)
  To: Moshe Shemesh
  Cc: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger,
	netdev, Vladyslav Tarasiuk

On Tue, Jun 08, 2021 at 01:32:27PM +0300, Moshe Shemesh wrote:
> +void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
> +		   const struct ethtool_module_eeprom *page_one)
> +{
> +	const __u8 *page_zero_data = page_zero->data;
> +
> +	cmis_show_identifier(page_zero_data);
> +	cmis_show_power_info(page_zero_data);
> +	cmis_show_connector(page_zero_data);
> +	cmis_show_cbl_asm_len(page_zero_data);
> +	cmis_show_sig_integrity(page_zero_data);
> +	cmis_show_mit_compliance(page_zero_data);
> +	cmis_show_mod_lvl_monitors(page_zero_data);
> +
> +	if (page_one)
> +		cmis_show_link_len_from_page(page_one->data - 0x80);
> +
> +	cmis_show_vendor_info(page_zero_data);
> +	cmis_show_rev_compliance(page_zero_data);
> +}
> diff --git a/cmis.h b/cmis.h
> new file mode 100644
> index 0000000..5b7ac38
> --- /dev/null
> +++ b/cmis.h
> @@ -0,0 +1,128 @@

[...]

> +void cmis4_show_all(const struct ethtool_module_eeprom *page_zero,
> +		    const struct ethtool_module_eeprom *page_one);

Should be cmis_show_all():

netlink/module-eeprom.c:335:17: warning: implicit declaration of function ‘cmis_show_all’; did you mean ‘cmis4_show_all’? [-Wimplicit-function-declaration]
  335 |                 cmis_show_all(page_zero, page_one);
      |                 ^~~~~~~~~~~~~
      |                 cmis4_show_all

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS
  2021-06-13 14:20   ` Ido Schimmel
@ 2021-06-15 16:12     ` Moshe Shemesh
  0 siblings, 0 replies; 7+ messages in thread
From: Moshe Shemesh @ 2021-06-15 16:12 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: Michal Kubecek, Andrew Lunn, Jakub Kicinski, Don Bollinger,
	netdev, Vladyslav Tarasiuk


On 6/13/2021 5:20 PM, Ido Schimmel wrote:
> External email: Use caution opening links or attachments
>
>
> On Tue, Jun 08, 2021 at 01:32:27PM +0300, Moshe Shemesh wrote:
>> +void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
>> +                const struct ethtool_module_eeprom *page_one)
>> +{
>> +     const __u8 *page_zero_data = page_zero->data;
>> +
>> +     cmis_show_identifier(page_zero_data);
>> +     cmis_show_power_info(page_zero_data);
>> +     cmis_show_connector(page_zero_data);
>> +     cmis_show_cbl_asm_len(page_zero_data);
>> +     cmis_show_sig_integrity(page_zero_data);
>> +     cmis_show_mit_compliance(page_zero_data);
>> +     cmis_show_mod_lvl_monitors(page_zero_data);
>> +
>> +     if (page_one)
>> +             cmis_show_link_len_from_page(page_one->data - 0x80);
>> +
>> +     cmis_show_vendor_info(page_zero_data);
>> +     cmis_show_rev_compliance(page_zero_data);
>> +}
>> diff --git a/cmis.h b/cmis.h
>> new file mode 100644
>> index 0000000..5b7ac38
>> --- /dev/null
>> +++ b/cmis.h
>> @@ -0,0 +1,128 @@
> [...]
>
>> +void cmis4_show_all(const struct ethtool_module_eeprom *page_zero,
>> +                 const struct ethtool_module_eeprom *page_one);
> Should be cmis_show_all():


I will fix and resend, thanks!

>
> netlink/module-eeprom.c:335:17: warning: implicit declaration of function ‘cmis_show_all’; did you mean ‘cmis4_show_all’? [-Wimplicit-function-declaration]
>    335 |                 cmis_show_all(page_zero, page_one);
>        |                 ^~~~~~~~~~~~~
>        |                 cmis4_show_all

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-06-15 16:13 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-08 10:32 [PATCH ethtool v3 0/4] Extend module EEPROM API Moshe Shemesh
2021-06-08 10:32 ` [PATCH ethtool v3 1/4] ethtool: Add netlink handler for getmodule (-m) Moshe Shemesh
2021-06-08 10:32 ` [PATCH ethtool v3 2/4] ethtool: Refactor human-readable module EEPROM output for new API Moshe Shemesh
2021-06-08 10:32 ` [PATCH ethtool v3 3/4] ethtool: Rename QSFP-DD identifiers to use CMIS Moshe Shemesh
2021-06-13 14:20   ` Ido Schimmel
2021-06-15 16:12     ` Moshe Shemesh
2021-06-08 10:32 ` [PATCH ethtool v3 4/4] ethtool: Update manpages to reflect changes to getmodule (-m) command Moshe Shemesh

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.