PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
"""Module EEPROM parsing and decoding"""
from enum import Enum, IntEnum
from struct import unpack
from typing import Union
# Generic I2C address for low page of all modules,
# where the module identifier can be found,
# from ethtool project, netlink/module-eeprom.c file
ETH_I2C_ADDRESS_LOW = 0x50
# SFF 8079 specific values,
# from ethtool project, sfpid.c file
SFF8079_PAGE_SIZE = 0x80
SFF8079_I2C_ADDRESS_LOW = ETH_I2C_ADDRESS_LOW
SFF8079_I2C_ADDRESS_HIGH = 0x51
# Length of SFF fields containing array,
# from SFF-8472 specification
SFF_FIELD_VENDOR_NAME_LEN = 16
SFF_FIELD_VENDOR_OUI_LEN = 3
SFF_FIELD_VENDOR_PN_LEN = 16
SFF_FIELD_VENDOR_REV_LEN = 4
SFF_FIELD_VENDOR_SN_LEN = 16
SFF_FIELD_DATE_CODE_LEN = 8
# Offset at which the A2 page is in the EEPROM blob returned by the kernel,
# from ethtool project, sfpdiag.c file
SFF_PAGE_A2_BASE = 0x100
class ModuleInfoField(Enum):
"""List of parsed fields describing the module information"""
IDENTIFIER = "Identifier"
EXTENDED_IDENTIFIER = "Extended identifier"
CONNECTOR = "Connector"
TRANSCEIVER_TYPE = "Transceiver type"
ENCODING = "Encoding"
BR_NOMINAL = "BR, Nominal, in MBd"
BR_MIN = "BR margin, max, in %"
BR_MAX = "BR margin, min, in %"
RATE_IDENTIFIER = "Rate identifier"
LENGTH_SFM_KM = "Length (SMF), in km"
LENGTH_SFM_100M = "Length (SMF), in m"
LENGTH_50UM_10M = "Length (50um), in m"
LENGTH_62_5UM_10M = "Length (62.5um), in m"
LENGTH_COPPER = "Length (Copper), in m"
LENGTH_OM3_10M = "Length (OM3), in m"
VENDOR_NAME = "Vendor name"
VENDOR_OUI = "Vendor OUI"
VENDOR_PN = "Vendor PN"
VENDOR_REV = "Vendor rev"
PASSIVE_COPPER_COMP = "Passive Cu compliance"
ACTIVE_COPPER_COMP = "Active Cu compliance"
LASER_WAVELENGTH = "Laser wavelength, in nm"
OPTION = "Option"
VENDOR_SN = "Vendor SN"
DATE_CODE = "Date code"
DIAG_MON_SUPPORT = "Optical diagnostics support"
ALARM_SUPPORT = "Alarm/warning flags implemented"
TEMP = "Module temperature, in °C"
TEMP_HALRM = "Module temperature high alarm"
TEMP_LALRM = "Module temperature low alarm"
TEMP_HWARN = "Module temperature high warning"
TEMP_LWARN = "Module temperature low warning"
BIAS = "Laser bias current, in mA"
BIAS_HALRM = "Laser bias current high alarm"
BIAS_LALRM = "Laser bias current low alarm"
BIAS_HWARN = "Laser bias current high warning"
BIAS_LWARN = "Laser bias current low warning"
VCC = "Module voltage, in V"
VCC_HALRM = "Module voltage high alarm"
VCC_LALRM = "Module voltage low alarm"
VCC_HWARN = "Module voltage high warning"
VCC_LWARN = "Module voltage low warning"
TX_PWR = "Laser output power, in mW"
TX_PWR_HALRM = "Laser output power high alarm"
TX_PWR_LALRM = "Laser output power low alarm"
TX_PWR_HWARN = "Laser output power high warning"
TX_PWR_LWARN = "Laser output power low warning"
RX_PWR = "Receiver signal, in mW"
RX_PWR_HALRM = "Laser rx power high alarm"
RX_PWR_LALRM = "Laser rx power low alarm"
RX_PWR_HWARN = "Laser rx power high warning"
RX_PWR_LWARN = "Laser rx power low warning"
class SffModuleType(IntEnum):
"""EEPROM Standards for plug in modules,
from Linux kernel, include/uapi/linux/ethtool.h file"""
SFF_8079 = 1
SFF_8472 = 2
SFF_8636 = 3
SFF_8436 = 4
class SffModulePageLength(IntEnum):
"""Module EEPROM page length,
from Linux kernel, include/uapi/linux/ethtool.h file"""
SFF_8079_LEN = 256
SFF_8472_LEN = 512
SFF_8636_LEN = 256
SFF_8436_LEN = 256
class SffOffset(IntEnum):
"""Offset of each data field, defined in
SFF-8472 specification, section 4.4"""
ID = 0
ID_EXT = 1
CONNECTOR = 2
TRANSCEIVER_3 = 3
TRANSCEIVER_4 = 4
TRANSCEIVER_5 = 5
TRANSCEIVER_6 = 6
TRANSCEIVER_7 = 7
TRANSCEIVER_8 = 8
TRANSCEIVER_9 = 9
TRANSCEIVER_10 = 10
ENCODING = 11
BR_NOMINAL = 12
RATE_IDENTIFIER = 13
LENGTH_SFM_KM = 14
LENGTH_SFM_100M = 15
LENGTH_50UM_10M = 16
LENGTH_62_5UM_10M = 17
LENGTH_COPPER = 18
LENGTH_OM3_10M = 19
VENDOR_NAME = 20 # 16 bytes
TRANSCEIVER_36 = 36
VENDOR_OUI = 37 # 3 bytes
VENDOR_PN = 40 # 16 bytes
VENDOR_REV = 56 # 4 bytes
LINK_CHAR_60 = 60
LINK_CHAR_61 = 61
# 62 is reserved
CC_BASE = 63
OPTIONS_64 = 64
OPTIONS_65 = 65
BR_MAX = 66
BR_MIN = 67
VENDOR_SN = 68 # 16 bytes
DATE_CODE = 84 # 8 bytes
DIAG_MON = 92
ENHANCED_OPTIONS = 93
SFF_8472_COMPLIANCE = 94
CC_EXT = 95
TEMP_HALRM = SFF_PAGE_A2_BASE + 0
TEMP_LALRM = SFF_PAGE_A2_BASE + 2
TEMP_HWARN = SFF_PAGE_A2_BASE + 4
TEMP_LWARN = SFF_PAGE_A2_BASE + 6
VCC_HALRM = SFF_PAGE_A2_BASE + 8
VCC_LALRM = SFF_PAGE_A2_BASE + 10
VCC_HWARN = SFF_PAGE_A2_BASE + 12
VCC_LWARN = SFF_PAGE_A2_BASE + 14
BIAS_HALRM = SFF_PAGE_A2_BASE + 16
BIAS_LALRM = SFF_PAGE_A2_BASE + 18
BIAS_HWARN = SFF_PAGE_A2_BASE + 20
BIAS_LWARN = SFF_PAGE_A2_BASE + 22
TX_PWR_HALRM = SFF_PAGE_A2_BASE + 24
TX_PWR_LALRM = SFF_PAGE_A2_BASE + 26
TX_PWR_HWARN = SFF_PAGE_A2_BASE + 28
TX_PWR_LWARN = SFF_PAGE_A2_BASE + 30
RX_PWR_HALRM = SFF_PAGE_A2_BASE + 32
RX_PWR_LALRM = SFF_PAGE_A2_BASE + 34
RX_PWR_HWARN = SFF_PAGE_A2_BASE + 36
RX_PWR_LWARN = SFF_PAGE_A2_BASE + 38
TEMP = SFF_PAGE_A2_BASE + 96
VCC = SFF_PAGE_A2_BASE + 98
BIAS = SFF_PAGE_A2_BASE + 100
TX_PWR = SFF_PAGE_A2_BASE + 102
RX_PWR = SFF_PAGE_A2_BASE + 104
ALRM_FLG_1 = SFF_PAGE_A2_BASE + 112
ALRM_FLG_2 = SFF_PAGE_A2_BASE + 113
WARN_FLG_1 = SFF_PAGE_A2_BASE + 116
WARN_FLG_2 = SFF_PAGE_A2_BASE + 117
class Sff8024Id(IntEnum):
"""Module identifiers, defined in SFF-8024 specification, section 4.2"""
UNKNOWN = 0x00
GBIC = 0x01
SOLDERED_MODULE = 0x02
SFP = 0x03
XBI_300_PIN = 0x04
XENPAK = 0x05
XFP = 0x06
XFF = 0x07
XFP_E = 0x08
XPAK = 0x09
X2 = 0x0A
DWDM_SFP = 0x0B
QSFP = 0x0C
QSFP_PLUS = 0x0D
CXP = 0x0E
HD4X = 0x0F
HD8X = 0x10
QSFP28 = 0x11
CXP2 = 0x12
CDFP = 0x13
HD4X_FANOUT = 0x14
HD8X_FANOUT = 0x15
CDFP_S3 = 0x16
MICRO_QSFP = 0x17
QSFP_DD = 0x18
OSFP = 0x19
DSFP = 0x1B
QSFP_PLUS_CMIS = 0x1E
SFP_DD_CMIS = 0x1F
SFP_PLUS_CMIS = 0x20
UNALLOCATED_LAST = 0x7F
VENDOR_START = 0x80
VENDOR_LAST = 0xFF
@property
def last(self):
"""Last allocated id"""
return self.SFP_PLUS_CMIS
class Sff8024Connector(IntEnum):
"""Connector references, defined in SFF-8024 specification, section 4.4"""
UNKNOWN = 0x00
SC = 0x01
FC_STYLE_1 = 0x02
FC_STYLE_2 = 0x03
BNC_TNC = 0x04
FC_COAX = 0x05
FIBER_JACK = 0x06
LC = 0x07
MT_RJ = 0x08
MU = 0x09
SG = 0x0A
OPT_PT = 0x0B
MPO = 0x0C
MPO_2 = 0x0D
# 0E-1F are reserved
HSDC_II = 0x20
COPPER_PT = 0x21
RJ45 = 0x22
NO_SEPARABLE = 0x23
MXC_2X16 = 0x24
CS_OPTICAL = 0x25
CS_OPTICAL_MINI = 0x26
MPO_2X12 = 0x27
MPO_1X16 = 0x28
NO_SEP_QSFP_DD = 0x6F
UNALLOCATED_LAST = 0x7F
VENDOR_START = 0x80
VENDOR_LAST = 0xFF
@property
def last(self):
"""Last allocated id"""
return self.MPO_1X16
class Sff8024Encoding(IntEnum):
"""Encoding references, defined in SFF-8024 specification, section 4.3"""
CODE_UNSPEC = 0x00
CODE_8B10B = 0x01
CODE_4B5B = 0x02
CODE_NRZ = 0x03
# SFF-8472 - Manchester
# SFF-8436/8636 - SONET Scrambled
CODE_04 = 0x04
# SFF-8472 - SONET Scrambled
# SFF-8436/8636 - 64B/66B
CODE_05 = 0x05
# SFF-8472 - 64B/66B
# SFF-8436/8636 - Manchester
CODE_06 = 0x06
CODE_256B = 0x07
CODE_PAM4 = 0x08
class Sff8024PowerMeas(IntEnum):
"""Power measurement type, defined in
SFF-8472 specification, section 8.8
"""
OMA = 0
AVERAGE_POWER = 1
class Sff8024AlarmWarningSrc(Enum):
"""Alarm and warning source, defined in
SFF-8472 specification, section 9.4
"""
TEMP = "TEMP"
VOLTAGE = "VCC"
BIAS = "BIAS"
TX_POWER = "TX_PWR"
RX_POWER = "RX_PWR"
class Sff8024AlarmWarningType(Enum):
"""Alarm and warning type, defined in
SFF-8472 specification, section 9.4
"""
HIGH_ALARM = "HALRM"
LOW_ALARM = "LALRM"
HIGH_WARNING = "HWARN"
LOW_WARNING = "LWARN"
def _is_bit_set(eeprom: tuple[int], offset: int, bit: int) -> bool:
"""Generic function to check if a bit is set in the EEPROM data
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param offset: byte offset
:type offset: int
:param bit: bit to check in the byte
:type bit: int
:return: bit set or not
:rtype: bool
"""
return eeprom[offset] & (1 << bit) != 0
def _sff8079_get_array(
eeprom: tuple[int], start: int, length: int, decode: bool = True
) -> str:
"""Generic function to extract an array from the EEPROM data
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param start: array start
:type start: int
:param length: array length
:type length: int
:param decode: decode the content as an ASCII string, defaults to True
:type decode: bool, optional
:return: extracted array
:rtype: str
"""
array = eeprom[start : start + length]
if decode:
array_desc = bytearray(array).decode().strip()
else:
array_desc = ":".join([f"{x:02x}" for x in array])
return array_desc
def _sff8472_get_temp(eeprom: tuple[int], offset: int) -> float:
"""Extract temperature from EEPROM array
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param offset: offset to read
:type offset: int
:return: temperature in °C
:rtype: float
"""
return round(
unpack("!h", bytearray([eeprom[offset], eeprom[offset + 1]]))[0] / 256,
2,
)
def _sff8472_get_bias(eeprom: tuple[int], offset: int) -> float:
"""Extract bias current from EEPROM array
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param offset: offset to read
:type offset: int
:return: bias current in mA
:rtype: float
"""
return (eeprom[offset] << 8 | eeprom[offset + 1]) / 500
def _sff8472_get_voltage_or_power(eeprom: tuple[int], offset: int) -> float:
"""Extract voltage or power from EEPROM array
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param offset: offset to read
:type offset: int
:return: voltage in V or power in mW
:rtype: float
"""
return (eeprom[offset] << 8 | eeprom[offset + 1]) / 10000
def sff8024_get_identifier(
eeprom: tuple[int], id_offset: int = SffOffset.ID
) -> tuple[str, str]:
"""Parsing of module identifier, defined
in SFF-8024 specification, section 4.2
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param id_offset: offset of identifier byte
:type id_offset: int
:return: parsed description and value
:rtype: tuple[str, str]
"""
module_id = eeprom[id_offset]
if module_id == Sff8024Id.UNKNOWN:
module_desc = "no module present, unknown, or unspecified"
elif module_id == Sff8024Id.GBIC:
module_desc = "GBIC"
elif module_id == Sff8024Id.SOLDERED_MODULE:
module_desc = "module soldered to motherboard"
elif module_id == Sff8024Id.SFP:
module_desc = "SFP"
elif module_id == Sff8024Id.XBI_300_PIN:
module_desc = "300 pin XBI"
elif module_id == Sff8024Id.XENPAK:
module_desc = "XENPAK"
elif module_id == Sff8024Id.XFP:
module_desc = "XFP"
elif module_id == Sff8024Id.XFF:
module_desc = "XFF"
elif module_id == Sff8024Id.XFP_E:
module_desc = "XFP-E"
elif module_id == Sff8024Id.XPAK:
module_desc = "XPAK"
elif module_id == Sff8024Id.X2:
module_desc = "X2"
elif module_id == Sff8024Id.DWDM_SFP:
module_desc = "DWDM-SFP"
elif module_id == Sff8024Id.QSFP:
module_desc = "QSFP"
elif module_id == Sff8024Id.QSFP_PLUS:
module_desc = "QSFP+"
elif module_id == Sff8024Id.CXP:
module_desc = "CXP"
elif module_id == Sff8024Id.HD4X:
module_desc = "Shielded Mini Multilane HD 4X"
elif module_id == Sff8024Id.HD8X:
module_desc = "Shielded Mini Multilane HD 8X"
elif module_id == Sff8024Id.QSFP28:
module_desc = "QSFP28"
elif module_id == Sff8024Id.CXP2:
module_desc = "CXP2/CXP28"
elif module_id == Sff8024Id.CDFP:
module_desc = "CDFP Style 1/Style 2"
elif module_id == Sff8024Id.HD4X_FANOUT:
module_desc = "Shielded Mini Multilane HD 4X Fanout Cable"
elif module_id == Sff8024Id.HD8X_FANOUT:
module_desc = "Shielded Mini Multilane HD 8X Fanout Cable"
elif module_id == Sff8024Id.CDFP_S3:
module_desc = "CDFP Style 3"
elif module_id == Sff8024Id.MICRO_QSFP:
module_desc = "microQSFP"
elif module_id == Sff8024Id.QSFP_DD:
module_desc = (
"QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628)"
)
elif module_id == Sff8024Id.OSFP:
module_desc = "OSFP 8X Pluggable Transceiver"
elif module_id == Sff8024Id.DSFP:
module_desc = "DSFP Dual Small Form Factor Pluggable Transceiver"
elif module_id == Sff8024Id.QSFP_PLUS_CMIS:
module_desc = (
"QSFP+ or later with Common Management"
" Interface Specification (CMIS)"
)
elif module_id == Sff8024Id.SFP_DD_CMIS:
module_desc = (
"SFP-DD Double Density 2X Pluggable Transceiver with"
" Common Management Interface Specification (CMIS)"
)
elif module_id == Sff8024Id.SFP_PLUS_CMIS:
module_desc = (
"SFP+ and later with Common Management"
" Interface Specification (CMIS)"
)
else:
module_desc = "reserved or unknown"
return (ModuleInfoField.IDENTIFIER.value, module_desc)
def sff8079_get_ext_identifier(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of extended module identifier, defined
in SFF-8472 specification, section 5.2
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
ext_id = eeprom[SffOffset.ID_EXT]
if ext_id == 0x00:
ext_id_desc = "GBIC not specified / not MOD_DEF compliant"
elif ext_id == 0x04:
ext_id_desc = "GBIC/SFP defined by 2-wire interface ID"
elif ext_id <= 0x07:
ext_id_desc = f"GBIC compliant with MOD_DEF {ext_id}"
else:
ext_id_desc = "unknown"
return (ModuleInfoField.EXTENDED_IDENTIFIER.value, ext_id_desc)
def sff8024_get_connector(
eeprom: tuple[int], offset: int = SffOffset.CONNECTOR
) -> tuple[str, str]:
"""Parsing of connector reference, defined
in SFF-8024 specification, section 4.4
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param offset: offset of connector byte
:type offset: int
:return: parsed description and value
:rtype: tuple[str, str]
"""
connector_id = eeprom[offset]
if connector_id == Sff8024Connector.UNKNOWN:
connector_desc = "unknown or unspecified"
elif connector_id == Sff8024Connector.SC:
connector_desc = "SC"
elif connector_id == Sff8024Connector.FC_STYLE_1:
connector_desc = "Fibre Channel Style 1 copper"
elif connector_id == Sff8024Connector.FC_STYLE_2:
connector_desc = "Fibre Channel Style 2 copper"
elif connector_id == Sff8024Connector.BNC_TNC:
connector_desc = "BNC/TNC"
elif connector_id == Sff8024Connector.FC_COAX:
connector_desc = "Fibre Channel coaxial headers"
elif connector_id == Sff8024Connector.FIBER_JACK:
connector_desc = "FibreJack"
elif connector_id == Sff8024Connector.LC:
connector_desc = "LC"
elif connector_id == Sff8024Connector.MT_RJ:
connector_desc = "MT-RJ"
elif connector_id == Sff8024Connector.MU:
connector_desc = "MU"
elif connector_id == Sff8024Connector.SG:
connector_desc = "SG"
elif connector_id == Sff8024Connector.OPT_PT:
connector_desc = "Optical pigtail"
elif connector_id == Sff8024Connector.MPO:
connector_desc = "MPO Parallel Optic"
elif connector_id == Sff8024Connector.MPO_2:
connector_desc = "MPO Parallel Optic - 2x16"
elif connector_id == Sff8024Connector.HSDC_II:
connector_desc = "HSSDC II"
elif connector_id == Sff8024Connector.COPPER_PT:
connector_desc = "Copper pigtail"
elif connector_id == Sff8024Connector.RJ45:
connector_desc = "RJ45"
elif connector_id == Sff8024Connector.NO_SEPARABLE:
connector_desc = "No separable connector"
elif connector_id == Sff8024Connector.MXC_2X16:
connector_desc = "MXC 2x16"
elif connector_id == Sff8024Connector.CS_OPTICAL:
connector_desc = "CS optical connector"
elif connector_id == Sff8024Connector.CS_OPTICAL_MINI:
connector_desc = "Mini CS optical connector"
elif connector_id == Sff8024Connector.MPO_2X12:
connector_desc = "MPO 2x12"
elif connector_id == Sff8024Connector.MPO_1X16:
connector_desc = "MPO 1x16"
else:
connector_desc = "reserved or unknown"
return (ModuleInfoField.CONNECTOR.value, connector_desc)
def sff8079_get_transceiver(eeprom: tuple[int]) -> tuple[str, list[str]]:
"""Parsing of transceiver reference, defined
in SFF-8472 specification, section 5.4
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, list[str]]
"""
transceiver_type = []
for offset, bit, type_str in (
(
SffOffset.TRANSCEIVER_3,
7,
"10G Ethernet: 10G Base-ER [SFF-8472 rev10.4 onwards]",
),
(SffOffset.TRANSCEIVER_3, 6, "10G Ethernet: 10G Base-LRM"),
(SffOffset.TRANSCEIVER_3, 5, "10G Ethernet: 10G Base-LR"),
(SffOffset.TRANSCEIVER_3, 4, "10G Ethernet: 10G Base-SR"),
(SffOffset.TRANSCEIVER_3, 3, "Infiniband: 1X SX"),
(SffOffset.TRANSCEIVER_3, 2, "Infiniband: 1X LX"),
(SffOffset.TRANSCEIVER_3, 1, "Infiniband: 1X Copper Active"),
(SffOffset.TRANSCEIVER_3, 0, "Infiniband: 1X Copper Passive"),
# ESCON Compliance Codes
(SffOffset.TRANSCEIVER_4, 7, "ESCON: ESCON MMF, 1310nm LED"),
(SffOffset.TRANSCEIVER_4, 6, "ESCON: ESCON SMF, 1310nm Laser"),
# SONET Compliance Codes
(SffOffset.TRANSCEIVER_4, 5, "SONET: OC-192, short reach"),
(SffOffset.TRANSCEIVER_4, 4, "SONET: SONET reach specifier bit 1"),
(SffOffset.TRANSCEIVER_4, 3, "SONET: SONET reach specifier bit 2"),
(SffOffset.TRANSCEIVER_4, 2, "SONET: OC-48, long reach"),
(SffOffset.TRANSCEIVER_4, 1, "SONET: OC-48, intermediate reach"),
(SffOffset.TRANSCEIVER_4, 0, "SONET: OC-48, short reach"),
(SffOffset.TRANSCEIVER_5, 6, "SONET: OC-12, single mode, long reach"),
(
SffOffset.TRANSCEIVER_5,
5,
"SONET: OC-12, single mode, inter. reach",
),
(SffOffset.TRANSCEIVER_5, 4, "SONET: OC-12, short reach"),
(SffOffset.TRANSCEIVER_5, 2, "SONET: OC-3, single mode, long reach"),
(SffOffset.TRANSCEIVER_5, 1, "SONET: OC-3, single mode, inter. reach"),
(SffOffset.TRANSCEIVER_5, 0, "SONET: OC-3, short reach"),
# Ethernet Compliance Codes
(SffOffset.TRANSCEIVER_6, 7, "Ethernet: BASE-PX"),
(SffOffset.TRANSCEIVER_6, 6, "Ethernet: BASE-BX10"),
(SffOffset.TRANSCEIVER_6, 5, "Ethernet: 100BASE-FX"),
(SffOffset.TRANSCEIVER_6, 4, "Ethernet: 100BASE-LX/LX10"),
(SffOffset.TRANSCEIVER_6, 3, "Ethernet: 1000BASE-T"),
(SffOffset.TRANSCEIVER_6, 2, "Ethernet: 1000BASE-CX"),
(SffOffset.TRANSCEIVER_6, 1, "Ethernet: 1000BASE-LX"),
(SffOffset.TRANSCEIVER_6, 0, "Ethernet: 1000BASE-SX"),
# Fibre Channel link length
(SffOffset.TRANSCEIVER_7, 7, "FC: very long distance (V)"),
(SffOffset.TRANSCEIVER_7, 6, "FC: short distance (S)"),
(SffOffset.TRANSCEIVER_7, 5, "FC: intermediate distance (I)"),
(SffOffset.TRANSCEIVER_7, 4, "FC: long distance (L)"),
(SffOffset.TRANSCEIVER_7, 3, "FC: medium distance (M)"),
# Fibre Channel transmitter technology
(SffOffset.TRANSCEIVER_7, 2, "FC: Shortwave laser, linear Rx (SA)"),
(SffOffset.TRANSCEIVER_7, 1, "FC: Longwave laser (LC)"),
(SffOffset.TRANSCEIVER_7, 0, "FC: Electrical inter-enclosure (EL)"),
(SffOffset.TRANSCEIVER_8, 7, "FC: Electrical intra-enclosure (EL)"),
(SffOffset.TRANSCEIVER_8, 6, "FC: Shortwave laser w/o OFC (SN)"),
(SffOffset.TRANSCEIVER_8, 5, "FC: Shortwave laser with OFC (SL)"),
(SffOffset.TRANSCEIVER_8, 4, "FC: Longwave laser (LL)"),
(SffOffset.TRANSCEIVER_8, 3, "Active Cable"),
(SffOffset.TRANSCEIVER_8, 2, "Passive Cable"),
(SffOffset.TRANSCEIVER_8, 1, "FC: Copper FC-BaseT"),
# Fibre Channel transmission media
(SffOffset.TRANSCEIVER_9, 7, "FC: Twin Axial Pair (TW)"),
(SffOffset.TRANSCEIVER_9, 6, "FC: Twisted Pair (TP)"),
(SffOffset.TRANSCEIVER_9, 5, "FC: Miniature Coax (MI)"),
(SffOffset.TRANSCEIVER_9, 4, "FC: Video Coax (TV)"),
(SffOffset.TRANSCEIVER_9, 3, "FC: Multimode, 62.5um (M6)"),
(SffOffset.TRANSCEIVER_9, 2, "FC: Multimode, 50um (M5)"),
(SffOffset.TRANSCEIVER_9, 0, "FC: Single Mode (SM)"),
# Fibre Channel speed
(SffOffset.TRANSCEIVER_10, 7, "FC: 1200 MBytes/sec"),
(SffOffset.TRANSCEIVER_10, 6, "FC: 800 MBytes/sec"),
(SffOffset.TRANSCEIVER_10, 4, "FC: 400 MBytes/sec"),
(SffOffset.TRANSCEIVER_10, 2, "FC: 200 MBytes/sec"),
(SffOffset.TRANSCEIVER_10, 0, "FC: 100 MBytes/sec"),
):
if _is_bit_set(eeprom, offset, bit):
transceiver_type.append(type_str)
for offset, value, type_str in (
# Extended Specification Compliance Codes from SFF-8024
(
SffOffset.TRANSCEIVER_36,
0x1,
"Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 5x10^(-5)",
),
(
SffOffset.TRANSCEIVER_36,
0x2,
"Extended: 100G Base-SR4 or 25GBase-SR",
),
(
SffOffset.TRANSCEIVER_36,
0x3,
"Extended: 100G Base-LR4 or 25GBase-LR",
),
(
SffOffset.TRANSCEIVER_36,
0x4,
"Extended: 100G Base-ER4 or 25GBase-ER",
),
(
SffOffset.TRANSCEIVER_36,
0x8,
"Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 5x10^(-5)",
),
(
SffOffset.TRANSCEIVER_36,
0xB,
"Extended: 100G Base-CR4 or 25G Base-CR CA-L",
),
(SffOffset.TRANSCEIVER_36, 0xC, "Extended: 25G Base-CR CA-S"),
(SffOffset.TRANSCEIVER_36, 0xD, "Extended: 25G Base-CR CA-N"),
(
SffOffset.TRANSCEIVER_36,
0x16,
"Extended: 10Gbase-T with SFI electrical interface",
),
(
SffOffset.TRANSCEIVER_36,
0x18,
"Extended: 100G AOC or 25GAUI C2M AOC with worst BER of 10^(-12)",
),
(
SffOffset.TRANSCEIVER_36,
0x19,
"Extended: 100G ACC or 25GAUI C2M ACC with worst BER of 10^(-12)",
),
(
SffOffset.TRANSCEIVER_36,
0x1A,
"Extended: 100GE-DWDM2 (DWDM transceiver using 2 wavelengths"
" on a 1550 nm DWDM grid with a reach up to 80 km",
),
(
SffOffset.TRANSCEIVER_36,
0x1B,
"Extended: 100G 1550nm WDM (4 wavelengths)",
),
(SffOffset.TRANSCEIVER_36, 0x1C, "Extended: 10Gbase-T Short Reach"),
(SffOffset.TRANSCEIVER_36, 0x1D, "Extended: 5GBASE-T"),
(SffOffset.TRANSCEIVER_36, 0x1E, "Extended: 2.5GBASE-T"),
(SffOffset.TRANSCEIVER_36, 0x1F, "Extended: 40G SWDM4"),
(SffOffset.TRANSCEIVER_36, 0x20, "Extended: 100G SWDM4"),
(SffOffset.TRANSCEIVER_36, 0x21, "Extended: 100G PAM4 BiDi"),
(
SffOffset.TRANSCEIVER_36,
0x22,
"Extended: 4WDM-10 MSA (10km version of 100G CWDM4"
" with same RS(528,514) FEC in host system)",
),
(
SffOffset.TRANSCEIVER_36,
0x23,
"Extended: 4WDM-20 MSA (20km version of 100GBASE-LR4"
" with RS(528,514) FEC in host system)",
),
(
SffOffset.TRANSCEIVER_36,
0x24,
"Extended: 4WDM-40 MSA (40km reach with APD receiver"
" and RS(528,514) FEC in host system)",
),
(
SffOffset.TRANSCEIVER_36,
0x25,
"Extended: 100GBASE-DR (clause 140), CAUI-4 (no FEC)",
),
(
SffOffset.TRANSCEIVER_36,
0x26,
"Extended: 100G-FR or 100GBASE-FR1 (clause 140), CAUI-4 (no FEC)",
),
(
SffOffset.TRANSCEIVER_36,
0x27,
"Extended: 100G-LR or 100GBASE-LR1 (clause 140), CAUI-4 (no FEC)",
),
(
SffOffset.TRANSCEIVER_36,
0x30,
"Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or"
" 200GAUI-4 C2M. Providing a worst BER of 10-6 or below",
),
(
SffOffset.TRANSCEIVER_36,
0x31,
"Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or"
" 200GAUI-4 C2M. Providing a worst BER of 10-6 or below",
),
(
SffOffset.TRANSCEIVER_36,
0x32,
"Extended: Active Copper Cable with 50GAUI, 100GAUI-2 or"
" 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC,"
" 10-5 for AUI, or below",
),
(
SffOffset.TRANSCEIVER_36,
0x33,
"Extended: Active Optical Cable with 50GAUI, 100GAUI-2 or"
" 200GAUI-4 C2M. Providing a worst BER of 2.6x10-4 for ACC,"
" 10-5 for AUI, or below",
),
(
SffOffset.TRANSCEIVER_36,
0x40,
"Extended: 50GBASE-CR, 100GBASE-CR2, or 200GBASE-CR4",
),
(
SffOffset.TRANSCEIVER_36,
0x41,
"Extended: 50GBASE-SR, 100GBASE-SR2, or 200GBASE-SR4",
),
(
SffOffset.TRANSCEIVER_36,
0x42,
"Extended: 50GBASE-FR or 200GBASE-DR4",
),
(SffOffset.TRANSCEIVER_36, 0x43, "Extended: 200GBASE-FR4"),
(SffOffset.TRANSCEIVER_36, 0x44, "Extended: 200G 1550 nm PSM4"),
(SffOffset.TRANSCEIVER_36, 0x45, "Extended: 50GBASE-LR"),
(SffOffset.TRANSCEIVER_36, 0x46, "Extended: 200GBASE-LR4"),
(SffOffset.TRANSCEIVER_36, 0x50, "Extended: 64GFC EA"),
(SffOffset.TRANSCEIVER_36, 0x51, "Extended: 64GFC SW"),
(SffOffset.TRANSCEIVER_36, 0x52, "Extended: 64GFC LW"),
(SffOffset.TRANSCEIVER_36, 0x53, "Extended: 128GFC EA"),
(SffOffset.TRANSCEIVER_36, 0x54, "Extended: 128GFC SW"),
(SffOffset.TRANSCEIVER_36, 0x55, "Extended: 128GFC LW"),
):
if eeprom[offset] == value:
transceiver_type.append(type_str)
return (ModuleInfoField.TRANSCEIVER_TYPE.value, transceiver_type)
def sff8024_get_encoding(
eeprom: tuple[int], sff_type: int, offset: int = SffOffset.ENCODING
) -> tuple[str, str]:
"""Parsing of encoding reference, defined
in SFF-8024 specification, section 4.3
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:param sff_type: module SFF type
:type sff_type: int
:param offset: offset of encoding byte
:type offset: int
:return: parsed description and value
:rtype: tuple[str, str]
"""
encoding_id = eeprom[offset]
encoding_desc = "reserved or unknown"
if encoding_id == Sff8024Encoding.CODE_UNSPEC:
encoding_desc = "unspecified"
elif encoding_id == Sff8024Encoding.CODE_8B10B:
encoding_desc = "8B/10B"
elif encoding_id == Sff8024Encoding.CODE_4B5B:
encoding_desc = "4B/5B"
elif encoding_id == Sff8024Encoding.CODE_NRZ:
encoding_desc = "NRZ"
elif encoding_id == Sff8024Encoding.CODE_04:
if sff_type == SffModuleType.SFF_8472:
encoding_desc = "Manchester"
elif sff_type == SffModuleType.SFF_8636:
encoding_desc = "SONET Scrambled"
elif encoding_id == Sff8024Encoding.CODE_05:
if sff_type == SffModuleType.SFF_8472:
encoding_desc = "SONET Scrambled"
elif sff_type == SffModuleType.SFF_8636:
encoding_desc = "64B/66B"
elif encoding_id == Sff8024Encoding.CODE_06:
if sff_type == SffModuleType.SFF_8472:
encoding_desc = "64B/66B"
elif sff_type == SffModuleType.SFF_8636:
encoding_desc = "Manchester"
elif encoding_id == Sff8024Encoding.CODE_256B:
encoding_desc = "256B/257B (transcoded FEC-enabled data)"
elif encoding_id == Sff8024Encoding.CODE_PAM4:
encoding_desc = "PAM4"
return (ModuleInfoField.ENCODING.value, encoding_desc)
def sff8079_get_baud_rates(eeprom: tuple[int]) -> list[tuple[str, int]]:
"""Parsing of signal rate, defined
in SFF-8472 specification, section 5.6
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, int]]
"""
if eeprom[SffOffset.BR_NOMINAL] == 0:
br_nom = 0
br_min = 0
br_max = 0
elif eeprom[SffOffset.BR_NOMINAL] == 255:
br_nom = eeprom[SffOffset.BR_MAX] * 250
br_max = eeprom[SffOffset.BR_MIN]
br_min = eeprom[SffOffset.BR_MIN]
else:
br_nom = eeprom[SffOffset.BR_NOMINAL] * 100
br_max = eeprom[SffOffset.BR_MAX]
br_min = eeprom[SffOffset.BR_MIN]
return [
(ModuleInfoField.BR_NOMINAL.value, br_nom),
(ModuleInfoField.BR_MIN.value, br_min),
(ModuleInfoField.BR_MAX.value, br_max),
]
def sff8079_get_rate_identifier(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of rate identifier, defined
in SFF-8472 specification, section 5.7
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
rate_id = eeprom[SffOffset.RATE_IDENTIFIER]
if rate_id == 0x00:
rate_desc = "unspecified"
elif rate_id == 0x01:
rate_desc = "4/2/1G Rate_Select & AS0/AS1"
elif rate_id == 0x02:
rate_desc = "8/4/2G Rx Rate_Select only"
elif rate_id == 0x03:
rate_desc = "8/4/2G Independent Rx & Tx Rate_Select"
elif rate_id == 0x04:
rate_desc = "8/4/2G Tx Rate_Select only"
else:
rate_desc = "reserved or unknown"
return (ModuleInfoField.RATE_IDENTIFIER.value, rate_desc)
def sff8079_get_lengths(eeprom: tuple[int]) -> list[tuple[str, int]]:
"""Parsing of link lengths, defined
in SFF-8472 specification, section 6
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, int]]
"""
sfm_km = eeprom[SffOffset.LENGTH_SFM_KM]
sfm_m = eeprom[SffOffset.LENGTH_SFM_100M] * 100
f50um_m = eeprom[SffOffset.LENGTH_50UM_10M] * 10
f62_5um_m = eeprom[SffOffset.LENGTH_62_5UM_10M] * 10
copper_m = eeprom[SffOffset.LENGTH_COPPER]
o3m_m = eeprom[SffOffset.LENGTH_OM3_10M] * 10
return [
(ModuleInfoField.LENGTH_SFM_KM.value, sfm_km),
(ModuleInfoField.LENGTH_SFM_100M.value, sfm_m),
(ModuleInfoField.LENGTH_50UM_10M.value, f50um_m),
(ModuleInfoField.LENGTH_62_5UM_10M.value, f62_5um_m),
(ModuleInfoField.LENGTH_COPPER.value, copper_m),
(ModuleInfoField.LENGTH_OM3_10M.value, o3m_m),
]
def sff8079_get_vendor_name(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor name, defined
in SFF-8472 specification, section 7.1
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.VENDOR_NAME.value,
_sff8079_get_array(
eeprom, SffOffset.VENDOR_NAME, SFF_FIELD_VENDOR_NAME_LEN
),
)
def sff8079_get_vendor_oui(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor Organizationally Unique Identifier, defined
in SFF-8472 specification, section 7.2
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.VENDOR_OUI.value,
_sff8079_get_array(
eeprom,
SffOffset.VENDOR_OUI,
SFF_FIELD_VENDOR_OUI_LEN,
decode=False,
),
)
def sff8079_get_vendor_pn(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor Part Number, defined
in SFF-8472 specification, section 7.3
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.VENDOR_PN.value,
_sff8079_get_array(
eeprom, SffOffset.VENDOR_PN, SFF_FIELD_VENDOR_PN_LEN
),
)
def sff8079_get_vendor_rev(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor revision number, defined
in SFF-8472 specification, section 7.4
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.VENDOR_REV.value,
_sff8079_get_array(
eeprom, SffOffset.VENDOR_REV, SFF_FIELD_VENDOR_REV_LEN
),
)
def sff8079_get_wavelength_or_copper_compliance(
eeprom: tuple[int],
) -> tuple[str, Union[str, int]]:
"""Parsing of link lengths, defined
in SFF-8472 specification, section 8.1
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, Union[str, int]]
"""
resp_desc: Union[str, int] = ""
if _is_bit_set(eeprom, SffOffset.TRANSCEIVER_8, 2):
resp_id = eeprom[SffOffset.LINK_CHAR_60]
resp_field = ModuleInfoField.PASSIVE_COPPER_COMP.value
if resp_id == 0x00:
resp_desc = "unspecified"
elif resp_id == 0x01:
resp_desc = "SFF-8431 appendix E"
else:
resp_desc = "unknown"
resp_desc += " [SFF-8472 rev10.4 only]"
elif _is_bit_set(eeprom, SffOffset.TRANSCEIVER_8, 3):
resp_id = eeprom[SffOffset.LINK_CHAR_60]
resp_field = ModuleInfoField.ACTIVE_COPPER_COMP.value
if resp_id == 0x00:
resp_desc = "unspecified"
elif resp_id == 0x01:
resp_desc = "SFF-8431 appendix E"
elif resp_id == 0x04:
resp_desc = "SFF-8431 limiting"
else:
resp_desc = "unknown"
resp_desc += " [SFF-8472 rev10.4 only]"
else:
resp_field = ModuleInfoField.LASER_WAVELENGTH.value
resp_desc = (eeprom[SffOffset.LINK_CHAR_60] << 8) | eeprom[
SffOffset.LINK_CHAR_61
]
return (resp_field, resp_desc)
def sff8079_get_options(eeprom: tuple[int]) -> tuple[str, list[str]]:
"""Parsing of options values, defined
in SFF-8472 specification, section 8.3
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, list[str]]
"""
options_desc = []
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 1):
options_desc.append("RX_LOS implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 2):
options_desc.append("RX_LOS implemented, inverted")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 3):
options_desc.append("TX_FAULT implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 4):
options_desc.append("TX_DISABLE implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 5):
options_desc.append("RATE_SELECT implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 6):
options_desc.append("Tunable transmitter technology")
if _is_bit_set(eeprom, SffOffset.OPTIONS_65, 7):
options_desc.append("Receiver decision threshold implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 0):
options_desc.append("Linear receiver output implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 1):
options_desc.append("Power level 2 requirement")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 2):
options_desc.append("Cooled transceiver implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 3):
options_desc.append("Retimer or CDR implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 4):
options_desc.append("Paging implemented")
if _is_bit_set(eeprom, SffOffset.OPTIONS_64, 5):
options_desc.append("Power level 3 requirement")
return (ModuleInfoField.OPTION.value, options_desc)
def sff8079_get_vendor_sn(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor Serial Number, defined
in SFF-8472 specification, section 8.6
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.VENDOR_SN.value,
_sff8079_get_array(
eeprom, SffOffset.VENDOR_SN, SFF_FIELD_VENDOR_SN_LEN
),
)
def sff8079_get_date_code(eeprom: tuple[int]) -> tuple[str, str]:
"""Parsing of vendor date code, defined
in SFF-8472 specification, section 8.7
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, str]
"""
return (
ModuleInfoField.DATE_CODE.value,
_sff8079_get_array(
eeprom, SffOffset.DATE_CODE, SFF_FIELD_DATE_CODE_LEN
),
)
def sff8472_get_diag_mon_support(eeprom: tuple[int]) -> tuple[str, bool]:
"""Parsing of diagnostic monitoring support, defined
in SFF-8472 specification, section 8.8
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, bool]
"""
return (
ModuleInfoField.DIAG_MON_SUPPORT.value,
_is_bit_set(eeprom, SffOffset.DIAG_MON, 6),
)
def sff8472_get_alarm_support(eeprom: tuple[int]) -> tuple[str, bool]:
"""Parsing of alarm support, defined
in SFF-8472 specification, section 8.8
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: tuple[str, bool]
"""
return (
ModuleInfoField.ALARM_SUPPORT.value,
_is_bit_set(eeprom, SffOffset.ENHANCED_OPTIONS, 7),
)
def sff8472_get_power_measurement_type(eeprom: tuple[int]) -> Sff8024PowerMeas:
"""Parsing of power measurement type, defined
in SFF-8472 specification, section 8.8
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed value
:rtype: Sff8024PowerMeas
"""
return Sff8024PowerMeas(_is_bit_set(eeprom, SffOffset.DIAG_MON, 3))
def sff8472_get_diag_mon(eeprom: tuple[int]) -> list[tuple[str, float]]:
"""Parsing of diagnostic monitoring, defined
in SFF-8472 specification, section 9.8
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, float]]
"""
rx_pwr_str: str = ModuleInfoField.RX_PWR.value
power_meas_type = sff8472_get_power_measurement_type(eeprom)
if power_meas_type == Sff8024PowerMeas.OMA:
rx_pwr_str += " (OMA)"
elif power_meas_type == Sff8024PowerMeas.AVERAGE_POWER:
rx_pwr_str += " (average optical power)"
return [
(
ModuleInfoField.TEMP.value,
_sff8472_get_temp(eeprom, SffOffset.TEMP),
),
(
ModuleInfoField.VCC.value,
_sff8472_get_voltage_or_power(eeprom, SffOffset.VCC),
),
(
ModuleInfoField.BIAS.value,
_sff8472_get_bias(eeprom, SffOffset.BIAS),
),
(
ModuleInfoField.TX_PWR.value,
_sff8472_get_voltage_or_power(eeprom, SffOffset.TX_PWR),
),
(rx_pwr_str, _sff8472_get_voltage_or_power(eeprom, SffOffset.RX_PWR)),
]
def sff8472_get_alarm_warning_thresholds(
eeprom: tuple[int],
) -> list[tuple[str, float]]:
"""Parsing of alarm and warning thresholds, defined
in SFF-8472 specification, section 9.4
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, float]]
"""
aw_thresholds: list[tuple[str, float]] = []
for aw_type, getter, suffix in (
(
Sff8024AlarmWarningSrc.TEMP.value,
_sff8472_get_temp,
"threshold, in °C",
),
(
Sff8024AlarmWarningSrc.VOLTAGE.value,
_sff8472_get_voltage_or_power,
"threshold, in V",
),
(
Sff8024AlarmWarningSrc.BIAS.value,
_sff8472_get_bias,
"threshold, in mA",
),
(
Sff8024AlarmWarningSrc.TX_POWER.value,
_sff8472_get_voltage_or_power,
"threshold, in mW",
),
(
Sff8024AlarmWarningSrc.RX_POWER.value,
_sff8472_get_voltage_or_power,
"threshold, in mW",
),
):
for aw in [aw.value for aw in Sff8024AlarmWarningType]:
aw_name = f"{aw_type}_{aw}"
aw_thresholds.append(
(
f"{getattr(ModuleInfoField, aw_name).value} {suffix}",
getter(eeprom, getattr(SffOffset, aw_name)),
)
)
return aw_thresholds
def sff8472_get_alarm_warning(eeprom: tuple[int]) -> list[tuple[str, bool]]:
"""Parsing of alarm and warning, defined
in SFF-8472 specification, section 9.9
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, bool]]
"""
return [
(aw_type, _is_bit_set(eeprom, offset, bit))
for aw_type, offset, bit in (
(ModuleInfoField.TEMP_HALRM.value, SffOffset.ALRM_FLG_1, 7),
(ModuleInfoField.TEMP_LALRM.value, SffOffset.ALRM_FLG_1, 6),
(ModuleInfoField.TEMP_HWARN.value, SffOffset.WARN_FLG_1, 7),
(ModuleInfoField.TEMP_LWARN.value, SffOffset.WARN_FLG_1, 6),
(ModuleInfoField.VCC_HALRM.value, SffOffset.ALRM_FLG_1, 5),
(ModuleInfoField.VCC_LALRM.value, SffOffset.ALRM_FLG_1, 4),
(ModuleInfoField.VCC_HWARN.value, SffOffset.WARN_FLG_1, 5),
(ModuleInfoField.VCC_LWARN.value, SffOffset.WARN_FLG_1, 4),
(ModuleInfoField.BIAS_HALRM.value, SffOffset.ALRM_FLG_1, 3),
(ModuleInfoField.BIAS_LALRM.value, SffOffset.ALRM_FLG_1, 2),
(ModuleInfoField.BIAS_HWARN.value, SffOffset.WARN_FLG_1, 3),
(ModuleInfoField.BIAS_LWARN.value, SffOffset.WARN_FLG_1, 2),
(ModuleInfoField.TX_PWR_HALRM.value, SffOffset.ALRM_FLG_1, 1),
(ModuleInfoField.TX_PWR_LALRM.value, SffOffset.ALRM_FLG_1, 0),
(ModuleInfoField.TX_PWR_HWARN.value, SffOffset.WARN_FLG_1, 1),
(ModuleInfoField.TX_PWR_LWARN.value, SffOffset.WARN_FLG_1, 0),
(ModuleInfoField.RX_PWR_HALRM.value, SffOffset.ALRM_FLG_2, 7),
(ModuleInfoField.RX_PWR_LALRM.value, SffOffset.ALRM_FLG_2, 6),
(ModuleInfoField.RX_PWR_HWARN.value, SffOffset.WARN_FLG_2, 7),
(ModuleInfoField.RX_PWR_LWARN.value, SffOffset.WARN_FLG_2, 6),
)
]
class ModuleInfoParser:
"""Class used to retrieve EEPROM data from a module
and to parse it to extract the module information
"""
def __init__(self, ethtool, ifname):
self.ethtool = ethtool
self.ifname = ifname
@staticmethod
def parse_sff8079_page0(
eeprom: tuple[int],
) -> list[tuple[str, Union[str, list[str], int]]]:
"""Parsing of EEPROM page 0 for SFF 8079 modules
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, Union[str, list[str], int]]
"""
return [
sff8024_get_identifier(eeprom),
sff8079_get_ext_identifier(eeprom),
sff8024_get_connector(eeprom),
sff8079_get_transceiver(eeprom),
sff8024_get_encoding(eeprom, SffModuleType.SFF_8472),
*sff8079_get_baud_rates(eeprom),
sff8079_get_rate_identifier(eeprom),
*sff8079_get_lengths(eeprom),
sff8079_get_wavelength_or_copper_compliance(eeprom),
sff8079_get_vendor_name(eeprom),
sff8079_get_vendor_oui(eeprom),
sff8079_get_vendor_pn(eeprom),
sff8079_get_vendor_rev(eeprom),
sff8079_get_options(eeprom),
sff8079_get_vendor_sn(eeprom),
sff8079_get_date_code(eeprom),
]
@staticmethod
def parse_sff8079_page2(
eeprom: tuple[int],
) -> list[tuple[str, Union[bool, float]]]:
"""Parsing of EEPROM page 2 for SFF 8079 modules
:param eeprom: EEPROM data
:type eeprom: tuple[int]
:return: parsed description and value
:rtype: list[tuple[str, Union[bool, float]]]
"""
return [
sff8472_get_diag_mon_support(eeprom),
*sff8472_get_diag_mon(eeprom),
sff8472_get_alarm_support(eeprom),
*sff8472_get_alarm_warning(eeprom),
*sff8472_get_alarm_warning_thresholds(eeprom),
]
def get_and_parse_sff8079(self):
"""EEPROM parsing for SFF 8079 modules"""
# Retrieve the first EEPROM page
resp = self.ethtool.get_module_eeprom(
self.ifname,
length=SFF8079_PAGE_SIZE,
i2c_address=SFF8079_I2C_ADDRESS_LOW,
)
raw_data = resp.data
parsed = self.parse_sff8079_page0(raw_data)
# Check if the second page should be retrieved
if not sff8472_get_diag_mon_support(raw_data)[0][1]:
return parsed
# Retrieve the second page
resp = self.ethtool.get_module_eeprom(
self.ifname,
length=SFF8079_PAGE_SIZE,
i2c_address=SFF8079_I2C_ADDRESS_HIGH,
)
# Add zero padding to align the EEPROM page
raw_data = raw_data + (0,) * (
SffModulePageLength.SFF_8079_LEN - len(raw_data)
)
raw_data += resp.data
parsed += self.parse_sff8079_page2(raw_data)
return parsed
def get_and_parse_sff8636(self):
"""EEPROM parsing for SFF 8636 modules"""
raise NotImplementedError(
"Parsing for SFF 8636 modules not implemented"
)
def get_and_parse_cmis(self):
"""EEPROM parsing for CMIS modules"""
raise NotImplementedError("Parsing for CMIS modules not implemented")
def parse_module_info(self):
"""Retrieve module identifier, and parse its EEPROM accordingly"""
# Fetch the SFF-8024 Identifier Value. For all supported standards, it
# is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
# revision 4.9.
resp = self.ethtool.get_module_eeprom(
self.ifname, length=1, i2c_address=ETH_I2C_ADDRESS_LOW
)
mod_type = resp.data[SffOffset.ID]
if mod_type in (
Sff8024Id.GBIC,
Sff8024Id.SOLDERED_MODULE,
Sff8024Id.SFP,
):
return self.get_and_parse_sff8079()
if mod_type in (Sff8024Id.QSFP, Sff8024Id.QSFP28, Sff8024Id.QSFP_PLUS):
return self.get_and_parse_sff8636()
if mod_type in (
Sff8024Id.QSFP_DD,
Sff8024Id.OSFP,
Sff8024Id.DSFP,
Sff8024Id.QSFP_PLUS_CMIS,
Sff8024Id.SFP_DD_CMIS,
Sff8024Id.SFP_PLUS_CMIS,
):
return self.get_and_parse_cmis()
# By default retrieve the first 128 bytes
return self.ethtool.get_module_eeprom(
self.ifname, length=128, i2c_address=ETH_I2C_ADDRESS_LOW
)
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E