Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 100 additions & 21 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,27 @@ def get_cdb_fw_handler(self):
def _get_vdm_key_to_db_prefix_map(self):
return CMIS_VDM_KEY_TO_DB_PREFIX_KEY_MAP

def _get_vdm_key_to_db_prefix_map_by_observable_type(self, observable_type):
"""
Returns the VDM key-to-DB-prefix map filtered by observable type,
using the 'B'/'S' classification from VDM_TYPE as the single source of truth.

Args:
observable_type: 'B' for basic (instantaneous), 'S' for statistic (min/max/avg)

Returns:
dict: Filtered subset of _get_vdm_key_to_db_prefix_map()
"""
vdm_type_dict = self.xcvr_eeprom.mem_map.codes.VDM_TYPE
matching_names = {
info[0] for info in vdm_type_dict.values()
if len(info) > 3 and info[3] == observable_type
}
return {
k: v for k, v in self._get_vdm_key_to_db_prefix_map().items()
if k in matching_names
}

@staticmethod
def _strip_str(val):
return val.rstrip() if isinstance(val, str) else val
Expand Down Expand Up @@ -1678,20 +1699,45 @@ def is_cdb_supported(self):

return False

@read_only_cached_api_return
def is_transceiver_vdm_supported(self):
'''
This function returns whether VDM is supported
'''
return self.vdm is not None and self.xcvr_eeprom.read(consts.VDM_SUPPORTED)

def get_vdm(self, field_option=None):
@read_only_cached_api_return
def is_vdm_statistic_supported(self):
'''
This function returns whether the optic advertises any VDM statistic
observable types (min/max/avg) in its VDM descriptor pages.

Returns:
bool: True if at least one statistic observable type is advertised, False otherwise.
'''
This function returns all the VDM items, including real time monitor value, threholds and flags
if self.vdm is None:
return False
return self.vdm.is_vdm_statistic_supported()

def get_vdm(self, field_option=None, observable_type=None):
'''
This function returns all the VDM items, including real time monitor value, thresholds and flags

Args:
field_option: Bitmask to select real value, threshold, and/or flag fields.
Defaults to ALL_FIELD (all fields).
observable_type: Bitmask to filter by observable type.
VDM_OBSERVABLE_BASIC (0x1) for basic (instantaneous) types,
VDM_OBSERVABLE_STATISTIC (0x2) for statistic (min/max/avg) types,
VDM_OBSERVABLE_ALL (0x3) for both. Defaults to VDM_OBSERVABLE_ALL.
'''
if self.vdm is None:
return {}
if field_option is None:
field_option = self.vdm.ALL_FIELD
vdm = self.vdm.get_vdm_allpage(field_option) if self.vdm is not None else {}
return vdm
if observable_type is None:
observable_type = self.vdm.VDM_OBSERVABLE_ALL
return self.vdm.get_vdm_allpage(field_option, observable_type) or {}

def get_module_firmware_fault_state_changed(self):
'''
Expand Down Expand Up @@ -2382,7 +2428,20 @@ def get_transceiver_loopback(self):

def get_transceiver_vdm_real_value(self):
"""
Retrieves VDM real value for this xcvr
Retrieves all VDM real values (both basic and statistic) for this xcvr.
This is a convenience method that merges basic and statistic results.

Returns:
Dictionary with all VDM real values.
"""
result = {}
result.update(self.get_transceiver_vdm_real_value_basic())
result.update(self.get_transceiver_vdm_real_value_statistic())
return result

def get_transceiver_vdm_real_value_basic(self):
"""
Retrieves basic VDM real values for this xcvr.

Returns:
A dict containing the following keys/values :
Expand All @@ -2394,21 +2453,9 @@ def get_transceiver_vdm_real_value(self):
esnr_host_input{lane_num} = FLOAT ; eSNR value in dB for host input
pam4_level_transition_media_input{lane_num} = FLOAT ; PAM4 level transition parameter in dB for media input
pam4_level_transition_host_input{lane_num} = FLOAT ; PAM4 level transition parameter in dB for host input
prefec_ber_min_media_input{lane_num} = FLOAT ; Pre-FEC BER minimum value for media input
prefec_ber_max_media_input{lane_num} = FLOAT ; Pre-FEC BER maximum value for media input
prefec_ber_avg_media_input{lane_num} = FLOAT ; Pre-FEC BER average value for media input
prefec_ber_curr_media_input{lane_num} = FLOAT ; Pre-FEC BER current value for media input
prefec_ber_min_host_input{lane_num} = FLOAT ; Pre-FEC BER minimum value for host input
prefec_ber_max_host_input{lane_num} = FLOAT ; Pre-FEC BER maximum value for host input
prefec_ber_avg_host_input{lane_num} = FLOAT ; Pre-FEC BER average value for host input
prefec_ber_curr_host_input{lane_num} = FLOAT ; Pre-FEC BER current value for host input
errored_frames_min_media_input{lane_num} = FLOAT ; Errored frames minimum value for media input
errored_frames_max_media_input{lane_num} = FLOAT ; Errored frames maximum value for media input
errored_frames_avg_media_input{lane_num} = FLOAT ; Errored frames average value for media input
errored_frames_curr_media_input{lane_num} = FLOAT ; Errored frames current value for media input
errored_frames_min_host_input{lane_num} = FLOAT ; Errored frames minimum value for host input
errored_frames_max_host_input{lane_num} = FLOAT ; Errored frames maximum value for host input
errored_frames_avg_host_input{lane_num} = FLOAT ; Errored frames average value for host input
errored_frames_curr_host_input{lane_num} = FLOAT ; Errored frames current value for host input

;C-CMIS specific fields
Expand All @@ -2433,8 +2480,40 @@ def get_transceiver_vdm_real_value(self):
========================================================================
"""
vdm_real_value_dict = dict()
vdm_raw_dict = self.get_vdm(self.vdm.VDM_REAL_VALUE)
for vdm_observable_type, db_key_name_prefix in self._get_vdm_key_to_db_prefix_map().items():
vdm_raw_dict = self.get_vdm(self.vdm.VDM_REAL_VALUE, self.vdm.VDM_OBSERVABLE_BASIC)
for vdm_observable_type, db_key_name_prefix in self._get_vdm_key_to_db_prefix_map_by_observable_type('B').items():
for lane in range(1, self.NUM_CHANNELS + 1):
db_key_name = f"{db_key_name_prefix}{lane}"
self._update_vdm_dict(vdm_real_value_dict, db_key_name, vdm_raw_dict, vdm_observable_type,
VdmSubtypeIndex.VDM_SUBTYPE_REAL_VALUE, lane)
return vdm_real_value_dict

def get_transceiver_vdm_real_value_statistic(self):
"""
Retrieves statistic (min/max/avg) VDM real values for this xcvr.

Returns:
A dict containing the following keys/values :
========================================================================
key = TRANSCEIVER_VDM_REAL_VALUE|ifname ; information module VDM sample on port
; field = value
prefec_ber_min_media_input{lane_num} = FLOAT ; Pre-FEC BER minimum value for media input
prefec_ber_max_media_input{lane_num} = FLOAT ; Pre-FEC BER maximum value for media input
prefec_ber_avg_media_input{lane_num} = FLOAT ; Pre-FEC BER average value for media input
prefec_ber_min_host_input{lane_num} = FLOAT ; Pre-FEC BER minimum value for host input
prefec_ber_max_host_input{lane_num} = FLOAT ; Pre-FEC BER maximum value for host input
prefec_ber_avg_host_input{lane_num} = FLOAT ; Pre-FEC BER average value for host input
errored_frames_min_media_input{lane_num} = FLOAT ; Errored frames minimum value for media input
errored_frames_max_media_input{lane_num} = FLOAT ; Errored frames maximum value for media input
errored_frames_avg_media_input{lane_num} = FLOAT ; Errored frames average value for media input
errored_frames_min_host_input{lane_num} = FLOAT ; Errored frames minimum value for host input
errored_frames_max_host_input{lane_num} = FLOAT ; Errored frames maximum value for host input
errored_frames_avg_host_input{lane_num} = FLOAT ; Errored frames average value for host input
========================================================================
"""
vdm_real_value_dict = dict()
vdm_raw_dict = self.get_vdm(self.vdm.VDM_REAL_VALUE, self.vdm.VDM_OBSERVABLE_STATISTIC)
for vdm_observable_type, db_key_name_prefix in self._get_vdm_key_to_db_prefix_map_by_observable_type('S').items():
for lane in range(1, self.NUM_CHANNELS + 1):
db_key_name = f"{db_key_name_prefix}{lane}"
self._update_vdm_dict(vdm_real_value_dict, db_key_name, vdm_raw_dict, vdm_observable_type,
Expand Down Expand Up @@ -2495,7 +2574,7 @@ def get_transceiver_vdm_thresholds(self):
rxsigpower_xxx{lane_num} = FLOAT ; rx signal power in dbm (high/low alarm/warning) ========================================================================
"""
vdm_thresholds_dict = dict()
vdm_raw_dict = self.get_vdm(self.vdm.VDM_THRESHOLD)
vdm_raw_dict = self.get_vdm(self.vdm.VDM_THRESHOLD, self.vdm.VDM_OBSERVABLE_ALL)
for vdm_observable_type, db_key_name_prefix in self._get_vdm_key_to_db_prefix_map().items():
for lane in range(1, self.NUM_CHANNELS + 1):
for vdm_threshold_type in range(VdmSubtypeIndex.VDM_SUBTYPE_HALARM_THRESHOLD.value, VdmSubtypeIndex.VDM_SUBTYPE_LWARN_THRESHOLD.value + 1):
Expand Down Expand Up @@ -2562,7 +2641,7 @@ def get_transceiver_vdm_flags(self):
rxsigpower_xxx{lane_num} = FLOAT ; rx signal power in dbm (high/low alarm/warning flag)
"""
vdm_flags_dict = dict()
vdm_raw_dict = self.get_vdm(self.vdm.VDM_FLAG)
vdm_raw_dict = self.get_vdm(self.vdm.VDM_FLAG, self.vdm.VDM_OBSERVABLE_ALL)
for vdm_observable_type, db_key_name_prefix in self._get_vdm_key_to_db_prefix_map().items():
for lane in range(1, self.NUM_CHANNELS + 1):
for vdm_flag_type in range(VdmSubtypeIndex.VDM_SUBTYPE_HALARM_FLAG.value, VdmSubtypeIndex.VDM_SUBTYPE_LWARN_FLAG.value + 1):
Expand Down
66 changes: 63 additions & 3 deletions sonic_platform_base/sonic_xcvr/api/public/cmisVDM.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ class CmisVdmApi(XcvrApi):
VDM_FLAG = 0x4
ALL_FIELD = 0xff

# Observable type filters
VDM_OBSERVABLE_BASIC = 0x1 # Basic (instantaneous) observable types
VDM_OBSERVABLE_STATISTIC = 0x2 # Statistic (min/max/avg) observable types
VDM_OBSERVABLE_ALL = 0x3 # Both basic and statistic

def __init__(self, xcvr_eeprom):
super(CmisVdmApi, self).__init__(xcvr_eeprom)

Expand All @@ -36,7 +41,7 @@ def get_F16(self, value):
result = mantissa*10**(scale_exponent-24)
return result

def get_vdm_page(self, page, VDM_flag_page, field_option=ALL_FIELD):
def get_vdm_page(self, page, VDM_flag_page, field_option=ALL_FIELD, observable_type=VDM_OBSERVABLE_ALL):
'''
This function returns VDM items from a specific VDM page.
Output format is a dictionary. Key is observable type; value is a dictionary.
Expand All @@ -52,6 +57,15 @@ def get_vdm_page(self, page, VDM_flag_page, field_option=ALL_FIELD):
vdm_high_warn_flag,
vdm_low_warn_flag
]

Args:
page: VDM descriptor page (0x20-0x23)
VDM_flag_page: Raw flag page data or None
field_option: Bitmask to select real value, threshold, and/or flag fields
observable_type: Bitmask to filter by observable type.
VDM_OBSERVABLE_BASIC (0x1) for basic (instantaneous) types,
VDM_OBSERVABLE_STATISTIC (0x2) for statistic (min/max/avg) types,
VDM_OBSERVABLE_ALL (0x3) for both.
'''
if page not in [0x20, 0x21, 0x22, 0x23]:
raise ValueError('Page not in VDM Descriptor range!')
Expand All @@ -75,6 +89,14 @@ def get_vdm_page(self, page, VDM_flag_page, field_option=ALL_FIELD):
continue

vdm_info_dict = VDM_TYPE_DICT[typeID]

# Filter by observable type (basic vs statistic)
vdm_obs_type = vdm_info_dict[3] if len(vdm_info_dict) > 3 else 'B'
if vdm_obs_type == 'B' and not (observable_type & self.VDM_OBSERVABLE_BASIC):
continue
if vdm_obs_type == 'S' and not (observable_type & self.VDM_OBSERVABLE_STATISTIC):
continue

thrshID = VDM_thresholdID[index]
vdm_type = vdm_info_dict[0]
vdm_format = vdm_info_dict[1]
Expand Down Expand Up @@ -178,7 +200,7 @@ def get_vdm_page(self, page, VDM_flag_page, field_option=ALL_FIELD):
vdm_low_warn_flag]
return vdm_Page_data

def get_vdm_allpage(self, field_option=ALL_FIELD ):
def get_vdm_allpage(self, field_option=ALL_FIELD, observable_type=VDM_OBSERVABLE_ALL):
'''
This function returns VDM items from all advertised VDM pages.
Output format is a dictionary. Key is observable type; value is a dictionary.
Expand All @@ -194,6 +216,13 @@ def get_vdm_allpage(self, field_option=ALL_FIELD ):
vdm_high_warn_flag,
vdm_low_warn_flag
]

Args:
field_option: Bitmask to select real value, threshold, and/or flag fields
observable_type: Bitmask to filter by observable type.
VDM_OBSERVABLE_BASIC (0x1) for basic (instantaneous) types,
VDM_OBSERVABLE_STATISTIC (0x2) for statistic (min/max/avg) types,
VDM_OBSERVABLE_ALL (0x3) for both.
'''
vdm_pages_supported = self.xcvr_eeprom.read(consts.VDM_SUPPORTED)
if not vdm_pages_supported:
Expand All @@ -210,6 +239,37 @@ def get_vdm_allpage(self, field_option=ALL_FIELD ):
vdm_flag_page = None

for page in range(VDM_START_PAGE, VDM_START_PAGE + vdm_groups_supported_raw + 1):
vdm_current_page = self.get_vdm_page(page, vdm_flag_page, field_option)
vdm_current_page = self.get_vdm_page(page, vdm_flag_page, field_option, observable_type)
vdm.update(vdm_current_page)
return vdm

def is_vdm_statistic_supported(self):
'''
Checks whether the optic advertises any VDM statistic observable types
by scanning the VDM descriptor pages for type IDs classified as 'S' in VDM_TYPE.

Returns:
bool: True if at least one statistic observable type is advertised, False otherwise.
'''
vdm_pages_supported = self.xcvr_eeprom.read(consts.VDM_SUPPORTED)
if not vdm_pages_supported:
return False
vdm_groups_supported_raw = self.xcvr_eeprom.read(consts.VDM_SUPPORTED_PAGE)
if vdm_groups_supported_raw is None:
return False

VDM_START_PAGE = 0x20
VDM_TYPE_DICT = self.xcvr_eeprom.mem_map.codes.VDM_TYPE

for page in range(VDM_START_PAGE, VDM_START_PAGE + vdm_groups_supported_raw + 1):
vdm_descriptor = self.xcvr_eeprom.read_raw(page * PAGE_SIZE + PAGE_OFFSET, PAGE_SIZE)
if not vdm_descriptor:
continue
# Odd addresses contain the VDM observable type IDs
vdm_typeIDs = vdm_descriptor[1::2]
for typeID in vdm_typeIDs:
if typeID in VDM_TYPE_DICT:
info = VDM_TYPE_DICT[typeID]
if len(info) > 3 and info[3] == 'S':
return True
return False
20 changes: 19 additions & 1 deletion sonic_platform_base/sonic_xcvr/api/xcvr_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,31 @@ def is_transceiver_vdm_supported(self):
"""
raise NotImplementedError

def is_vdm_statistic_supported(self):
"""
Retrieves whether the optic advertises any VDM statistic observables (applicable for CMIS and C-CMIS)
"""
raise NotImplementedError

def get_transceiver_vdm_real_value(self):
"""
Retrieves VDM real (sample) values for this xcvr (applicable for CMIS and C-CMIS)
Retrieves all VDM real (sample) values for this xcvr (applicable for CMIS and C-CMIS)
Specifically, it retrieves sample data from pages 24h to 27h
"""
raise NotImplementedError

def get_transceiver_vdm_real_value_basic(self):
"""
Retrieves basic (instantaneous) VDM real values for this xcvr (applicable for CMIS and C-CMIS)
"""
raise NotImplementedError

def get_transceiver_vdm_real_value_statistic(self):
"""
Retrieves statistic (min/max/avg) VDM real values for this xcvr (applicable for CMIS and C-CMIS)
"""
raise NotImplementedError

def get_transceiver_vdm_thresholds(self):
"""
Retrieves VDM thresholds for this xcvr (applicable for CMIS and C-CMIS)
Expand Down
Loading
Loading