All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] ethtool: Add QSFP-DD support
@ 2020-07-31  8:47 Adrian Pop
  2020-08-02 16:55 ` Andrew Lunn
                   ` (3 more replies)
  0 siblings, 4 replies; 14+ messages in thread
From: Adrian Pop @ 2020-07-31  8:47 UTC (permalink / raw)
  Cc: netdev, linville, davem, kuba, jiri, vadimp, popadrian1996,
	mlxsw, idosch, andrew

The Common Management Interface Specification (CMIS) for QSFP-DD shares
some similarities with other form factors such as QSFP or SFP, but due to
the fact that the module memory map is different, the current ethtool
version is not able to provide relevant information about an interface.

This patch 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 specifications document.

Page 0x00 (lower and higher memory) are always implemented, so the ethtool
expects at least 256 bytes if the identifier matches the one for QSFP-DD.
For optical connected cables, additional pages are usually available (the
contain module defined  thresholds or lane diagnostic information). In
this case, ethtool expects to receive 768 bytes in the following format:

    +----------+----------+----------+----------+----------+----------+
    |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
    |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
    |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
    |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
    +----------+----------+----------+----------+----------+----------

Several functions from qsfp.c could be reused, so an additional parameter
was added to each and the functions were moved to sff-common.c.

Signed-off-by: Adrian Pop <popadrian1996@gmail.com>
Tested-by: Ido Schimmel <idosch@mellanox.com>
---
 Makefile.am  |   7 +-
 qsfp-dd.c    | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qsfp-dd.h    | 236 ++++++++++++++++++++++
 qsfp.c       |  60 ++----
 qsfp.h       |   8 -
 sff-common.c |  52 +++++
 sff-common.h |  26 ++-
 7 files changed, 894 insertions(+), 56 deletions(-)
 create mode 100644 qsfp-dd.c
 create mode 100644 qsfp-dd.h

diff --git a/Makefile.am b/Makefile.am
index 2abb274..9fd8024 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,7 +17,8 @@ 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
+		  igc.c \
+		  qsfp-dd.c qsfp-dd.h
 endif
 
 if ENABLE_BASH_COMPLETION
@@ -47,12 +48,12 @@ endif
 
 TESTS = test-cmdline
 check_PROGRAMS = test-cmdline
-test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
+test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
 test_cmdline_CFLAGS = -DTEST_ETHTOOL
 if !ETHTOOL_ENABLE_NETLINK
 TESTS += test-features
 check_PROGRAMS += test-features
-test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES) 
+test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES)
 test_features_CFLAGS = -DTEST_ETHTOOL
 endif
 
diff --git a/qsfp-dd.c b/qsfp-dd.c
new file mode 100644
index 0000000..4d533f8
--- /dev/null
+++ b/qsfp-dd.c
@@ -0,0 +1,561 @@
+/**
+ * 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.
+ *
+ * Page 0x00 (lower and higher memory) are always implemented, so the ethtool
+ * expects at least 256 bytes if the identifier matches the one for QSFP-DD.
+ * For optical connected cables, additional pages are usually available (they
+ * contain module defined thresholds or lane diagnostic information). In
+ * this case, ethtool expects to receive 768 bytes in the following format:
+ *
+ *     +----------+----------+----------+----------+----------+----------+
+ *     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
+ *     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
+ *     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
+ *     |   128b   |   128b   |   128b   |   128b   |   128b   |   128b   |
+ *     +----------+----------+----------+----------+----------+----------+
+ */
+
+#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.2fkm\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));
+	}
+}
+
+/**
+ * Read the high/low alarms or warnings for a specific channel. This
+ * information is found in the i'th bit of each byte associated with
+ * one of the aforementioned properties. A value greater than zero
+ * means the alarm/warning is turned on.
+ * The values are stored in the qsfp_dd_diags structure, in the rxaw
+ * or txaw array (each element corresponds to an alarm/warning).
+ */
+static void qsfp_dd_read_aw_for_channel(const __u8 *id, int ch, int mode,
+					struct qsfp_dd_diags * const sd)
+{
+	__u8 cmsk = (1 << ch);
+
+	if (mode == QSFP_DD_READ_TX) {
+		sd->txaw[ch][HA] = id[QSFP_DD_TX_HA_OFFSET] & cmsk;
+		sd->txaw[ch][LA] = id[QSFP_DD_TX_LA_OFFSET] & cmsk;
+		sd->txaw[ch][HW] = id[QSFP_DD_TX_HW_OFFSET] & cmsk;
+		sd->txaw[ch][LW] = id[QSFP_DD_TX_LW_OFFSET] & cmsk;
+	} else {
+		sd->rxaw[ch][HA] = id[QSFP_DD_RX_HA_OFFSET] & cmsk;
+		sd->rxaw[ch][LA] = id[QSFP_DD_RX_LA_OFFSET] & cmsk;
+		sd->rxaw[ch][HW] = id[QSFP_DD_RX_HW_OFFSET] & cmsk;
+		sd->rxaw[ch][LW] = id[QSFP_DD_RX_LW_OFFSET] & cmsk;
+	}
+}
+
+/*
+ * 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))
+
+/**
+ * Get and parse relevant diagnostic information for the current module.
+ * These are stored for every channel in a qsfp_dd_diags structure.
+ */
+static void
+qsfp_dd_parse_diagnostics(const __u8 *id, struct qsfp_dd_diags *const sd)
+{
+	__u16 rx_power_offset;
+	__u16 tx_power_offset;
+	__u16 tx_bias_offset;
+	__u16 temp_offset;
+	__u16 volt_offset;
+	int i;
+
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		/*
+		 * Add Tx/Rx output/input optical power relevant information.
+		 * To access the info for the ith lane, we have to skip i * 2
+		 * bytes starting from the offset of the first lane for that
+		 * specific channel property.
+		 */
+		tx_bias_offset = QSFP_DD_TX_BIAS_START_OFFSET + (i << 1);
+		rx_power_offset = QSFP_DD_RX_PWR_START_OFFSET + (i << 1);
+		tx_power_offset = QSFP_DD_TX_PWR_START_OFFSET + (i << 1);
+
+		sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset);
+		sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
+		sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
+
+		/* Add alarms/warnings related info */
+		qsfp_dd_read_aw_for_channel(id, i, QSFP_DD_READ_TX, sd);
+		qsfp_dd_read_aw_for_channel(id, i, QSFP_DD_READ_RX, sd);
+	}
+
+	/**
+	 * Gather Module-Level Monitor Thresholds and Lane-specific Monitor
+	 * Thresholds. These values are stored in two bytes (MSB, LSB) in
+	 * the following order: HA, LA, HW, LW, thus we only need the start
+	 * offset for each property.
+	 */
+	for (i = 0; i < 4; ++i) {
+		tx_power_offset = QSFP_DD_TXPW_THRS_START_OFFSET + (i << 1);
+		sd->tx_power[i] = OFFSET_TO_U16(tx_power_offset);
+
+		rx_power_offset = QSFP_DD_RXPW_THRS_START_OFFSET + (i << 1);
+		sd->rx_power[i] = OFFSET_TO_U16(rx_power_offset);
+
+		tx_bias_offset = QSFP_DD_TXBI_THRS_START_OFFSET + (i << 1);
+		sd->bias_cur[i] = OFFSET_TO_U16(tx_bias_offset);
+
+		temp_offset = QSFP_DD_TEMP_THRS_START_OFFSET + (i << 1);
+		sd->sfp_temp[i] = OFFSET_TO_TEMP(temp_offset);
+
+		volt_offset = QSFP_DD_VOLT_THRS_START_OFFSET + (i << 1);
+		sd->sfp_voltage[i] = OFFSET_TO_U16(volt_offset);
+	}
+}
+
+/**
+ * Print the Module-Level Monitor Thresholds and the Lane-specific
+ * Monitor Thresholds. This is the same function as the one in the
+ * sff-common.c file, but is using a struct qsfp_dd_diags as a pa-
+ * rameter.
+ */
+static void qsfp_dd_show_thresholds(const struct qsfp_dd_diags sd)
+{
+	PRINT_BIAS("Laser bias current high alarm threshold",
+		   sd.bias_cur[HA]);
+	PRINT_BIAS("Laser bias current low alarm threshold",
+		   sd.bias_cur[LA]);
+	PRINT_BIAS("Laser bias current high warning threshold",
+		   sd.bias_cur[HW]);
+	PRINT_BIAS("Laser bias current low warning threshold",
+		   sd.bias_cur[LW]);
+
+	PRINT_xX_PWR("Laser output power high alarm threshold",
+		     sd.tx_power[HA]);
+	PRINT_xX_PWR("Laser output power low alarm threshold",
+		     sd.tx_power[LA]);
+	PRINT_xX_PWR("Laser output power high warning threshold",
+		     sd.tx_power[HW]);
+	PRINT_xX_PWR("Laser output power low warning threshold",
+		     sd.tx_power[LW]);
+
+	PRINT_TEMP("Module temperature high alarm threshold",
+		   sd.sfp_temp[HA]);
+	PRINT_TEMP("Module temperature low alarm threshold",
+		   sd.sfp_temp[LA]);
+	PRINT_TEMP("Module temperature high warning threshold",
+		   sd.sfp_temp[HW]);
+	PRINT_TEMP("Module temperature low warning threshold",
+		   sd.sfp_temp[LW]);
+
+	PRINT_VCC("Module voltage high alarm threshold",
+		  sd.sfp_voltage[HA]);
+	PRINT_VCC("Module voltage low alarm threshold",
+		  sd.sfp_voltage[LA]);
+	PRINT_VCC("Module voltage high warning threshold",
+		  sd.sfp_voltage[HW]);
+	PRINT_VCC("Module voltage low warning threshold",
+		  sd.sfp_voltage[LW]);
+
+	PRINT_xX_PWR("Laser rx power high alarm threshold",
+		     sd.rx_power[HA]);
+	PRINT_xX_PWR("Laser rx power low alarm threshold",
+		     sd.rx_power[LA]);
+	PRINT_xX_PWR("Laser rx power high warning threshold",
+		     sd.rx_power[HW]);
+	PRINT_xX_PWR("Laser rx power low warning threshold",
+		     sd.rx_power[LW]);
+}
+
+/**
+ * Print relevant lane specific monitor information for each of
+ * the 8 available channels. Relevant documents:
+ * [1] CMIS Rev. 3:
+ * --> pag. 50, section 1.7.2.4, Table 22
+ * --> pag. 53, section 1.7.2.7, Table 26
+ * --> pag. 76, section 1.7.5.1, Table 50
+ * --> pag. 78, section 1.7.5.2, Table 51
+ * --> pag. 98, section 1.7.7.2, Table 67
+ *
+ * [2] CMIS Rev. 4:
+ * --> pag. 84, section 8.2.4, Table 8-6
+ * --> pag. 89, section 8.2.9, Table 8-12
+ * --> pag. 112, section 8.5.1/2, Table 8-41/42
+ * --> pag. 137, section 8.8.2, Table 8-60/61
+ * --> pag. 140, section 8.8.3, Table 8-62
+ */
+static void qsfp_dd_show_sig_optical_pwr(const __u8 *id, __u32 eeprom_len)
+{
+	static const char * const aw_strings[] = {
+		"%s power high alarm   (Channel %d)",
+		"%s power low alarm    (Channel %d)",
+		"%s power high warning (Channel %d)",
+		"%s power low warning  (Channel %d)"
+	};
+	__u8 module_type = id[QSFP_DD_MODULE_TYPE_OFFSET];
+	char field_desc[QSFP_DD_MAX_DESC_SIZE];
+	struct qsfp_dd_diags sd = { { 0 } };
+	const char *cs = "%s (Channel %d)";
+	int i, j;
+
+	/* Print current temperature & voltage */
+	PRINT_TEMP("Module temperature",
+		   OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET));
+	PRINT_VCC("Module voltage",
+		  OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
+
+	/**
+	 * The thresholds and the high/low alarms/warnings are available
+	 * only if an optical interface (MMF/SMF) is present (if this is
+	 * the case, it means that 5 pages are available).
+	 */
+	if (module_type != QSFP_DD_MT_MMF &&
+	    module_type != QSFP_DD_MT_SMF &&
+	    eeprom_len != QSFP_DD_EEPROM_5PAG)
+		return;
+
+	/* Extract the diagnostic variables */
+	qsfp_dd_parse_diagnostics(id, &sd);
+
+	/* Print Tx bias current monitor values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Tx bias current monitor", i + 1);
+		PRINT_BIAS(field_desc, sd.scd[i].bias_cur);
+	}
+
+	/* Print Tx output optical power values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Tx output optical power", i + 1);
+		PRINT_xX_PWR(field_desc, sd.scd[i].tx_power);
+	}
+
+	/* Print Rx output optical power values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Rx input optical power", i + 1);
+		PRINT_xX_PWR(field_desc, sd.scd[i].rx_power);
+	}
+
+	/* Print the Rx alarms/warnings for each channel */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		for (j = 0; j < 4; ++j) {
+			snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE,
+				 aw_strings[j], "Rx", i + 1);
+			printf("\t%-41s : %s\n", field_desc,
+			       ONOFF(sd.rxaw[i][j]));
+		}
+	}
+
+	/* Print the Tx alarms/warnings for each channel */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		for (j = 0; j < 4; ++j) {
+			snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE,
+				 aw_strings[j], "Tx", i + 1);
+			printf("\t%-41s : %s\n", field_desc,
+			       ONOFF(sd.rxaw[i][j]));
+		}
+	}
+
+	qsfp_dd_show_thresholds(sd);
+}
+
+/**
+ * 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_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");
+}
+
+/**
+ * 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, __u32 eeprom_len)
+{
+	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_sig_optical_pwr(id, eeprom_len);
+	qsfp_dd_show_link_len(id);
+	qsfp_dd_show_vendor_info(id);
+	qsfp_dd_show_rev_compliance(id);
+}
diff --git a/qsfp-dd.h b/qsfp-dd.h
new file mode 100644
index 0000000..a7a7051
--- /dev/null
+++ b/qsfp-dd.h
@@ -0,0 +1,236 @@
+#ifndef QSFP_DD_H__
+#define QSFP_DD_H__
+
+#define QSFP_DD_PAG_SIZE			0x80
+#define QSFP_DD_EEPROM_5PAG			(0x80 * 6)
+#define QSFP_DD_MAX_CHANNELS			0x08
+#define QSFP_DD_MAX_DESC_SIZE			0x2A
+#define QSFP_DD_READ_TX				0x00
+#define QSFP_DD_READ_RX				0x01
+
+/* Struct for the current/power of a channel */
+struct qsfp_dd_channel_diags {
+	__u16 bias_cur;
+	__u16 rx_power;
+	__u16 tx_power;
+};
+
+struct qsfp_dd_diags {
+	/* Voltage in 0.1mV units; the first 4 elements represent
+	 * the high/low alarm, high/low warning and the last one
+	 * represent the current voltage of the module.
+	 */
+	__u16 sfp_voltage[4];
+
+	/**
+	 * Temperature in 16-bit signed 1/256 Celsius; the first 4
+	 * elements represent the high/low alarm, high/low warning
+	 * and the last one represent the current temp of the module.
+	 */
+	__s16 sfp_temp[4];
+
+	/* Tx bias current in 2uA units */
+	__u16 bias_cur[4];
+
+	/* Measured TX Power */
+	__u16 tx_power[4];
+
+	/* Measured RX Power */
+	__u16 rx_power[4];
+
+	/* Rx alarms and warnings */
+	bool rxaw[QSFP_DD_MAX_CHANNELS][4];
+
+	/* Tx alarms and warnings */
+	bool txaw[QSFP_DD_MAX_CHANNELS][4];
+
+	struct qsfp_dd_channel_diags scd[QSFP_DD_MAX_CHANNELS];
+};
+
+#define HA					0
+#define LA					1
+#define HW					2
+#define LW					3
+
+/* 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		0xBD
+
+/* 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
+
+/*-----------------------------------------------------------------------
+ * For optical connected cables (the eeprom length is equal to  640 bytes
+ * = QSFP_DD_EEPROM_WITH_OPTICAL), the memory has the following format:
+ * Bytes   0-127: page  0 (lower)
+ * Bytes 128-255: page  0 (higher)
+ * Bytes 256-383: page  1 (higher)
+ * Bytes 384-511: page  2 (higher)
+ * Bytes 512-639: page 16 (higher)
+ * Bytes 640-768: page 17 (higher)
+ *
+ * Since for pages with an index > 0 the lower part is missing from the memory,
+ * but the offset values are still in the [128, 255) range, the real offset in
+ * the eeprom memory must be calculated as following:
+ * RealOffset = PageIndex * 0x80 + LocalOffset
+
+ * The page index is the index of the page, starting from 0: page 0 has index
+ * 1, page 1 has index 1, page 2 has index 2, page 16 has index 3 and page 17
+ * has index 4.
+ */
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x01: contains advertising fields that define properties
+ * that are unique to active modules and cable assemblies.
+ * RealOffset = 1 * 0x80 + LocalOffset
+ */
+#define PAG01H_OFFSET				(0x01 * 0x80)
+
+/* Supported Link Length (Page 1) */
+#define QSFP_DD_SMF_LEN_OFFSET			(PAG01H_OFFSET + 0x84)
+#define QSFP_DD_OM5_LEN_OFFSET			(PAG01H_OFFSET + 0x85)
+#define QSFP_DD_OM4_LEN_OFFSET			(PAG01H_OFFSET + 0x86)
+#define QSFP_DD_OM3_LEN_OFFSET			(PAG01H_OFFSET + 0x87)
+#define QSFP_DD_OM2_LEN_OFFSET			(PAG01H_OFFSET + 0x88)
+
+/* Wavelength (Page 1) */
+#define QSFP_DD_NOM_WAVELENGTH_MSB		(PAG01H_OFFSET + 0x8A)
+#define QSFP_DD_NOM_WAVELENGTH_LSB		(PAG01H_OFFSET + 0x8B)
+#define QSFP_DD_WAVELENGTH_TOL_MSB		(PAG01H_OFFSET + 0x8C)
+#define QSFP_DD_WAVELENGTH_TOL_LSB		(PAG01H_OFFSET + 0x8D)
+
+/* Signal integrity controls */
+#define QSFP_DD_SIG_INTEG_TX_OFFSET		(PAG01H_OFFSET + 0xA1)
+#define QSFP_DD_SIG_INTEG_RX_OFFSET		(PAG01H_OFFSET + 0xA2)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x02: contains module defined threshdolds and lane-
+ * specific monitors.
+ * RealOffset = 2 * 0x80 + LocalOffset
+ */
+#define PAG02H_OFFSET				(0x02 * 0x80)
+#define QSFP_DD_TEMP_THRS_START_OFFSET		(PAG02H_OFFSET + 0x80)
+#define QSFP_DD_VOLT_THRS_START_OFFSET		(PAG02H_OFFSET + 0x88)
+#define QSFP_DD_TXPW_THRS_START_OFFSET		(PAG02H_OFFSET + 0xB0)
+#define QSFP_DD_TXBI_THRS_START_OFFSET		(PAG02H_OFFSET + 0xB8)
+#define QSFP_DD_RXPW_THRS_START_OFFSET		(PAG02H_OFFSET + 0xC0)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x10: contains dynamic control bytes.
+ * RealOffset = 3 * 0x80 + LocalOffset
+ */
+#define PAG10H_OFFSET				(0x03 * 0x80)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x11: contains lane dynamic status bytes.
+ * RealOffset = 4 * 0x80 + LocalOffset
+ */
+#define PAG11H_OFFSET				(0x04 * 0x80)
+#define QSFP_DD_TX_PWR_START_OFFSET		(PAG11H_OFFSET + 0x9A)
+#define QSFP_DD_TX_BIAS_START_OFFSET		(PAG11H_OFFSET + 0xAA)
+#define QSFP_DD_RX_PWR_START_OFFSET		(PAG11H_OFFSET + 0xBA)
+
+/* HA = High Alarm; LA = Low Alarm
+ * HW = High Warning; LW = Low Warning
+ */
+#define QSFP_DD_TX_HA_OFFSET			(PAG11H_OFFSET + 0x8B)
+#define QSFP_DD_TX_LA_OFFSET			(PAG11H_OFFSET + 0x8C)
+#define QSFP_DD_TX_HW_OFFSET			(PAG11H_OFFSET + 0x8D)
+#define QSFP_DD_TX_LW_OFFSET			(PAG11H_OFFSET + 0x8E)
+
+#define QSFP_DD_RX_HA_OFFSET			(PAG11H_OFFSET + 0x95)
+#define QSFP_DD_RX_LA_OFFSET			(PAG11H_OFFSET + 0x96)
+#define QSFP_DD_RX_HW_OFFSET			(PAG11H_OFFSET + 0x97)
+#define QSFP_DD_RX_LW_OFFSET			(PAG11H_OFFSET + 0x98)
+
+#define YESNO(x) (((x) != 0) ? "Yes" : "No")
+#define ONOFF(x) (((x) != 0) ? "On" : "Off")
+
+void qsfp_dd_show_all(const __u8 *id, __u32 eeprom_len);
+
+#endif /* QSFP_DD_H__ */
diff --git a/qsfp.c b/qsfp.c
index a8b69c9..2f740bb 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -58,6 +58,7 @@
 #include "internal.h"
 #include "sff-common.h"
 #include "qsfp.h"
+#include "qsfp-dd.h"
 
 #define MAX_DESC_SIZE	42
 
@@ -579,9 +580,9 @@ static void sff8636_show_rate_identifier(const __u8 *id)
 			id[SFF8636_EXT_RS_OFFSET]);
 }
 
-static void sff8636_show_oui(const __u8 *id)
+static void sff8636_show_oui(const __u8 *id, int id_offset)
 {
-	sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
+	sff8024_show_oui(id, id_offset);
 }
 
 static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
@@ -662,38 +663,7 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
 
 static void sff8636_show_revision_compliance(const __u8 *id)
 {
-	static const char *pfx =
-		"\tRevision Compliance                       :";
-
-	switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) {
-	case SFF8636_REV_UNSPECIFIED:
-		printf("%s Revision not specified\n", pfx);
-		break;
-	case SFF8636_REV_8436_48:
-		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8436_8636:
-		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8636_13:
-		printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8636_14:
-		printf("%s SFF-8636 Rev 1.4\n", pfx);
-		break;
-	case SFF8636_REV_8636_15:
-		printf("%s SFF-8636 Rev 1.5\n", pfx);
-		break;
-	case SFF8636_REV_8636_20:
-		printf("%s SFF-8636 Rev 2.0\n", pfx);
-		break;
-	case SFF8636_REV_8636_27:
-		printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
-		break;
-	default:
-		printf("%s Unallocated\n", pfx);
-		break;
-	}
+	sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET);
 }
 
 /*
@@ -846,10 +816,15 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
 
 		sff_show_thresholds(sd);
 	}
-
 }
+
 void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 {
+	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+		qsfp_dd_show_all(id, eeprom_len);
+		return;
+	}
+
 	sff8636_show_identifier(id);
 	if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
@@ -858,6 +833,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 		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);
@@ -872,17 +848,19 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 		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_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");
+				SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
 		sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
-			       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
+				SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
 		sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
-			       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
+				SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
 		sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
-			       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+				SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+
 		sff8636_show_revision_compliance(id);
 		sff8636_show_dom(id, eeprom_len);
 	}
diff --git a/qsfp.h b/qsfp.h
index 3215932..9636b0c 100644
--- a/qsfp.h
+++ b/qsfp.h
@@ -31,14 +31,6 @@
 #define	SFF8636_ID_OFFSET	0x00
 
 #define	SFF8636_REV_COMPLIANCE_OFFSET	0x01
-#define	 SFF8636_REV_UNSPECIFIED		0x00
-#define	 SFF8636_REV_8436_48			0x01
-#define	 SFF8636_REV_8436_8636			0x02
-#define	 SFF8636_REV_8636_13			0x03
-#define	 SFF8636_REV_8636_14			0x04
-#define	 SFF8636_REV_8636_15			0x05
-#define	 SFF8636_REV_8636_20			0x06
-#define	 SFF8636_REV_8636_27			0x07
 
 #define	SFF8636_STATUS_2_OFFSET	0x02
 /* Flat Memory:0- Paging, 1- Page 0 only */
diff --git a/sff-common.c b/sff-common.c
index 7700cbe..5490b5f 100644
--- a/sff-common.c
+++ b/sff-common.c
@@ -46,6 +46,7 @@ void sff_show_ascii(const __u8 *id, unsigned int first_reg,
 	printf("\t%-41s : ", name);
 	while (first_reg <= last_reg && id[last_reg] == ' ')
 		last_reg--;
+
 	for (reg = first_reg; reg <= last_reg; reg++) {
 		val = id[reg];
 		putchar(((val >= 32) && (val <= 126)) ? val : '_');
@@ -136,6 +137,9 @@ void sff8024_show_identifier(const __u8 *id, int id_offset)
 	case SFF8024_ID_MICRO_QSFP:
 		printf(" (microQSFP)\n");
 		break;
+	case SFF8024_ID_QSFP_DD:
+		printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n");
+		break;
 	default:
 		printf(" (reserved or unknown)\n");
 		break;
@@ -203,6 +207,18 @@ void sff8024_show_connector(const __u8 *id, int ctor_offset)
 	case SFF8024_CTOR_MXC_2x16:
 		printf(" (MXC 2x16)\n");
 		break;
+	case SFF8024_CTOR_CS_OPTICAL:
+		printf(" (CS optical connector)\n");
+		break;
+	case SFF8024_CTOR_CS_OPTICAL_MINI:
+		printf(" (Mini CS optical connector)\n");
+		break;
+	case SFF8024_CTOR_MPO_2X12:
+		printf(" (MPO 2x12)\n");
+		break;
+	case SFF8024_CTOR_MPO_1X16:
+		printf(" (MPO 1x16)\n");
+		break;
 	default:
 		printf(" (reserved or unknown)\n");
 		break;
@@ -302,3 +318,39 @@ void sff_show_thresholds(struct sff_diags sd)
 	PRINT_xX_PWR("Laser rx power low warning threshold",
 		     sd.rx_power[LWARN]);
 }
+
+void sff_show_revision_compliance(const __u8 *id, int rev_offset)
+{
+	static const char *pfx =
+		"\tRevision Compliance                       :";
+
+	switch (id[rev_offset]) {
+	case SFF8636_REV_UNSPECIFIED:
+		printf("%s Revision not specified\n", pfx);
+		break;
+	case SFF8636_REV_8436_48:
+		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8436_8636:
+		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8636_13:
+		printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8636_14:
+		printf("%s SFF-8636 Rev 1.4\n", pfx);
+		break;
+	case SFF8636_REV_8636_15:
+		printf("%s SFF-8636 Rev 1.5\n", pfx);
+		break;
+	case SFF8636_REV_8636_20:
+		printf("%s SFF-8636 Rev 2.0\n", pfx);
+		break;
+	case SFF8636_REV_8636_27:
+		printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
+		break;
+	default:
+		printf("%s Unallocated\n", pfx);
+		break;
+	}
+}
diff --git a/sff-common.h b/sff-common.h
index 5562b4d..0d04851 100644
--- a/sff-common.h
+++ b/sff-common.h
@@ -26,7 +26,17 @@
 #include <stdio.h>
 #include "internal.h"
 
-#define SFF8024_ID_OFFSET				0x00
+/* Revision compliance */
+#define	 SFF8636_REV_UNSPECIFIED		0x00
+#define	 SFF8636_REV_8436_48			0x01
+#define	 SFF8636_REV_8436_8636			0x02
+#define	 SFF8636_REV_8636_13			0x03
+#define	 SFF8636_REV_8636_14			0x04
+#define	 SFF8636_REV_8636_15			0x05
+#define	 SFF8636_REV_8636_20			0x06
+#define	 SFF8636_REV_8636_27			0x07
+
+#define  SFF8024_ID_OFFSET				0x00
 #define  SFF8024_ID_UNKNOWN				0x00
 #define  SFF8024_ID_GBIC				0x01
 #define  SFF8024_ID_SOLDERED_MODULE		0x02
@@ -51,7 +61,8 @@
 #define  SFF8024_ID_HD8X_FANOUT			0x15
 #define  SFF8024_ID_CDFP_S3				0x16
 #define  SFF8024_ID_MICRO_QSFP			0x17
-#define  SFF8024_ID_LAST				SFF8024_ID_MICRO_QSFP
+#define  SFF8024_ID_QSFP_DD				0x18
+#define  SFF8024_ID_LAST				SFF8024_ID_QSFP_DD
 #define  SFF8024_ID_UNALLOCATED_LAST	0x7F
 #define  SFF8024_ID_VENDOR_START		0x80
 #define  SFF8024_ID_VENDOR_LAST			0xFF
@@ -76,8 +87,14 @@
 #define  SFF8024_CTOR_RJ45				0x22
 #define  SFF8024_CTOR_NO_SEPARABLE		0x23
 #define  SFF8024_CTOR_MXC_2x16			0x24
-#define  SFF8024_CTOR_LAST				SFF8024_CTOR_MXC_2x16
-#define  SFF8024_CTOR_UNALLOCATED_LAST	0x7F
+#define  SFF8024_CTOR_CS_OPTICAL		0x25
+#define  SFF8024_CTOR_CS_OPTICAL_MINI		0x26
+#define  SFF8024_CTOR_MPO_2X12		    	0x27
+#define  SFF8024_CTOR_MPO_1X16		    	0x28
+#define  SFF8024_CTOR_LAST			SFF8024_CTOR_MPO_1X16
+
+#define  SFF8024_CTOR_NO_SEP_QSFP_DD 		0x6F
+#define  SFF8024_CTOR_UNALLOCATED_LAST		0x7F
 #define  SFF8024_CTOR_VENDOR_START		0x80
 #define  SFF8024_CTOR_VENDOR_LAST		0xFF
 
@@ -185,5 +202,6 @@ void sff8024_show_oui(const __u8 *id, int id_offset);
 void sff8024_show_identifier(const __u8 *id, int id_offset);
 void sff8024_show_connector(const __u8 *id, int ctor_offset);
 void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type);
+void sff_show_revision_compliance(const __u8 *id, int rev_offset);
 
 #endif /* SFF_COMMON_H__ */
-- 
2.17.1


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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-07-31  8:47 [PATCH] ethtool: Add QSFP-DD support Adrian Pop
@ 2020-08-02 16:55 ` Andrew Lunn
  2020-08-04  7:44 ` Ido Schimmel
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 14+ messages in thread
From: Andrew Lunn @ 2020-08-02 16:55 UTC (permalink / raw)
  To: Adrian Pop; +Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, idosch

On Fri, Jul 31, 2020 at 11:47:25AM +0300, Adrian Pop wrote:
> The Common Management Interface Specification (CMIS) for QSFP-DD shares
> some similarities with other form factors such as QSFP or SFP, but due to
> the fact that the module memory map is different, the current ethtool
> version is not able to provide relevant information about an interface.
> 
> This patch 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 specifications document.
> 
> Page 0x00 (lower and higher memory) are always implemented, so the ethtool
> expects at least 256 bytes if the identifier matches the one for QSFP-DD.
> For optical connected cables, additional pages are usually available (the
> contain module defined  thresholds or lane diagnostic information). In
> this case, ethtool expects to receive 768 bytes in the following format:
> 
>     +----------+----------+----------+----------+----------+----------+
>     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
>     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
>     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
>     |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
>     +----------+----------+----------+----------+----------+----------

Hi Adrian

Didn't we discuss that page 3 might be useful? I would prefer not to
document that pages 0x10 and 0x11 would follow page 2 until we have a
driver which does actually provide pages 0x10 and 0x11.

       Andrew

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-07-31  8:47 [PATCH] ethtool: Add QSFP-DD support Adrian Pop
  2020-08-02 16:55 ` Andrew Lunn
@ 2020-08-04  7:44 ` Ido Schimmel
  2020-08-04  8:23   ` Adrian Pop
  2020-08-04 10:14 ` Michal Kubecek
  2020-08-04 10:30 ` Michal Kubecek
  3 siblings, 1 reply; 14+ messages in thread
From: Ido Schimmel @ 2020-08-04  7:44 UTC (permalink / raw)
  To: Adrian Pop
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, idosch, andrew

Hi Adrian, thanks again for submitting this patch. I got two comments
off-list. Sharing them here.

On Fri, Jul 31, 2020 at 11:47:25AM +0300, Adrian Pop wrote:
> +/**
> + * 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.2fkm\n", fn, val);

Should be:

printf("\t%-41s : %0.2fm\n", fn, val);

Since the specification says "Link length base value in meters".

Before:

        Cable assembly length                     : 0.50km

After:

        Cable assembly length                     : 0.50m

> +}

...

> diff --git a/qsfp-dd.h b/qsfp-dd.h
> new file mode 100644
> index 0000000..a7a7051
> --- /dev/null
> +++ b/qsfp-dd.h
> @@ -0,0 +1,236 @@
> +#ifndef QSFP_DD_H__
> +#define QSFP_DD_H__
> +
> +#define QSFP_DD_PAG_SIZE			0x80
> +#define QSFP_DD_EEPROM_5PAG			(0x80 * 6)
> +#define QSFP_DD_MAX_CHANNELS			0x08
> +#define QSFP_DD_MAX_DESC_SIZE			0x2A
> +#define QSFP_DD_READ_TX				0x00
> +#define QSFP_DD_READ_RX				0x01
> +
> +/* Struct for the current/power of a channel */
> +struct qsfp_dd_channel_diags {
> +	__u16 bias_cur;
> +	__u16 rx_power;
> +	__u16 tx_power;
> +};
> +
> +struct qsfp_dd_diags {
> +	/* Voltage in 0.1mV units; the first 4 elements represent
> +	 * the high/low alarm, high/low warning and the last one
> +	 * represent the current voltage of the module.
> +	 */
> +	__u16 sfp_voltage[4];
> +
> +	/**
> +	 * Temperature in 16-bit signed 1/256 Celsius; the first 4
> +	 * elements represent the high/low alarm, high/low warning
> +	 * and the last one represent the current temp of the module.
> +	 */
> +	__s16 sfp_temp[4];
> +
> +	/* Tx bias current in 2uA units */
> +	__u16 bias_cur[4];
> +
> +	/* Measured TX Power */
> +	__u16 tx_power[4];
> +
> +	/* Measured RX Power */
> +	__u16 rx_power[4];
> +
> +	/* Rx alarms and warnings */
> +	bool rxaw[QSFP_DD_MAX_CHANNELS][4];
> +
> +	/* Tx alarms and warnings */
> +	bool txaw[QSFP_DD_MAX_CHANNELS][4];
> +
> +	struct qsfp_dd_channel_diags scd[QSFP_DD_MAX_CHANNELS];
> +};
> +
> +#define HA					0
> +#define LA					1
> +#define HW					2
> +#define LW					3
> +
> +/* 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		0xBD

According to the specification (section 8.3.7), the offset is 188, so
should be 0xBC.

Before:

        Date code                                 : 200507  _

After:

        Date code                                 : 200507

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-08-04  7:44 ` Ido Schimmel
@ 2020-08-04  8:23   ` Adrian Pop
  0 siblings, 0 replies; 14+ messages in thread
From: Adrian Pop @ 2020-08-04  8:23 UTC (permalink / raw)
  To: Ido Schimmel
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, Ido Schimmel,
	Andrew Lunn

Hi Andrew, Ido!

> Hi Adrian, thanks again for submitting this patch. I got two comments
> off-list. Sharing them here.

Thanks for pointing that out, I took a look and you're right. I'll fix them.

> Didn't we discuss that page 3 might be useful? I would prefer not to
> document that pages 0x10 and 0x11 would follow page 2 until we have a
> driver which does actually provide pages 0x10 and 0x11.

Should I just remove that bit about 0x10 and 0x11 from the commit
message (so keep only the first two paragraphs) and leave the code as
it is?

Adrian

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-07-31  8:47 [PATCH] ethtool: Add QSFP-DD support Adrian Pop
  2020-08-02 16:55 ` Andrew Lunn
  2020-08-04  7:44 ` Ido Schimmel
@ 2020-08-04 10:14 ` Michal Kubecek
  2020-08-04 10:25   ` Adrian Pop
  2020-08-04 10:30 ` Michal Kubecek
  3 siblings, 1 reply; 14+ messages in thread
From: Michal Kubecek @ 2020-08-04 10:14 UTC (permalink / raw)
  To: Adrian Pop
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, idosch, andrew

[-- Attachment #1: Type: text/plain, Size: 3596 bytes --]

On Fri, Jul 31, 2020 at 11:47:25AM +0300, Adrian Pop wrote:
> The Common Management Interface Specification (CMIS) for QSFP-DD shares
> some similarities with other form factors such as QSFP or SFP, but due to
> the fact that the module memory map is different, the current ethtool
> version is not able to provide relevant information about an interface.
> 
> This patch 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 specifications document.
> 
> Page 0x00 (lower and higher memory) are always implemented, so the ethtool
> expects at least 256 bytes if the identifier matches the one for QSFP-DD.
> For optical connected cables, additional pages are usually available (the
> contain module defined  thresholds or lane diagnostic information). In
> this case, ethtool expects to receive 768 bytes in the following format:
> 
>     +----------+----------+----------+----------+----------+----------+
>     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
>     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
>     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
>     |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
>     +----------+----------+----------+----------+----------+----------
> 
> Several functions from qsfp.c could be reused, so an additional parameter
> was added to each and the functions were moved to sff-common.c.
> 
> Signed-off-by: Adrian Pop <popadrian1996@gmail.com>
> Tested-by: Ido Schimmel <idosch@mellanox.com>

AFAICS the kernel counterpart is going to reach mainline in 5.9-rc1
merge window. Please base your patch on "next" branch or wait until next
is merged into master after 5.8 release (which should be later today or
tomorrow).

> ---
>  Makefile.am  |   7 +-
>  qsfp-dd.c    | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  qsfp-dd.h    | 236 ++++++++++++++++++++++
>  qsfp.c       |  60 ++----
>  qsfp.h       |   8 -
>  sff-common.c |  52 +++++
>  sff-common.h |  26 ++-
>  7 files changed, 894 insertions(+), 56 deletions(-)
>  create mode 100644 qsfp-dd.c
>  create mode 100644 qsfp-dd.h
> 
> diff --git a/Makefile.am b/Makefile.am
> index 2abb274..9fd8024 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -17,7 +17,8 @@ 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
> +		  igc.c \
> +		  qsfp-dd.c qsfp-dd.h

There is no need to start a new line.

>  endif
>  
>  if ENABLE_BASH_COMPLETION
> @@ -47,12 +48,12 @@ endif
>  
>  TESTS = test-cmdline
>  check_PROGRAMS = test-cmdline
> -test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES) 
> +test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
>  test_cmdline_CFLAGS = -DTEST_ETHTOOL
>  if !ETHTOOL_ENABLE_NETLINK
>  TESTS += test-features
>  check_PROGRAMS += test-features
> -test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES) 
> +test_features_SOURCES = test-features.c test-common.c $(ethtool_SOURCES)
>  test_features_CFLAGS = -DTEST_ETHTOOL
>  endif
>  

Do not mix unrelated changes like whitespace cleanups with your
functional changes, it makes it harder to see what the patch is doing.
Please split the cleanups (there are some more in qsfp.c) into
a separate patch.

Michal

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-08-04 10:14 ` Michal Kubecek
@ 2020-08-04 10:25   ` Adrian Pop
  2020-08-04 17:18     ` Michal Kubecek
  0 siblings, 1 reply; 14+ messages in thread
From: Adrian Pop @ 2020-08-04 10:25 UTC (permalink / raw)
  To: Michal Kubecek
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, Ido Schimmel,
	Andrew Lunn

>
> AFAICS the kernel counterpart is going to reach mainline in 5.9-rc1
> merge window. Please base your patch on "next" branch or wait until next
> is merged into master after 5.8 release (which should be later today or
> tomorrow).

I will wait until tomorrow and rebase my patch onto master then.

>
> Do not mix unrelated changes like whitespace cleanups with your
> functional changes, it makes it harder to see what the patch is doing.
> Please split the cleanups (there are some more in qsfp.c) into
> a separate patch.

I'll create separate patches for functional changes/cleanups. Thanks
for letting me know.

Adrian

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-07-31  8:47 [PATCH] ethtool: Add QSFP-DD support Adrian Pop
                   ` (2 preceding siblings ...)
  2020-08-04 10:14 ` Michal Kubecek
@ 2020-08-04 10:30 ` Michal Kubecek
  3 siblings, 0 replies; 14+ messages in thread
From: Michal Kubecek @ 2020-08-04 10:30 UTC (permalink / raw)
  To: Adrian Pop
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, idosch, andrew

[-- Attachment #1: Type: text/plain, Size: 1175 bytes --]

I noticed one more minor problem:

On Fri, Jul 31, 2020 at 11:47:25AM +0300, Adrian Pop wrote:
> +static void qsfp_dd_show_sig_optical_pwr(const __u8 *id, __u32 eeprom_len)
> +{
> +	static const char * const aw_strings[] = {
> +		"%s power high alarm   (Channel %d)",
> +		"%s power low alarm    (Channel %d)",
> +		"%s power high warning (Channel %d)",
> +		"%s power low warning  (Channel %d)"
> +	};
> +	__u8 module_type = id[QSFP_DD_MODULE_TYPE_OFFSET];
> +	char field_desc[QSFP_DD_MAX_DESC_SIZE];
> +	struct qsfp_dd_diags sd = { { 0 } };

This causes a compiler warning with recent gcc:

  qsfp-dd.c: In function ‘qsfp_dd_show_sig_optical_pwr’:
  qsfp-dd.c:438:9: warning: missing initializer for field ‘sfp_temp’ of ‘struct qsfp_dd_diags’ [-Wmissing-field-initializers]
    438 |  struct qsfp_dd_diags sd = { { 0 } };
        |         ^~~~~~~~~~~~~
  In file included from qsfp-dd.c:26:
  qsfp-dd.h:30:8: note: ‘sfp_temp’ declared here
     30 |  __s16 sfp_temp[4];
        |        ^~~~~~~~

An empty initializer like

	struct qsfp_dd_diags sd = {};

should be fine (and is already used in many other places).

Michal

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-08-04 10:25   ` Adrian Pop
@ 2020-08-04 17:18     ` Michal Kubecek
  2020-08-06 15:13       ` Adrian Pop
  0 siblings, 1 reply; 14+ messages in thread
From: Michal Kubecek @ 2020-08-04 17:18 UTC (permalink / raw)
  To: Adrian Pop
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, Ido Schimmel,
	Andrew Lunn

On Tue, Aug 04, 2020 at 11:25:48AM +0100, Adrian Pop wrote:
> >
> > AFAICS the kernel counterpart is going to reach mainline in 5.9-rc1
> > merge window. Please base your patch on "next" branch or wait until next
> > is merged into master after 5.8 release (which should be later today or
> > tomorrow).
> 
> I will wait until tomorrow and rebase my patch onto master then.

Branch "next" is merged into master now so you can base v2 on master.

Michal

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2020-08-04 17:18     ` Michal Kubecek
@ 2020-08-06 15:13       ` Adrian Pop
  0 siblings, 0 replies; 14+ messages in thread
From: Adrian Pop @ 2020-08-06 15:13 UTC (permalink / raw)
  To: Michal Kubecek
  Cc: netdev, linville, davem, kuba, jiri, vadimp, mlxsw, Ido Schimmel,
	Andrew Lunn

>
> Branch "next" is merged into master now so you can base v2 on master.
>

Hi Michal!

Just submitted v2. I fixed the little problems noticed by Ido and you.
The submission consists of only one patch, since I noticed that some
extra whitespace 'fixes' were wrongly introduced by my editor so I
supposed the code is already well formatted and there's no need for a
separate one. I hope everything is okay now.

Adrian

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

* Re: [PATCH] ethtool: Add QSFP-DD support
       [not found] ` <20191115201508.GF24205@lunn.ch>
@ 2019-11-15 21:34   ` Adrian Pop
  0 siblings, 0 replies; 14+ messages in thread
From: Adrian Pop @ 2019-11-15 21:34 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, linville

Hi Andrew!

The QSFP-DD support I added doesn't replace and/or overrides the
current QSFP support and memory mapping. When I started developing
this, I tried to reuse as much as the old code as possible, but most
of the properties where placed at totally different offsets.

In qsfp.c, I added this:

void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
{
    if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
        qsfp_dd_show_all(id, eeprom_len);
    return;
    }
    [...]

This was needed because although modinfo.type (see ethtool.c,
do_getmodule) is still ETH_MODULE_SFF_8636, the byte in the eeprom
containing the ID was different (0x18 for QSFP-DD). Since QSFP and
QSFP-DD were not fully backwards compatible, I decided to use the ID
to differentiate between them (this is done in the same way for QSFP+
for example).

If I run an older version of ethtool on an interface from a network
card having a QSFP-DD transceiver plugged in, the tool prints 0x18 and
exits. With my changes, if we have a QSFP-DD, the tool prints the
correct stats without altering the old flow for normal QSFPs.

Of course, as you stated in the previous messages, it still looks like
I'm trying to define an KAPI. If we still want to keep page 3 and just
append 0x10 and 0x11 after it, that can be easily done (although there
aren't any stats needed for QSFP-DD that are found in page 0x03).

Adrian

On Fri, 15 Nov 2019 at 20:15, Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Sat, Nov 09, 2019 at 02:42:05PM +0200, Adrian Pop wrote:
> > The Common Management Interface Specification (CMIS) for QSFP-DD shares
> > some similarities with other form factors such as QSFP or SFP, but due to
> > the fact that the module memory map is different, the current ethtool
> > version is not able to provide relevant information about an interface.
> >
> > This patch 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 specifications document.
> >
> > Page 0x00 (lower and higher memory) are always implemented, so the ethtool
> > expects at least 256 bytes if the identifier matches the one for QSFP-DD.
> > For optical connected cables, additional pages are usually available (the
> > contain module defined  thresholds or lane diagnostic information). In
> > this case, ethtool expects to receive 768 bytes in the following format:
> >
> >     +----------+----------+----------+----------+----------+----------+
> >     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
> >     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
> >     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
> >     |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
> >     +----------+----------+----------+----------+----------+----------
>
> Hi Adrian
>
> From ethtool/qsfp.c
>
>  *      b) SFF 8636 based 640 bytes memory layout is presented for parser
>  *
>  *           SFF 8636 based QSFP Memory Map
>  *
>  *           2-Wire Serial Address: 1010000x
>  *
>  *           Lower Page 00h (128 bytes)
>  *           ======================
>  *           |                     |
>  *           |Page Select Byte(127)|
>  *           ======================
>  *                    |
>  *                    V
>  *           ----------------------------------------
>  *          |             |            |             |
>  *          V             V            V             V
>  *       ----------   ----------   ---------    ------------
>  *      | Upper    | | Upper    | | Upper    | | Upper      |
>  *      | Page 00h | | Page 01h | | Page 02h | | Page 03h   |
>  *      |          | |(Optional)| |(Optional)| | (Optional) |
>  *      |          | |          | |          | |            |
>  *      |          | |          | |          | |            |
>  *      |    ID    | |   AST    | |  User    | |  For       |
>  *      |  Fields  | |  Table   | | EEPROM   | |  Cable     |
>  *      |          | |          | | Data     | | Assemblies |
>  *      |          | |          | |          | |            |
>  *      |          | |          | |          | |            |
>  *      -----------  -----------   ----------  --------------
>
> I don't think dropping page 3 is good for backwards/forwards
> compatibility. An old ethtool is going to decode what it thinks is
> page 3 when in fact it is page 0x10. For backwards compatibility, you
> should not dropping page 3, but append pages 0x10 and 0x11 after page
> 3.
>
> Then there is the question, what do we do when page 4 is needed by
> some other module? Maybe the better solution is to wait until netlink
> ethtool is available. We can then label each page with an attribute
> indicate what page it actually is, and so allow sparse pages.
>
>          Andrew

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2019-11-13 13:59     ` Andrew Lunn
@ 2019-11-15 19:00       ` Adrian Pop
  0 siblings, 0 replies; 14+ messages in thread
From: Adrian Pop @ 2019-11-15 19:00 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: netdev, linville

Hi Andrew

You are right about defining a KAPI. Unfortunately due to the nature
of my company's business we can't really mainline our driver. I'll try
to add the QSFP-DD support myself in the future, although I have
nothing planned at the moment. Until then, maybe this thread will be
helpful for any other contributors that will decide to work on a
driver. As I said, to provide the same stats for QSFP-DD as for QSFP,
ethtool needs an extra page. My pull request is well documented and
contains information about this.

Adrian

On Wed, 13 Nov 2019 at 13:59, Andrew Lunn <andrew@lunn.ch> wrote:
>
> On Wed, Nov 13, 2019 at 12:56:51PM +0000, Adrian Pop wrote:
> > Hi Andrew!
> >
> > Thank you for your email. At the moment, there are no in-kernel drivers that
> > require this support (the 1st version of the Common Management Interface
> > Specification for the QSFP-DD 8X pluggable transceivers came out this year in
> > May [0]). I first came across implementing this extra functionality to ethtool
> > at my company, where we use a custom driver for a NIC that works with the new
> > QSFP-DD transceivers. All the ethtool readings for QSFP-DD were correct and I
> > can provide a sample if needed. Another example of somebody putting QSFP-DD
> > support in their products is Exablaze with their ExaNIC [1] and I'm sure that
> > with time there will be more. Unfortunately at the moment I'm not able to
> > provide help with an update on the mainline driver in the kernel.
> >
> > I know that ETH_MODULE_SFF_8436_MAX_LEN is 640 bytes, but to provide for
> > QSFP-DD all the stats as for QSFP (basically to maintain the same behavior/
> > functionality), we came to the conclusion that we need to read more pages than
> > before, since the memory map is different and the data is more spread around.
> >
> > Please let me know if you have any other feedback and/or suggestions for the
> > patch.
>
> Hi Adrian
>
> You are defining a kernel API here. It is very unusual to define a
> KAPI without the mainline kernel actually implementing it. So in my
> opinion, we need an in kernel implementation first.
>
> I guess you are not going to mainline your driver? The ExaNIC is also
> a long way from being mainline'able.
>
> Could i suggest you extend drivers/net/phy/sfp.c to support QSFP-DD?
> Or maybe one of Intels or Mellanox drivers? Basically, any hardware
> with an in kernel driver you can get your hands on and test. Maybe you
> already have something which you use of interoperability testing.
>
>      Andrew

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

* Re: [PATCH] ethtool: Add QSFP-DD support
       [not found]   ` <CAL_jBfQhVAy24xbz_VbpPM0QtRu-Uzawhyn=AY0b41B9=v3Ytg@mail.gmail.com>
@ 2019-11-13 13:59     ` Andrew Lunn
  2019-11-15 19:00       ` Adrian Pop
  0 siblings, 1 reply; 14+ messages in thread
From: Andrew Lunn @ 2019-11-13 13:59 UTC (permalink / raw)
  To: Adrian Pop; +Cc: netdev, linville

On Wed, Nov 13, 2019 at 12:56:51PM +0000, Adrian Pop wrote:
> Hi Andrew!
> 
> Thank you for your email. At the moment, there are no in-kernel drivers that
> require this support (the 1st version of the Common Management Interface
> Specification for the QSFP-DD 8X pluggable transceivers came out this year in
> May [0]). I first came across implementing this extra functionality to ethtool
> at my company, where we use a custom driver for a NIC that works with the new
> QSFP-DD transceivers. All the ethtool readings for QSFP-DD were correct and I
> can provide a sample if needed. Another example of somebody putting QSFP-DD
> support in their products is Exablaze with their ExaNIC [1] and I'm sure that
> with time there will be more. Unfortunately at the moment I'm not able to
> provide help with an update on the mainline driver in the kernel.
> 
> I know that ETH_MODULE_SFF_8436_MAX_LEN is 640 bytes, but to provide for
> QSFP-DD all the stats as for QSFP (basically to maintain the same behavior/
> functionality), we came to the conclusion that we need to read more pages than
> before, since the memory map is different and the data is more spread around.
> 
> Please let me know if you have any other feedback and/or suggestions for the
> patch.

Hi Adrian

You are defining a kernel API here. It is very unusual to define a
KAPI without the mainline kernel actually implementing it. So in my
opinion, we need an in kernel implementation first.

I guess you are not going to mainline your driver? The ExaNIC is also
a long way from being mainline'able.

Could i suggest you extend drivers/net/phy/sfp.c to support QSFP-DD?
Or maybe one of Intels or Mellanox drivers? Basically, any hardware
with an in kernel driver you can get your hands on and test. Maybe you
already have something which you use of interoperability testing.

     Andrew

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

* Re: [PATCH] ethtool: Add QSFP-DD support
  2019-11-09 12:42 Adrian Pop
@ 2019-11-09 15:33 ` Andrew Lunn
       [not found]   ` <CAL_jBfQhVAy24xbz_VbpPM0QtRu-Uzawhyn=AY0b41B9=v3Ytg@mail.gmail.com>
       [not found] ` <20191115201508.GF24205@lunn.ch>
  1 sibling, 1 reply; 14+ messages in thread
From: Andrew Lunn @ 2019-11-09 15:33 UTC (permalink / raw)
  To: Adrian Pop; +Cc: netdev, linville

On Sat, Nov 09, 2019 at 02:42:05PM +0200, Adrian Pop wrote:
> The Common Management Interface Specification (CMIS) for QSFP-DD shares
> some similarities with other form factors such as QSFP or SFP, but due to
> the fact that the module memory map is different, the current ethtool
> version is not able to provide relevant information about an interface.
> 
> This patch 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 specifications document.
> 
> Page 0x00 (lower and higher memory) are always implemented, so the ethtool
> expects at least 256 bytes if the identifier matches the one for QSFP-DD.
> For optical connected cables, additional pages are usually available (the
> contain module defined  thresholds or lane diagnostic information). In
> this case, ethtool expects to receive 768 bytes in the following format:
> 
>     +----------+----------+----------+----------+----------+----------+
>     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
>     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
>     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
>     |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
>     +----------+----------+----------+----------+----------+----------

Hi Adrian

Which in kernel driver is using this format? As far as i can see, only
mlx5 makes use of ETH_MODULE_SFF_8436_MAX_LEN, which is 640
bytes. After a very quick look, i could not see any driver returning
768 bytes.

     Andrew

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

* [PATCH] ethtool: Add QSFP-DD support
@ 2019-11-09 12:42 Adrian Pop
  2019-11-09 15:33 ` Andrew Lunn
       [not found] ` <20191115201508.GF24205@lunn.ch>
  0 siblings, 2 replies; 14+ messages in thread
From: Adrian Pop @ 2019-11-09 12:42 UTC (permalink / raw)
  Cc: netdev, linville, Adrian Pop

The Common Management Interface Specification (CMIS) for QSFP-DD shares
some similarities with other form factors such as QSFP or SFP, but due to
the fact that the module memory map is different, the current ethtool
version is not able to provide relevant information about an interface.

This patch 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 specifications document.

Page 0x00 (lower and higher memory) are always implemented, so the ethtool
expects at least 256 bytes if the identifier matches the one for QSFP-DD.
For optical connected cables, additional pages are usually available (the
contain module defined  thresholds or lane diagnostic information). In
this case, ethtool expects to receive 768 bytes in the following format:

    +----------+----------+----------+----------+----------+----------+
    |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
    |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
    |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
    |   128B   |   128B   |   128B   |   128B   |   128B   |   128B   |
    +----------+----------+----------+----------+----------+----------

Several functions from qsfp.c could be reused, so an additional parameter
was added to each and the functions were moved to sff-common.c.

Signed-off-by: Adrian Pop <popadrian1996@gmail.com>
---
 Makefile.am  |   3 +-
 qsfp-dd.c    | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++
 qsfp-dd.h    | 236 ++++++++++++++++++++++
 qsfp.c       |  60 ++----
 qsfp.h       |   8 -
 sff-common.c |  52 +++++
 sff-common.h |  26 ++-
 7 files changed, 891 insertions(+), 54 deletions(-)
 create mode 100644 qsfp-dd.c
 create mode 100644 qsfp-dd.h

diff --git a/Makefile.am b/Makefile.am
index 3af4d4c..cb48bd9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,7 +14,8 @@ ethtool_SOURCES += \
 		  pcnet32.c realtek.c tg3.c marvell.c vioc.c	\
 		  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
+		  ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c \
+		  qsfp-dd.c qsfp-dd.h
 endif
 
 if ENABLE_BASH_COMPLETION
diff --git a/qsfp-dd.c b/qsfp-dd.c
new file mode 100644
index 0000000..be84647
--- /dev/null
+++ b/qsfp-dd.c
@@ -0,0 +1,560 @@
+/**
+ * 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.
+ *
+ * Page 0x00 (lower and higher memory) are always implemented, so the ethtool
+ * expects at least 256 bytes if the identifier matches the one for QSFP-DD.
+ * For optical connected cables, additional pages are usually available (they
+ * contain module defined thresholds or lane diagnostic information). In
+ * this case, ethtool expects to receive 768 bytes in the following format:
+ *
+ *     +----------+----------+----------+----------+----------+----------+
+ *     |   Page   |   Page   |   Page   |   Page   |   Page   |   Page   |
+ *     |   0x00   |   0x00   |   0x01   |   0x02   |   0x10   |   0x11   |
+ *     |  (lower) | (higher) | (higher) | (higher) | (higher) | (higher) |
+ *     |   128b   |   128b   |   128b   |   128b   |   128b   |   128b   |
+ *     +----------+----------+----------+----------+----------+----------+
+ */
+
+#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.2fkm\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));
+	}
+}
+
+/**
+ * Read the high/low alarms or warnings for a specific channel. This
+ * information is found in the i'th bit of each byte associated with
+ * one of the aforementioned properties. A value greater than zero
+ * means the alarm/warning is turned on.
+ * The values are stored in the qsfp_dd_diags structure, in the rxaw
+ * or txaw array (each element corresponds to an alarm/warning).
+ */
+static void qsfp_dd_read_aw_for_channel(const __u8 *id, int ch, int mode,
+					struct qsfp_dd_diags * const sd)
+{
+	__u8 cmsk = (1 << ch);
+
+	if (mode == QSFP_DD_READ_TX) {
+		sd->txaw[ch][HA] = id[QSFP_DD_TX_HA_OFFSET] & cmsk;
+		sd->txaw[ch][LA] = id[QSFP_DD_TX_LA_OFFSET] & cmsk;
+		sd->txaw[ch][HW] = id[QSFP_DD_TX_HW_OFFSET] & cmsk;
+		sd->txaw[ch][LW] = id[QSFP_DD_TX_LW_OFFSET] & cmsk;
+	} else {
+		sd->rxaw[ch][HA] = id[QSFP_DD_RX_HA_OFFSET] & cmsk;
+		sd->rxaw[ch][LA] = id[QSFP_DD_RX_LA_OFFSET] & cmsk;
+		sd->rxaw[ch][HW] = id[QSFP_DD_RX_HW_OFFSET] & cmsk;
+		sd->rxaw[ch][LW] = id[QSFP_DD_RX_LW_OFFSET] & cmsk;
+	}
+}
+
+/*
+ * 2-byte internal temperature conversions:
+ * First byte is a signed 8-bit integer, which is the temp decimal part
+ * Second byte are 1/256th of degree, which are added to the dec part.
+ */
+#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+
+/**
+ * Get and parse relevant diagnostic information for the current module.
+ * These are stored, for every channel, in a qsfp_dd_diags structure.
+ */
+static void
+qsfp_dd_parse_diagnostics(const __u8 *id, struct qsfp_dd_diags *const sd)
+{
+	__u16 rx_power_offset;
+	__u16 tx_power_offset;
+	__u16 tx_bias_offset;
+	__u16 temp_offset;
+	__u16 volt_offset;
+	int i;
+
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		/*
+		 * Add Tx/Rx output/input optical power relevant information.
+		 * To access the info for the ith lane, we have to skip i * 2
+		 * bytes starting from the offset of the first lane for that
+		 * specific channel property.
+		 */
+		tx_bias_offset = QSFP_DD_TX_BIAS_START_OFFSET + (i << 1);
+		rx_power_offset = QSFP_DD_RX_PWR_START_OFFSET + (i << 1);
+		tx_power_offset = QSFP_DD_TX_PWR_START_OFFSET + (i << 1);
+
+		sd->scd[i].bias_cur = OFFSET_TO_U16(tx_bias_offset);
+		sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
+		sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
+
+		/* Add alarms/warnings related info */
+		qsfp_dd_read_aw_for_channel(id, i, QSFP_DD_READ_TX, sd);
+		qsfp_dd_read_aw_for_channel(id, i, QSFP_DD_READ_RX, sd);
+	}
+
+	/**
+	 * Gather Module-Level Monitor Thresholds and Lane-specific Monitor
+	 * Thresholds. These values are stored in two bytes (MSB, LSB) in
+	 * the following order: HA, LA, HW, LW, thus we only need the start
+	 * offset for each property.
+	 */
+	for (i = 0; i < 4; ++i) {
+		tx_power_offset = QSFP_DD_TXPW_THRS_START_OFFSET + (i << 1);
+		sd->tx_power[i] = OFFSET_TO_U16(tx_power_offset);
+
+		rx_power_offset = QSFP_DD_RXPW_THRS_START_OFFSET + (i << 1);
+		sd->rx_power[i] = OFFSET_TO_U16(rx_power_offset);
+
+		tx_bias_offset = QSFP_DD_TXBI_THRS_START_OFFSET + (i << 1);
+		sd->bias_cur[i] = OFFSET_TO_U16(tx_bias_offset);
+
+		temp_offset = QSFP_DD_TEMP_THRS_START_OFFSET + (i << 1);
+		sd->sfp_temp[i] = OFFSET_TO_TEMP(temp_offset);
+
+		volt_offset = QSFP_DD_VOLT_THRS_START_OFFSET + (i << 1);
+		sd->sfp_voltage[i] = OFFSET_TO_U16(volt_offset);
+	}
+}
+
+/**
+ * Print the Module-Level Monitor Thresholds and the Lane-specific
+ * Monitor Thresholds. This is the same function as the one in the
+ * sff-common.c file, but is using a struct qsfp_dd_diags as a pa-
+ * rameter.
+ */
+static void qsfp_dd_show_thresholds(const struct qsfp_dd_diags sd)
+{
+	PRINT_BIAS("Laser bias current high alarm threshold",
+		   sd.bias_cur[HA]);
+	PRINT_BIAS("Laser bias current low alarm threshold",
+		   sd.bias_cur[LA]);
+	PRINT_BIAS("Laser bias current high warning threshold",
+		   sd.bias_cur[HW]);
+	PRINT_BIAS("Laser bias current low warning threshold",
+		   sd.bias_cur[LW]);
+
+	PRINT_xX_PWR("Laser output power high alarm threshold",
+		     sd.tx_power[HA]);
+	PRINT_xX_PWR("Laser output power low alarm threshold",
+		     sd.tx_power[LA]);
+	PRINT_xX_PWR("Laser output power high warning threshold",
+		     sd.tx_power[HW]);
+	PRINT_xX_PWR("Laser output power low warning threshold",
+		     sd.tx_power[LW]);
+
+	PRINT_TEMP("Module temperature high alarm threshold",
+		   sd.sfp_temp[HA]);
+	PRINT_TEMP("Module temperature low alarm threshold",
+		   sd.sfp_temp[LA]);
+	PRINT_TEMP("Module temperature high warning threshold",
+		   sd.sfp_temp[HW]);
+	PRINT_TEMP("Module temperature low warning threshold",
+		   sd.sfp_temp[LW]);
+
+	PRINT_VCC("Module voltage high alarm threshold",
+		  sd.sfp_voltage[HA]);
+	PRINT_VCC("Module voltage low alarm threshold",
+		  sd.sfp_voltage[LA]);
+	PRINT_VCC("Module voltage high warning threshold",
+		  sd.sfp_voltage[HW]);
+	PRINT_VCC("Module voltage low warning threshold",
+		  sd.sfp_voltage[LW]);
+
+	PRINT_xX_PWR("Laser rx power high alarm threshold",
+		     sd.rx_power[HA]);
+	PRINT_xX_PWR("Laser rx power low alarm threshold",
+		     sd.rx_power[LA]);
+	PRINT_xX_PWR("Laser rx power high warning threshold",
+		     sd.rx_power[HW]);
+	PRINT_xX_PWR("Laser rx power low warning threshold",
+		     sd.rx_power[LW]);
+}
+
+/**
+ * Print relevant lane specific monitor information for each of
+ * the 8 available channels. Relevant documents:
+ * [1] CMIS Rev. 3:
+ * --> pag. 50, section 1.7.2.4, Table 22
+ * --> pag. 53, section 1.7.2.7, Table 26
+ * --> pag. 76, section 1.7.5.1, Table 50
+ * --> pag. 78, section 1.7.5.2, Table 51
+ * --> pag. 98, section 1.7.7.2, Table 67
+ *
+ * [2] CMIS Rev. 4:
+ * --> pag. 84, section 8.2.4, Table 8-6
+ * --> pag. 89, section 8.2.9, Table 8-12
+ * --> pag. 112, section 8.5.1/2, Table 8-41/42
+ * --> pag. 137, section 8.8.2, Table 8-60/61
+ * --> pag. 140, section 8.8.3, Table 8-62
+ */
+static void qsfp_dd_show_sig_optical_pwr(const __u8 *id, __u32 eeprom_len)
+{
+	static const char * const aw_strings[] = {
+		"%s power high alarm   (Channel %d)",
+		"%s power low alarm    (Channel %d)",
+		"%s power high warning (Channel %d)",
+		"%s power low warning  (Channel %d)"
+	};
+	__u8 module_type = id[QSFP_DD_MODULE_TYPE_OFFSET];
+	char field_desc[QSFP_DD_MAX_DESC_SIZE];
+	struct qsfp_dd_diags sd = { { 0 } };
+	const char *cs = "%s (Channel %d)";
+	int i, j;
+
+	/* Print current temperature & voltage */
+	PRINT_TEMP("Module temperature",
+		   OFFSET_TO_TEMP(QSFP_DD_CURR_TEMP_OFFSET));
+	PRINT_VCC("Module voltage",
+		  OFFSET_TO_U16(QSFP_DD_CURR_CURR_OFFSET));
+
+	/**
+	 * The thresholds and the high/low alarms/warnings are available
+	 * only if an optical interface (MMF/SMF) is present (if this is
+	 * the case, it means that 5 pages are available).
+	 */
+	if (module_type != QSFP_DD_MT_MMF &&
+	    module_type != QSFP_DD_MT_SMF &&
+	    eeprom_len != QSFP_DD_EEPROM_5PAG)
+		return;
+
+	/* Extract the diagnostic variables */
+	qsfp_dd_parse_diagnostics(id, &sd);
+
+	/* Print Tx bias current monitor values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Tx bias current monitor", i + 1);
+		PRINT_BIAS(field_desc, sd.scd[i].bias_cur);
+	}
+
+	/* Print Tx output optical power values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Tx output optical power", i + 1);
+		PRINT_xX_PWR(field_desc, sd.scd[i].tx_power);
+	}
+
+	/* Print Rx output optical power values */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE, cs,
+			 "Rx input optical power", i + 1);
+		PRINT_xX_PWR(field_desc, sd.scd[i].rx_power);
+	}
+
+	/* Print the Rx alarms/warnings for each channel */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		for (j = 0; j < 4; ++j) {
+			snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE,
+				 aw_strings[j], "Rx", i + 1);
+			printf("\t%-41s : %s\n", field_desc,
+			       ONOFF(sd.rxaw[i][j]));
+		}
+	}
+
+	/* Print the Tx alarms/warnings for each channel */
+	for (i = 0; i < QSFP_DD_MAX_CHANNELS; ++i) {
+		for (j = 0; j < 4; ++j) {
+			snprintf(field_desc, QSFP_DD_MAX_DESC_SIZE,
+				 aw_strings[j], "Tx", i + 1);
+			printf("\t%-41s : %s\n", field_desc,
+			       ONOFF(sd.rxaw[i][j]));
+		}
+	}
+
+	qsfp_dd_show_thresholds(sd);
+}
+
+/**
+ * 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_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");
+}
+
+/**
+ * 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, __u32 eeprom_len)
+{
+	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_sig_optical_pwr(id, eeprom_len);
+	qsfp_dd_show_link_len(id);
+	qsfp_dd_show_vendor_info(id);
+	qsfp_dd_show_rev_compliance(id);
+}
diff --git a/qsfp-dd.h b/qsfp-dd.h
new file mode 100644
index 0000000..a530b2e
--- /dev/null
+++ b/qsfp-dd.h
@@ -0,0 +1,236 @@
+#ifndef QSFP_DD_H__
+#define QSFP_DD_H__
+
+#define QSFP_DD_PAG_SIZE			0x80
+#define QSFP_DD_EEPROM_5PAG			(0x80 * 6)
+#define QSFP_DD_MAX_CHANNELS			0x08
+#define QSFP_DD_MAX_DESC_SIZE			0x2A
+#define QSFP_DD_READ_TX				0x00
+#define QSFP_DD_READ_RX				0x01
+
+/* Struct for the current/power of a channel */
+struct qsfp_dd_channel_diags {
+	__u16 bias_cur;
+	__u16 rx_power;
+	__u16 tx_power;
+};
+
+struct qsfp_dd_diags {
+	/* Voltage in 0.1mV units; the first 4 elements represent
+	 * the high/low alarm, high/low warning and the last one
+	 * represent the current voltage of the module.
+	 */
+	__u16 sfp_voltage[4];
+
+	/**
+	 * Temperature in 16-bit signed 1/256 Celsius; the first 4
+	 * elements represent the high/low alarm, high/low warning
+	 * and the last one represent the current temp of the module.
+	 */
+	__s16 sfp_temp[4];
+
+	/* Tx bias current in 2uA units */
+	__u16 bias_cur[4];
+
+	/* Measured TX Power */
+	__u16 tx_power[4];
+
+	/* Measured RX Power */
+	__u16 rx_power[4];
+
+	/* Rx alarms and warnings */
+	bool rxaw[QSFP_DD_MAX_CHANNELS][4];
+
+	/* Tx alarms and warnings */
+	bool txaw[QSFP_DD_MAX_CHANNELS][4];
+
+	struct qsfp_dd_channel_diags scd[QSFP_DD_MAX_CHANNELS];
+};
+
+#define HA					0
+#define LA					1
+#define HW					2
+#define LW					3
+
+/* 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		0xBD
+
+/* 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
+
+/*-----------------------------------------------------------------------
+ * For optical connected cables (the eeprom length is equal to  640 bytes
+ * = QSFP_DD_EEPROM_WITH_OPTICAL), the memory has the following format:
+ * Bytes   0-127: page  0 (lower)
+ * Bytes 128-255: page  0 (higher)
+ * Bytes 256-383: page  1 (higher)
+ * Bytes 384-511: page  2 (higher)
+ * Bytes 512-639: page 16 (higher)
+ * Bytes 640-768: page 17 (higher)
+ *
+ * Since for pages with an index > 0 the lower part is missing from the memory,
+ * but the offset values are still in the [128, 255) range, the real offset in
+ * the eeprom memory must be calculated as following:
+ * RealOffset = PageIndex * 0x80 + LocalOffset
+
+ * The page index is the index of the page, starting from 0: page 0 has index
+ * 1, page 1 has index 1, page 2 has index 2, page 16 has index 3 and page 17
+ * has index 4.
+ */
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x01: contains advertising fields that define properties
+ * that are unique to active modules and cable assemblies.
+ * RealOffset = 1 * 0x80 + LocalOffset
+ */
+#define PAG01H_OFFSET				(0x01 * 0x80)
+
+/* Supported Link Length (Page 1) */
+#define QSFP_DD_SMF_LEN_OFFSET			(PAG01H_OFFSET + 0x84)
+#define QSFP_DD_OM5_LEN_OFFSET			(PAG01H_OFFSET + 0x85)
+#define QSFP_DD_OM4_LEN_OFFSET			(PAG01H_OFFSET + 0x86)
+#define QSFP_DD_OM3_LEN_OFFSET			(PAG01H_OFFSET + 0x87)
+#define QSFP_DD_OM2_LEN_OFFSET			(PAG01H_OFFSET + 0x88)
+
+/* Wavelength (Page 1) */
+#define QSFP_DD_NOM_WAVELENGTH_MSB		(PAG01H_OFFSET + 0x8A)
+#define QSFP_DD_NOM_WAVELENGTH_LSB		(PAG01H_OFFSET + 0x8B)
+#define QSFP_DD_WAVELENGTH_TOL_MSB		(PAG01H_OFFSET + 0x8C)
+#define QSFP_DD_WAVELENGTH_TOL_LSB		(PAG01H_OFFSET + 0x8D)
+
+/* Signal integrity controls */
+#define QSFP_DD_SIG_INTEG_TX_OFFSET		(PAG01H_OFFSET + 0xA1)
+#define QSFP_DD_SIG_INTEG_RX_OFFSET		(PAG01H_OFFSET + 0xA2)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x02: contains module defined threshdolds and lane-
+ * specific monitors.
+ * RealOffset = 2 * 0x80 + LocalOffset
+ */
+#define PAG02H_OFFSET				(0x02 * 0x80)
+#define QSFP_DD_TEMP_THRS_START_OFFSET		(PAG02H_OFFSET + 0x80)
+#define QSFP_DD_VOLT_THRS_START_OFFSET		(PAG02H_OFFSET + 0x88)
+#define QSFP_DD_TXPW_THRS_START_OFFSET		(PAG02H_OFFSET + 0xB0)
+#define QSFP_DD_TXBI_THRS_START_OFFSET		(PAG02H_OFFSET + 0xB8)
+#define QSFP_DD_RXPW_THRS_START_OFFSET		(PAG02H_OFFSET + 0xC0)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x10: contains dynamic control bytes.
+ * RealOffset = 3 * 0x80 + LocalOffset
+ */
+#define PAG16H_OFFSET				(0x03 * 0x80)
+
+/*-----------------------------------------------------------------------
+ * Upper Memory Page 0x11: contains lane dynamic status bytes.
+ * RealOffset = 4 * 0x80 + LocalOffset
+ */
+#define PAG11H_OFFSET				(0x04 * 0x80)
+#define QSFP_DD_TX_PWR_START_OFFSET		(PAG11H_OFFSET + 0x9A)
+#define QSFP_DD_TX_BIAS_START_OFFSET		(PAG11H_OFFSET + 0xAA)
+#define QSFP_DD_RX_PWR_START_OFFSET		(PAG11H_OFFSET + 0xBA)
+
+/* HA = High Alarm; LA = Low Alarm
+ * HW = High Warning; LW = Low Warning
+ */
+#define QSFP_DD_TX_HA_OFFSET			(PAG11H_OFFSET + 0x8B)
+#define QSFP_DD_TX_LA_OFFSET			(PAG11H_OFFSET + 0x8C)
+#define QSFP_DD_TX_HW_OFFSET			(PAG11H_OFFSET + 0x8D)
+#define QSFP_DD_TX_LW_OFFSET			(PAG11H_OFFSET + 0x8E)
+
+#define QSFP_DD_RX_HA_OFFSET			(PAG11H_OFFSET + 0x95)
+#define QSFP_DD_RX_LA_OFFSET			(PAG11H_OFFSET + 0x96)
+#define QSFP_DD_RX_HW_OFFSET			(PAG11H_OFFSET + 0x97)
+#define QSFP_DD_RX_LW_OFFSET			(PAG11H_OFFSET + 0x98)
+
+#define YESNO(x) (((x) != 0) ? "Yes" : "No")
+#define ONOFF(x) (((x) != 0) ? "On" : "Off")
+
+void qsfp_dd_show_all(const __u8 *id, __u32 eeprom_len);
+
+#endif /* QSFP_DD_H__ */
diff --git a/qsfp.c b/qsfp.c
index d0774b0..f1be5ff 100644
--- a/qsfp.c
+++ b/qsfp.c
@@ -58,6 +58,7 @@
 #include "internal.h"
 #include "sff-common.h"
 #include "qsfp.h"
+#include "qsfp-dd.h"
 
 #define MAX_DESC_SIZE	42
 
@@ -478,9 +479,9 @@ static void sff8636_show_rate_identifier(const __u8 *id)
 			id[SFF8636_EXT_RS_OFFSET]);
 }
 
-static void sff8636_show_oui(const __u8 *id)
+static void sff8636_show_oui(const __u8 *id, int id_offset)
 {
-	sff8024_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
+	sff8024_show_oui(id, id_offset);
 }
 
 static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
@@ -561,38 +562,7 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
 
 static void sff8636_show_revision_compliance(const __u8 *id)
 {
-	static const char *pfx =
-		"\tRevision Compliance                       :";
-
-	switch (id[SFF8636_REV_COMPLIANCE_OFFSET]) {
-	case SFF8636_REV_UNSPECIFIED:
-		printf("%s Revision not specified\n", pfx);
-		break;
-	case SFF8636_REV_8436_48:
-		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8436_8636:
-		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8636_13:
-		printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
-		break;
-	case SFF8636_REV_8636_14:
-		printf("%s SFF-8636 Rev 1.4\n", pfx);
-		break;
-	case SFF8636_REV_8636_15:
-		printf("%s SFF-8636 Rev 1.5\n", pfx);
-		break;
-	case SFF8636_REV_8636_20:
-		printf("%s SFF-8636 Rev 2.0\n", pfx);
-		break;
-	case SFF8636_REV_8636_27:
-		printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
-		break;
-	default:
-		printf("%s Unallocated\n", pfx);
-		break;
-	}
+	sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET);
 }
 
 /*
@@ -745,10 +715,15 @@ static void sff8636_show_dom(const __u8 *id, __u32 eeprom_len)
 
 		sff_show_thresholds(sd);
 	}
-
 }
+
 void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 {
+	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+		qsfp_dd_show_all(id, eeprom_len);
+		return;
+	}
+
 	sff8636_show_identifier(id);
 	if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
@@ -757,6 +732,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 		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);
@@ -771,17 +747,19 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
 		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_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");
+				SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
 		sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
-			       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
+				SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
 		sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
-			       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
+				SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
 		sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
-			       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+				SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+
 		sff8636_show_revision_compliance(id);
 		sff8636_show_dom(id, eeprom_len);
 	}
diff --git a/qsfp.h b/qsfp.h
index b623174..a2dfb63 100644
--- a/qsfp.h
+++ b/qsfp.h
@@ -31,14 +31,6 @@
 #define	SFF8636_ID_OFFSET	0x00
 
 #define	SFF8636_REV_COMPLIANCE_OFFSET	0x01
-#define	 SFF8636_REV_UNSPECIFIED		0x00
-#define	 SFF8636_REV_8436_48			0x01
-#define	 SFF8636_REV_8436_8636			0x02
-#define	 SFF8636_REV_8636_13			0x03
-#define	 SFF8636_REV_8636_14			0x04
-#define	 SFF8636_REV_8636_15			0x05
-#define	 SFF8636_REV_8636_20			0x06
-#define	 SFF8636_REV_8636_27			0x07
 
 #define	SFF8636_STATUS_2_OFFSET	0x02
 /* Flat Memory:0- Paging, 1- Page 0 only */
diff --git a/sff-common.c b/sff-common.c
index 7700cbe..5490b5f 100644
--- a/sff-common.c
+++ b/sff-common.c
@@ -46,6 +46,7 @@ void sff_show_ascii(const __u8 *id, unsigned int first_reg,
 	printf("\t%-41s : ", name);
 	while (first_reg <= last_reg && id[last_reg] == ' ')
 		last_reg--;
+
 	for (reg = first_reg; reg <= last_reg; reg++) {
 		val = id[reg];
 		putchar(((val >= 32) && (val <= 126)) ? val : '_');
@@ -136,6 +137,9 @@ void sff8024_show_identifier(const __u8 *id, int id_offset)
 	case SFF8024_ID_MICRO_QSFP:
 		printf(" (microQSFP)\n");
 		break;
+	case SFF8024_ID_QSFP_DD:
+		printf(" (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))\n");
+		break;
 	default:
 		printf(" (reserved or unknown)\n");
 		break;
@@ -203,6 +207,18 @@ void sff8024_show_connector(const __u8 *id, int ctor_offset)
 	case SFF8024_CTOR_MXC_2x16:
 		printf(" (MXC 2x16)\n");
 		break;
+	case SFF8024_CTOR_CS_OPTICAL:
+		printf(" (CS optical connector)\n");
+		break;
+	case SFF8024_CTOR_CS_OPTICAL_MINI:
+		printf(" (Mini CS optical connector)\n");
+		break;
+	case SFF8024_CTOR_MPO_2X12:
+		printf(" (MPO 2x12)\n");
+		break;
+	case SFF8024_CTOR_MPO_1X16:
+		printf(" (MPO 1x16)\n");
+		break;
 	default:
 		printf(" (reserved or unknown)\n");
 		break;
@@ -302,3 +318,39 @@ void sff_show_thresholds(struct sff_diags sd)
 	PRINT_xX_PWR("Laser rx power low warning threshold",
 		     sd.rx_power[LWARN]);
 }
+
+void sff_show_revision_compliance(const __u8 *id, int rev_offset)
+{
+	static const char *pfx =
+		"\tRevision Compliance                       :";
+
+	switch (id[rev_offset]) {
+	case SFF8636_REV_UNSPECIFIED:
+		printf("%s Revision not specified\n", pfx);
+		break;
+	case SFF8636_REV_8436_48:
+		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8436_8636:
+		printf("%s SFF-8436 Rev 4.8 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8636_13:
+		printf("%s SFF-8636 Rev 1.3 or earlier\n", pfx);
+		break;
+	case SFF8636_REV_8636_14:
+		printf("%s SFF-8636 Rev 1.4\n", pfx);
+		break;
+	case SFF8636_REV_8636_15:
+		printf("%s SFF-8636 Rev 1.5\n", pfx);
+		break;
+	case SFF8636_REV_8636_20:
+		printf("%s SFF-8636 Rev 2.0\n", pfx);
+		break;
+	case SFF8636_REV_8636_27:
+		printf("%s SFF-8636 Rev 2.5/2.6/2.7\n", pfx);
+		break;
+	default:
+		printf("%s Unallocated\n", pfx);
+		break;
+	}
+}
diff --git a/sff-common.h b/sff-common.h
index 5562b4d..0d04851 100644
--- a/sff-common.h
+++ b/sff-common.h
@@ -26,7 +26,17 @@
 #include <stdio.h>
 #include "internal.h"
 
-#define SFF8024_ID_OFFSET				0x00
+/* Revision compliance */
+#define	 SFF8636_REV_UNSPECIFIED		0x00
+#define	 SFF8636_REV_8436_48			0x01
+#define	 SFF8636_REV_8436_8636			0x02
+#define	 SFF8636_REV_8636_13			0x03
+#define	 SFF8636_REV_8636_14			0x04
+#define	 SFF8636_REV_8636_15			0x05
+#define	 SFF8636_REV_8636_20			0x06
+#define	 SFF8636_REV_8636_27			0x07
+
+#define  SFF8024_ID_OFFSET				0x00
 #define  SFF8024_ID_UNKNOWN				0x00
 #define  SFF8024_ID_GBIC				0x01
 #define  SFF8024_ID_SOLDERED_MODULE		0x02
@@ -51,7 +61,8 @@
 #define  SFF8024_ID_HD8X_FANOUT			0x15
 #define  SFF8024_ID_CDFP_S3				0x16
 #define  SFF8024_ID_MICRO_QSFP			0x17
-#define  SFF8024_ID_LAST				SFF8024_ID_MICRO_QSFP
+#define  SFF8024_ID_QSFP_DD				0x18
+#define  SFF8024_ID_LAST				SFF8024_ID_QSFP_DD
 #define  SFF8024_ID_UNALLOCATED_LAST	0x7F
 #define  SFF8024_ID_VENDOR_START		0x80
 #define  SFF8024_ID_VENDOR_LAST			0xFF
@@ -76,8 +87,14 @@
 #define  SFF8024_CTOR_RJ45				0x22
 #define  SFF8024_CTOR_NO_SEPARABLE		0x23
 #define  SFF8024_CTOR_MXC_2x16			0x24
-#define  SFF8024_CTOR_LAST				SFF8024_CTOR_MXC_2x16
-#define  SFF8024_CTOR_UNALLOCATED_LAST	0x7F
+#define  SFF8024_CTOR_CS_OPTICAL		0x25
+#define  SFF8024_CTOR_CS_OPTICAL_MINI		0x26
+#define  SFF8024_CTOR_MPO_2X12		    	0x27
+#define  SFF8024_CTOR_MPO_1X16		    	0x28
+#define  SFF8024_CTOR_LAST			SFF8024_CTOR_MPO_1X16
+
+#define  SFF8024_CTOR_NO_SEP_QSFP_DD 		0x6F
+#define  SFF8024_CTOR_UNALLOCATED_LAST		0x7F
 #define  SFF8024_CTOR_VENDOR_START		0x80
 #define  SFF8024_CTOR_VENDOR_LAST		0xFF
 
@@ -185,5 +202,6 @@ void sff8024_show_oui(const __u8 *id, int id_offset);
 void sff8024_show_identifier(const __u8 *id, int id_offset);
 void sff8024_show_connector(const __u8 *id, int ctor_offset);
 void sff8024_show_encoding(const __u8 *id, int encoding_offset, int sff_type);
+void sff_show_revision_compliance(const __u8 *id, int rev_offset);
 
 #endif /* SFF_COMMON_H__ */
-- 
2.17.1


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

end of thread, other threads:[~2020-08-06 17:14 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-31  8:47 [PATCH] ethtool: Add QSFP-DD support Adrian Pop
2020-08-02 16:55 ` Andrew Lunn
2020-08-04  7:44 ` Ido Schimmel
2020-08-04  8:23   ` Adrian Pop
2020-08-04 10:14 ` Michal Kubecek
2020-08-04 10:25   ` Adrian Pop
2020-08-04 17:18     ` Michal Kubecek
2020-08-06 15:13       ` Adrian Pop
2020-08-04 10:30 ` Michal Kubecek
  -- strict thread matches above, loose matches on Subject: below --
2019-11-09 12:42 Adrian Pop
2019-11-09 15:33 ` Andrew Lunn
     [not found]   ` <CAL_jBfQhVAy24xbz_VbpPM0QtRu-Uzawhyn=AY0b41B9=v3Ytg@mail.gmail.com>
2019-11-13 13:59     ` Andrew Lunn
2019-11-15 19:00       ` Adrian Pop
     [not found] ` <20191115201508.GF24205@lunn.ch>
2019-11-15 21:34   ` Adrian Pop

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.