Skip to content

Commit 321e40e

Browse files
authored
Add support for rx_disable, rx_disable_channel in CMIS_API for transceivers (sonic-net#74)
<!-- Provide a general summary of your changes in the Title above --> #### Description This PR introduces **RX disable support** for CMIS-compliant transceivers, aligning it with TX disable functionality. <!-- Describe your changes in detail This PR introduces **RX disable support** for CMIS-compliant transceivers, aligning it with TX disable functionality. --> #### Motivation and Context - Added the following methods in `CmisApi` and `XcvrApi`: - `get_rx_disable()` - `get_rx_disable_channel()` - `rx_disable()` - `rx_disable_channel()` - Updated `SfpOptoeBase` to expose RX disable functionality. <!-- Why is this change required? What problem does it solve? If this pull request closes/resolves an open Issue, make sure you include the text "fixes #xxxx", "closes #xxxx" or "resolves #xxxx" here --> - Defined `RX_DISABLE_SUPPORT_FIELD` in `consts.py`. - Added `RX_DISABLE_FIELD` at **Byte 138** in `CmisMemMap`. - Updated `CTRLS_ADVT_FIELD` to include `RX_DISABLE_SUPPORT_FIELD`. - Integrated RX disable methods into `sfp_optoe_base.py`, mirroring TX disable operations. #### How Has This Been Tested? UT and deploy on testbed, please see attached testbed results <!-- Please describe in detail how you tested your changes. Include details of your testing environment, and the tests you ran to see how your change affects other areas of the code, etc. --> ``` sfp1 = platform_chassis.get_sfp(1)>>> platform_chassis = sonic_platform.platform.Platform().get_chassis() >>> sfp1 = platform_chassis.get_sfp(1) >>> sfp1.get_rx_disable() [True, False, False, False, False, False, False, False] >>> sfp1.rx_disable_channel(1, 1) True >>> sfp1.get_rx_disable() [True, False, False, False, False, False, False, False] >>> sfp1.rx_disable_channel(1, 0) True >>> sfp1.get_rx_disable() [False, False, False, False, False, False, False, False] >>> sfp1.rx_disable_channel(2, 0) True >>> sfp1.rx_disable_channel(2, 1) True >>> sfp1.get_rx_disable() [False, True, False, False, False, False, False, False] >>> sfp1.rx_disable_channel(2, 0) True >>> sfp1.get_rx_disable() [False, False, False, False, False, False, False, False] >>> ``` #### Additional Information (Optional)
1 parent a193da4 commit 321e40e

File tree

7 files changed

+226
-1
lines changed

7 files changed

+226
-1
lines changed

sonic_platform_base/sonic_xcvr/api/public/cmis.py

+46
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,52 @@ def tx_disable_channel(self, channel, disable):
869869

870870
return self.xcvr_eeprom.write(consts.TX_DISABLE_FIELD, channel_state)
871871

872+
def get_rx_disable_support(self):
873+
return not self.is_flat_memory() and self.xcvr_eeprom.read(consts.RX_DISABLE_SUPPORT_FIELD)
874+
875+
def get_rx_disable(self):
876+
rx_disable_support = self.get_rx_disable_support()
877+
if rx_disable_support is None:
878+
return None
879+
if not rx_disable_support:
880+
return ["N/A" for _ in range(self.NUM_CHANNELS)]
881+
rx_disable = self.xcvr_eeprom.read(consts.RX_DISABLE_FIELD)
882+
if rx_disable is None:
883+
return None
884+
return [bool(rx_disable & (1 << i)) for i in range(self.NUM_CHANNELS)]
885+
886+
def rx_disable(self, rx_disable):
887+
val = 0xFF if rx_disable else 0x0
888+
return self.xcvr_eeprom.write(consts.RX_DISABLE_FIELD, val)
889+
890+
def get_rx_disable_channel(self):
891+
rx_disable_support = self.get_rx_disable_support()
892+
if rx_disable_support is None:
893+
return None
894+
if not rx_disable_support:
895+
return 'N/A'
896+
return self.xcvr_eeprom.read(consts.RX_DISABLE_FIELD)
897+
898+
def rx_disable_channel(self, channel, disable):
899+
# Check if channel state is available
900+
channel_state = self.get_rx_disable_channel()
901+
if channel_state is None or channel_state == 'N/A':
902+
return False
903+
904+
# Disable or enable the specific channel based on the input
905+
if 1 <= channel <= self.NUM_CHANNELS:
906+
# The channel is represented by individual RegBitField entries
907+
field_name = f"{consts.RX_DISABLE_FIELD}_{channel}"
908+
909+
if disable:
910+
# Disable the channel (set the bit to 1)
911+
return self.xcvr_eeprom.write(field_name, 1)
912+
else:
913+
# Enable the channel (set the bit to 0)
914+
return self.xcvr_eeprom.write(field_name, 0)
915+
916+
return False
917+
872918
def get_laser_tuning_summary(self):
873919
'''
874920
This function returns laser tuning status summary on media lane

sonic_platform_base/sonic_xcvr/api/xcvr_api.py

+60
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,38 @@ def get_model(self):
2828
"""
2929
raise NotImplementedError
3030

31+
def get_rx_disable(self):
32+
"""
33+
Retrieves the rx_disable status of this xcvr.
34+
35+
Returns:
36+
A list of boolean values, representing the RX disable status
37+
of each available channel. The value is True if the xcvr channel
38+
is RX disabled, False if not.
39+
40+
E.g., for a transceiver with four channels: [False, False, True, False]
41+
42+
If RX disable status is unsupported on the xcvr, each list element should be "N/A" instead.
43+
44+
If there is an issue with reading the xcvr, None should be returned.
45+
"""
46+
raise NotImplementedError
47+
48+
def get_rx_disable_channel(self):
49+
"""
50+
Retrieves the RX disabled channels in this xcvr.
51+
52+
Returns:
53+
A hex of 4 bits (bit 0 to bit 3 as channel 0 to channel 3) to represent
54+
RX channels which have been disabled in this xcvr.
55+
As an example, a returned value of 0x5 indicates that channel 0
56+
and channel 2 have been disabled.
57+
58+
If there is an issue with reading the xcvr, None should be returned.
59+
"""
60+
raise NotImplementedError
61+
62+
3163
def get_serial(self):
3264
"""
3365
Retrieves the serial number of the xcvr
@@ -397,6 +429,34 @@ def get_tx_disable_channel(self):
397429
"""
398430
raise NotImplementedError
399431

432+
def rx_disable(self, rx_disable):
433+
"""
434+
Disable xcvr RX for all channels
435+
436+
Args:
437+
rx_disable : A Boolean, True to enable rx_disable mode, False to disable
438+
rx_disable mode.
439+
440+
Returns:
441+
A boolean, True if rx_disable is set successfully, False if not
442+
"""
443+
raise NotImplementedError
444+
445+
def rx_disable_channel(self, channel, disable):
446+
"""
447+
Sets the rx_disable for specified xcvr channels
448+
449+
Args:
450+
channel : A hex of 4 bits (bit 0 to bit 3) which represent channel 0 to 3,
451+
e.g. 0x5 for channel 0 and channel 2.
452+
disable : A boolean, True to disable RX channels specified in channel,
453+
False to enable
454+
455+
Returns:
456+
A boolean, True if successful, False if not
457+
"""
458+
raise NotImplementedError
459+
400460
def get_module_temperature(self):
401461
"""
402462
Retrieves the temperature of this xcvr

sonic_platform_base/sonic_xcvr/fields/consts.py

+3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
RX_POWER_HIGH_WARNING_FIELD = "RxPowerHighWarning"
3333
RX_POWER_LOW_WARNING_FIELD = "RxPowerLowWarning"
3434

35+
RX_DISABLE_FIELD = "RxDisable"
36+
RX_DISABLE_SUPPORT_FIELD = "RxDisableSupported"
37+
3538
TEMPERATURE_FIELD = "Temperature"
3639
TEMP_SUPPORT_FIELD = "Temperature Monitoring Implemented"
3740
TEMP_THRESHOLDS_FIELD = "TempThresholds"

sonic_platform_base/sonic_xcvr/mem_maps/public/cmis.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ def __init__(self, codes):
215215
RegBitField(consts.TX_DISABLE_SUPPORT_FIELD, 1),
216216
size=2, format="<H"
217217
),
218+
NumberRegField(consts.CTRLS_ADVT_FIELD, self.getaddr(0x1, 156),
219+
RegBitField(consts.RX_DISABLE_SUPPORT_FIELD, 1),
220+
size=2, format="<H"
221+
),
218222
NumberRegField(consts.TX_FLAGS_ADVT_FIELD, self.getaddr(0x1, 157),
219223
RegBitField(consts.TX_FAULT_SUPPORT_FIELD, 0),
220224
RegBitField(consts.TX_LOS_SUPPORT_FIELD, 1),
@@ -272,7 +276,11 @@ def __init__(self, codes):
272276

273277
self.LANE_DATAPATH_CTRL = RegGroupField(consts.LANE_DATAPATH_CTRL_FIELD,
274278
NumberRegField(consts.DATAPATH_DEINIT_FIELD, self.getaddr(0x10, 128), ro=False),
275-
NumberRegField(consts.TX_DISABLE_FIELD, self.getaddr(0x10, 130), ro=False)
279+
NumberRegField(consts.TX_DISABLE_FIELD, self.getaddr(0x10, 130), ro=False),
280+
NumberRegField(consts.RX_DISABLE_FIELD, self.getaddr(0x10, 138), ro=False,
281+
*(RegBitField("%s_%d" % (consts.RX_DISABLE_FIELD, channel), bitpos, ro=False)
282+
for channel, bitpos in zip(range(1, 9), range(0, 8))) # 8 channels
283+
)
276284
)
277285

278286
self.LANE_DATAPATH_STATUS = RegGroupField(consts.LANE_DATAPATH_STATUS_FIELD,

sonic_platform_base/sonic_xcvr/sfp_optoe_base.py

+17
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ def get_tx_fault(self):
160160
return tx_fault
161161
return None
162162

163+
def get_rx_disable(self):
164+
api = self.get_xcvr_api()
165+
return api.get_rx_disable() if api is not None else None
166+
167+
def get_rx_disable_channel(self):
168+
api = self.get_xcvr_api()
169+
return api.get_rx_disable_channel() if api is not None else None
170+
163171
def get_tx_disable(self):
164172
api = self.get_xcvr_api()
165173
return api.get_tx_disable() if api is not None else None
@@ -224,6 +232,15 @@ def tx_disable_channel(self, channel, disable):
224232
api = self.get_xcvr_api()
225233
return api.tx_disable_channel(channel, disable) if api is not None else None
226234

235+
def rx_disable(self, rx_disable):
236+
api = self.get_xcvr_api()
237+
return api.rx_disable(rx_disable) if api is not None else None
238+
239+
def rx_disable_channel(self, channel, disable):
240+
api = self.get_xcvr_api()
241+
return api.rx_disable_channel(channel, disable) if api is not None else None
242+
243+
227244
def get_power_override(self):
228245
api = self.get_xcvr_api()
229246
return api.get_power_override() if api is not None else None

tests/sonic_xcvr/test_cmis.py

+75
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,81 @@ def test_tx_disable_channel(self, mock_response, input_param):
575575
self.api.get_tx_disable_channel.return_value = mock_response
576576
self.api.tx_disable_channel(*input_param)
577577

578+
@pytest.mark.parametrize("mock_response, expected", [
579+
([True, {'RxLOS1': 0}], [False]),
580+
([False, {'RxLOS1': 0}], ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A']),
581+
([None, None], None),
582+
([True, None], None)
583+
])
584+
def test_get_rx_los(self, mock_response, expected):
585+
self.api.get_rx_los_support = MagicMock()
586+
self.api.get_rx_los_support.return_value = mock_response[0]
587+
self.api.xcvr_eeprom.read = MagicMock()
588+
self.api.xcvr_eeprom.read.return_value = mock_response[1]
589+
result = self.api.get_rx_los()
590+
assert result == expected
591+
592+
@pytest.mark.parametrize("mock_response, expected", [
593+
([False, True], True)
594+
])
595+
def test_get_rx_disable_support(self, mock_response, expected):
596+
self.api.is_flat_memory = MagicMock()
597+
self.api.is_flat_memory.return_value = mock_response[0]
598+
self.api.xcvr_eeprom.read = MagicMock()
599+
self.api.xcvr_eeprom.read.return_value = mock_response[1]
600+
result = self.api.get_rx_disable_support()
601+
assert result == expected
602+
603+
@pytest.mark.parametrize("mock_response, expected", [
604+
([True, 0x00], [False, False, False, False, False, False, False, False]),
605+
([False, 0x00], ['N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A', 'N/A']),
606+
([None, None], None),
607+
([True, None], None)
608+
])
609+
def test_get_rx_disable(self, mock_response, expected):
610+
self.api.get_rx_disable_support = MagicMock()
611+
self.api.get_rx_disable_support.return_value = mock_response[0]
612+
self.api.xcvr_eeprom.read = MagicMock()
613+
self.api.xcvr_eeprom.read.return_value = mock_response[1]
614+
result = self.api.get_rx_disable()
615+
assert result == expected
616+
617+
@pytest.mark.parametrize("input_param", [
618+
(True), (False)
619+
])
620+
def test_rx_disable(self, input_param):
621+
rc = self.api.rx_disable(input_param)
622+
assert(rc != None)
623+
624+
@pytest.mark.parametrize("mock_response, expected", [
625+
([True, 0x00], 0),
626+
([False, 0x00], 'N/A'),
627+
([None, None], None)
628+
])
629+
def test_get_rx_disable_channel(self, mock_response, expected):
630+
self.api.get_rx_disable_support = MagicMock()
631+
self.api.get_rx_disable_support.return_value = mock_response[0]
632+
self.api.xcvr_eeprom.read = MagicMock()
633+
self.api.xcvr_eeprom.read.return_value = mock_response[1]
634+
result = self.api.get_rx_disable_channel()
635+
assert result == expected
636+
637+
@pytest.mark.parametrize("mock_response, input_param", [
638+
(0, (0xff, True)),
639+
(0, (0, True)),
640+
(0, (0, False)),
641+
(0, (1, False)),
642+
(0, (1, True)),
643+
(None, (0, False))
644+
])
645+
def test_rx_disable_channel(self, mock_response, input_param):
646+
self.api.get_rx_disable_channel = MagicMock()
647+
self.api.get_rx_disable_channel.return_value = mock_response
648+
self.api.xcvr_eeprom.write = MagicMock()
649+
self.api.xcvr_eeprom.write.return_value = mock_response
650+
rc = self.api.rx_disable_channel(*input_param)
651+
assert(rc != None)
652+
578653
@pytest.mark.parametrize("mock_response, expected", [
579654
(1, ['TuningComplete']),
580655
(62, ['TargetOutputPowerOOR', 'FineTuningOutOfRange', 'TuningNotAccepted',

tests/sonic_xcvr/test_sfp_optoe_base.py

+16
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ def test_unfreeze_vdm_stats(self, mock_response1, mock_response2, expected):
6060
result = self.sfp_optoe_api.unfreeze_vdm_stats()
6161
assert result == expected
6262

63+
@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
64+
(0, cmis_api, 0),
65+
(1, cmis_api, 1),
66+
(None, None, None),
67+
(False, cmis_api, False),
68+
])
69+
def test_get_rx_disable_channel(self, mock_response1, mock_response2, expected):
70+
self.sfp_optoe_api.get_xcvr_api = MagicMock()
71+
self.sfp_optoe_api.get_xcvr_api.return_value = mock_response2
72+
self.cmis_api.get_rx_disable_channel = MagicMock()
73+
self.cmis_api.get_rx_disable_channel.return_value = mock_response1
74+
75+
result = self.sfp_optoe_api.get_rx_disable_channel()
76+
assert result == expected
77+
78+
6379
@pytest.mark.parametrize("mock_response1, mock_response2, expected", [
6480
(0, cmis_api, 0),
6581
(1, cmis_api, 1),

0 commit comments

Comments
 (0)