diff --git a/sonic_platform_base/sonic_xcvr/api/public/cmis.py b/sonic_platform_base/sonic_xcvr/api/public/cmis.py index 73f6d4def484..589604476367 100644 --- a/sonic_platform_base/sonic_xcvr/api/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/api/public/cmis.py @@ -1,3 +1,4 @@ + """ cmis.py diff --git a/sonic_platform_base/sonic_xcvr/api/public/sff8472.py b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py new file mode 100644 index 000000000000..22dede94c9a2 --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/api/public/sff8472.py @@ -0,0 +1,299 @@ +""" + sff8472.py + + Implementation of XcvrApi that corresponds to the SFF-8472 specification for + SFP+ pluggable transceivers. +""" +from ...fields import consts +from ..xcvr_api import XcvrApi + +class Sff8472Api(XcvrApi): + NUM_CHANNELS = 1 + + def __init__(self, xcvr_eeprom): + super(Sff8472Api, self).__init__(xcvr_eeprom) + + def get_model(self): + return self.xcvr_eeprom.read(consts.VENDOR_PART_NO_FIELD) + + def get_serial(self): + return self.xcvr_eeprom.read(consts.VENDOR_SERIAL_NO_FIELD) + + def get_transceiver_info(self): + serial_id = self.xcvr_eeprom.read(consts.SERIAL_ID_FIELD) + if serial_id is None: + return None + smf_km_len = serial_id[consts.LENGTH_SMF_KM_FIELD] + smf_m_len = serial_id[consts.LENGTH_SMF_M_FIELD] + om4_len = serial_id[consts.LENGTH_OM4_FIELD] + om3_len = serial_id[consts.LENGTH_OM3_FIELD] + om2_len = serial_id[consts.LENGTH_OM2_FIELD] + om1_len = serial_id[consts.LENGTH_OM1_FIELD] + + len_types = ["Length SMF (km)", "Length SMF (100m)", "Length OM2 (10m)", "Length OM1(10m)", "Length OM4(10m)", "Length OM3(10m)"] + cable_len = 0 + cable_type = None + for len, type in zip([smf_km_len, smf_m_len, om2_len, om1_len, om4_len, om3_len], len_types): + if len > 0: + cable_len = len + cable_type = type + + xcvr_info = { + "type": serial_id[consts.ID_FIELD], + "type_abbrv_name": serial_id[consts.ID_ABBRV_FIELD], + "hardware_rev": serial_id[consts.VENDOR_REV_FIELD], + "serial": serial_id[consts.VENDOR_SERIAL_NO_FIELD], + "manufacturer": serial_id[consts.VENDOR_NAME_FIELD], + "model": serial_id[consts.VENDOR_PART_NO_FIELD], + "connector": serial_id[consts.CONNECTOR_FIELD], + "encoding": serial_id[consts.ENCODING_FIELD], + "ext_identifier": serial_id[consts.EXT_ID_FIELD], + "ext_rateselect_compliance": serial_id[consts.RATE_ID_FIELD], + "cable_type": cable_type, + "cable_length": float(cable_len), + "nominal_bit_rate": serial_id[consts.NOMINAL_BR_FIELD], + "specification_compliance": str(serial_id[consts.SPEC_COMPLIANCE_FIELD]), + "vendor_date": serial_id[consts.VENDOR_DATE_FIELD], + "vendor_oui": serial_id[consts.VENDOR_OUI_FIELD], + "application_advertisement": "N/A", + } + + return xcvr_info + + def get_transceiver_bulk_status(self): + rx_los = self.get_rx_los() + tx_fault = self.get_tx_fault() + tx_disable = self.get_tx_disable() + tx_disabled_channel = self.get_tx_disable_channel() + temp = self.get_module_temperature() + voltage = self.get_voltage() + tx_bias = self.get_tx_bias() + rx_power = self.get_rx_power() + tx_power = self.get_tx_power() + read_failed = rx_los is None or \ + tx_fault is None or \ + tx_disable is None or \ + tx_disabled_channel is None or \ + temp is None or \ + voltage is None or \ + tx_bias is None or \ + rx_power is None or \ + tx_power is None + if read_failed: + return None + + bulk_status = { + "rx_los": all(rx_los) if self.get_rx_los_support() else 'N/A', + "tx_fault": all(tx_fault) if self.get_tx_fault_support() else 'N/A', + "tx_disable": all(tx_disable), + "tx_disabled_channel": tx_disabled_channel, + "temperature": temp, + "voltage": voltage + } + + for i in range(1, self.NUM_CHANNELS + 1): + bulk_status["tx%dbias" % i] = tx_bias[i - 1] + bulk_status["rx%dpower" % i] = rx_power[i - 1] + bulk_status["tx%dpower" % i] = tx_power[i - 1] + + # Added to avoid failing xcvrd. Ideally xcvrd should be fixed so that this is not necessary + for i in range(2, 5): + bulk_status["tx%dbias" % i] = 'N/A' + bulk_status["rx%dpower" % i] = 'N/A' + bulk_status["tx%dpower" % i] = 'N/A' + + return bulk_status + + def get_transceiver_threshold_info(self): + threshold_info_keys = ['temphighalarm', 'temphighwarning', + 'templowalarm', 'templowwarning', + 'vcchighalarm', 'vcchighwarning', + 'vcclowalarm', 'vcclowwarning', + 'rxpowerhighalarm', 'rxpowerhighwarning', + 'rxpowerlowalarm', 'rxpowerlowwarning', + 'txpowerhighalarm', 'txpowerhighwarning', + 'txpowerlowalarm', 'txpowerlowwarning', + 'txbiashighalarm', 'txbiashighwarning', + 'txbiaslowalarm', 'txbiaslowwarning' + ] + threshold_info_dict = dict.fromkeys(threshold_info_keys, 'N/A') + thresh_support = self.get_transceiver_thresholds_support() + if thresh_support is None: + return None + if not thresh_support: + return threshold_info_dict + thresholds = self.xcvr_eeprom.read(consts.THRESHOLDS_FIELD) + if thresholds is None: + return threshold_info_dict + + return { + "temphighalarm": float("{:.3f}".format(thresholds[consts.TEMP_HIGH_ALARM_FIELD])), + "templowalarm": float("{:.3f}".format(thresholds[consts.TEMP_LOW_ALARM_FIELD])), + "temphighwarning": float("{:.3f}".format(thresholds[consts.TEMP_HIGH_WARNING_FIELD])), + "templowwarning": float("{:.3f}".format(thresholds[consts.TEMP_LOW_WARNING_FIELD])), + "vcchighalarm": float("{:.3f}".format(thresholds[consts.VOLTAGE_HIGH_ALARM_FIELD])), + "vcclowalarm": float("{:.3f}".format(thresholds[consts.VOLTAGE_LOW_ALARM_FIELD])), + "vcchighwarning": float("{:.3f}".format(thresholds[consts.VOLTAGE_HIGH_WARNING_FIELD])), + "vcclowwarning": float("{:.3f}".format(thresholds[consts.VOLTAGE_LOW_WARNING_FIELD])), + "rxpowerhighalarm": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.RX_POWER_HIGH_ALARM_FIELD]))), + "rxpowerlowalarm": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.RX_POWER_LOW_ALARM_FIELD]))), + "rxpowerhighwarning": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.RX_POWER_HIGH_WARNING_FIELD]))), + "rxpowerlowwarning": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.RX_POWER_LOW_WARNING_FIELD]))), + "txpowerhighalarm": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.TX_POWER_HIGH_ALARM_FIELD]))), + "txpowerlowalarm": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.TX_POWER_LOW_ALARM_FIELD]))), + "txpowerhighwarning": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.TX_POWER_HIGH_WARNING_FIELD]))), + "txpowerlowwarning": float("{:.3f}".format(self.mw_to_dbm(thresholds[consts.TX_POWER_LOW_WARNING_FIELD]))), + "txbiashighalarm": float("{:.3f}".format(thresholds[consts.TX_BIAS_HIGH_ALARM_FIELD])), + "txbiaslowalarm": float("{:.3f}".format(thresholds[consts.TX_BIAS_LOW_ALARM_FIELD])), + "txbiashighwarning": float("{:.3f}".format(thresholds[consts.TX_BIAS_HIGH_WARNING_FIELD])), + "txbiaslowwarning": float("{:.3f}".format(thresholds[consts.TX_BIAS_LOW_WARNING_FIELD])) + } + + def get_rx_los(self): + rx_los_support = self.get_rx_los_support() + if rx_los_support is None: + return None + if not rx_los_support: + return ["N/A"] + rx_los = self.xcvr_eeprom.read(consts.RX_LOS_FIELD) + if rx_los is None: + return None + return [rx_los] + + def get_tx_fault(self): + tx_fault_support = self.get_tx_fault_support() + if tx_fault_support is None: + return None + if not tx_fault_support: + return ["N/A"] + tx_fault = self.xcvr_eeprom.read(consts.TX_FAULT_FIELD) + if tx_fault is None: + return None + return [tx_fault] + + def get_tx_disable(self): + tx_disable_support = self.get_tx_disable_support() + if tx_disable_support is None: + return None + if not tx_disable_support: + return ["N/A"] + tx_disable = self.xcvr_eeprom.read(consts.TX_DISABLE_FIELD) + tx_disable_select = self.xcvr_eeprom.read(consts.TX_DISABLE_SELECT_FIELD) + if tx_disable is None or tx_disable_select is None: + return None + return [tx_disable or tx_disable_select] + + def get_tx_disable_channel(self): + tx_disable_list = self.get_tx_disable() + if tx_disable_list is None: + return None + if tx_disable_list[0] == "N/A": + return "N/A" + return int(tx_disable_list[0]) + + def get_module_temperature(self): + if not self.get_temperature_support(): + return 'N/A' + temp = self.xcvr_eeprom.read(consts.TEMPERATURE_FIELD) + if temp is None: + return None + return float("{:.3f}".format(temp)) + + def get_voltage(self): + voltage_support = self.get_voltage_support() + if voltage_support is None: + return None + if not voltage_support: + return 'N/A' + voltage = self.xcvr_eeprom.read(consts.VOLTAGE_FIELD) + if voltage is None: + return None + return float("{:.3f}".format(voltage)) + + def get_tx_bias(self): + tx_bias_support = self.get_tx_bias_support() + if tx_bias_support is None: + return None + if not tx_bias_support: + return ["N/A"] + tx_bias = self.xcvr_eeprom.read(consts.TX_BIAS_FIELD) + if tx_bias is None: + return None + return [float("{:.3f}".format(tx_bias))] + + def get_rx_power(self): + rx_power_support = self.get_rx_power_support() + if rx_power_support is None: + return None + if not rx_power_support: + return ["N/A"] + rx_power = self.xcvr_eeprom.read(consts.RX_POWER_FIELD) + if rx_power is None: + return None + return [float("{:.3f}".format(rx_power))] + + def get_tx_power(self): + tx_power_support = self.get_tx_power_support() + if tx_power_support is None: + return None + if not tx_power_support: + return ["N/A"] + tx_power = self.xcvr_eeprom.read(consts.TX_POWER_FIELD) + if tx_power is None: + return None + return [float("{:.3f}".format(tx_power))] + + def tx_disable(self, tx_disable): + return self.xcvr_eeprom.write(consts.TX_DISABLE_SELECT_FIELD, tx_disable) + + def tx_disable_channel(self, channel, disable): + channel_state = self.get_tx_disable_channel() + if channel_state is None or channel_state == "N/A": + return False + + for i in range(self.NUM_CHANNELS): + mask = (1 << i) + if not (channel & mask): + continue + if disable: + channel_state |= mask + else: + channel_state &= ~mask + + return self.xcvr_eeprom.write(consts.TX_DISABLE_FIELD, channel_state) + + def is_flat_memory(self): + return not self.xcvr_eeprom.read(consts.PAGING_SUPPORT_FIELD) + + def get_temperature_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_voltage_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_tx_power_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_rx_power_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_rx_los_support(self): + return self.xcvr_eeprom.read(consts.RX_LOS_SUPPORT_FIELD) + + def get_tx_bias_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_tx_fault_support(self): + return self.xcvr_eeprom.read(consts.TX_FAULT_SUPPORT_FIELD) + + def get_tx_disable_support(self): + return self.xcvr_eeprom.read(consts.TX_DISABLE_SUPPORT_FIELD) + + def get_transceiver_thresholds_support(self): + return self.xcvr_eeprom.read(consts.DDM_SUPPORT_FIELD) + + def get_lpmode_support(self): + return False + + def get_power_override_support(self): + return False diff --git a/sonic_platform_base/sonic_xcvr/codes/public/sff8472.py b/sonic_platform_base/sonic_xcvr/codes/public/sff8472.py new file mode 100644 index 000000000000..596f20377fd2 --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/codes/public/sff8472.py @@ -0,0 +1,143 @@ +""" + sff8472.py + + SFF-8472 Rev 12.3 +""" + +from .sff8024 import Sff8024 + +class Sff8472Codes(Sff8024): + ENCODINGS = { + 0: "Unspecified", + 1: "8B/10B", + 2: "4B/5B", + 3: "NRZ", + 4: "Manchester", + 5: "SONET Scrambled", + 6: "64B/66B", + 7: "256B/257B (transcoded FEC-enabled data)", + 8: "PAM4", + } + + EXT_IDENTIFIERS = { + 0: "GBIC definition is not specified", + 1: "GBIC is compliant with MOD_DEF 1", + 2: "GBIC is compliant with MOD_DEF 2", + 3: "GBIC is compliant with MOD_DEF 3", + 4: "GBIC/SFP defined by two-wire interface ID", + 5: "GBIC is compliant with MOD_DEF 5", + 6: "GBIC is compliant with MOD_DEF 6", + 7: "GBIC is compliant with MOD_DEF 7" + } + + POWER_CLASSES = { + 0: "Power Class 1 Module (1.5W max.)", + 64: "Power Class 2 Module (2.0W max.)", + 128: "Power Class 3 Module (2.5W max.)", + 192: "Power Class 4 Module (3.5W max.)", + 193: "Power Class 5 Module (4.0W max.)", + 194: "Power Class 6 Module (4.5W max.)", + 197: "Power Class 7 Module (5.0W max.)", + 32: "Power Class 8 Module", + } + + ETHERNET_10G_COMPLIANCE = { + 16: "10GBASE-SR", + 32: "10GBASE-LR", + 64: "10GBASE-LRM", + 128: "10GBASE-ER", + } + + INFINIBAND_COMPLIANCE = { + 1: "1X Copper Passive", + 2: "1X Copper Active", + 4: "1X LX", + 8: "1X SX" + } + + ESCON_COMPLIANCE = { + 128: "ESCON MMF, 1310nm LED", + 64: "ESCON SMF, 1310nm Laser" + } + + SONET_COMPLIANCE = { + 1: "OC 3 short reach", + 2: "OC 3, single mode, intermediate reach", + 4: "OC 3, single mode, long reach", + 16: "OC 12, short reach", + 32: "OC 12, single mode, intermediate reach", + 64: "OC 12, single mode, long reach", + 256: "OC 48, short reach", + 512: "OC 48, intermediate reach", + 1024: "OC 48, long reach", + 2048: "SONET reach specifier bit 2", + 4096: "SONET reach specifier bit 1", + 8192: "OC 192, short reach" + } + + ETHERNET_COMPLIANCE = { + 1: "1000BASE-SX", + 2: "1000BASE-LX", + 4: "1000BASE-CX", + 8: "1000BASE-T", + 16: "100BASE-LX/LX10", + 32: "100BASE-FX", + 64: "BASE-BX10", + 128: "BASE-PX" + } + + FIBRE_CHANNEL_LINK_LENGTH = { + 8: "Medium (M)", + 16: "Long distance (L)", + 32: "Intermediate distance (I)", + 64: "Short distance (S)", + 128: "Very long distance (V)" + } + + FIBRE_CHANNEL_TRANSMITTER_TECH = { + 16: "Longwave Laser (LL)", + 32: "Shortwave laser w OFC (SL)", + 64: "Shortwave laser w/o OFC (SN)", + 128: "Electrical intra-enclosure (EL)", + 256: "Electrical inter-enclosure (EL)", + 512: "Longwave laser (LC)", + 1024: "Shortwave laser, linear RX (SA)" + } + + SFP_CABLE_TECH = { + 4: "Passive Cable", + 8: "Active Cable" + } + + FIBRE_CHANNEL_TRANSMISSION_MEDIA = { + 1: "Single Mode (SM)", + 4: "Multi-mode 50m (M5, M5E)", + 8: "Multi-mode 62.5m (M6)", + 16: "Video Coax (TV)", + 32: "Miniature Coax (MI)", + 64: "Twisted Pair (TP", + 128: "Twin Axial Pair (TW)" + } + + FIBRE_CHANNEL_SPEED = { + 1: "100 MBytes/sec", + 2: "Extended", + 4: "200 MBytes/sec", + 8: "3200 MBytes/sec", + 16: "400 MBytes/sec", + 32: "1600 MBytes/sec", + 64: "800 MBytes/sec", + 128: "1200 MBytes/sec" + } + + RATE_IDENTIFIERS = { + 1: "SFF-8079 (4/2/1G Rate_Select & AS0/AS1)", + 2: "SFF-8431 (8/4/2G Rx Rate_Select only)", + 4: "SFF-8431 (8/4/2G Tx Rate_Select only)", + 6: "SFF-8431 (8/4/2G Independent Rx & Tx Rate_select)", + 8: "FC-PI-5 (16/8/4G Rx Rate_select only) High=16G only, Low=8G/4G", + 10: "FC-PI-5 (16/8/4G Independent Rx, Tx Rate_select) High=16G only, Low=8G/4G", + 12: "FC-PI-6 (32/16/8 Independent Rx, Tx Rate_select) High=32G only, Low=16G/8G", + 14: "10/8G Rx and Tx Rate_Select controlling the operation or locking modes of the internal signal conditioner, retimer or CDR", + 16: "FC-PI-7 (64/32/16 Independent Rx, Tx Rate_select) High=32GFC and 64GFC, Low=16GFC", + } diff --git a/sonic_platform_base/sonic_xcvr/fields/consts.py b/sonic_platform_base/sonic_xcvr/fields/consts.py index 87823867afa9..b4cc4bea06bd 100644 --- a/sonic_platform_base/sonic_xcvr/fields/consts.py +++ b/sonic_platform_base/sonic_xcvr/fields/consts.py @@ -105,6 +105,41 @@ REV_COMPLIANCE_FIELD = "Revision Compliance" +# SFF-8472 + +ENHANCED_OPTIONS_FIELD = "Enhanced Options" +ETHERNET_10G_COMPLIANCE_FIELD = "10G Ethernet Compliance" +INFINIBAND_COMPLIANCE_FIELD = "Infiniband Compliance" +ESCON_COMPLIANCE_FIELD = "ESCON Compliance" +ETHERNET_COMPLIANCE_FIELD = "Ethernet Compliance" +SFP_CABLE_TECH_FIELD = "SFP+ Cable Technology" +RATE_ID_FIELD = "Rate Identifier" +EXT_CAL_FIELD = "Externally Calibrated" +INT_CAL_FIELD = "Internally Calibrated" +DOM_EXT_CAL_CONST_FIELD = "Calibration Constants for External Calibration" +RX_LOS_SUPPORT_FIELD = "RxLOSSupported" +RX_PWR_4_FIELD = "Rx_PWR(4)" +RX_PWR_3_FIELD = "Rx_PWR(3)" +RX_PWR_2_FIELD = "Rx_PWR(2)" +RX_PWR_1_FIELD = "Rx_PWR(1)" +RX_PWR_0_FIELD = "Rx_PWR(0)" +TX_I_SLOPE_FIELD = "Tx_I(Slope)" +TX_I_OFFSET_FIELD = "Tx_I(Offset)" +TX_PWR_SLOPE_FIELD = "Tx_PWR(Slope)" +TX_PWR_OFFSET_FIELD = "Tx_PWR(Offset)" +T_SLOPE_FIELD = "T (Slope)" +T_OFFSET_FIELD = "T (Offet)" +V_SLOPE_FIELD = "V (Slope)" +V_OFFSET_FIELD = "V (Offset)" + +DDM_SUPPORT_FIELD = "Digital Diagnostic Monitoring Implemented" +DOM_FIELD = "DOM" + +PAGING_SUPPORT_FIELD = "Paging Implemented" + +STATUS_CTRL_FIELD = "Status/Control" +TX_DISABLE_SELECT_FIELD = "Soft TX Disable Select" + # CMIS ADMIN_INFO_FIELD = "AdminInfo" diff --git a/sonic_platform_base/sonic_xcvr/fields/public/sff8472.py b/sonic_platform_base/sonic_xcvr/fields/public/sff8472.py new file mode 100644 index 000000000000..5059e2ff2682 --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/fields/public/sff8472.py @@ -0,0 +1,103 @@ +import struct + +from ..xcvr_field import NumberRegField +from .. import consts + +class TempField(NumberRegField): + def __init__(self, name, offset, *fields, **kwargs): + kwargs["deps"] = [consts.INT_CAL_FIELD, consts.EXT_CAL_FIELD, consts.T_SLOPE_FIELD, consts.T_OFFSET_FIELD] + super(TempField, self).__init__(name, offset, *fields, **kwargs) + + def decode(self, raw_data, **decoded_deps): + int_cal = decoded_deps.get(consts.INT_CAL_FIELD) + ext_cal = decoded_deps.get(consts.EXT_CAL_FIELD) + measured_val = struct.unpack(self.format, raw_data)[0] + if int_cal: + return measured_val / self.scale + elif ext_cal: + t_slope = decoded_deps.get(consts.T_SLOPE_FIELD) + t_offset = decoded_deps.get(consts.T_OFFSET_FIELD) + result = t_slope * measured_val + t_offset + return result / self.scale + return float('NaN') + +class VoltageField(NumberRegField): + def __init__(self, name, offset, *fields, **kwargs): + kwargs["deps"] = [consts.INT_CAL_FIELD, consts.EXT_CAL_FIELD, consts.V_SLOPE_FIELD, consts.V_OFFSET_FIELD] + super(VoltageField, self).__init__(name, offset, *fields, **kwargs) + + def decode(self, raw_data, **decoded_deps): + int_cal = decoded_deps.get(consts.INT_CAL_FIELD) + ext_cal = decoded_deps.get(consts.EXT_CAL_FIELD) + measured_val = struct.unpack(self.format, raw_data)[0] + if int_cal: + return measured_val / self.scale + elif ext_cal: + v_slope = decoded_deps.get(consts.V_SLOPE_FIELD) + v_offset = decoded_deps.get(consts.V_OFFSET_FIELD) + result = v_slope * measured_val + v_offset + return result / self.scale + return float('NaN') + +class TxBiasField(NumberRegField): + def __init__(self, name, offset, *fields, **kwargs): + kwargs["deps"] = [consts.INT_CAL_FIELD, consts.EXT_CAL_FIELD, consts.TX_I_SLOPE_FIELD, consts.TX_I_OFFSET_FIELD] + super(TxBiasField, self).__init__(name, offset, *fields, **kwargs) + + def decode(self, raw_data, **decoded_deps): + int_cal = decoded_deps.get(consts.INT_CAL_FIELD) + ext_cal = decoded_deps.get(consts.EXT_CAL_FIELD) + measured_val = struct.unpack(self.format, raw_data)[0] + if int_cal: + return measured_val / self.scale + elif ext_cal: + tx_i_slope = decoded_deps.get(consts.TX_I_SLOPE_FIELD) + tx_i_offset = decoded_deps.get(consts.TX_I_OFFSET_FIELD) + result = tx_i_slope * measured_val + tx_i_offset + return result / self.scale + return float('NaN') + +class TxPowerField(NumberRegField): + def __init__(self, name, offset, *fields, **kwargs): + kwargs["deps"] = [consts.INT_CAL_FIELD, consts.EXT_CAL_FIELD, consts.TX_PWR_SLOPE_FIELD, consts.TX_PWR_OFFSET_FIELD] + super(TxPowerField, self).__init__(name, offset, *fields, **kwargs) + + def decode(self, raw_data, **decoded_deps): + int_cal = decoded_deps.get(consts.INT_CAL_FIELD) + ext_cal = decoded_deps.get(consts.EXT_CAL_FIELD) + measured_val = struct.unpack(self.format, raw_data)[0] + if int_cal: + return measured_val / self.scale + elif ext_cal: + tx_pwr_slope = decoded_deps.get(consts.TX_PWR_SLOPE_FIELD) + tx_pwr_offset = decoded_deps.get(consts.TX_PWR_OFFSET_FIELD) + result = tx_pwr_slope * measured_val + tx_pwr_offset + return result / self.scale + return float('NaN') + +class RxPowerField(NumberRegField): + def __init__(self, name, offset, *fields, **kwargs): + kwargs["deps"] = [consts.INT_CAL_FIELD, consts.EXT_CAL_FIELD, consts.RX_PWR_0_FIELD, consts.RX_PWR_1_FIELD, + consts.RX_PWR_2_FIELD, consts.RX_PWR_3_FIELD, consts.RX_PWR_4_FIELD] + super(RxPowerField, self).__init__(name, offset, *fields, **kwargs) + + def decode(self, raw_data, **decoded_deps): + int_cal = decoded_deps.get(consts.INT_CAL_FIELD) + ext_cal = decoded_deps.get(consts.EXT_CAL_FIELD) + measured_val = struct.unpack(self.format, raw_data)[0] + if int_cal: + return measured_val / self.scale + elif ext_cal: + rx_pwr_4 = decoded_deps.get(consts.RX_PWR_4_FIELD) + rx_pwr_3 = decoded_deps.get(consts.RX_PWR_3_FIELD) + rx_pwr_2 = decoded_deps.get(consts.RX_PWR_2_FIELD) + rx_pwr_1 = decoded_deps.get(consts.RX_PWR_1_FIELD) + rx_pwr_0 = decoded_deps.get(consts.RX_PWR_0_FIELD) + + result = rx_pwr_4 * measured_val * 1e4 + \ + rx_pwr_3 * measured_val * 1e3 + \ + rx_pwr_2 * measured_val * 1e2 + \ + rx_pwr_1 * measured_val + \ + rx_pwr_0 + return result / self.scale + return float('NaN') diff --git a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py index e7d37622d88e..3c36a843e6be 100644 --- a/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py +++ b/sonic_platform_base/sonic_xcvr/fields/xcvr_field.py @@ -162,6 +162,22 @@ def encode(self, val, raw_state=None): return bytearray(struct.pack(self.format, int(val * self.scale))) return bytearray(struct.pack(self.format, val)) +class FixedNumberRegField(NumberRegField): + """ + Interprets byte(s) as a fixed-point number + """ + def __init__(self, name, offset, num_frac_bits, *fields, **kwargs): + super(FixedNumberRegField, self).__init__(name, offset, *fields, **kwargs) + self.num_frac_bits = num_frac_bits + + def decode(self, raw_data, **decoded_deps): + decoded = super(FixedNumberRegField, self).decode(raw_data, **decoded_deps) + return decoded / (1 << self.num_frac_bits) + + def encode(self, val, raw_state=None): + bin = val * (1 << self.num_frac_bits) + return super(FixedNumberRegField, self).encode(bin, raw_state) + class StringRegField(RegField): """ Interprets byte(s) as a string diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py index c792c2bad074..9a7f2442de9c 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py @@ -122,18 +122,18 @@ def __init__(self, codes): NumberRegField(consts.VOLTAGE_LOW_ALARM_FIELD, self.getaddr(0x2, 138), size=2, format=">H", scale=10000.0), NumberRegField(consts.VOLTAGE_HIGH_WARNING_FIELD, self.getaddr(0x2, 140), size=2, format=">H", scale=10000.0), NumberRegField(consts.VOLTAGE_LOW_WARNING_FIELD, self.getaddr(0x2, 142), size=2, format=">H", scale=10000.0), - NumberRegField(consts.TX_POWER_HIGH_ALARM_FIELD, self.getaddr(0x2, 176), size=2, format=">H", scale=1000.0), - NumberRegField(consts.TX_POWER_LOW_ALARM_FIELD, self.getaddr(0x2, 178), size=2, format=">H", scale=1000.0), - NumberRegField(consts.TX_POWER_HIGH_WARNING_FIELD, self.getaddr(0x2, 180), size=2, format=">H", scale=1000.0), - NumberRegField(consts.TX_POWER_LOW_WARNING_FIELD, self.getaddr(0x2, 182), size=2, format=">H", scale=1000.0), + NumberRegField(consts.TX_POWER_HIGH_ALARM_FIELD, self.getaddr(0x2, 176), size=2, format=">H", scale=10000.0), + NumberRegField(consts.TX_POWER_LOW_ALARM_FIELD, self.getaddr(0x2, 178), size=2, format=">H", scale=10000.0), + NumberRegField(consts.TX_POWER_HIGH_WARNING_FIELD, self.getaddr(0x2, 180), size=2, format=">H", scale=10000.0), + NumberRegField(consts.TX_POWER_LOW_WARNING_FIELD, self.getaddr(0x2, 182), size=2, format=">H", scale=10000.0), NumberRegField(consts.TX_BIAS_HIGH_ALARM_FIELD, self.getaddr(0x2, 184), size=2, format=">H", scale=500.0), NumberRegField(consts.TX_BIAS_LOW_ALARM_FIELD, self.getaddr(0x2, 186), size=2, format=">H", scale=500.0), NumberRegField(consts.TX_BIAS_HIGH_WARNING_FIELD, self.getaddr(0x2, 188), size=2, format=">H", scale=500.0), NumberRegField(consts.TX_BIAS_LOW_WARNING_FIELD, self.getaddr(0x2, 190), size=2, format=">H", scale=500.0), - NumberRegField(consts.RX_POWER_HIGH_ALARM_FIELD, self.getaddr(0x2, 192), size=2, format=">H", scale=1000.0), - NumberRegField(consts.RX_POWER_LOW_ALARM_FIELD, self.getaddr(0x2, 194), size=2, format=">H", scale=1000.0), - NumberRegField(consts.RX_POWER_HIGH_WARNING_FIELD, self.getaddr(0x2, 196), size=2, format=">H", scale=1000.0), - NumberRegField(consts.RX_POWER_LOW_WARNING_FIELD, self.getaddr(0x2, 198), size=2, format=">H", scale=1000.0), + NumberRegField(consts.RX_POWER_HIGH_ALARM_FIELD, self.getaddr(0x2, 192), size=2, format=">H", scale=10000.0), + NumberRegField(consts.RX_POWER_LOW_ALARM_FIELD, self.getaddr(0x2, 194), size=2, format=">H", scale=10000.0), + NumberRegField(consts.RX_POWER_HIGH_WARNING_FIELD, self.getaddr(0x2, 196), size=2, format=">H", scale=10000.0), + NumberRegField(consts.RX_POWER_LOW_WARNING_FIELD, self.getaddr(0x2, 198), size=2, format=">H", scale=10000.0), NumberRegField(consts.AUX1_HIGH_ALARM, self.getaddr(0x2, 144), format=">h", size=2), NumberRegField(consts.AUX1_LOW_ALARM, self.getaddr(0x2, 146), format=">h", size=2), NumberRegField(consts.AUX1_HIGH_WARN, self.getaddr(0x2, 148), format=">h", size=2), @@ -167,7 +167,7 @@ def __init__(self, codes): ), RegGroupField(consts.TX_POWER_FIELD, - *(NumberRegField("OpticalPowerTx%dField" % channel, self.getaddr(0x11, offset), size=2, format=">H", scale=1000.0) + *(NumberRegField("OpticalPowerTx%dField" % channel, self.getaddr(0x11, offset), size=2, format=">H", scale=10000.0) for channel, offset in zip(range(1, 9), range(154, 170, 2))) ), RegGroupField(consts.TX_BIAS_FIELD, @@ -175,7 +175,7 @@ def __init__(self, codes): for channel, offset in zip(range(1, 9), range(170, 186, 2))) ), RegGroupField(consts.RX_POWER_FIELD, - *(NumberRegField("OpticalPowerRx%dField" % channel, self.getaddr(0x11, offset), size=2, format=">H", scale=1000.0) + *(NumberRegField("OpticalPowerRx%dField" % channel, self.getaddr(0x11, offset), size=2, format=">H", scale=10000.0) for channel, offset in zip(range(1, 9), range(186, 202, 2))) ), diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8436.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8436.py index 51c99997513e..0e01b1b2bc3a 100644 --- a/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8436.py +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8436.py @@ -91,7 +91,7 @@ def __init__(self, codes): ) self.RX_POWER = RegGroupField(consts.RX_POWER_FIELD, - *(NumberRegField("Rx%dPowerField" % channel, self.get_addr(0, offset), size=2, format=">H", scale=1000) + *(NumberRegField("Rx%dPowerField" % channel, self.get_addr(0, offset), size=2, format=">H", scale=10000) for channel, offset in zip(range(1, 5), range(34, 41, 2))) ) @@ -125,10 +125,10 @@ def __init__(self, codes): ) self.RX_POWER_THRESHOLDS = RegGroupField(consts.RX_POWER_THRESHOLDS_FIELD, - NumberRegField(consts.RX_POWER_HIGH_ALARM_FIELD, self.get_addr(3, 176), size=2, format=">H", scale=1000), - NumberRegField(consts.RX_POWER_LOW_ALARM_FIELD, self.get_addr(3, 178), size=2, format=">H", scale=1000), - NumberRegField(consts.RX_POWER_HIGH_WARNING_FIELD, self.get_addr(3, 180), size=2, format=">H", scale=1000), - NumberRegField(consts.RX_POWER_LOW_WARNING_FIELD, self.get_addr(3, 182), size=2, format=">H", scale=1000), + NumberRegField(consts.RX_POWER_HIGH_ALARM_FIELD, self.get_addr(3, 176), size=2, format=">H", scale=10000), + NumberRegField(consts.RX_POWER_LOW_ALARM_FIELD, self.get_addr(3, 178), size=2, format=">H", scale=10000), + NumberRegField(consts.RX_POWER_HIGH_WARNING_FIELD, self.get_addr(3, 180), size=2, format=">H", scale=10000), + NumberRegField(consts.RX_POWER_LOW_WARNING_FIELD, self.get_addr(3, 182), size=2, format=">H", scale=10000), ) self.TX_BIAS_THRESHOLDS = RegGroupField(consts.TX_BIAS_THRESHOLDS_FIELD, diff --git a/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8472.py b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8472.py new file mode 100644 index 000000000000..dd53b51252c8 --- /dev/null +++ b/sonic_platform_base/sonic_xcvr/mem_maps/public/sff8472.py @@ -0,0 +1,172 @@ +""" + sff8472.py + + Implementation of XcvrMemMap for SFF-8472 Rev 12.3 +""" + +from ..xcvr_mem_map import XcvrMemMap +from ...fields.xcvr_field import ( + CodeRegField, + DateField, + FixedNumberRegField, + HexRegField, + NumberRegField, + RegBitField, + RegGroupField, + StringRegField, +) +from ...fields import consts +from ...fields.public.sff8472 import ( + RxPowerField, + TempField, + TxBiasField, + TxPowerField, + VoltageField, +) + +class Sff8472MemMap(XcvrMemMap): + def __init__(self, codes): + super(Sff8472MemMap, self).__init__(codes) + + self.SERIAL_ID = RegGroupField(consts.SERIAL_ID_FIELD, + CodeRegField(consts.ID_FIELD, self.get_addr(0xA0, None, 0), self.codes.XCVR_IDENTIFIERS), + CodeRegField(consts.ID_ABBRV_FIELD, self.get_addr(0xA0, None, 0), self.codes.XCVR_IDENTIFIER_ABBRV), + CodeRegField(consts.EXT_ID_FIELD, self.get_addr(0xA0, None, 1), self.codes.EXT_IDENTIFIERS), + CodeRegField(consts.CONNECTOR_FIELD, self.get_addr(0xA0, None, 2), self.codes.CONNECTORS), + RegGroupField(consts.SPEC_COMPLIANCE_FIELD, + CodeRegField(consts.ETHERNET_10G_COMPLIANCE_FIELD, self.get_addr(0xA0, None, 3), self.codes.ETHERNET_10G_COMPLIANCE, + *(RegBitField("%s_%d" % (consts.ETHERNET_10G_COMPLIANCE_FIELD, bit), bit) for bit in range(4, 8)) + ), + CodeRegField(consts.INFINIBAND_COMPLIANCE_FIELD, self.get_addr(0xA0, None, 3), self.codes.INFINIBAND_COMPLIANCE, + *(RegBitField("%s_%d" % (consts.INFINIBAND_COMPLIANCE_FIELD, bit), bit) for bit in range(0, 4)) + ), + CodeRegField(consts.ESCON_COMPLIANCE_FIELD, self.get_addr(0xA0, None, 4), self.codes.ESCON_COMPLIANCE, + *(RegBitField("%s_%d" % (consts.ESCON_COMPLIANCE_FIELD, bit), bit) for bit in range(6, 8)) + ), + CodeRegField(consts.SONET_COMPLIANCE_FIELD, self.get_addr(0xA0, None, 4), self.codes.SONET_COMPLIANCE, + *(RegBitField("%s_%d" % (consts.SONET_COMPLIANCE_FIELD, bit), bit) for bit in range(0, 14)), + size=2, format=">H" + ), + CodeRegField(consts.ETHERNET_COMPLIANCE_FIELD, self.get_addr(0xA0, None, 6), self.codes.ETHERNET_COMPLIANCE), + CodeRegField(consts.FIBRE_CHANNEL_LINK_LENGTH_FIELD, self.get_addr(0xA0, None, 7), self.codes.FIBRE_CHANNEL_LINK_LENGTH, + *(RegBitField("%s_%d" % (consts.FIBRE_CHANNEL_LINK_LENGTH_FIELD, bit), bit) for bit in range(3, 8)) + ), + CodeRegField(consts.FIBRE_CHANNEL_TRANSMITTER_TECH_FIELD, self.get_addr(0xA0, None, 7), self.codes.FIBRE_CHANNEL_TRANSMITTER_TECH, + *(RegBitField("%s_%d" % (consts.FIBRE_CHANNEL_TRANSMITTER_TECH_FIELD, bit), bit) for bit in range(4, 11)), + size=2, format=">H" + ), + CodeRegField(consts.SFP_CABLE_TECH_FIELD, self.get_addr(0xA0, None, 8), self.codes.SFP_CABLE_TECH, + *(RegBitField("%s_%d" % (consts.SFP_CABLE_TECH_FIELD, bit), bit) for bit in range(0, 4)) + ), + CodeRegField(consts.FIBRE_CHANNEL_TRANSMISSION_MEDIA_FIELD, self.get_addr(0xA0, None, 9), self.codes.FIBRE_CHANNEL_TRANSMISSION_MEDIA), + CodeRegField(consts.FIBRE_CHANNEL_SPEED_FIELD, self.get_addr(0xA0, None, 10), self.codes.FIBRE_CHANNEL_SPEED), + ), + CodeRegField(consts.ENCODING_FIELD, self.get_addr(0xA0, None, 11), self.codes.ENCODINGS), + NumberRegField(consts.NOMINAL_BR_FIELD, self.get_addr(0xA0, None, 12)), + CodeRegField(consts.RATE_ID_FIELD, self.get_addr(0xA0, None, 13), self.codes.RATE_IDENTIFIERS), + NumberRegField(consts.LENGTH_SMF_KM_FIELD, self.get_addr(0xA0, None, 14)), + NumberRegField(consts.LENGTH_SMF_M_FIELD, self.get_addr(0xA0, None, 15)), + NumberRegField(consts.LENGTH_OM2_FIELD, self.get_addr(0xA0, None, 16)), + NumberRegField(consts.LENGTH_OM1_FIELD, self.get_addr(0xA0, None, 17)), + NumberRegField(consts.LENGTH_OM4_FIELD, self.get_addr(0xA0, None, 18)), + NumberRegField(consts.LENGTH_OM3_FIELD, self.get_addr(0xA0, None, 19)), + StringRegField(consts.VENDOR_NAME_FIELD, self.get_addr(0xA0, None, 20), size=16), + HexRegField(consts.VENDOR_OUI_FIELD, self.get_addr(0xA0, None, 37), size=3), + StringRegField(consts.VENDOR_PART_NO_FIELD, self.get_addr(0xA0, None, 40), size=16), + StringRegField(consts.VENDOR_REV_FIELD, self.get_addr(0xA0, None, 56), size=4), + NumberRegField(consts.OPTIONS_FIELD, self.get_addr(0xA0, None, 64), + RegBitField(consts.PAGING_SUPPORT_FIELD, 4), + size=2, format="