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
204 changes: 201 additions & 3 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from ..xcvr_api import XcvrApi

import logging
from ...codes.public.cmis import CmisCodes
from ...codes.public.sff8024 import Sff8024
from ...fields import consts
from ..xcvr_api import XcvrApi
from .cmisCDB import CmisCdbApi
Expand Down Expand Up @@ -50,6 +52,12 @@ def get_module_type(self):
'''
return self.xcvr_eeprom.read(consts.ID_FIELD)

def get_module_type_abbreviation(self):
'''
This function returns the SFF8024Identifier (module type / form-factor). Table 4-1 in SFF-8024 Rev4.6
'''
return self.xcvr_eeprom.read(consts.ID_ABBRV_FIELD)

def get_connector_type(self):
'''
This function returns module connector. Table 4-3 in SFF-8024 Rev4.6
Expand Down Expand Up @@ -134,10 +142,10 @@ def get_transceiver_info(self):
"nominal_bit_rate": 0, # Not supported
"specification_compliance": admin_info[consts.MEDIA_TYPE_FIELD],
"vendor_date": admin_info[consts.VENDOR_DATE_FIELD],
"vendor_oui": admin_info[consts.VENDOR_OUI_FIELD],
# TODO
"application_advertisement": "N/A",
"vendor_oui": admin_info[consts.VENDOR_OUI_FIELD]
}
appl_advt = self.get_application_advertisement()
xcvr_info['application_advertisement'] = str(appl_advt) if len(appl_advt) > 0 else 'N/A'
xcvr_info['host_electrical_interface'] = self.get_host_electrical_interface()
xcvr_info['media_interface_code'] = self.get_module_media_interface()
xcvr_info['host_lane_count'] = self.get_host_lane_count()
Expand Down Expand Up @@ -876,6 +884,24 @@ def reset_module(self, reset = False):
else:
return True

def reset(self):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

"""
Reset SFP and return all user module settings to their default state.

Returns:
A boolean, True if successful, False if not
"""
if self.reset_module(True):
# minimum waiting time for the TWI to be functional again
time.sleep(2)
# buffer time
for retries in range(5):
state = self.get_module_state()
if state in ['ModuleReady', 'ModuleLowPwr']:
return True
time.sleep(1)
return False

def get_lpmode(self):
'''
Retrieves Low power module status
Expand Down Expand Up @@ -1745,4 +1771,176 @@ def get_transceiver_loopback(self):
for lane in range(1, self.NUM_CHANNELS+1):
trans_loopback['host_input_loopback_lane%d' % lane] = 'N/A'
return trans_loopback

def set_datapath_init(self, channel):
"""
Put the CMIS datapath into the initialized state

Args:
channel:
Integer, a bitmask of the lanes on the host side
e.g. 0x5 for lane 0 and lane 2.

Returns:
Boolean, true if success otherwise false
"""
cmis_major = self.xcvr_eeprom.read(consts.CMIS_MAJOR_REVISION)
data = self.xcvr_eeprom.read(consts.DATAPATH_DEINIT_FIELD)
for lane in range(self.NUM_CHANNELS):
if ((1 << lane) & channel) == 0:
continue
if cmis_major >= 4: # CMIS v4 onwards
data &= ~(1 << lane)
else: # CMIS v3
data |= (1 << lane)
self.xcvr_eeprom.write(consts.DATAPATH_DEINIT_FIELD, data)

def set_datapath_deinit(self, channel):
"""
Put the CMIS datapath into the de-initialized state

Args:
channel:
Integer, a bitmask of the lanes on the host side
e.g. 0x5 for lane 0 and lane 2.

Returns:
Boolean, true if success otherwise false
"""
cmis_major = self.xcvr_eeprom.read(consts.CMIS_MAJOR_REVISION)
data = self.xcvr_eeprom.read(consts.DATAPATH_DEINIT_FIELD)
for lane in range(self.NUM_CHANNELS):
if ((1 << lane) & channel) == 0:
continue
if cmis_major >= 4: # CMIS v4 onwards
data |= (1 << lane)
else: # CMIS v3
data &= ~(1 << lane)
self.xcvr_eeprom.write(consts.DATAPATH_DEINIT_FIELD, data)

def get_application_advertisement(self):
"""
Get the application advertisement of the CMIS transceiver

Returns:
Dictionary, the application advertisement
"""
map = {
Sff8024.MODULE_MEDIA_TYPE[1]: consts.MODULE_MEDIA_INTERFACE_850NM,
Sff8024.MODULE_MEDIA_TYPE[2]: consts.MODULE_MEDIA_INTERFACE_SM,
Sff8024.MODULE_MEDIA_TYPE[3]: consts.MODULE_MEDIA_INTERFACE_PASSIVE_COPPER,
Sff8024.MODULE_MEDIA_TYPE[4]: consts.MODULE_MEDIA_INTERFACE_ACTIVE_CABLE,
Sff8024.MODULE_MEDIA_TYPE[5]: consts.MODULE_MEDIA_INTERFACE_BASE_T
}

ret = {}
dic = self.xcvr_eeprom.read(consts.APPLS_ADVT_FIELD)
for app in range(1, 16):
buf = {}

key = "{}_{}".format(consts.HOST_ELECTRICAL_INTERFACE, app)
val = dic.get(key)
if val in [None, 'Unknown', 'Undefined']:
break
buf['host_electrical_interface_id'] = val

prefix = map.get(self.xcvr_eeprom.read(consts.MEDIA_TYPE_FIELD))
if prefix is None:
break
key = "{}_{}".format(prefix, app)
val = dic.get(key)
if val in [None, 'Unknown', 'Undefined']:
break
buf['module_media_interface_id'] = val

key = "{}_{}".format(consts.MEDIA_LANE_COUNT, app)
val = dic.get(key)
if val is None:
break
buf['media_lane_count'] = val

key = "{}_{}".format(consts.HOST_LANE_COUNT, app)
val = dic.get(key)
if val is None:
break
buf['host_lane_count'] = val

key = "{}_{}".format(consts.HOST_LANE_ASSIGNMENT_OPTION, app)
val = dic.get(key)
if val is None:
break
buf['host_lane_assignment_options'] = val

ret[app] = buf
return ret

def get_application(self, lane):
"""
Get the CMIS selected application code of a host lane

Args:
lane:
Integer, the zero-based lane id on the host side

Returns:
Integer, the transceiver-specific application code
"""
appl = 0
if lane in range(self.NUM_CHANNELS) and not self.is_flat_memory():
name = "{}_{}_{}".format(consts.STAGED_CTRL_APSEL_FIELD, 0, lane + 1)
appl = self.xcvr_eeprom.read(name) >> 4

return (appl & 0xf)

def set_application(self, channel, appl_code):
"""
Update the selected application code to the specified lanes on the host side

Args:
channel:
Integer, a bitmask of the lanes on the host side
e.g. 0x5 for lane 0 and lane 2.
appl_code:
Integer, the desired application code

Returns:
Boolean, true if success otherwise false
"""
# Update the application selection
lane_first = -1
for lane in range(self.NUM_CHANNELS):
if ((1 << lane) & channel) == 0:
continue
if lane_first < 0:
lane_first = lane
addr = "{}_{}_{}".format(consts.STAGED_CTRL_APSEL_FIELD, 0, lane + 1)
data = (appl_code << 4) | (lane_first << 1)
self.xcvr_eeprom.write(addr, data)

# Apply DataPathInit
return self.xcvr_eeprom.write("%s_%d" % (consts.STAGED_CTRL_APPLY_DPINIT_FIELD, 0), channel)

def get_error_description(self):
dp_state = self.get_datapath_state()
conf_state = self.get_config_datapath_hostlane_status()
for lane in range(self.NUM_CHANNELS):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you define a function (get_cmis_num_channels()) to get the count?. Optics like DR4 has only 4 channels. we can't assume 8 as default number of channels.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but please note these are host lanes connected to the MAC/ASIC, hence it's 8 lanes in DR4, as to QSFP+ CMIS, it's most likely 4 lanes.

e.g. Here is an example of INNOLIGHT 400G DR4, the ConfigError reside at 0x94a, while DPState are at 0x900

root@sonic:~# hexdump -C -n 256 -s 0x880 /sys/bus/i2c/devices/40-0050/eeprom
00000880  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000890  00 21 21 25 25 29 29 2d  2d ff 00 00 33 33 33 33  |.!!%%))--...3333|
000008a0  ff ff 22 22 22 22 00 00  00 00 22 22 22 22 00 00  |..""""....""""..|
000008b0  00 00 00 00 10 10 10 10  10 10 10 10 ff 00 00 33  |...............3|
000008c0  33 33 33 ff ff 22 22 22  22 00 00 00 00 22 22 22  |333..""""...."""|
000008d0  22 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |"...............|
000008e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000900  44 44 44 44 00 00 00 00  00 00 00 00 00 00 00 00  |DDDD............|
00000910  00 00 00 00 00 00 00 00  00 00 3e db 3e 51 3c ca  |..........>.>Q<.|
00000920  3d 09 00 00 00 00 00 00  00 00 9c 40 9c 40 9c 40  |=..........@.@.@|
00000930  9c 40 00 00 00 00 00 00  00 00 3a 7c 56 23 31 23  |.@........:|V#1#|
00000940  32 7e 00 00 00 00 00 00  00 00 11 11 11 11 21 21  |2~............!!|
00000950  25 25 29 29 2d 2d ff 00  00 33 33 33 33 ff ff 22  |%%))--...3333.."|
00000960  22 22 22 00 00 00 00 22  22 22 22 00 00 00 00 00  |"""...."""".....|
00000970  11 12 13 14 00 00 00 00  11 12 13 14 00 00 00 00  |................|

name = "{}_{}_{}".format(consts.STAGED_CTRL_APSEL_FIELD, 0, lane + 1)
appl = self.xcvr_eeprom.read(name)
if (appl is None) or ((appl >> 4) == 0):
continue
Comment on lines +1927 to +1930
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this api should return error state of datapath and module any time it is called. but looks like that is not the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does return the failure in the current application init sequence.


name = "DP{}State".format(lane + 1)
if dp_state[name] != CmisCodes.DATAPATH_STATE[4]:
return dp_state[name]

name = "ConfigStatusLane{}".format(lane + 1)
if conf_state[name] != CmisCodes.CONFIG_STATUS[1]:
return conf_state[name]

state = self.get_module_state()
if state != CmisCodes.MODULE_STATE[3]:
return state

return None

# TODO: other XcvrApi methods
2 changes: 1 addition & 1 deletion sonic_platform_base/sonic_xcvr/api/public/cmisCDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, xcvr_eeprom):
super(CmisCdbApi, self).__init__(xcvr_eeprom)
self.cdb_instance_supported = self.xcvr_eeprom.read(consts.CDB_SUPPORT)
self.failed_status_dict = self.xcvr_eeprom.mem_map.codes.CDB_FAIL_STATUS
assert self.cdb_instance_supported != 0
#assert self.cdb_instance_supported != 0
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This leads to a crash on my 400G DR4 optic (CMIS v4, AVAGO#AFCT-93DRPHZ-AZ2)
i.e.
admin@sonic:$ sudo sfputil show eeprom -p Ethernet0
Traceback (most recent call last):
File "/usr/local/bin/sfputil", line 8, in
sys.exit(cli())
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 764, in call
return self.main(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/lib/python3.7/dist-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/usr/local/lib/python3.7/dist-packages/sfputil/main.py", line 562, in eeprom
xcvr_info = platform_chassis.get_sfp(physical_port).get_transceiver_info()
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sonic_xcvr/sfp_optoe_base.py", line 23, in get_transceiver_info
api = self.get_xcvr_api()
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sfp_base.py", line 451, in get_xcvr_api
self.refresh_xcvr_api()
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sfp_base.py", line 441, in refresh_xcvr_api
self._xcvr_api = self._xcvr_api_factory.create_xcvr_api()
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sonic_xcvr/xcvr_api_factory.py", line 47, in create_xcvr_api
api = CmisApi(xcvr_eeprom)
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sonic_xcvr/api/public/cmis.py", line 29, in init
self.cdb = CmisCdbApi(xcvr_eeprom)
File "/usr/local/lib/python3.7/dist-packages/sonic_platform_base/sonic_xcvr/api/public/cmisCDB.py", line 29, in init
assert self.cdb_instance_supported != 0
AssertionError
admin@sonic:
$


def cdb1_chkflags(self):
'''
Expand Down
7 changes: 7 additions & 0 deletions sonic_platform_base/sonic_xcvr/fields/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@
TRANS_CONFIG_FIELD = "TransceiverConfig"
MODULE_LEVEL_CONTROL = "ModuleControl"

APPLS_ADVT_FIELD = "Applications Advertisement"
CTRLS_ADVT_FIELD = "Supported Controls Advertisement"
FLAGS_ADVT_FIELD = "Supported Flags Advertisement"
PAGE_SUPPORT_ADVT_FIELD = "Supported Pages Advertisement"
Expand All @@ -278,13 +279,19 @@
LANE_MON_ADVT_FIELD = "Supported Lane Monitor Advertisement"
LANE_DATAPATH_CTRL_FIELD = "Lane Control and Data Path Control"
LANE_DATAPATH_STATUS_FIELD = "Lane Status and Data Path Status"
DATAPATH_DEINIT_FIELD = "Data Path Deinit"
LEN_MULT_FIELD = "LengthMultiplier"
MAX_POWER_FIELD = "MaxPower"
MGMT_CHAR_FIELD = "Management Characteristics"
MGMT_CHAR_MISC_FIELD = "Management Characteristics (Misc)"

MODULE_CHAR_ADVT_FIELD = "Module Characteristics Advertising"

STAGED_CTRL_FIELD = "Staged Control Set"
STAGED_CTRL_APPLY_DPINIT_FIELD = "Staged Control Set Apply DataPathInit"
STAGED_CTRL_APPLY_IMMEDIATE_FIELD = "Staged Control Set Apply Immediate"
STAGED_CTRL_APSEL_FIELD = "Staged Control Set ApSel"

# C-CMIS

# Module configuration support fields
Expand Down
Loading