Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sfp-refactoring] Initial support for CMIS application initialization #219

Merged
merged 9 commits into from
Dec 7, 2021
20 changes: 12 additions & 8 deletions sonic_platform_base/sonic_xcvr/api/public/cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -892,10 +892,14 @@ def reset(self):
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):
time.sleep(1)
if self.get_module_state() != 'Unknown':
state = self.get_module_state()
if state in ['ModuleReady']:
Copy link
Collaborator

Choose a reason for hiding this comment

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

module won't transition to 'ModuleReady' state from 'ModuleLowPwr' unless LowPwrS is FALSE so this check will fail on those cases where LowPwrS is TRUE. so we should expect the module to be in one of the steady state i.e either ModuleReady or ModuleLowPwr

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The trick is, the reset_module() always clear LowPwrS, but yes, I could improve the reset() accordingly

return True
time.sleep(1)
return False

def get_lpmode(self):
Expand Down Expand Up @@ -1916,15 +1920,15 @@ def set_application(self, channel, appl_code):
# Apply DataPathInit
return self.xcvr_eeprom.write("%s_%d" % (consts.STAGED_CTRL_APPLY_DPINIT_FIELD, 0), channel)

def get_num_channels(self):
if self.get_module_type_abbreviation() == 'QSFP+C':
return 4
return self.NUM_CHANNELS

def get_error_description(self):
dp_state = self.get_datapath_state()
conf_state = self.get_config_datapath_hostlane_status()
for lane in range(self.get_num_channels()):
for lane in range(self.NUM_CHANNELS):
Copy link
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
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
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
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]
Expand Down
149 changes: 146 additions & 3 deletions tests/sonic_xcvr/test_cmis.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from sonic_platform_base.sonic_xcvr.mem_maps.public.cmis import CmisMemMap
from sonic_platform_base.sonic_xcvr.xcvr_eeprom import XcvrEeprom
from sonic_platform_base.sonic_xcvr.codes.public.cmis import CmisCodes
from sonic_platform_base.sonic_xcvr.fields.consts import LENGTH_ASSEMBLY_FIELD, LEN_MULT_FIELD
from sonic_platform_base.sonic_xcvr.codes.public.sff8024 import Sff8024
from sonic_platform_base.sonic_xcvr.fields import consts

class TestCmis(object):
codes = CmisCodes
Expand Down Expand Up @@ -825,6 +826,17 @@ def test_get_supported_power_config(self, mock_response, expected):
def test_reset_module(self):
self.api.reset_module(True)

def test_reset(self):
self.api.xcvr_eeprom.write = MagicMock()
self.api.get_module_state = MagicMock()
self.api.get_module_state.return_value = 'ModuleReady'
result = self.api.reset()
assert result
assert self.api.xcvr_eeprom.write.call_count == 1
kall = self.api.xcvr_eeprom.write.call_args
assert kall is not None
assert kall[0] == (consts.MODULE_LEVEL_CONTROL, 0x8)

def test_set_low_power(self):
self.api.is_flat_memory = MagicMock()
self.api.is_flat_memory.return_value = False
Expand Down Expand Up @@ -1886,9 +1898,140 @@ def test_get_transceiver_loopback(self, mock_response, expected):
assert result == expected

def test_cable_len(self):
cable_len_field = self.mem_map.get_field(LENGTH_ASSEMBLY_FIELD)
cable_len_field = self.mem_map.get_field(consts.LENGTH_ASSEMBLY_FIELD)
data = bytearray([0xFF])
dep = {LEN_MULT_FIELD: 0b11}
dep = {consts.LEN_MULT_FIELD: 0b11}
decoded = cable_len_field.decode(data, **dep)
assert decoded == 6300

def test_set_datapath_init(self):
self.api.xcvr_eeprom.write = MagicMock()
self.api.xcvr_eeprom.read = MagicMock()

self.api.xcvr_eeprom.read.side_effect = [0x3, 0x00]
self.api.set_datapath_init(0xff)
kall = self.api.xcvr_eeprom.write.call_args
assert kall is not None
assert kall[0] == (consts.DATAPATH_DEINIT_FIELD, 0xff)

self.api.xcvr_eeprom.read.side_effect = [0x4, 0x00]
self.api.set_datapath_init(0xff)
kall = self.api.xcvr_eeprom.write.call_args
assert kall is not None
assert kall[0] == (consts.DATAPATH_DEINIT_FIELD, 0x00)

def test_set_datapath_deinit(self):
self.api.xcvr_eeprom.write = MagicMock()
self.api.xcvr_eeprom.read = MagicMock()

self.api.xcvr_eeprom.read.side_effect = [0x3, 0x00]
self.api.set_datapath_deinit(0xff)
kall = self.api.xcvr_eeprom.write.call_args
assert kall is not None
assert kall[0] == (consts.DATAPATH_DEINIT_FIELD, 0x00)

self.api.xcvr_eeprom.read.side_effect = [0x4, 0x00]
self.api.set_datapath_deinit(0xff)
kall = self.api.xcvr_eeprom.write.call_args
assert kall is not None
assert kall[0] == (consts.DATAPATH_DEINIT_FIELD, 0xff)

def test_get_application_advertisement(self):
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.side_effect = [
{
consts.HOST_ELECTRICAL_INTERFACE + "_1": "400GAUI-8 C2M (Annex 120E)",
consts.MODULE_MEDIA_INTERFACE_SM + "_1": "400GBASE-DR4 (Cl 124)",
consts.MEDIA_LANE_COUNT + "_1": 4,
consts.HOST_LANE_COUNT + "_1": 8,
consts.HOST_LANE_ASSIGNMENT_OPTION + "_1": 0x01
},
Sff8024.MODULE_MEDIA_TYPE[2]
]
result = self.api.get_application_advertisement()

assert len(result) == 1
assert result[1]['host_electrical_interface_id'] == '400GAUI-8 C2M (Annex 120E)'
assert result[1]['module_media_interface_id'] == '400GBASE-DR4 (Cl 124)'
assert result[1]['host_lane_count'] == 8
assert result[1]['media_lane_count'] == 4
assert result[1]['host_lane_assignment_options'] == 0x01

def test_get_application(self):
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.return_value = 0x20

self.api.is_flat_memory = MagicMock()
self.api.is_flat_memory.return_value = False
appl = self.api.get_application(0)
assert appl == 2

appl = self.api.get_application(2)
assert appl == 2

appl = self.api.get_application(self.api.NUM_CHANNELS)
assert appl == 0

self.api.is_flat_memory.return_value = True
appl = self.api.get_application(0)
assert appl == 0

appl = self.api.get_application(2)
assert appl == 0

appl = self.api.get_application(self.api.NUM_CHANNELS)
assert appl == 0

def test_set_application(self):
self.api.xcvr_eeprom.write = MagicMock()

self.api.xcvr_eeprom.write.call_count = 0
self.api.set_application(0x00, 1)
assert self.api.xcvr_eeprom.write.call_count == 1

self.api.xcvr_eeprom.write.call_count = 0
self.api.set_application(0x01, 1)
assert self.api.xcvr_eeprom.write.call_count == 1 + 1

self.api.xcvr_eeprom.write.call_count = 0
self.api.set_application(0x0f, 1)
assert self.api.xcvr_eeprom.write.call_count == 4 + 1

self.api.xcvr_eeprom.write.call_count = 0
self.api.set_application(0xff, 1)
assert self.api.xcvr_eeprom.write.call_count == 8 + 1

self.api.xcvr_eeprom.write.call_count = 0
self.api.set_application(0x7fffffff, 1)
assert self.api.xcvr_eeprom.write.call_count == self.api.NUM_CHANNELS + 1

def test_get_error_description(self):
self.api.get_module_state = MagicMock()
self.api.get_module_state.return_value = 'ModuleReady'
self.api.get_datapath_state = MagicMock()
self.api.get_datapath_state.return_value = {
'DP1State': 'DataPathActivated',
'DP2State': 'DataPathActivated',
'DP3State': 'DataPathActivated',
'DP4State': 'DataPathActivated',
'DP5State': 'DataPathActivated',
'DP6State': 'DataPathActivated',
'DP7State': 'DataPathActivated',
'DP8State': 'DataPathActivated'
}
self.api.get_config_datapath_hostlane_status = MagicMock()
self.api.get_config_datapath_hostlane_status.return_value = {
'ConfigStatusLane1': 'ConfigSuccess',
'ConfigStatusLane2': 'ConfigSuccess',
'ConfigStatusLane3': 'ConfigSuccess',
'ConfigStatusLane4': 'ConfigSuccess',
'ConfigStatusLane5': 'ConfigSuccess',
'ConfigStatusLane6': 'ConfigSuccess',
'ConfigStatusLane7': 'ConfigSuccess',
'ConfigStatusLane8': 'ConfigSuccess'
}
self.api.xcvr_eeprom.read = MagicMock()
self.api.xcvr_eeprom.read.return_value = 0x10

result = self.api.get_error_description()
assert result is None