vnet: add SFF8472 and SFF8636 diagnostics 44/43544/16
authorPim van Pelt <[email protected]>
Wed, 13 Aug 2025 22:02:34 +0000 (00:02 +0200)
committerDamjan Marion <[email protected]>
Thu, 28 Aug 2025 19:10:14 +0000 (19:10 +0000)
Using device-class.eeprom_read_function, extract the type of
EEPROM, and create a parser for SFF8472 (SFP+/SFP/SFP28) and
SFF8636 (QSFP+/QSFP28/QSFP-DD) to show module diagnostics.

When available, read EEPROM page A2h and report on DDM values:
show int transceiver <itf> eeprom
show int transceiver <itf> module [verbose]
show int transceiver <itf> diag [verbose]

Type: improvement
Change-Id: Iae41b9753f31bc1a8d32b2c42d396cd743864147
Signed-off-by: [email protected]
src/vnet/CMakeLists.txt
src/vnet/ethernet/sfp.c
src/vnet/ethernet/sfp.h
src/vnet/ethernet/sfp_sff8472.c [new file with mode: 0644]
src/vnet/ethernet/sfp_sff8472.h [new file with mode: 0644]
src/vnet/ethernet/sfp_sff8636.c [new file with mode: 0644]
src/vnet/ethernet/sfp_sff8636.h [new file with mode: 0644]
src/vnet/interface.c
src/vnet/interface.h
src/vnet/interface_cli.c

index e3abe93..03dbe03 100644 (file)
@@ -160,6 +160,8 @@ list(APPEND VNET_SOURCES
   ethernet/node.c
   ethernet/pg.c
   ethernet/sfp.c
+  ethernet/sfp_sff8472.c
+  ethernet/sfp_sff8636.c
   ethernet/p2p_ethernet.c
   ethernet/p2p_ethernet_input.c
   ethernet/p2p_ethernet_api.c
@@ -177,6 +179,8 @@ list(APPEND VNET_HEADERS
   ethernet/packet.h
   ethernet/types.def
   ethernet/sfp.h
+  ethernet/sfp_sff8472.h
+  ethernet/sfp_sff8636.h
   ethernet/p2p_ethernet.h
   ethernet/arp_packet.h
 )
index 182fdbf..44de49b 100644 (file)
@@ -14,6 +14,8 @@
  */
 
 #include <vnet/ethernet/sfp.h>
+#include <vnet/ethernet/sfp_sff8472.h>
+#include <vnet/ethernet/sfp_sff8636.h>
 
 static u8 *
 format_space_terminated (u8 * s, va_list * args)
@@ -28,8 +30,8 @@ format_space_terminated (u8 * s, va_list * args)
   return s;
 }
 
-static u8 *
-format_sfp_id (u8 * s, va_list * args)
+u8 *
+format_sfp_id (u8 *s, va_list *args)
 {
   u32 id = va_arg (*args, u32);
   char *t = 0;
@@ -44,6 +46,42 @@ format_sfp_id (u8 * s, va_list * args)
   return format (s, "%s", t);
 }
 
+u8 *
+format_sfp_connector (u8 *s, va_list *args)
+{
+  u32 connector = va_arg (*args, u32);
+  char *t = 0;
+  switch (connector)
+    {
+#define _(v, str)                                                             \
+  case v:                                                                     \
+    t = str;                                                                  \
+    break;
+      foreach_sfp_connector
+#undef _
+       default : return format (s, "unknown 0x%x", connector);
+    }
+  return format (s, "%s", t);
+}
+
+u8 *
+format_sfp_encoding (u8 *s, va_list *args)
+{
+  u32 encoding = va_arg (*args, u32);
+  char *t = 0;
+  switch (encoding)
+    {
+#define _(v, str)                                                             \
+  case v:                                                                     \
+    t = str;                                                                  \
+    break;
+      foreach_sfp_encoding
+#undef _
+       default : return format (s, "unknown 0x%x", encoding);
+    }
+  return format (s, "%s", t);
+}
+
 static u8 *
 format_sfp_compatibility (u8 * s, va_list * args)
 {
@@ -60,8 +98,8 @@ format_sfp_compatibility (u8 * s, va_list * args)
   return format (s, "%s", t);
 }
 
-u32
-sfp_is_comatible (sfp_eeprom_t * e, sfp_compatibility_t c)
+static u32
+sfp_is_compatible (sfp_eeprom_t *e, sfp_compatibility_t c)
 {
   static struct
   {
@@ -88,7 +126,7 @@ format_sfp_eeprom (u8 * s, va_list * args)
 
   s = format (s, "compatibility:");
   for (i = 0; i < SFP_N_COMPATIBILITY; i++)
-    if (sfp_is_comatible (e, i))
+    if (sfp_is_compatible (e, i))
       s = format (s, " %U", format_sfp_compatibility, i);
 
   s = format (s, "\n%Uvendor: %U, part %U",
@@ -111,6 +149,131 @@ format_sfp_eeprom (u8 * s, va_list * args)
   return s;
 }
 
+void
+sfp_eeprom_decode_base (vlib_main_t *vm, sfp_eeprom_t *se, u8 is_terse,
+                       vnet_interface_eeprom_type_t eeprom_type)
+{
+  u8 vendor_name[17] = { 0 };
+  u8 vendor_pn[17] = { 0 };
+  u8 vendor_rev[5] = { 0 };
+  u8 vendor_sn[17] = { 0 };
+  u8 date_code[9] = { 0 };
+  u16 wavelength;
+
+  vlib_cli_output (vm, "  Module Base Information:");
+  /* Vendor information */
+  clib_memcpy (vendor_name, se->vendor_name, 16);
+  /* Trim trailing spaces */
+  for (int i = 15; i >= 0 && vendor_name[i] == ' '; i--)
+    vendor_name[i] = '\0';
+  vlib_cli_output (vm, "    Vendor Name: %s", vendor_name);
+
+  vlib_cli_output (vm, "    Vendor OUI: %02x:%02x:%02x", se->vendor_oui[0],
+                  se->vendor_oui[1], se->vendor_oui[2]);
+
+  clib_memcpy (vendor_pn, se->vendor_part_number, 16);
+  /* Trim trailing spaces */
+  for (int i = 15; i >= 0 && vendor_pn[i] == ' '; i--)
+    vendor_pn[i] = '\0';
+  vlib_cli_output (vm, "    Vendor Part Number: %s", vendor_pn);
+
+  clib_memcpy (vendor_sn, se->vendor_serial_number, 16);
+  /* Trim trailing spaces */
+  for (int i = 15; i >= 0 && vendor_sn[i] == ' '; i--)
+    vendor_sn[i] = '\0';
+  vlib_cli_output (vm, "    Vendor Serial Number: %s", vendor_sn);
+
+  if (is_terse)
+    return;
+
+  vlib_cli_output (vm, "    Identifier: 0x%02x (%U)", se->id, format_sfp_id,
+                  se->id);
+  vlib_cli_output (vm, "    Extended Identifier: 0x%02x", se->extended_id);
+  vlib_cli_output (vm, "    Connector: 0x%02x (%U)", se->connector_type,
+                  format_sfp_connector, se->connector_type);
+  vlib_cli_output (vm, "    Encoding: 0x%02x (%U)", se->encoding,
+                  format_sfp_encoding, se->encoding);
+  vlib_cli_output (vm, "    Nominal Bit Rate: %u00 Mbps",
+                  se->nominal_bit_rate_100mbits_per_sec);
+
+  /* Length information */
+  if (se->length[0])
+    vlib_cli_output (vm, "    Length (SMF): %u km", se->length[0]);
+  if (se->length[1])
+    vlib_cli_output (vm, "    Length (SMF): %u00 m", se->length[1]);
+  if (se->length[2])
+    vlib_cli_output (vm, "    Length (OM2 50um): %u0 m", se->length[2]);
+  if (se->length[3])
+    vlib_cli_output (vm, "    Length (OM1 62.5um): %u0 m", se->length[3]);
+  if (se->length[4])
+    vlib_cli_output (vm, "    Length (Copper/OM3): %u m", se->length[4]);
+
+  /* NOTE(pim): SFF8472 specifies a 4 byte vendor revision followed by a 2-byte
+   * wavelength_or_att. However SFF8636 specifies a 2 byte vendor revision
+   * followed by a 2 byte wavelength_or_att, followed by a 2 byte
+   * wavelength_tolerance */
+  if (eeprom_type == VNET_INTERFACE_EEPROM_TYPE_SFF8472)
+    {
+      clib_memcpy (vendor_rev, se->vendor_revision, 4);
+      /* Trim trailing spaces */
+      for (int i = 3; i >= 0 && vendor_rev[i] == ' '; i--)
+       vendor_rev[i] = '\0';
+      vlib_cli_output (vm, "    Vendor Revision: %s", vendor_rev);
+
+      wavelength = clib_net_to_host_u16 (se->wavelength_or_att);
+      if (wavelength)
+       vlib_cli_output (vm, "    Wavelength: %u nm", wavelength);
+    }
+  if (eeprom_type == VNET_INTERFACE_EEPROM_TYPE_SFF8636 ||
+      eeprom_type == VNET_INTERFACE_EEPROM_TYPE_SFF8436)
+    {
+
+      clib_memcpy (vendor_rev, se->vendor_revision, 2);
+      /* Trim trailing spaces */
+      for (int i = 1; i >= 0 && vendor_rev[i] == ' '; i--)
+       vendor_rev[i] = '\0';
+      vlib_cli_output (vm, "    Vendor Revision: %s", vendor_rev);
+
+      u16 sff8636_wavelength_or_att =
+       (se->vendor_revision[2] << 8) + se->vendor_revision[3];
+      f64 wl_nm = ((f64) sff8636_wavelength_or_att) * 0.05;
+      vlib_cli_output (vm, "    Wavelength: %.3f nm", wl_nm);
+    }
+
+  clib_memcpy (date_code, se->vendor_date_code, 8);
+  vlib_cli_output (vm, "    Date Code: %.8s", date_code);
+}
+
+void
+sfp_eeprom_module (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                  u8 is_terse)
+{
+  sfp_eeprom_t *se = (sfp_eeprom_t *) eeprom->eeprom_raw;
+  if (eeprom->eeprom_type == VNET_INTERFACE_EEPROM_TYPE_SFF8636 ||
+      eeprom->eeprom_type == VNET_INTERFACE_EEPROM_TYPE_SFF8436)
+    {
+      se = (sfp_eeprom_t *) (eeprom->eeprom_raw + 0x80);
+    }
+
+  return sfp_eeprom_decode_base (vm, se, is_terse, eeprom->eeprom_type);
+}
+
+void
+sfp_eeprom_diagnostics (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                       u8 is_terse)
+{
+  switch (eeprom->eeprom_type)
+    {
+    case VNET_INTERFACE_EEPROM_TYPE_SFF8472:
+      return sff8472_decode_diagnostics (vm, eeprom, is_terse);
+    case VNET_INTERFACE_EEPROM_TYPE_SFF8436:
+    case VNET_INTERFACE_EEPROM_TYPE_SFF8636:
+      return sff8636_decode_diagnostics (vm, eeprom, is_terse);
+    default:
+      vlib_cli_output (vm, "  Module Diagnostics: not availalbe");
+    }
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
index f4c62aa..b19cba3 100644 (file)
@@ -17,6 +17,7 @@
 #define included_vnet_optics_sfp_h
 
 #include <vppinfra/format.h>
+#include <vnet/interface.h>
 
 #define foreach_sfp_id                         \
   _ (UNKNOWN, "unknown")                       \
@@ -66,18 +67,24 @@ typedef struct
   u8 ext_module_codes;
   u8 vendor_oui[3];
   u8 vendor_part_number[16];
-  u8 vendor_revision[2];
-  /* 16 bit value network byte order. */
-  u8 wavelength_or_att[2];
-  u8 wavelength_tolerance_or_att[2];
-  u8 max_case_temp;
-  u8 cc_base;
-
-  u8 link_codes;
-  u8 options[3];
+
+  /* NOTE: SFF8472 defines vendor revision as 4 bytes, followed by 2 bytes of
+   * wavelength_or_att SFF8636 defines vendor revision as 2 bytes, followed by
+   * u16 wavelength_or_att, then u16 wavelength_tolerance
+   */
+  u8 vendor_revision[4]; /* SFF8636 has wavelength in vendor_revision[2+3] */
+  u16 wavelength_or_att; /* SFF8472 has wavelength here; SFF8636 has
+                           wavelength_tolerance here */
+
+  u8 reserved_62; /* Byte 62: Reserved */
+  u8 cc_base;    /* Byte 63: checksum for first 64 bytes */
+  u8 option_values[2];
+  u8 signalling_rate_max;
+  u8 signalling_rate_min;
   u8 vendor_serial_number[16];
   u8 vendor_date_code[8];
-  u8 reserved92[3];
+  u8 diag_monitoring_type; /* Byte 92 */
+  u8 reserved93[2];
   u8 checksum_63_to_94;
   u8 vendor_specific[32];
   u8 reserved128[384];
@@ -96,6 +103,16 @@ sfp_eeprom_is_valid (sfp_eeprom_t * e)
   return sum == e->cc_base;
 }
 
+/* Show the EEPROM module information */
+void sfp_eeprom_module (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                       u8 is_terse);
+void sfp_eeprom_diagnostics (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                            u8 is_terse);
+
+/* Base SFP EEPROM decoding function */
+void sfp_eeprom_decode_base (vlib_main_t *vm, sfp_eeprom_t *se, u8 is_terse,
+                            vnet_interface_eeprom_type_t eeprom_type);
+
 /* _ (byte_index, bit_index, name) */
 #define foreach_sfp_compatibility              \
   _ (0, 0, 40g_active_cable)                   \
@@ -128,9 +145,58 @@ typedef enum
     SFP_N_COMPATIBILITY,
 } sfp_compatibility_t;
 
-u32 sfp_is_comatible (sfp_eeprom_t * e, sfp_compatibility_t c);
+#define foreach_sfp_encoding                                                  \
+  _ (0x01, "8B/10B")                                                          \
+  _ (0x02, "4B/5B")                                                           \
+  _ (0x03, "NRZ")                                                             \
+  _ (0x04, "4B/5B (FC-100)")                                                  \
+  _ (0x05, "Manchester")                                                      \
+  _ (0x06, "64B/66B")                                                         \
+  _ (0x07, "256B/257B")                                                       \
+  _ (0x08, "PAM4")
+
+typedef enum
+{
+#define _(v, s) SFP_ENCODING_##v = v,
+  foreach_sfp_encoding
+#undef _
+} sfp_encoding_t;
+
+#define foreach_sfp_connector                                                 \
+  _ (0x01, "SC")                                                              \
+  _ (0x02, "Fibre Channel Style 1 copper")                                    \
+  _ (0x03, "Fibre Channel Style 2 copper")                                    \
+  _ (0x04, "BNC/TNC")                                                         \
+  _ (0x05, "Fibre Channel coaxial")                                           \
+  _ (0x06, "Fiber Jack")                                                      \
+  _ (0x07, "LC")                                                              \
+  _ (0x08, "MT-RJ")                                                           \
+  _ (0x09, "MU")                                                              \
+  _ (0x0A, "SG")                                                              \
+  _ (0x0B, "Optical pigtail")                                                 \
+  _ (0x0C, "MPO 1x12 Parallel Optic")                                         \
+  _ (0x0D, "MPO 2x16 Parallel Optic")                                         \
+  _ (0x20, "HSSDC II")                                                        \
+  _ (0x21, "Copper pigtail")                                                  \
+  _ (0x22, "RJ45")                                                            \
+  _ (0x23, "No separable connector")                                          \
+  _ (0x24, "MXC 2x16")                                                        \
+  _ (0x25, "CS optical connector")                                            \
+  _ (0x26, "SN optical connector")                                            \
+  _ (0x27, "MPO 2x12 Parallel Optic")                                         \
+  _ (0x28, "MPO 1x16 Parallel Optic")
+
+typedef enum
+{
+#define _(v, s) SFP_CONNECTOR_##v = v,
+  foreach_sfp_connector
+#undef _
+} sfp_connector_t;
 
 format_function_t format_sfp_eeprom;
+format_function_t format_sfp_id;
+format_function_t format_sfp_encoding;
+format_function_t format_sfp_connector;
 
 #endif /* included_vnet_optics_sfp_h */
 
diff --git a/src/vnet/ethernet/sfp_sff8472.c b/src/vnet/ethernet/sfp_sff8472.c
new file mode 100644 (file)
index 0000000..a446716
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2025 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+#include <vlib/vlib.h>
+#include <vppinfra/clib.h>
+#include <vnet/ethernet/sfp_sff8472.h>
+
+static f64
+sff8472_convert_temperature (u16 raw_temp)
+{
+  i16 temp = (i16) raw_temp;
+  return (f64) temp / 256.0;
+}
+
+static f64
+sff8472_convert_voltage (u16 raw_voltage)
+{
+  return (f64) raw_voltage / 10000.0;
+}
+
+static f64
+sff8472_convert_current (u16 raw_current)
+{
+  return (f64) raw_current * 2.0 / 1000.0;
+}
+
+static f64
+sff8472_convert_power (u16 raw_power)
+{
+  return (f64) raw_power / 10000.0;
+}
+
+static f64
+sff8472_mw_to_dbm (f64 power_mw)
+{
+  if (power_mw <= 0.0)
+    return -40.0; /* Use -40 dBm for zero/negative power to avoid log(0) */
+  return 10.0 * log10 (power_mw);
+}
+
+static f64
+sff8472_ieee754_to_f64 (u32 ieee754_be)
+{
+  union
+  {
+    u32 u;
+    f32 f;
+  } converter;
+  converter.u = clib_net_to_host_u32 (ieee754_be);
+  return (f64) converter.f;
+}
+
+static void
+sff8472_apply_calibration (sff8472_diag_t *diag, f64 *temp, f64 *vcc,
+                          f64 *tx_bias, f64 *tx_power, f64 *rx_power)
+{
+  f64 temp_slope, temp_offset;
+  f64 vcc_slope, vcc_offset;
+  f64 bias_slope, bias_offset;
+  f64 txpwr_slope, txpwr_offset;
+  f64 rx_raw;
+  f64 rxpwr_cal[5];
+  int i;
+
+  /* Extract calibration constants */
+  temp_slope = (f64) clib_net_to_host_u16 (diag->temp_slope) / 256.0;
+  temp_offset = (f64) (i16) clib_net_to_host_u16 (diag->temp_offset);
+  *temp = (*temp * temp_slope) + temp_offset;
+
+  vcc_slope = (f64) clib_net_to_host_u16 (diag->voltage_slope) / 256.0;
+  vcc_offset = (f64) (i16) clib_net_to_host_u16 (diag->voltage_offset);
+  *vcc = (*vcc * vcc_slope) + vcc_offset;
+
+  bias_slope = (f64) clib_net_to_host_u16 (diag->tx_bias_slope) / 256.0;
+  bias_offset = (f64) (i16) clib_net_to_host_u16 (diag->tx_bias_offset);
+  *tx_bias = (*tx_bias * bias_slope) + bias_offset;
+
+  txpwr_slope = (f64) clib_net_to_host_u16 (diag->tx_power_slope) / 256.0;
+  txpwr_offset = (f64) (i16) clib_net_to_host_u16 (diag->tx_power_offset);
+  *tx_power = (*tx_power * txpwr_slope) + txpwr_offset;
+
+  /* Apply polynomial calibration for RX Power
+   * SFF-8472 section 9.3 External Calibration
+   */
+  for (i = 0; i < 5; i++)
+    {
+      rxpwr_cal[i] = sff8472_ieee754_to_f64 (diag->rx_power_cal[i]);
+    }
+
+  rx_raw = *rx_power;
+  *rx_power = rxpwr_cal[0] + (rx_raw * rxpwr_cal[1]) +
+             (rx_raw * rx_raw * rxpwr_cal[2]) +
+             (rx_raw * rx_raw * rx_raw * rxpwr_cal[3]) +
+             (rx_raw * rx_raw * rx_raw * rx_raw * rxpwr_cal[4]);
+}
+
+void
+sff8472_decode_diagnostics (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                           u8 is_terse)
+{
+  f64 temp, vcc, tx_bias, tx_power, rx_power;
+  f64 temp_high_alarm, temp_low_alarm, temp_high_warn, temp_low_warn;
+  f64 vcc_high_alarm, vcc_low_alarm, vcc_high_warn, vcc_low_warn;
+  f64 bias_high_alarm, bias_low_alarm, bias_high_warn, bias_low_warn;
+  f64 tx_power_high_alarm, tx_power_low_alarm, tx_power_high_warn,
+    tx_power_low_warn;
+  f64 rx_power_high_alarm, rx_power_low_alarm, rx_power_high_warn,
+    rx_power_low_warn;
+
+  vlib_cli_output (vm, "  Module Diagnostics:");
+  if (eeprom->eeprom_len <= 256)
+    {
+      vlib_cli_output (vm, "    Not supported (no A2h data)");
+      return;
+    }
+
+  u8 *eeprom_data_a2 = eeprom->eeprom_raw;
+  /* Check if we have A0h+A2h (512 bytes) */
+  if (eeprom->eeprom_len >= 512)
+    {
+      eeprom_data_a2 = eeprom->eeprom_raw + 256; /* A2 starts at byte 256 */
+    }
+
+  sff8472_diag_t *diag = (sff8472_diag_t *) eeprom_data_a2;
+  temp =
+    sff8472_convert_temperature (clib_net_to_host_u16 (diag->temperature));
+  vcc = sff8472_convert_voltage (clib_net_to_host_u16 (diag->vcc));
+  tx_bias = sff8472_convert_current (clib_net_to_host_u16 (diag->tx_bias));
+  tx_power = sff8472_convert_power (clib_net_to_host_u16 (diag->tx_power));
+  rx_power = sff8472_convert_power (clib_net_to_host_u16 (diag->rx_power));
+
+  /* Check if external calibration is required (A0 page byte 92, bit 4) */
+  if (eeprom->eeprom_len >= 512 && (eeprom->eeprom_raw[92] & 0x10))
+    {
+      sff8472_apply_calibration (diag, &temp, &vcc, &tx_bias, &tx_power,
+                                &rx_power);
+    }
+
+  vlib_cli_output (vm, "    Current Values:");
+  vlib_cli_output (vm, "      Temperature: %.2f °C", temp);
+  vlib_cli_output (vm, "      Supply Voltage: %.4f V", vcc);
+  vlib_cli_output (vm, "      TX Bias Current: %.2f mA", tx_bias);
+  vlib_cli_output (vm, "      TX Average Power: %.4f mW (%.2f dBm)", tx_power,
+                  sff8472_mw_to_dbm (tx_power));
+  vlib_cli_output (vm, "      RX Average Power: %.4f mW (%.2f dBm)", rx_power,
+                  sff8472_mw_to_dbm (rx_power));
+
+  if (is_terse || eeprom->eeprom_len <= 256 + sizeof (sff8472_diag_t))
+    {
+      return;
+    }
+
+  temp_high_alarm =
+    sff8472_convert_temperature (clib_net_to_host_u16 (diag->temp_high_alarm));
+  temp_low_alarm =
+    sff8472_convert_temperature (clib_net_to_host_u16 (diag->temp_low_alarm));
+  temp_high_warn = sff8472_convert_temperature (
+    clib_net_to_host_u16 (diag->temp_high_warning));
+  temp_low_warn = sff8472_convert_temperature (
+    clib_net_to_host_u16 (diag->temp_low_warning));
+
+  vcc_high_alarm =
+    sff8472_convert_voltage (clib_net_to_host_u16 (diag->voltage_high_alarm));
+  vcc_low_alarm =
+    sff8472_convert_voltage (clib_net_to_host_u16 (diag->voltage_low_alarm));
+  vcc_high_warn = sff8472_convert_voltage (
+    clib_net_to_host_u16 (diag->voltage_high_warning));
+  vcc_low_warn =
+    sff8472_convert_voltage (clib_net_to_host_u16 (diag->voltage_low_warning));
+
+  bias_high_alarm =
+    sff8472_convert_current (clib_net_to_host_u16 (diag->bias_high_alarm));
+  bias_low_alarm =
+    sff8472_convert_current (clib_net_to_host_u16 (diag->bias_low_alarm));
+  bias_high_warn =
+    sff8472_convert_current (clib_net_to_host_u16 (diag->bias_high_warning));
+  bias_low_warn =
+    sff8472_convert_current (clib_net_to_host_u16 (diag->bias_low_warning));
+
+  tx_power_high_alarm =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->tx_power_high_alarm));
+  tx_power_low_alarm =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->tx_power_low_alarm));
+  tx_power_high_warn =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->tx_power_high_warning));
+  tx_power_low_warn =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->tx_power_low_warning));
+
+  rx_power_high_alarm =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->rx_power_high_alarm));
+  rx_power_low_alarm =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->rx_power_low_alarm));
+  rx_power_high_warn =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->rx_power_high_warning));
+  rx_power_low_warn =
+    sff8472_convert_power (clib_net_to_host_u16 (diag->rx_power_low_warning));
+
+  vlib_cli_output (vm, "    Alarm Thresholds:");
+  vlib_cli_output (vm, "      Temperature High: %.2f °C, Low: %.2f °C",
+                  temp_high_alarm, temp_low_alarm);
+  vlib_cli_output (vm, "      Voltage High: %.4f V, Low: %.4f V",
+                  vcc_high_alarm, vcc_low_alarm);
+  vlib_cli_output (vm, "      Bias Current High: %.2f mA, Low: %.2f mA",
+                  bias_high_alarm, bias_low_alarm);
+  vlib_cli_output (
+    vm, "      TX Power High: %.4f mW (%.2f dBm), Low: %.4f mW (%.2f dBm)",
+    tx_power_high_alarm, sff8472_mw_to_dbm (tx_power_high_alarm),
+    tx_power_low_alarm, sff8472_mw_to_dbm (tx_power_low_alarm));
+  vlib_cli_output (
+    vm, "      RX Power High: %.4f mW (%.2f dBm), Low: %.4f mW (%.2f dBm)",
+    rx_power_high_alarm, sff8472_mw_to_dbm (rx_power_high_alarm),
+    rx_power_low_alarm, sff8472_mw_to_dbm (rx_power_low_alarm));
+
+  vlib_cli_output (vm, "    Warning Thresholds:");
+  vlib_cli_output (vm, "      Temperature High: %.2f °C, Low: %.2f °C",
+                  temp_high_warn, temp_low_warn);
+  vlib_cli_output (vm, "      Voltage High: %.4f V, Low: %.4f V",
+                  vcc_high_warn, vcc_low_warn);
+  vlib_cli_output (vm, "      Bias Current High: %.2f mA, Low: %.2f mA",
+                  bias_high_warn, bias_low_warn);
+  vlib_cli_output (
+    vm, "      TX Power High: %.4f mW (%.2f dBm), Low: %.4f mW (%.2f dBm)",
+    tx_power_high_warn, sff8472_mw_to_dbm (tx_power_high_warn),
+    tx_power_low_warn, sff8472_mw_to_dbm (tx_power_low_warn));
+  vlib_cli_output (
+    vm, "      RX Power High: %.4f mW (%.2f dBm), Low: %.4f mW (%.2f dBm)",
+    rx_power_high_warn, sff8472_mw_to_dbm (rx_power_high_warn),
+    rx_power_low_warn, sff8472_mw_to_dbm (rx_power_low_warn));
+}
diff --git a/src/vnet/ethernet/sfp_sff8472.h b/src/vnet/ethernet/sfp_sff8472.h
new file mode 100644 (file)
index 0000000..e54005a
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2025 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_ethernet_sff8472_h__
+#define __included_ethernet_sff8472_h__
+
+#include <vlib/vlib.h>
+#include <vppinfra/types.h>
+#include <vnet/ethernet/sfp.h>
+
+/* SFF-8472 A2 page - Diagnostic fields */
+typedef struct
+{
+  /* Alarm and warning thresholds (bytes 0-55) */
+  u16 temp_high_alarm;      /* 0-1: Temperature high alarm */
+  u16 temp_low_alarm;       /* 2-3: Temperature low alarm */
+  u16 temp_high_warning;     /* 4-5: Temperature high warning */
+  u16 temp_low_warning;             /* 6-7: Temperature low warning */
+  u16 voltage_high_alarm;    /* 8-9: Voltage high alarm */
+  u16 voltage_low_alarm;     /* 10-11: Voltage low alarm */
+  u16 voltage_high_warning;  /* 12-13: Voltage high warning */
+  u16 voltage_low_warning;   /* 14-15: Voltage low warning */
+  u16 bias_high_alarm;      /* 16-17: Bias high alarm */
+  u16 bias_low_alarm;       /* 18-19: Bias low alarm */
+  u16 bias_high_warning;     /* 20-21: Bias high warning */
+  u16 bias_low_warning;             /* 22-23: Bias low warning */
+  u16 tx_power_high_alarm;   /* 24-25: TX power high alarm */
+  u16 tx_power_low_alarm;    /* 26-27: TX power low alarm */
+  u16 tx_power_high_warning; /* 28-29: TX power high warning */
+  u16 tx_power_low_warning;  /* 30-31: TX power low warning */
+  u16 rx_power_high_alarm;   /* 32-33: RX power high alarm */
+  u16 rx_power_low_alarm;    /* 34-35: RX power low alarm */
+  u16 rx_power_high_warning; /* 36-37: RX power high warning */
+  u16 rx_power_low_warning;  /* 38-39: RX power low warning */
+  u8 reserved_40_55[16];     /* 40-55: Reserved/Other fields */
+  /* Calibration constants for external calibration (bytes 56-95) */
+  u32 rx_power_cal[5]; /* 56-75: RX power calibration coefficients (IEEE 754
+                          float) */
+  u16 tx_bias_slope;   /* 76-77: TX bias slope calibration */
+  u16 tx_bias_offset;  /* 78-79: TX bias offset calibration */
+  u16 tx_power_slope;  /* 80-81: TX power slope calibration */
+  u16 tx_power_offset; /* 82-83: TX power offset calibration */
+  u16 temp_slope;      /* 84-85: Temperature slope calibration */
+  u16 temp_offset;     /* 86-87: Temperature offset calibration */
+  u16 voltage_slope;   /* 88-89: Voltage slope calibration */
+  u16 voltage_offset;  /* 90-91: Voltage offset calibration */
+  u8 reserved_92_95[4]; /* 92-95: Reserved */
+  /* Real-time diagnostic values (bytes 96-105) */
+  u16 temperature;         /* 96-97: Temperature */
+  u16 vcc;                 /* 98-99: Supply voltage */
+  u16 tx_bias;             /* 100-101: TX bias current */
+  u16 tx_power;                    /* 102-103: TX average optical power */
+  u16 rx_power;                    /* 104-105: RX average optical power */
+  u8 reserved_106_109[4];   /* 106-109: Reserved */
+  u8 status_control[2];            /* 110-111: Status/Control */
+  u8 alarm1;               /* 112: Temp, VCC, TX Bias, TX Power alarms */
+  u8 alarm2;               /* 113: Rx, Laser Temp, TEC current alarms */
+  u8 tx_input_equalization; /* 114: Tx Input equalization HIGH / LOW */
+  u8 rx_output_emphasis;    /* 115: Rx output emphasis HIGH / LOW */
+  u8 warning1;             /* 116: Temp, VCC, TX Bias, TX power warnings */
+  u8 warning2;             /* 117: Rx, Laser Temp, TEC current warning */
+  u8 emc_status[2];        /* 118-119: Extended Module Control status */
+  u8 reserved_120_126[7];   /* 120-126: Vendor specific Locations */
+  u8 page_select;          /* 127: Page Select */
+} sff8472_diag_t;
+
+STATIC_ASSERT (sizeof (sff8472_diag_t) == 128,
+              "sff8472_diag_t must be 128 bytes");
+
+/* Function declarations */
+void sff8472_decode_diagnostics (vlib_main_t *vm,
+                                vnet_interface_eeprom_t *eeprom, u8 is_terse);
+
+#endif /* __included_ethernet_sff8472_h__ */
diff --git a/src/vnet/ethernet/sfp_sff8636.c b/src/vnet/ethernet/sfp_sff8636.c
new file mode 100644 (file)
index 0000000..7c13dd9
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2025 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <math.h>
+#include <vlib/vlib.h>
+#include <vppinfra/clib.h>
+#include <vnet/interface.h>
+#include <vnet/ethernet/sfp_sff8636.h>
+
+static f64
+sff8636_convert_temperature (u16 raw_temp)
+{
+  i16 temp = (i16) raw_temp;
+  return (f64) temp / 256.0;
+}
+
+static f64
+sff8636_convert_voltage (u16 raw_voltage)
+{
+  /* SFF-8636: 100 µV per LSB */
+  return (f64) raw_voltage / 10000.0;
+}
+
+static f64
+sff8636_convert_current (u16 raw_current)
+{
+  /* SFF-8636: 2 µA per LSB */
+  return (f64) raw_current * 2.0 / 1000.0;
+}
+
+static f64
+sff8636_convert_power (u16 raw_power)
+{
+  /* SFF-8636: 0.1 µW per LSB */
+  return (f64) raw_power * 0.1 / 1000.0;
+}
+
+static f64
+sff8636_mw_to_dbm (f64 power_mw)
+{
+  if (power_mw <= 0.0)
+    return -40.0; /* Use -40 dBm for zero/negative power to avoid log(0) */
+  return 10.0 * log10 (power_mw);
+}
+
+void
+sff8636_decode_diagnostics (vlib_main_t *vm, vnet_interface_eeprom_t *eeprom,
+                           u8 is_terse)
+{
+  f64 temp, vcc;
+  f64 temp_high_alarm, temp_low_alarm, temp_high_warn, temp_low_warn;
+  f64 vcc_high_alarm, vcc_low_alarm, vcc_high_warn, vcc_low_warn;
+  int i;
+
+  vlib_cli_output (vm, "  Module Diagnostics:");
+
+  if (eeprom->eeprom_len < sizeof (sff8636_eeprom_t))
+    {
+      vlib_cli_output (vm, "    Not supported (insufficient data)");
+      return;
+    }
+
+  sff8636_eeprom_t *diag = (sff8636_eeprom_t *) eeprom->eeprom_raw;
+  temp =
+    sff8636_convert_temperature (clib_net_to_host_u16 (diag->temperature));
+  vcc = sff8636_convert_voltage (clib_net_to_host_u16 (diag->vcc));
+
+  vlib_cli_output (vm, "    Current Values:");
+  vlib_cli_output (vm, "      Temperature: %.2f °C", temp);
+  vlib_cli_output (vm, "      Supply Voltage: %.4f V", vcc);
+
+  /* Per-lane values */
+  for (i = 0; i < 4; i++)
+    {
+      f64 tx_bias, tx_power, rx_power;
+      tx_bias =
+       sff8636_convert_current (clib_net_to_host_u16 (diag->tx_bias[i]));
+      rx_power =
+       sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power[i]));
+      tx_power =
+       sff8636_convert_power (clib_net_to_host_u16 (diag->tx_power[i]));
+
+      vlib_cli_output (vm, "      Lane %d:", i + 1);
+      vlib_cli_output (vm, "        TX Bias Current: %.2f mA", tx_bias);
+      vlib_cli_output (vm, "        TX Average Power: %.4f mW (%.2f dBm)",
+                      tx_power, sff8636_mw_to_dbm (tx_power));
+      vlib_cli_output (vm, "        RX Average Power: %.4f mW (%.2f dBm)",
+                      rx_power, sff8636_mw_to_dbm (rx_power));
+    }
+
+  if (is_terse)
+    {
+      return;
+    }
+
+  /* Temperature thresholds at bytes 512-519 */
+  temp_high_alarm =
+    sff8636_convert_temperature (clib_net_to_host_u16 (diag->temp_high_alarm));
+  temp_low_alarm =
+    sff8636_convert_temperature (clib_net_to_host_u16 (diag->temp_low_alarm));
+  temp_high_warn =
+    sff8636_convert_temperature (clib_net_to_host_u16 (diag->temp_high_warn));
+  temp_low_warn =
+    sff8636_convert_temperature (clib_net_to_host_u16 (diag->temp_low_warn));
+
+  /* Voltage thresholds at bytes 520-527 */
+  vcc_high_alarm =
+    sff8636_convert_voltage (clib_net_to_host_u16 (diag->vcc_high_alarm));
+  vcc_low_alarm =
+    sff8636_convert_voltage (clib_net_to_host_u16 (diag->vcc_low_alarm));
+  vcc_high_warn =
+    sff8636_convert_voltage (clib_net_to_host_u16 (diag->vcc_high_warn));
+  vcc_low_warn =
+    sff8636_convert_voltage (clib_net_to_host_u16 (diag->vcc_low_warn));
+
+  vlib_cli_output (vm, "");
+  vlib_cli_output (vm, "    Alarm Thresholds:");
+  vlib_cli_output (vm, "      Temperature High: %.2f °C, Low: %.2f °C",
+                  temp_high_alarm, temp_low_alarm);
+  vlib_cli_output (vm, "      Voltage High: %.4f V, Low: %.4f V",
+                  vcc_high_alarm, vcc_low_alarm);
+
+  vlib_cli_output (
+    vm, "      Bias Current High: %.2f mA, Low: %.2f mA",
+    sff8636_convert_current (clib_net_to_host_u16 (diag->tx_bias_high_alarm)),
+    sff8636_convert_current (clib_net_to_host_u16 (diag->tx_bias_low_alarm)));
+  vlib_cli_output (
+    vm,
+    "      TX Power High: %.4f mW (%.2f dBm), Low: "
+    "%.4f mW (%.2f dBm)",
+    sff8636_convert_power (clib_net_to_host_u16 (diag->tx_power_high_alarm)),
+    sff8636_mw_to_dbm (sff8636_convert_power (
+      clib_net_to_host_u16 (diag->tx_power_high_alarm))),
+    sff8636_convert_power (clib_net_to_host_u16 (diag->tx_power_low_alarm)),
+    sff8636_mw_to_dbm (sff8636_convert_power (
+      clib_net_to_host_u16 (diag->tx_power_low_alarm))));
+  vlib_cli_output (
+    vm,
+    "      RX Power High: %.4f mW (%.2f dBm), Low: "
+    "%.4f mW (%.2f dBm)",
+    sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_high_alarm)),
+    sff8636_mw_to_dbm (sff8636_convert_power (
+      clib_net_to_host_u16 (diag->rx_power_high_alarm))),
+    sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_low_alarm)),
+    sff8636_mw_to_dbm (sff8636_convert_power (
+      clib_net_to_host_u16 (diag->rx_power_low_alarm))));
+
+  vlib_cli_output (vm, "");
+  vlib_cli_output (vm, "    Warning Thresholds:");
+  vlib_cli_output (vm, "      Temperature High: %.2f °C, Low: %.2f °C",
+                  temp_high_warn, temp_low_warn);
+  vlib_cli_output (vm, "      Voltage High: %.4f V, Low: %.4f V",
+                  vcc_high_warn, vcc_low_warn);
+  vlib_cli_output (
+    vm,
+    "      RX Power High: %.4f mW (%.2f dBm), Low: "
+    "%.4f mW (%.2f dBm)",
+    sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_high_warn)),
+    sff8636_mw_to_dbm (
+      sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_high_warn))),
+    sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_low_warn)),
+    sff8636_mw_to_dbm (
+      sff8636_convert_power (clib_net_to_host_u16 (diag->rx_power_low_warn))));
+}
diff --git a/src/vnet/ethernet/sfp_sff8636.h b/src/vnet/ethernet/sfp_sff8636.h
new file mode 100644 (file)
index 0000000..9adf90d
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2025 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_ethernet_sff8636_h__
+#define __included_ethernet_sff8636_h__
+
+#include <vlib/vlib.h>
+#include <vppinfra/types.h>
+
+/* SFF-8636 is a 640 byte EEPROM.
+ * Any SFF-8436-compliant module will generally respond correctly to an
+ * SFF-8636 reader, because the lower and upper Page 00h structures are largely
+ * preserved.
+ */
+
+typedef struct
+{
+  u8 identifier;         // Byte 0
+  u8 status[2];                  // Bytes 1-2 (see Table 6-2)
+  u8 interrupt_flags[19]; // Bytes 3-21 (Tables 6-4, 6-5, 6-6)
+
+  // ---------------- Device Free Side Monitors ----------------
+  u16 temperature;     // Bytes 22-23 (1/256 °C per LSB)
+  u8 reserved_fsm[2];  // Bytes 24-25 (vendor-specific / reserved)
+  u16 vcc;            // Bytes 26-27 (100 µV per LSB)
+  u8 reserved_fsm2[6]; // Bytes 28-33 (vendor-specific / reserved)
+
+  // ---------------- Per-lane channel monitors ----------------
+  // Lane 1–4, each has 4 bytes: TX bias, TX power, RX power, reserved
+  u16 rx_power[4];      // Bytes 34-41 (2 bytes per lane, 0.1 µW scaling)
+  u16 tx_bias[4];       // Bytes 42-49 (2 bytes per lane, µA scaling)
+  u16 tx_power[4];      // Bytes 50-57 (2 bytes per lane, 0.1 µW scaling)
+  u16 reserved_lane[4];         // Bytes 58-65 (reserved/future use)
+  u8 reserved_66_85[20]; // Bytes 66-85
+  u8 control[13];     // Bytes 86-98 (Table 6-9; includes App Select bytes per
+                     // Table 6-12)
+  u8 reserved_99;     // Byte 99
+  u8 hw_int_mask[7];  // Bytes 100-106 (Table 6-14)
+  u8 reserved_107;    // Byte 107
+  u8 device_props[7]; // Bytes 108-114 (Table 6-15; incl. PCIe use at 111-112
+                     // per Fig 6-1)
+  u8 reserved_115_118[4]; // Bytes 115-118
+  u8 password_change[4];  // Bytes 119-122 (optional, write-only in device)
+  u8 password_entry[4];          // Bytes 123-126 (optional, write-only in device)
+  u8 page_select;        // Byte 127 (Page Select)
+
+  // 128–255 (Base ID portion)
+  u8 base_id[128]; // this is the standard sfp_eeprom_t at offset 0x80
+
+  // 256-511 (page01, page02)
+  u8 reserved_page01[128]; // 256-383
+  u8 reserved_page02[128]; // 384-511
+
+  // 512–559 : Free-Side Device Thresholds and Channel Thresholds
+  // Typically: temp high alarm/warn, low warn/alarm; Vcc high/low thresholds,
+  // etc.
+  u16 temp_high_alarm; // 512-513 (0.00390625 °C/LSB)
+  u16 temp_low_alarm;  // 514-515
+  u16 temp_high_warn;  // 516-517
+  u16 temp_low_warn;   // 518-519
+  u8 reserved_fsdct[8]; // 520-527
+
+  u16 vcc_high_alarm;   // 528-529 (100 µV/LSB)
+  u16 vcc_low_alarm;    // 530-531
+  u16 vcc_high_warn;    // 532-533
+  u16 vcc_low_warn;     // 534-535
+  u8 reserved_fsdct2[8]; // 536-543
+
+  u8 reserved_device_thresh[16]; // 544-559
+
+  // 560–607 : Channel Thresholds (48 bytes)
+  // Per-channel: TX bias, TX power, RX power alarms/warns
+  u16 rx_power_high_alarm; // 560-56 (0.1 µW/LSB)
+  u16 rx_power_low_alarm;  // 562-563
+  u16 rx_power_high_warn;  // 564-565
+  u16 rx_power_low_warn;   // 566-567
+
+  u16 tx_bias_high_alarm;  // 568-569 (µA/LSB)
+  u16 tx_bias_low_alarm;   // 570-571
+  u16 tx_bias_high_warn;   // 572-573
+  u16 tx_bias_low_warn;           // 574-575
+                          //
+  u16 tx_power_high_alarm; // 576-577 (0.1 µW/LSB)
+  u16 tx_power_low_alarm;  // 578-579
+  u16 tx_power_high_warn;  // 580-581
+  u16 tx_power_low_warn;   // 582-583
+
+  u8 reserved_ct[24]; // 584-607
+
+  // 608–613 : TX EQ / RX Output / Temp Control (6 bytes)
+  u8 tx_eq_settings[4];            // 608-611 (per-channel EQ/emphasis)
+  u8 rx_output_settings;    // 612
+  u8 temp_control_settings; // 613
+
+  // 614–625 : Channel Controls (12 bytes)
+  u8 channel_controls[12]; // 614-625 (enable, polarity, squelch, etc.)
+
+  // 626–635 : Channel Monitor Masks (10 bytes)
+  u8 channel_monitor_masks[10]; // 626-635 (interrupt mask bits per
+                               // channel/parameter)
+
+  // 636–639 : Reserved
+  u8 reserved_636_639[4]; // 636-639
+} sff8636_eeprom_t;
+
+STATIC_ASSERT (sizeof (sff8636_eeprom_t) == 640,
+              "sff8636_eeprom_t must be 640 bytes");
+
+/* Function declarations */
+void sff8636_decode_diagnostics (vlib_main_t *vm,
+                                vnet_interface_eeprom_t *eeprom, u8 is_terse);
+
+#endif /* __included_ethernet_sff8636_h__ */
index 5fb2ff6..b6c48dc 100644 (file)
@@ -1794,6 +1794,24 @@ default_build_rewrite (vnet_main_t * vnm,
   return (NULL);
 }
 
+u8 *
+format_vnet_interface_eeprom_type (u8 *s, va_list *args)
+{
+  u32 eeprom_type = va_arg (*args, u32);
+  char *t = 0;
+  switch (eeprom_type)
+    {
+#define _(n, v, str)                                                          \
+  case v:                                                                     \
+    t = str;                                                                  \
+    break;
+      foreach_vnet_interface_eeprom_type
+#undef _
+       default : return format (s, "unknown 0x%x", eeprom_type);
+    }
+  return format (s, "%s", t);
+}
+
 void
 default_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
 {
index c9778f9..93df4f0 100644 (file)
@@ -98,10 +98,25 @@ typedef clib_error_t *(vnet_interface_rss_queues_set_t)
   (struct vnet_main_t * vnm, struct vnet_hw_interface_t * hi,
    clib_bitmap_t * bitmap);
 
+/* Interface EEPROM types */
+#define foreach_vnet_interface_eeprom_type                                    \
+  _ (UNKNOWN, 0x00, "unknown")                                                \
+  _ (SFF8079, 0x01, "SFF-8079")                                               \
+  _ (SFF8472, 0x02, "SFF-8472")                                               \
+  _ (SFF8636, 0x03, "SFF-8636")                                               \
+  _ (SFF8436, 0x04, "SFF-8436")
+
+typedef enum
+{
+#define _(n, v, s) VNET_INTERFACE_EEPROM_TYPE_##n = v,
+  foreach_vnet_interface_eeprom_type
+#undef _
+} vnet_interface_eeprom_type_t;
+
 /* EEPROM structure for physical network devices */
 typedef struct
 {
-  u32 eeprom_type; /* from linux/ethtool.h */
+  vnet_interface_eeprom_type_t eeprom_type; /* from linux/ethtool.h */
   u32 eeprom_len;
   u8 eeprom_raw[1024];
 } vnet_interface_eeprom_t;
@@ -1137,6 +1152,8 @@ typedef struct
 
 int vnet_pcap_dispatch_trace_configure (vnet_pcap_dispatch_trace_args_t *);
 
+u8 *format_vnet_interface_eeprom_type (u8 *s, va_list *args);
+
 extern vlib_node_registration_t vnet_interface_output_node;
 extern vlib_node_registration_t vnet_interface_output_arc_end_node;
 
index 4b1828d..b49ad24 100644 (file)
@@ -56,6 +56,7 @@
 #include <vnet/hash/hash.h>
 #include <vnet/dev/dev.h>
 #include <vnet/dev/dev_funcs.h>
+#include <vnet/ethernet/sfp.h>
 
 static int
 compare_interface_names (void *a1, void *a2)
@@ -2689,6 +2690,8 @@ show_interface_transceiver_output (vlib_main_t *vm, vnet_hw_interface_t *hi,
     }
 
   vlib_cli_output (vm, "Interface: %v", hi->name);
+  vlib_cli_output (vm, "  EEPROM Type: 0x%02x (%U)", eeprom->eeprom_type,
+                  format_vnet_interface_eeprom_type, eeprom->eeprom_type);
 
   /* Default to module if none are set */
   if (!show_module && !show_diag && !show_eeprom)
@@ -2696,7 +2699,6 @@ show_interface_transceiver_output (vlib_main_t *vm, vnet_hw_interface_t *hi,
 
   if (show_eeprom)
     {
-      vlib_cli_output (vm, "  EEPROM Type: 0x%x", eeprom->eeprom_type);
       vlib_cli_output (vm, "  EEPROM Length: %u bytes", eeprom->eeprom_len);
       vlib_cli_output (vm, "  EEPROM Data:");
 
@@ -2739,12 +2741,12 @@ show_interface_transceiver_output (vlib_main_t *vm, vnet_hw_interface_t *hi,
 
   if (show_module)
     {
-      vlib_cli_output (vm, "  module: not implemented yet");
+      sfp_eeprom_module (vm, eeprom, is_terse);
     }
 
   if (show_diag)
     {
-      vlib_cli_output (vm, "  diag: not implemented yet");
+      sfp_eeprom_diagnostics (vm, eeprom, is_terse);
     }
 
 done: