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
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
)
*/
#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)
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;
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)
{
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
{
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",
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
*
#define included_vnet_optics_sfp_h
#include <vppinfra/format.h>
+#include <vnet/interface.h>
#define foreach_sfp_id \
_ (UNKNOWN, "unknown") \
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];
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) \
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 */
--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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__ */
--- /dev/null
+/*
+ * 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))));
+}
--- /dev/null
+/*
+ * 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__ */
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)
{
(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;
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;
#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)
}
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)
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:");
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: